update tui with new mutations
This commit is contained in:
1 parent
f21d099ae4
commit
8c07b4059f
13 files changed
+335
-330
No files matched your search
@@ -493,7 +493,7 @@ impl<'a> StateMember<'a> {
|
|||||||
let mask = self
|
let mask = self
|
||||||
.dep_mems
|
.dep_mems
|
||||||
.iter()
|
.iter()
|
||||||
.map(|m| 1 << m.id)
|
.map(|m| 1u64 << m.id)
|
||||||
.fold(1 << self.mem.id, |a, b| a | b);
|
.fold(1 << self.mem.id, |a, b| a | b);
|
||||||
quote! {
|
quote! {
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ dioxus-core = { path = "../core", version = "^0.2.1" }
|
|||||||
dioxus-html = { path = "../html", version = "^0.2.1" }
|
dioxus-html = { path = "../html", version = "^0.2.1" }
|
||||||
dioxus-core-macro = { path = "../core-macro", version = "^0.2.1" }
|
dioxus-core-macro = { path = "../core-macro", version = "^0.2.1" }
|
||||||
|
|
||||||
taffy = "0.2.0"
|
taffy = "0.2.1"
|
||||||
smallvec = "1.6"
|
smallvec = "1.6"
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
anymap = "0.12.1"
|
anymap = "0.12.1"
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ pub mod utils;
|
|||||||
|
|
||||||
/// A id for a node that lives in the real dom.
|
/// A id for a node that lives in the real dom.
|
||||||
pub type RealNodeId = NodeId;
|
pub type RealNodeId = NodeId;
|
||||||
pub(crate) type FxDashMap<K, V> = dashmap::DashMap<K, V, BuildHasherDefault<FxHasher>>;
|
pub type FxDashMap<K, V> = dashmap::DashMap<K, V, BuildHasherDefault<FxHasher>>;
|
||||||
pub(crate) type FxDashSet<K> = dashmap::DashSet<K, BuildHasherDefault<FxHasher>>;
|
pub type FxDashSet<K> = dashmap::DashSet<K, BuildHasherDefault<FxHasher>>;
|
||||||
pub type SendAnyMap = anymap::Map<dyn anymap::any::Any + Send + Sync + 'static>;
|
pub type SendAnyMap = anymap::Map<dyn anymap::any::Any + Send + Sync + 'static>;
|
||||||
|
|
||||||
/// Used in derived state macros
|
/// Used in derived state macros
|
||||||
|
|||||||
@@ -151,184 +151,180 @@ impl<S: State> RealDom<S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the dom with some mutations and return a set of nodes that were updated. Pass the dirty nodes to update_state.
|
/// Updates the dom with some mutations and return a set of nodes that were updated. Pass the dirty nodes to update_state.
|
||||||
pub fn apply_mutations(&mut self, mutations_vec: Vec<Mutations>) -> DirtyNodeStates {
|
pub fn apply_mutations(&mut self, mutations: Mutations) -> DirtyNodeStates {
|
||||||
let mut nodes_updated: FxHashMap<RealNodeId, NodeMask> = FxHashMap::default();
|
let mut nodes_updated: FxHashMap<RealNodeId, NodeMask> = FxHashMap::default();
|
||||||
for mutations in mutations_vec {
|
for template in mutations.templates {
|
||||||
for template in mutations.templates {
|
let mut template_root_ids = Vec::new();
|
||||||
let mut template_root_ids = Vec::new();
|
for root in template.roots {
|
||||||
for root in template.roots {
|
if let Some(id) = self.create_template_node(root, &mut nodes_updated) {
|
||||||
if let Some(id) = self.create_template_node(root, &mut nodes_updated) {
|
template_root_ids.push(id);
|
||||||
template_root_ids.push(id);
|
}
|
||||||
|
}
|
||||||
|
self.templates
|
||||||
|
.insert(template.name.to_string(), template_root_ids);
|
||||||
|
}
|
||||||
|
for e in mutations.edits {
|
||||||
|
use dioxus_core::Mutation::*;
|
||||||
|
match e {
|
||||||
|
AppendChildren { id, m } => {
|
||||||
|
let children = self.stack.split_off(m);
|
||||||
|
let parent = self.element_to_node_id(id);
|
||||||
|
for child in children {
|
||||||
|
self.add_child(parent, child);
|
||||||
|
mark_dirty(parent, NodeMask::ALL, &mut nodes_updated);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.templates
|
AssignId { path, id } => {
|
||||||
.insert(template.name.to_string(), template_root_ids);
|
self.set_element_id(self.load_child(path), id);
|
||||||
}
|
}
|
||||||
for e in mutations.edits {
|
CreatePlaceholder { id } => {
|
||||||
use dioxus_core::Mutation::*;
|
let node = Node::new(NodeType::Placeholder);
|
||||||
match e {
|
let node_id = self.create_node(node);
|
||||||
AppendChildren { id, m } => {
|
self.set_element_id(node_id, id);
|
||||||
let children = self.stack.split_off(m);
|
self.stack.push(node_id);
|
||||||
let parent = self.element_to_node_id(id);
|
mark_dirty(node_id, NodeMask::ALL, &mut nodes_updated);
|
||||||
for child in children {
|
}
|
||||||
self.add_child(parent, child);
|
CreateTextNode { value, id } => {
|
||||||
mark_dirty(parent, NodeMask::ALL, &mut nodes_updated);
|
let node = Node::new(NodeType::Text {
|
||||||
}
|
text: value.to_string(),
|
||||||
|
});
|
||||||
|
let node_id = self.create_node(node);
|
||||||
|
let node = self.tree.get_mut(node_id).unwrap();
|
||||||
|
node.node_data.element_id = Some(id);
|
||||||
|
self.stack.push(node_id);
|
||||||
|
mark_dirty(node_id, NodeMask::new().with_text(), &mut nodes_updated);
|
||||||
|
}
|
||||||
|
HydrateText { path, value, id } => {
|
||||||
|
let node_id = self.load_child(path);
|
||||||
|
self.set_element_id(node_id, id);
|
||||||
|
let node = self.tree.get_mut(node_id).unwrap();
|
||||||
|
if let NodeType::Text { text } = &mut node.node_data.node_type {
|
||||||
|
*text = value.to_string();
|
||||||
}
|
}
|
||||||
AssignId { path, id } => {
|
mark_dirty(node_id, NodeMask::new().with_text(), &mut nodes_updated);
|
||||||
self.set_element_id(self.load_child(path), id);
|
}
|
||||||
|
LoadTemplate { name, index, id } => {
|
||||||
|
let template_id = self.templates[name][index];
|
||||||
|
let clone_id = self.clone_node(template_id, &mut nodes_updated);
|
||||||
|
self.set_element_id(clone_id, id);
|
||||||
|
}
|
||||||
|
ReplaceWith { id, m } => {
|
||||||
|
let new_nodes = self.stack.split_off(m);
|
||||||
|
let old_node_id = self.element_to_node_id(id);
|
||||||
|
for new in new_nodes {
|
||||||
|
self.tree.insert_after(old_node_id, new);
|
||||||
|
mark_dirty(new, NodeMask::ALL, &mut nodes_updated);
|
||||||
}
|
}
|
||||||
CreatePlaceholder { id } => {
|
self.tree.remove(old_node_id);
|
||||||
let node = Node::new(NodeType::Placeholder);
|
}
|
||||||
let node_id = self.create_node(node);
|
ReplacePlaceholder { path, m } => {
|
||||||
self.set_element_id(node_id, id);
|
let new_nodes = self.stack.split_off(m);
|
||||||
self.stack.push(node_id);
|
let old_node_id = self.load_child(path);
|
||||||
mark_dirty(node_id, NodeMask::ALL, &mut nodes_updated);
|
for new in new_nodes {
|
||||||
|
self.tree.insert_after(old_node_id, new);
|
||||||
|
mark_dirty(new, NodeMask::ALL, &mut nodes_updated);
|
||||||
}
|
}
|
||||||
CreateTextNode { value, id } => {
|
self.tree.remove(old_node_id);
|
||||||
let node = Node::new(NodeType::Text {
|
}
|
||||||
text: value.to_string(),
|
InsertAfter { id, m } => {
|
||||||
});
|
let new_nodes = self.stack.split_off(m);
|
||||||
let node_id = self.create_node(node);
|
let old_node_id = self.element_to_node_id(id);
|
||||||
let node = self.tree.get_mut(node_id).unwrap();
|
for new in new_nodes {
|
||||||
node.node_data.element_id = Some(id);
|
self.tree.insert_after(old_node_id, new);
|
||||||
self.stack.push(node_id);
|
mark_dirty(new, NodeMask::ALL, &mut nodes_updated);
|
||||||
mark_dirty(node_id, NodeMask::new().with_text(), &mut nodes_updated);
|
|
||||||
}
|
}
|
||||||
HydrateText { path, value, id } => {
|
}
|
||||||
let node_id = self.load_child(path);
|
InsertBefore { id, m } => {
|
||||||
self.set_element_id(node_id, id);
|
let new_nodes = self.stack.split_off(m);
|
||||||
let node = self.tree.get_mut(node_id).unwrap();
|
let old_node_id = self.element_to_node_id(id);
|
||||||
if let NodeType::Text { text } = &mut node.node_data.node_type {
|
for new in new_nodes {
|
||||||
*text = value.to_string();
|
self.tree.insert_before(old_node_id, new);
|
||||||
}
|
mark_dirty(new, NodeMask::ALL, &mut nodes_updated);
|
||||||
mark_dirty(node_id, NodeMask::new().with_text(), &mut nodes_updated);
|
|
||||||
}
|
}
|
||||||
LoadTemplate { name, index, id } => {
|
}
|
||||||
let template_id = self.templates[name][index];
|
SetAttribute {
|
||||||
let clone_id = self.clone_node(template_id, &mut nodes_updated);
|
name,
|
||||||
self.set_element_id(clone_id, id);
|
value,
|
||||||
|
id,
|
||||||
|
ns,
|
||||||
|
} => {
|
||||||
|
let node_id = self.element_to_node_id(id);
|
||||||
|
let node = self.tree.get_mut(node_id).unwrap();
|
||||||
|
if let NodeType::Element { attributes, .. } = &mut node.node_data.node_type {
|
||||||
|
attributes.insert(
|
||||||
|
OwnedAttributeDiscription {
|
||||||
|
name: name.to_string(),
|
||||||
|
namespace: ns.map(|s| s.to_string()),
|
||||||
|
volatile: false,
|
||||||
|
},
|
||||||
|
crate::node::OwnedAttributeValue::Text(value.to_string()),
|
||||||
|
);
|
||||||
|
mark_dirty(
|
||||||
|
node_id,
|
||||||
|
NodeMask::new_with_attrs(AttributeMask::single(name)),
|
||||||
|
&mut nodes_updated,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
ReplaceWith { id, m } => {
|
}
|
||||||
let new_nodes = self.stack.split_off(m);
|
SetBoolAttribute { name, value, id } => {
|
||||||
let old_node_id = self.element_to_node_id(id);
|
let node_id = self.element_to_node_id(id);
|
||||||
for new in new_nodes {
|
let node = self.tree.get_mut(node_id).unwrap();
|
||||||
self.tree.insert_after(old_node_id, new);
|
if let NodeType::Element { attributes, .. } = &mut node.node_data.node_type {
|
||||||
mark_dirty(new, NodeMask::ALL, &mut nodes_updated);
|
attributes.insert(
|
||||||
}
|
OwnedAttributeDiscription {
|
||||||
self.tree.remove(old_node_id);
|
name: name.to_string(),
|
||||||
|
namespace: None,
|
||||||
|
volatile: false,
|
||||||
|
},
|
||||||
|
crate::node::OwnedAttributeValue::Bool(value),
|
||||||
|
);
|
||||||
|
mark_dirty(
|
||||||
|
node_id,
|
||||||
|
NodeMask::new_with_attrs(AttributeMask::single(name)),
|
||||||
|
&mut nodes_updated,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
ReplacePlaceholder { path, m } => {
|
}
|
||||||
let new_nodes = self.stack.split_off(m);
|
SetText { value, id } => {
|
||||||
let old_node_id = self.load_child(path);
|
let node_id = self.element_to_node_id(id);
|
||||||
for new in new_nodes {
|
let node = self.tree.get_mut(node_id).unwrap();
|
||||||
self.tree.insert_after(old_node_id, new);
|
if let NodeType::Text { text } = &mut node.node_data.node_type {
|
||||||
mark_dirty(new, NodeMask::ALL, &mut nodes_updated);
|
*text = value.to_string();
|
||||||
}
|
|
||||||
self.tree.remove(old_node_id);
|
|
||||||
}
|
}
|
||||||
InsertAfter { id, m } => {
|
mark_dirty(node_id, NodeMask::new().with_text(), &mut nodes_updated);
|
||||||
let new_nodes = self.stack.split_off(m);
|
}
|
||||||
let old_node_id = self.element_to_node_id(id);
|
NewEventListener { name, scope: _, id } => {
|
||||||
for new in new_nodes {
|
let node_id = self.element_to_node_id(id);
|
||||||
self.tree.insert_after(old_node_id, new);
|
let node = self.tree.get_mut(node_id).unwrap();
|
||||||
mark_dirty(new, NodeMask::ALL, &mut nodes_updated);
|
if let NodeType::Element { listeners, .. } = &mut node.node_data.node_type {
|
||||||
}
|
match self.nodes_listening.get_mut(name) {
|
||||||
}
|
Some(hs) => {
|
||||||
InsertBefore { id, m } => {
|
hs.insert(node_id);
|
||||||
let new_nodes = self.stack.split_off(m);
|
}
|
||||||
let old_node_id = self.element_to_node_id(id);
|
None => {
|
||||||
for new in new_nodes {
|
let mut hs = FxHashSet::default();
|
||||||
self.tree.insert_before(old_node_id, new);
|
hs.insert(node_id);
|
||||||
mark_dirty(new, NodeMask::ALL, &mut nodes_updated);
|
self.nodes_listening.insert(name.to_string(), hs);
|
||||||
}
|
|
||||||
}
|
|
||||||
SetAttribute {
|
|
||||||
name,
|
|
||||||
value,
|
|
||||||
id,
|
|
||||||
ns,
|
|
||||||
} => {
|
|
||||||
let node_id = self.element_to_node_id(id);
|
|
||||||
let node = self.tree.get_mut(node_id).unwrap();
|
|
||||||
if let NodeType::Element { attributes, .. } = &mut node.node_data.node_type
|
|
||||||
{
|
|
||||||
attributes.insert(
|
|
||||||
OwnedAttributeDiscription {
|
|
||||||
name: name.to_string(),
|
|
||||||
namespace: ns.map(|s| s.to_string()),
|
|
||||||
volatile: false,
|
|
||||||
},
|
|
||||||
crate::node::OwnedAttributeValue::Text(value.to_string()),
|
|
||||||
);
|
|
||||||
mark_dirty(
|
|
||||||
node_id,
|
|
||||||
NodeMask::new_with_attrs(AttributeMask::single(name)),
|
|
||||||
&mut nodes_updated,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SetBoolAttribute { name, value, id } => {
|
|
||||||
let node_id = self.element_to_node_id(id);
|
|
||||||
let node = self.tree.get_mut(node_id).unwrap();
|
|
||||||
if let NodeType::Element { attributes, .. } = &mut node.node_data.node_type
|
|
||||||
{
|
|
||||||
attributes.insert(
|
|
||||||
OwnedAttributeDiscription {
|
|
||||||
name: name.to_string(),
|
|
||||||
namespace: None,
|
|
||||||
volatile: false,
|
|
||||||
},
|
|
||||||
crate::node::OwnedAttributeValue::Bool(value),
|
|
||||||
);
|
|
||||||
mark_dirty(
|
|
||||||
node_id,
|
|
||||||
NodeMask::new_with_attrs(AttributeMask::single(name)),
|
|
||||||
&mut nodes_updated,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SetText { value, id } => {
|
|
||||||
let node_id = self.element_to_node_id(id);
|
|
||||||
let node = self.tree.get_mut(node_id).unwrap();
|
|
||||||
if let NodeType::Text { text } = &mut node.node_data.node_type {
|
|
||||||
*text = value.to_string();
|
|
||||||
}
|
|
||||||
mark_dirty(node_id, NodeMask::new().with_text(), &mut nodes_updated);
|
|
||||||
}
|
|
||||||
NewEventListener { name, scope: _, id } => {
|
|
||||||
let node_id = self.element_to_node_id(id);
|
|
||||||
let node = self.tree.get_mut(node_id).unwrap();
|
|
||||||
if let NodeType::Element { listeners, .. } = &mut node.node_data.node_type {
|
|
||||||
match self.nodes_listening.get_mut(name) {
|
|
||||||
Some(hs) => {
|
|
||||||
hs.insert(node_id);
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
let mut hs = FxHashSet::default();
|
|
||||||
hs.insert(node_id);
|
|
||||||
self.nodes_listening.insert(name.to_string(), hs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
listeners.insert(name.to_string());
|
|
||||||
}
|
}
|
||||||
|
listeners.insert(name.to_string());
|
||||||
}
|
}
|
||||||
RemoveEventListener { id, name } => {
|
}
|
||||||
let node_id = self.element_to_node_id(id);
|
RemoveEventListener { id, name } => {
|
||||||
let node = self.tree.get_mut(node_id).unwrap();
|
let node_id = self.element_to_node_id(id);
|
||||||
if let NodeType::Element { listeners, .. } = &mut node.node_data.node_type {
|
let node = self.tree.get_mut(node_id).unwrap();
|
||||||
listeners.remove(name);
|
if let NodeType::Element { listeners, .. } = &mut node.node_data.node_type {
|
||||||
}
|
listeners.remove(name);
|
||||||
self.nodes_listening.get_mut(name).unwrap().remove(&node_id);
|
|
||||||
}
|
|
||||||
Remove { id } => {
|
|
||||||
let node_id = self.element_to_node_id(id);
|
|
||||||
self.tree.remove(node_id);
|
|
||||||
}
|
|
||||||
PushRoot { id } => {
|
|
||||||
let node_id = self.element_to_node_id(id);
|
|
||||||
self.stack.push(node_id);
|
|
||||||
}
|
}
|
||||||
|
self.nodes_listening.get_mut(name).unwrap().remove(&node_id);
|
||||||
|
}
|
||||||
|
Remove { id } => {
|
||||||
|
let node_id = self.element_to_node_id(id);
|
||||||
|
self.tree.remove(node_id);
|
||||||
|
}
|
||||||
|
PushRoot { id } => {
|
||||||
|
let node_id = self.element_to_node_id(id);
|
||||||
|
self.stack.push(node_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,10 +23,11 @@ crossterm = "0.23.0"
|
|||||||
anyhow = "1.0.42"
|
anyhow = "1.0.42"
|
||||||
tokio = { version = "1.15.0", features = ["full"] }
|
tokio = { version = "1.15.0", features = ["full"] }
|
||||||
futures = "0.3.19"
|
futures = "0.3.19"
|
||||||
taffy = "0.1.0"
|
taffy = "0.2.1"
|
||||||
smallvec = "1.6"
|
smallvec = "1.6"
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
anymap = "0.12.1"
|
anymap = "0.12.1"
|
||||||
|
futures-channel = "0.3.25"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
dioxus = { path = "../dioxus" }
|
dioxus = { path = "../dioxus" }
|
||||||
|
|||||||
+15
-14
@@ -1,6 +1,7 @@
|
|||||||
use crate::{node::PreventDefault, Dom};
|
use crate::{node::PreventDefault, TuiDom};
|
||||||
|
|
||||||
use dioxus_native_core::{
|
use dioxus_native_core::{
|
||||||
|
tree::TreeView,
|
||||||
utils::{ElementProduced, PersistantElementIter},
|
utils::{ElementProduced, PersistantElementIter},
|
||||||
RealNodeId,
|
RealNodeId,
|
||||||
};
|
};
|
||||||
@@ -10,7 +11,6 @@ use std::{cmp::Ordering, num::NonZeroU16};
|
|||||||
|
|
||||||
use dioxus_native_core::{
|
use dioxus_native_core::{
|
||||||
node_ref::{AttributeMask, NodeMask, NodeView},
|
node_ref::{AttributeMask, NodeMask, NodeView},
|
||||||
real_dom::NodeType,
|
|
||||||
state::NodeDepState,
|
state::NodeDepState,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -64,7 +64,8 @@ pub(crate) struct Focus {
|
|||||||
pub level: FocusLevel,
|
pub level: FocusLevel,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NodeDepState<()> for Focus {
|
impl NodeDepState for Focus {
|
||||||
|
type DepState = ();
|
||||||
type Ctx = ();
|
type Ctx = ();
|
||||||
const NODE_MASK: NodeMask =
|
const NODE_MASK: NodeMask =
|
||||||
NodeMask::new_with_attrs(AttributeMask::Static(FOCUS_ATTRIBUTES)).with_listeners();
|
NodeMask::new_with_attrs(AttributeMask::Static(FOCUS_ATTRIBUTES)).with_listeners();
|
||||||
@@ -77,7 +78,7 @@ impl NodeDepState<()> for Focus {
|
|||||||
{
|
{
|
||||||
if let Some(index) = a
|
if let Some(index) = a
|
||||||
.value
|
.value
|
||||||
.as_int32()
|
.as_int()
|
||||||
.or_else(|| a.value.as_text().and_then(|v| v.parse::<i32>().ok()))
|
.or_else(|| a.value.as_text().and_then(|v| v.parse::<i32>().ok()))
|
||||||
{
|
{
|
||||||
match index.cmp(&0) {
|
match index.cmp(&0) {
|
||||||
@@ -126,7 +127,7 @@ pub(crate) struct FocusState {
|
|||||||
|
|
||||||
impl FocusState {
|
impl FocusState {
|
||||||
/// Returns true if the focus has changed.
|
/// Returns true if the focus has changed.
|
||||||
pub fn progress(&mut self, rdom: &mut Dom, forward: bool) -> bool {
|
pub fn progress(&mut self, rdom: &mut TuiDom, forward: bool) -> bool {
|
||||||
if let Some(last) = self.last_focused_id {
|
if let Some(last) = self.last_focused_id {
|
||||||
if rdom[last].state.prevent_default == PreventDefault::KeyDown {
|
if rdom[last].state.prevent_default == PreventDefault::KeyDown {
|
||||||
return false;
|
return false;
|
||||||
@@ -234,10 +235,10 @@ impl FocusState {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn prune(&mut self, mutations: &dioxus_core::Mutations, rdom: &Dom) {
|
pub(crate) fn prune(&mut self, mutations: &dioxus_core::Mutations, rdom: &TuiDom) {
|
||||||
fn remove_children(
|
fn remove_children(
|
||||||
to_prune: &mut [&mut Option<RealNodeId>],
|
to_prune: &mut [&mut Option<RealNodeId>],
|
||||||
rdom: &Dom,
|
rdom: &TuiDom,
|
||||||
removed: RealNodeId,
|
removed: RealNodeId,
|
||||||
) {
|
) {
|
||||||
for opt in to_prune.iter_mut() {
|
for opt in to_prune.iter_mut() {
|
||||||
@@ -247,8 +248,8 @@ impl FocusState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let NodeType::Element { children, .. } = &rdom[removed].node_data.node_type {
|
if let Some(children) = &rdom.children_ids(removed) {
|
||||||
for child in children {
|
for child in *children {
|
||||||
remove_children(to_prune, rdom, *child);
|
remove_children(to_prune, rdom, *child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -258,22 +259,22 @@ impl FocusState {
|
|||||||
}
|
}
|
||||||
for m in &mutations.edits {
|
for m in &mutations.edits {
|
||||||
match m {
|
match m {
|
||||||
dioxus_core::DomEdit::ReplaceWith { root, .. } => remove_children(
|
dioxus_core::Mutation::ReplaceWith { id, .. } => remove_children(
|
||||||
&mut [&mut self.last_focused_id],
|
&mut [&mut self.last_focused_id],
|
||||||
rdom,
|
rdom,
|
||||||
rdom.resolve_maybe_id(*root),
|
rdom.element_to_node_id(*id),
|
||||||
),
|
),
|
||||||
dioxus_core::DomEdit::Remove { root } => remove_children(
|
dioxus_core::Mutation::Remove { id } => remove_children(
|
||||||
&mut [&mut self.last_focused_id],
|
&mut [&mut self.last_focused_id],
|
||||||
rdom,
|
rdom,
|
||||||
rdom.resolve_maybe_id(*root),
|
rdom.element_to_node_id(*id),
|
||||||
),
|
),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_focus(&mut self, rdom: &mut Dom, id: RealNodeId) {
|
pub(crate) fn set_focus(&mut self, rdom: &mut TuiDom, id: RealNodeId) {
|
||||||
if let Some(old) = self.last_focused_id.replace(id) {
|
if let Some(old) = self.last_focused_id.replace(id) {
|
||||||
rdom[old].state.focused = false;
|
rdom[old].state.focused = false;
|
||||||
}
|
}
|
||||||
|
|||||||
+88
-81
@@ -2,7 +2,8 @@ use crossterm::event::{
|
|||||||
Event as TermEvent, KeyCode as TermKeyCode, KeyModifiers, MouseButton, MouseEventKind,
|
Event as TermEvent, KeyCode as TermKeyCode, KeyModifiers, MouseButton, MouseEventKind,
|
||||||
};
|
};
|
||||||
use dioxus_core::*;
|
use dioxus_core::*;
|
||||||
use dioxus_native_core::RealNodeId;
|
use dioxus_native_core::tree::TreeView;
|
||||||
|
use dioxus_native_core::NodeId;
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
|
|
||||||
use dioxus_html::geometry::euclid::{Point2D, Rect, Size2D};
|
use dioxus_html::geometry::euclid::{Point2D, Rect, Size2D};
|
||||||
@@ -12,19 +13,25 @@ use dioxus_html::geometry::{
|
|||||||
use dioxus_html::input_data::keyboard_types::{Code, Key, Location, Modifiers};
|
use dioxus_html::input_data::keyboard_types::{Code, Key, Location, Modifiers};
|
||||||
use dioxus_html::input_data::MouseButtonSet as DioxusMouseButtons;
|
use dioxus_html::input_data::MouseButtonSet as DioxusMouseButtons;
|
||||||
use dioxus_html::input_data::{MouseButton as DioxusMouseButton, MouseButtonSet};
|
use dioxus_html::input_data::{MouseButton as DioxusMouseButton, MouseButtonSet};
|
||||||
use dioxus_html::{event_bubbles, on::*};
|
use dioxus_html::{event_bubbles, FocusData, KeyboardData, MouseData, WheelData};
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
any::Any,
|
||||||
cell::{RefCell, RefMut},
|
cell::{RefCell, RefMut},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::Arc,
|
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
use taffy::geometry::{Point, Size};
|
use taffy::geometry::{Point, Size};
|
||||||
use taffy::{prelude::Layout, Taffy};
|
use taffy::{prelude::Layout, Taffy};
|
||||||
|
|
||||||
use crate::FocusState;
|
use crate::FocusState;
|
||||||
use crate::{Dom, Node};
|
use crate::{TuiDom, TuiNode};
|
||||||
|
|
||||||
|
pub(crate) struct Event {
|
||||||
|
pub id: ElementId,
|
||||||
|
pub name: &'static str,
|
||||||
|
pub data: Rc<dyn Any>,
|
||||||
|
pub bubbles: bool,
|
||||||
|
}
|
||||||
|
|
||||||
// a wrapper around the input state for easier access
|
// a wrapper around the input state for easier access
|
||||||
// todo: fix loop
|
// todo: fix loop
|
||||||
@@ -71,12 +78,12 @@ enum EventData {
|
|||||||
Keyboard(KeyboardData),
|
Keyboard(KeyboardData),
|
||||||
}
|
}
|
||||||
impl EventData {
|
impl EventData {
|
||||||
fn into_any(self) -> Arc<dyn Any + Send + Sync> {
|
fn into_any(self) -> Rc<dyn Any + Send + Sync> {
|
||||||
match self {
|
match self {
|
||||||
Self::Mouse(m) => Arc::new(m),
|
Self::Mouse(m) => Rc::new(m),
|
||||||
Self::Wheel(w) => Arc::new(w),
|
Self::Wheel(w) => Rc::new(w),
|
||||||
Self::Screen(s) => Arc::new(s),
|
Self::Screen(s) => Rc::new(s),
|
||||||
Self::Keyboard(k) => Arc::new(k),
|
Self::Keyboard(k) => Rc::new(k),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -164,9 +171,9 @@ impl InnerInputState {
|
|||||||
fn update(
|
fn update(
|
||||||
&mut self,
|
&mut self,
|
||||||
evts: &mut Vec<EventCore>,
|
evts: &mut Vec<EventCore>,
|
||||||
resolved_events: &mut Vec<UserEvent>,
|
resolved_events: &mut Vec<Event>,
|
||||||
layout: &Taffy,
|
layout: &Taffy,
|
||||||
dom: &mut Dom,
|
dom: &mut TuiDom,
|
||||||
) {
|
) {
|
||||||
let previous_mouse = self.mouse.clone();
|
let previous_mouse = self.mouse.clone();
|
||||||
|
|
||||||
@@ -192,33 +199,33 @@ impl InnerInputState {
|
|||||||
|
|
||||||
if old_focus != self.focus_state.last_focused_id {
|
if old_focus != self.focus_state.last_focused_id {
|
||||||
// elements with listeners will always have a element id
|
// elements with listeners will always have a element id
|
||||||
if let Some(RealNodeId::ElementId(id)) = self.focus_state.last_focused_id {
|
if let Some(id) = self.focus_state.last_focused_id {
|
||||||
resolved_events.push(UserEvent {
|
let element = dom.tree.get(id).unwrap();
|
||||||
scope_id: None,
|
if let Some(id) = element.node_data.element_id {
|
||||||
priority: EventPriority::Medium,
|
resolved_events.push(Event {
|
||||||
name: "focus",
|
name: "focus",
|
||||||
element: Some(id),
|
id,
|
||||||
data: Arc::new(FocusData {}),
|
data: Rc::new(FocusData {}),
|
||||||
bubbles: event_bubbles("focus"),
|
bubbles: event_bubbles("focus"),
|
||||||
});
|
});
|
||||||
resolved_events.push(UserEvent {
|
resolved_events.push(Event {
|
||||||
scope_id: None,
|
name: "focusin",
|
||||||
priority: EventPriority::Medium,
|
id,
|
||||||
name: "focusin",
|
data: Rc::new(FocusData {}),
|
||||||
element: Some(id),
|
bubbles: event_bubbles("focusin"),
|
||||||
data: Arc::new(FocusData {}),
|
});
|
||||||
bubbles: event_bubbles("focusin"),
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
if let Some(RealNodeId::ElementId(id)) = old_focus {
|
if let Some(id) = old_focus {
|
||||||
resolved_events.push(UserEvent {
|
let element = dom.tree.get(id).unwrap();
|
||||||
scope_id: None,
|
if let Some(id) = element.node_data.element_id {
|
||||||
priority: EventPriority::Medium,
|
resolved_events.push(Event {
|
||||||
name: "focusout",
|
name: "focusout",
|
||||||
element: Some(id),
|
id,
|
||||||
data: Arc::new(FocusData {}),
|
data: Rc::new(FocusData {}),
|
||||||
bubbles: event_bubbles("focusout"),
|
bubbles: event_bubbles("focusout"),
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,9 +237,9 @@ impl InnerInputState {
|
|||||||
fn resolve_mouse_events(
|
fn resolve_mouse_events(
|
||||||
&mut self,
|
&mut self,
|
||||||
previous_mouse: Option<MouseData>,
|
previous_mouse: Option<MouseData>,
|
||||||
resolved_events: &mut Vec<UserEvent>,
|
resolved_events: &mut Vec<Event>,
|
||||||
layout: &Taffy,
|
layout: &Taffy,
|
||||||
dom: &mut Dom,
|
dom: &mut TuiDom,
|
||||||
) {
|
) {
|
||||||
fn layout_contains_point(layout: &Layout, point: ScreenPoint) -> bool {
|
fn layout_contains_point(layout: &Layout, point: ScreenPoint) -> bool {
|
||||||
let Point { x, y } = layout.location;
|
let Point { x, y } = layout.location;
|
||||||
@@ -244,28 +251,29 @@ impl InnerInputState {
|
|||||||
|
|
||||||
fn try_create_event(
|
fn try_create_event(
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
data: Arc<dyn Any + Send + Sync>,
|
data: Rc<dyn Any>,
|
||||||
will_bubble: &mut FxHashSet<RealNodeId>,
|
will_bubble: &mut FxHashSet<NodeId>,
|
||||||
resolved_events: &mut Vec<UserEvent>,
|
resolved_events: &mut Vec<Event>,
|
||||||
node: &Node,
|
node: &TuiNode,
|
||||||
dom: &Dom,
|
dom: &TuiDom,
|
||||||
) {
|
) {
|
||||||
// only trigger event if the event was not triggered already by a child
|
// only trigger event if the event was not triggered already by a child
|
||||||
let id = node.mounted_id();
|
let id = node.node_data.node_id;
|
||||||
if will_bubble.insert(id) {
|
if will_bubble.insert(id) {
|
||||||
let mut parent = node.node_data.parent;
|
let mut parent = dom.parent(id);
|
||||||
while let Some(parent_id) = parent {
|
while let Some(current_parent) = parent {
|
||||||
|
let parent_id = current_parent.node_data.node_id;
|
||||||
will_bubble.insert(parent_id);
|
will_bubble.insert(parent_id);
|
||||||
parent = dom[parent_id].node_data.parent;
|
parent = dom.parent(parent_id);
|
||||||
|
}
|
||||||
|
if let Some(id) = node.mounted_id() {
|
||||||
|
resolved_events.push(Event {
|
||||||
|
name,
|
||||||
|
id,
|
||||||
|
data,
|
||||||
|
bubbles: event_bubbles(name),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
resolved_events.push(UserEvent {
|
|
||||||
scope_id: None,
|
|
||||||
priority: EventPriority::Medium,
|
|
||||||
name,
|
|
||||||
element: Some(id.as_element_id()),
|
|
||||||
data,
|
|
||||||
bubbles: event_bubbles(name),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,7 +334,7 @@ impl InnerInputState {
|
|||||||
if currently_contains && previously_contained {
|
if currently_contains && previously_contained {
|
||||||
try_create_event(
|
try_create_event(
|
||||||
"mousemove",
|
"mousemove",
|
||||||
Arc::new(prepare_mouse_data(mouse_data, &node_layout)),
|
Rc::new(prepare_mouse_data(mouse_data, &node_layout)),
|
||||||
&mut will_bubble,
|
&mut will_bubble,
|
||||||
resolved_events,
|
resolved_events,
|
||||||
node,
|
node,
|
||||||
@@ -350,7 +358,7 @@ impl InnerInputState {
|
|||||||
if currently_contains && !previously_contained {
|
if currently_contains && !previously_contained {
|
||||||
try_create_event(
|
try_create_event(
|
||||||
"mouseenter",
|
"mouseenter",
|
||||||
Arc::new(mouse_data.clone()),
|
Rc::new(mouse_data.clone()),
|
||||||
&mut will_bubble,
|
&mut will_bubble,
|
||||||
resolved_events,
|
resolved_events,
|
||||||
node,
|
node,
|
||||||
@@ -373,7 +381,7 @@ impl InnerInputState {
|
|||||||
if currently_contains && !previously_contained {
|
if currently_contains && !previously_contained {
|
||||||
try_create_event(
|
try_create_event(
|
||||||
"mouseover",
|
"mouseover",
|
||||||
Arc::new(prepare_mouse_data(mouse_data, &node_layout)),
|
Rc::new(prepare_mouse_data(mouse_data, &node_layout)),
|
||||||
&mut will_bubble,
|
&mut will_bubble,
|
||||||
resolved_events,
|
resolved_events,
|
||||||
node,
|
node,
|
||||||
@@ -393,7 +401,7 @@ impl InnerInputState {
|
|||||||
if currently_contains {
|
if currently_contains {
|
||||||
try_create_event(
|
try_create_event(
|
||||||
"mousedown",
|
"mousedown",
|
||||||
Arc::new(prepare_mouse_data(mouse_data, &node_layout)),
|
Rc::new(prepare_mouse_data(mouse_data, &node_layout)),
|
||||||
&mut will_bubble,
|
&mut will_bubble,
|
||||||
resolved_events,
|
resolved_events,
|
||||||
node,
|
node,
|
||||||
@@ -414,7 +422,7 @@ impl InnerInputState {
|
|||||||
if currently_contains {
|
if currently_contains {
|
||||||
try_create_event(
|
try_create_event(
|
||||||
"mouseup",
|
"mouseup",
|
||||||
Arc::new(prepare_mouse_data(mouse_data, &node_layout)),
|
Rc::new(prepare_mouse_data(mouse_data, &node_layout)),
|
||||||
&mut will_bubble,
|
&mut will_bubble,
|
||||||
resolved_events,
|
resolved_events,
|
||||||
node,
|
node,
|
||||||
@@ -436,7 +444,7 @@ impl InnerInputState {
|
|||||||
if currently_contains {
|
if currently_contains {
|
||||||
try_create_event(
|
try_create_event(
|
||||||
"click",
|
"click",
|
||||||
Arc::new(prepare_mouse_data(mouse_data, &node_layout)),
|
Rc::new(prepare_mouse_data(mouse_data, &node_layout)),
|
||||||
&mut will_bubble,
|
&mut will_bubble,
|
||||||
resolved_events,
|
resolved_events,
|
||||||
node,
|
node,
|
||||||
@@ -459,7 +467,7 @@ impl InnerInputState {
|
|||||||
if currently_contains {
|
if currently_contains {
|
||||||
try_create_event(
|
try_create_event(
|
||||||
"contextmenu",
|
"contextmenu",
|
||||||
Arc::new(prepare_mouse_data(mouse_data, &node_layout)),
|
Rc::new(prepare_mouse_data(mouse_data, &node_layout)),
|
||||||
&mut will_bubble,
|
&mut will_bubble,
|
||||||
resolved_events,
|
resolved_events,
|
||||||
node,
|
node,
|
||||||
@@ -483,7 +491,7 @@ impl InnerInputState {
|
|||||||
if currently_contains {
|
if currently_contains {
|
||||||
try_create_event(
|
try_create_event(
|
||||||
"wheel",
|
"wheel",
|
||||||
Arc::new(w.clone()),
|
Rc::new(w.clone()),
|
||||||
&mut will_bubble,
|
&mut will_bubble,
|
||||||
resolved_events,
|
resolved_events,
|
||||||
node,
|
node,
|
||||||
@@ -508,7 +516,7 @@ impl InnerInputState {
|
|||||||
if !currently_contains && previously_contained {
|
if !currently_contains && previously_contained {
|
||||||
try_create_event(
|
try_create_event(
|
||||||
"mouseleave",
|
"mouseleave",
|
||||||
Arc::new(prepare_mouse_data(mouse_data, &node_layout)),
|
Rc::new(prepare_mouse_data(mouse_data, &node_layout)),
|
||||||
&mut will_bubble,
|
&mut will_bubble,
|
||||||
resolved_events,
|
resolved_events,
|
||||||
node,
|
node,
|
||||||
@@ -531,7 +539,7 @@ impl InnerInputState {
|
|||||||
if !currently_contains && previously_contained {
|
if !currently_contains && previously_contained {
|
||||||
try_create_event(
|
try_create_event(
|
||||||
"mouseout",
|
"mouseout",
|
||||||
Arc::new(prepare_mouse_data(mouse_data, &node_layout)),
|
Rc::new(prepare_mouse_data(mouse_data, &node_layout)),
|
||||||
&mut will_bubble,
|
&mut will_bubble,
|
||||||
resolved_events,
|
resolved_events,
|
||||||
node,
|
node,
|
||||||
@@ -549,7 +557,7 @@ impl InnerInputState {
|
|||||||
let currently_contains = layout_contains_point(node_layout, new_pos);
|
let currently_contains = layout_contains_point(node_layout, new_pos);
|
||||||
|
|
||||||
if currently_contains && node.state.focus.level.focusable() {
|
if currently_contains && node.state.focus.level.focusable() {
|
||||||
focus_id = Some(node.mounted_id());
|
focus_id = Some(node.node_data.node_id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if let Some(id) = focus_id {
|
if let Some(id) = focus_id {
|
||||||
@@ -564,12 +572,11 @@ impl InnerInputState {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_abs_layout(node: &Node, dom: &Dom, taffy: &Taffy) -> Layout {
|
fn get_abs_layout(node: &TuiNode, dom: &TuiDom, taffy: &Taffy) -> Layout {
|
||||||
let mut node_layout = *taffy.layout(node.state.layout.node.unwrap()).unwrap();
|
let mut node_layout = *taffy.layout(node.state.layout.node.unwrap()).unwrap();
|
||||||
let mut current = node;
|
let mut current = node;
|
||||||
|
|
||||||
while let Some(parent_id) = current.node_data.parent {
|
while let Some(parent) = dom.parent(current.node_data.node_id) {
|
||||||
let parent = &dom[parent_id];
|
|
||||||
current = parent;
|
current = parent;
|
||||||
let parent_layout = taffy.layout(parent.state.layout.node.unwrap()).unwrap();
|
let parent_layout = taffy.layout(parent.state.layout.node.unwrap()).unwrap();
|
||||||
node_layout.location.x += parent_layout.location.x;
|
node_layout.location.x += parent_layout.location.x;
|
||||||
@@ -614,11 +621,11 @@ impl RinkInputHandler {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn prune(&self, mutations: &dioxus_core::Mutations, rdom: &Dom) {
|
pub(crate) fn prune(&self, mutations: &dioxus_core::Mutations, rdom: &TuiDom) {
|
||||||
self.state.borrow_mut().focus_state.prune(mutations, rdom);
|
self.state.borrow_mut().focus_state.prune(mutations, rdom);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_events(&self, layout: &Taffy, dom: &mut Dom) -> Vec<UserEvent> {
|
pub(crate) fn get_events(&self, layout: &Taffy, dom: &mut TuiDom) -> Vec<Event> {
|
||||||
let mut resolved_events = Vec::new();
|
let mut resolved_events = Vec::new();
|
||||||
|
|
||||||
(*self.state).borrow_mut().update(
|
(*self.state).borrow_mut().update(
|
||||||
@@ -651,7 +658,7 @@ impl RinkInputHandler {
|
|||||||
})
|
})
|
||||||
.map(|evt| (evt.0, evt.1.into_any()));
|
.map(|evt| (evt.0, evt.1.into_any()));
|
||||||
|
|
||||||
let mut hm: FxHashMap<&'static str, Vec<Arc<dyn Any + Send + Sync>>> = FxHashMap::default();
|
let mut hm: FxHashMap<&'static str, Vec<Rc<dyn Any + Send + Sync>>> = FxHashMap::default();
|
||||||
for (event, data) in events {
|
for (event, data) in events {
|
||||||
if let Some(v) = hm.get_mut(event) {
|
if let Some(v) = hm.get_mut(event) {
|
||||||
v.push(data);
|
v.push(data);
|
||||||
@@ -663,14 +670,14 @@ impl RinkInputHandler {
|
|||||||
for node in dom.get_listening_sorted(event) {
|
for node in dom.get_listening_sorted(event) {
|
||||||
for data in &datas {
|
for data in &datas {
|
||||||
if node.state.focused {
|
if node.state.focused {
|
||||||
resolved_events.push(UserEvent {
|
if let Some(id) = node.mounted_id() {
|
||||||
scope_id: None,
|
resolved_events.push(Event {
|
||||||
priority: EventPriority::Medium,
|
name: event,
|
||||||
name: event,
|
id,
|
||||||
element: Some(node.mounted_id().as_element_id()),
|
data: data.clone(),
|
||||||
data: data.clone(),
|
bubbles: event_bubbles(event),
|
||||||
bubbles: event_bubbles(event),
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-12
@@ -1,12 +1,10 @@
|
|||||||
use std::cell::RefCell;
|
use std::sync::{Arc, Mutex};
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use dioxus_core::*;
|
use dioxus_core::*;
|
||||||
use dioxus_native_core::layout_attributes::apply_layout_attributes;
|
use dioxus_native_core::layout_attributes::apply_layout_attributes;
|
||||||
|
use dioxus_native_core::node::OwnedAttributeView;
|
||||||
use dioxus_native_core::node_ref::{AttributeMask, NodeMask, NodeView};
|
use dioxus_native_core::node_ref::{AttributeMask, NodeMask, NodeView};
|
||||||
use dioxus_native_core::real_dom::OwnedAttributeView;
|
|
||||||
use dioxus_native_core::state::ChildDepState;
|
use dioxus_native_core::state::ChildDepState;
|
||||||
use dioxus_native_core::RealNodeId;
|
|
||||||
use dioxus_native_core_macro::sorted_str_slice;
|
use dioxus_native_core_macro::sorted_str_slice;
|
||||||
use taffy::prelude::*;
|
use taffy::prelude::*;
|
||||||
|
|
||||||
@@ -42,8 +40,8 @@ pub(crate) struct TaffyLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ChildDepState for TaffyLayout {
|
impl ChildDepState for TaffyLayout {
|
||||||
type Ctx = Rc<RefCell<Taffy>>;
|
type Ctx = Arc<Mutex<Taffy>>;
|
||||||
type DepState = Self;
|
type DepState = (Self,);
|
||||||
// use tag to force this to be called when a node is built
|
// use tag to force this to be called when a node is built
|
||||||
const NODE_MASK: NodeMask =
|
const NODE_MASK: NodeMask =
|
||||||
NodeMask::new_with_attrs(AttributeMask::Static(SORTED_LAYOUT_ATTRS))
|
NodeMask::new_with_attrs(AttributeMask::Static(SORTED_LAYOUT_ATTRS))
|
||||||
@@ -54,14 +52,14 @@ impl ChildDepState for TaffyLayout {
|
|||||||
fn reduce<'a>(
|
fn reduce<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
node: NodeView,
|
node: NodeView,
|
||||||
children: impl Iterator<Item = &'a Self::DepState>,
|
children: impl Iterator<Item = (&'a Self,)>,
|
||||||
ctx: &Self::Ctx,
|
ctx: &Self::Ctx,
|
||||||
) -> bool
|
) -> bool
|
||||||
where
|
where
|
||||||
Self::DepState: 'a,
|
Self::DepState: 'a,
|
||||||
{
|
{
|
||||||
let mut changed = false;
|
let mut changed = false;
|
||||||
let mut taffy = ctx.borrow_mut();
|
let mut taffy = ctx.lock().expect("poisoned taffy");
|
||||||
let mut style = Style::default();
|
let mut style = Style::default();
|
||||||
if let Some(text) = node.text() {
|
if let Some(text) = node.text() {
|
||||||
let char_len = text.chars().count();
|
let char_len = text.chars().count();
|
||||||
@@ -81,7 +79,7 @@ impl ChildDepState for TaffyLayout {
|
|||||||
taffy.set_style(n, style).unwrap();
|
taffy.set_style(n, style).unwrap();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.node = PossiblyUninitalized::Initialized(taffy.new_node(style, &[]).unwrap());
|
self.node = PossiblyUninitalized::Initialized(taffy.new_leaf(style).unwrap());
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -101,14 +99,14 @@ impl ChildDepState for TaffyLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// the root node fills the entire area
|
// the root node fills the entire area
|
||||||
if node.id() == RealNodeId::ElementId(ElementId(0)) {
|
if node.id() == Some(ElementId(0)) {
|
||||||
apply_layout_attributes("width", "100%", &mut style);
|
apply_layout_attributes("width", "100%", &mut style);
|
||||||
apply_layout_attributes("height", "100%", &mut style);
|
apply_layout_attributes("height", "100%", &mut style);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set all direct nodes as our children
|
// Set all direct nodes as our children
|
||||||
let mut child_layout = vec![];
|
let mut child_layout = vec![];
|
||||||
for l in children {
|
for (l,) in children {
|
||||||
child_layout.push(l.node.unwrap());
|
child_layout.push(l.node.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +119,7 @@ impl ChildDepState for TaffyLayout {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.node = PossiblyUninitalized::Initialized(
|
self.node = PossiblyUninitalized::Initialized(
|
||||||
taffy.new_node(style, &child_layout).unwrap(),
|
taffy.new_with_children(style, &child_layout).unwrap(),
|
||||||
);
|
);
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-33
@@ -1,24 +1,23 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use anymap::AnyMap;
|
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
event::{DisableMouseCapture, EnableMouseCapture, Event as TermEvent, KeyCode, KeyModifiers},
|
event::{DisableMouseCapture, EnableMouseCapture, Event as TermEvent, KeyCode, KeyModifiers},
|
||||||
execute,
|
execute,
|
||||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||||
};
|
};
|
||||||
use dioxus_core::exports::futures_channel::mpsc::unbounded;
|
use futures_channel::mpsc::unbounded;
|
||||||
use dioxus_core::*;
|
use dioxus_core::*;
|
||||||
use dioxus_native_core::{real_dom::RealDom, RealNodeId};
|
use dioxus_native_core::{real_dom::RealDom, NodeId, SendAnyMap, FxDashSet};
|
||||||
use focus::FocusState;
|
use focus::FocusState;
|
||||||
use futures::{
|
use futures::{
|
||||||
channel::mpsc::{UnboundedReceiver, UnboundedSender},
|
channel::mpsc::{UnboundedReceiver, UnboundedSender},
|
||||||
pin_mut, StreamExt,
|
pin_mut, StreamExt,
|
||||||
};
|
};
|
||||||
use query::Query;
|
use query::Query;
|
||||||
use std::cell::RefCell;
|
use std::{cell::RefCell, sync::{Mutex, Arc}};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::{io, time::Duration};
|
use std::{io, time::Duration};
|
||||||
use taffy::Taffy;
|
use taffy::Taffy;
|
||||||
pub use taffy::{geometry::Point, prelude::Size};
|
pub use taffy::{geometry::Point, prelude::*};
|
||||||
use tui::{backend::CrosstermBackend, layout::Rect, Terminal};
|
use tui::{backend::CrosstermBackend, layout::Rect, Terminal};
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
@@ -80,10 +79,10 @@ pub fn launch_cfg(app: Component<()>, cfg: Config) {
|
|||||||
|
|
||||||
let cx = dom.base_scope();
|
let cx = dom.base_scope();
|
||||||
let rdom = Rc::new(RefCell::new(RealDom::new()));
|
let rdom = Rc::new(RefCell::new(RealDom::new()));
|
||||||
let taffy = Rc::new(RefCell::new(Taffy::new()));
|
let taffy = Arc::new(Mutex::new(Taffy::new()));
|
||||||
cx.provide_root_context(state);
|
cx.provide_context(state);
|
||||||
cx.provide_root_context(TuiContext { tx: event_tx_clone });
|
cx.provide_context(TuiContext { tx: event_tx_clone });
|
||||||
cx.provide_root_context(Query {
|
cx.provide_context(Query {
|
||||||
rdom: rdom.clone(),
|
rdom: rdom.clone(),
|
||||||
stretch: taffy.clone(),
|
stretch: taffy.clone(),
|
||||||
});
|
});
|
||||||
@@ -91,8 +90,8 @@ pub fn launch_cfg(app: Component<()>, cfg: Config) {
|
|||||||
{
|
{
|
||||||
let mut rdom = rdom.borrow_mut();
|
let mut rdom = rdom.borrow_mut();
|
||||||
let mutations = dom.rebuild();
|
let mutations = dom.rebuild();
|
||||||
let to_update = rdom.apply_mutations(vec![mutations]);
|
let to_update = rdom.apply_mutations(mutations);
|
||||||
let mut any_map = AnyMap::new();
|
let mut any_map = SendAnyMap::new();
|
||||||
any_map.insert(taffy.clone());
|
any_map.insert(taffy.clone());
|
||||||
let _to_rerender = rdom.update_state(to_update, any_map);
|
let _to_rerender = rdom.update_state(to_update, any_map);
|
||||||
}
|
}
|
||||||
@@ -114,8 +113,8 @@ fn render_vdom(
|
|||||||
mut event_reciever: UnboundedReceiver<InputEvent>,
|
mut event_reciever: UnboundedReceiver<InputEvent>,
|
||||||
handler: RinkInputHandler,
|
handler: RinkInputHandler,
|
||||||
cfg: Config,
|
cfg: Config,
|
||||||
rdom: Rc<RefCell<Dom>>,
|
rdom: Rc<RefCell<TuiDom>>,
|
||||||
taffy: Rc<RefCell<Taffy>>,
|
taffy: Arc<Mutex<Taffy>>,
|
||||||
mut register_event: impl FnMut(crossterm::event::Event),
|
mut register_event: impl FnMut(crossterm::event::Event),
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
tokio::runtime::Builder::new_current_thread()
|
tokio::runtime::Builder::new_current_thread()
|
||||||
@@ -133,10 +132,8 @@ fn render_vdom(
|
|||||||
terminal.clear().unwrap();
|
terminal.clear().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut to_rerender: rustc_hash::FxHashSet<RealNodeId> =
|
let mut to_rerender =FxDashSet::default();
|
||||||
vec![RealNodeId::ElementId(ElementId(0))]
|
to_rerender.insert(NodeId(0));
|
||||||
.into_iter()
|
|
||||||
.collect();
|
|
||||||
let mut updated = true;
|
let mut updated = true;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@@ -153,17 +150,17 @@ fn render_vdom(
|
|||||||
|
|
||||||
if !to_rerender.is_empty() || updated {
|
if !to_rerender.is_empty() || updated {
|
||||||
updated = false;
|
updated = false;
|
||||||
fn resize(dims: Rect, taffy: &mut Taffy, rdom: &Dom) {
|
fn resize(dims: Rect, taffy: &mut Taffy, rdom: &TuiDom) {
|
||||||
let width = dims.width;
|
let width = dims.width;
|
||||||
let height = dims.height;
|
let height = dims.height;
|
||||||
let root_node = rdom[ElementId(0)].state.layout.node.unwrap();
|
let root_node = rdom[NodeId(0)].state.layout.node.unwrap();
|
||||||
|
|
||||||
taffy
|
taffy
|
||||||
.compute_layout(
|
.compute_layout(
|
||||||
root_node,
|
root_node,
|
||||||
Size {
|
Size {
|
||||||
width: taffy::prelude::Number::Defined((width - 1) as f32),
|
width: AvailableSpace::Definite((width - 1) as f32),
|
||||||
height: taffy::prelude::Number::Defined((height - 1) as f32),
|
height: AvailableSpace::Definite((height - 1) as f32),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -172,15 +169,15 @@ fn render_vdom(
|
|||||||
terminal.draw(|frame| {
|
terminal.draw(|frame| {
|
||||||
let rdom = rdom.borrow();
|
let rdom = rdom.borrow();
|
||||||
// size is guaranteed to not change when rendering
|
// size is guaranteed to not change when rendering
|
||||||
resize(frame.size(), &mut taffy.borrow_mut(), &rdom);
|
resize(frame.size(), &mut taffy.lock().expect("taffy lock poisoned"), &rdom);
|
||||||
let root = &rdom[ElementId(0)];
|
let root = &rdom[NodeId(0)];
|
||||||
render::render_vnode(
|
render::render_vnode(
|
||||||
frame,
|
frame,
|
||||||
&taffy.borrow(),
|
&taffy.lock().expect("taffy lock poisoned"),
|
||||||
&rdom,
|
&rdom,
|
||||||
root,
|
root,
|
||||||
cfg,
|
cfg,
|
||||||
Point::zero(),
|
Point::ZERO,
|
||||||
);
|
);
|
||||||
})?;
|
})?;
|
||||||
} else {
|
} else {
|
||||||
@@ -192,7 +189,7 @@ fn render_vdom(
|
|||||||
width: 100,
|
width: 100,
|
||||||
height: 100,
|
height: 100,
|
||||||
},
|
},
|
||||||
&mut taffy.borrow_mut(),
|
&mut taffy.lock().expect("taffy lock poisoned"),
|
||||||
&rdom,
|
&rdom,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -234,23 +231,21 @@ fn render_vdom(
|
|||||||
{
|
{
|
||||||
let evts = {
|
let evts = {
|
||||||
let mut rdom = rdom.borrow_mut();
|
let mut rdom = rdom.borrow_mut();
|
||||||
handler.get_events(&taffy.borrow(), &mut rdom)
|
handler.get_events(&taffy.lock().expect("taffy lock poisoned"), &mut rdom)
|
||||||
};
|
};
|
||||||
{
|
{
|
||||||
updated |= handler.state().focus_state.clean();
|
updated |= handler.state().focus_state.clean();
|
||||||
}
|
}
|
||||||
for e in evts {
|
for e in evts {
|
||||||
vdom.handle_message(SchedulerMsg::Event(e));
|
vdom.handle_event(e.name, e.data, e.id, e.bubbles)
|
||||||
}
|
}
|
||||||
let mut rdom = rdom.borrow_mut();
|
let mut rdom = rdom.borrow_mut();
|
||||||
let mutations = vdom.work_with_deadline(|| false);
|
let mutations = vdom.render_immediate();
|
||||||
for m in &mutations {
|
handler.prune(&mutations, &rdom);
|
||||||
handler.prune(m, &rdom);
|
|
||||||
}
|
|
||||||
// updates the dom's nodes
|
// updates the dom's nodes
|
||||||
let to_update = rdom.apply_mutations(mutations);
|
let to_update = rdom.apply_mutations(mutations);
|
||||||
// update the style and layout
|
// update the style and layout
|
||||||
let mut any_map = AnyMap::new();
|
let mut any_map = SendAnyMap::new();
|
||||||
any_map.insert(taffy.clone());
|
any_map.insert(taffy.clone());
|
||||||
to_rerender = rdom.update_state(to_update, any_map);
|
to_rerender = rdom.update_state(to_update, any_map);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ use crate::style_attributes::StyleModifier;
|
|||||||
use dioxus_native_core::{real_dom::RealDom, state::*};
|
use dioxus_native_core::{real_dom::RealDom, state::*};
|
||||||
use dioxus_native_core_macro::{sorted_str_slice, State};
|
use dioxus_native_core_macro::{sorted_str_slice, State};
|
||||||
|
|
||||||
pub(crate) type Dom = RealDom<NodeState>;
|
pub(crate) type TuiDom = RealDom<NodeState>;
|
||||||
pub(crate) type Node = dioxus_native_core::real_dom::Node<NodeState>;
|
pub(crate) type TuiNode = dioxus_native_core::node::Node<NodeState>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, State, Default)]
|
#[derive(Debug, Clone, State, Default)]
|
||||||
pub(crate) struct NodeState {
|
pub(crate) struct NodeState {
|
||||||
#[child_dep_state(layout, RefCell<Stretch>)]
|
#[child_dep_state(layout, Mutex<Stretch>)]
|
||||||
pub layout: TaffyLayout,
|
pub layout: TaffyLayout,
|
||||||
#[parent_dep_state(style)]
|
#[parent_dep_state(style)]
|
||||||
pub style: StyleModifier,
|
pub style: StyleModifier,
|
||||||
@@ -45,7 +45,8 @@ impl Default for PreventDefault {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NodeDepState<()> for PreventDefault {
|
impl NodeDepState for PreventDefault {
|
||||||
|
type DepState = ();
|
||||||
type Ctx = ();
|
type Ctx = ();
|
||||||
|
|
||||||
const NODE_MASK: dioxus_native_core::node_ref::NodeMask =
|
const NODE_MASK: dioxus_native_core::node_ref::NodeMask =
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use std::{
|
use std::{
|
||||||
cell::{Ref, RefCell},
|
cell::{Ref, RefCell},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
|
sync::{Arc, Mutex, MutexGuard},
|
||||||
};
|
};
|
||||||
|
|
||||||
use dioxus_core::ElementId;
|
use dioxus_core::ElementId;
|
||||||
@@ -10,7 +11,7 @@ use taffy::{
|
|||||||
Taffy,
|
Taffy,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::Dom;
|
use crate::TuiDom;
|
||||||
|
|
||||||
/// Allows querying the layout of nodes after rendering. It will only provide a correct value after a node is rendered.
|
/// Allows querying the layout of nodes after rendering. It will only provide a correct value after a node is rendered.
|
||||||
/// Provided as a root context for all tui applictions.
|
/// Provided as a root context for all tui applictions.
|
||||||
@@ -45,24 +46,28 @@ use crate::Dom;
|
|||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Query {
|
pub struct Query {
|
||||||
pub(crate) rdom: Rc<RefCell<Dom>>,
|
pub(crate) rdom: Rc<RefCell<TuiDom>>,
|
||||||
pub(crate) stretch: Rc<RefCell<Taffy>>,
|
pub(crate) stretch: Arc<Mutex<Taffy>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Query {
|
impl Query {
|
||||||
pub fn get(&self, id: ElementId) -> ElementRef {
|
pub fn get(&self, id: ElementId) -> ElementRef {
|
||||||
ElementRef::new(self.rdom.borrow(), self.stretch.borrow(), id)
|
ElementRef::new(
|
||||||
|
self.rdom.borrow(),
|
||||||
|
self.stretch.lock().expect("taffy lock poisoned"),
|
||||||
|
id,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ElementRef<'a> {
|
pub struct ElementRef<'a> {
|
||||||
inner: Ref<'a, Dom>,
|
inner: Ref<'a, TuiDom>,
|
||||||
stretch: Ref<'a, Taffy>,
|
stretch: MutexGuard<'a, Taffy>,
|
||||||
id: ElementId,
|
id: ElementId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ElementRef<'a> {
|
impl<'a> ElementRef<'a> {
|
||||||
fn new(inner: Ref<'a, Dom>, stretch: Ref<'a, Taffy>, id: ElementId) -> Self {
|
fn new(inner: Ref<'a, TuiDom>, stretch: MutexGuard<'a, Taffy>, id: ElementId) -> Self {
|
||||||
Self { inner, stretch, id }
|
Self { inner, stretch, id }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use dioxus_native_core::tree::TreeView;
|
||||||
use std::io::Stdout;
|
use std::io::Stdout;
|
||||||
use taffy::{
|
use taffy::{
|
||||||
geometry::Point,
|
geometry::Point,
|
||||||
@@ -10,7 +11,7 @@ use crate::{
|
|||||||
style::{RinkColor, RinkStyle},
|
style::{RinkColor, RinkStyle},
|
||||||
style_attributes::{BorderEdge, BorderStyle},
|
style_attributes::{BorderEdge, BorderStyle},
|
||||||
widget::{RinkBuffer, RinkCell, RinkWidget, WidgetWithContext},
|
widget::{RinkBuffer, RinkCell, RinkWidget, WidgetWithContext},
|
||||||
Config, Dom, Node,
|
Config, TuiDom, TuiNode,
|
||||||
};
|
};
|
||||||
|
|
||||||
const RADIUS_MULTIPLIER: [f32; 2] = [1.0, 0.5];
|
const RADIUS_MULTIPLIER: [f32; 2] = [1.0, 0.5];
|
||||||
@@ -18,12 +19,12 @@ const RADIUS_MULTIPLIER: [f32; 2] = [1.0, 0.5];
|
|||||||
pub(crate) fn render_vnode(
|
pub(crate) fn render_vnode(
|
||||||
frame: &mut tui::Frame<CrosstermBackend<Stdout>>,
|
frame: &mut tui::Frame<CrosstermBackend<Stdout>>,
|
||||||
layout: &Taffy,
|
layout: &Taffy,
|
||||||
rdom: &Dom,
|
rdom: &TuiDom,
|
||||||
node: &Node,
|
node: &TuiNode,
|
||||||
cfg: Config,
|
cfg: Config,
|
||||||
parent_location: Point<f32>,
|
parent_location: Point<f32>,
|
||||||
) {
|
) {
|
||||||
use dioxus_native_core::real_dom::NodeType;
|
use dioxus_native_core::node::NodeType;
|
||||||
|
|
||||||
if let NodeType::Placeholder = &node.node_data.node_type {
|
if let NodeType::Placeholder = &node.node_data.node_type {
|
||||||
return;
|
return;
|
||||||
@@ -68,7 +69,7 @@ pub(crate) fn render_vnode(
|
|||||||
frame.render_widget(WidgetWithContext::new(label, cfg), area);
|
frame.render_widget(WidgetWithContext::new(label, cfg), area);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NodeType::Element { children, .. } => {
|
NodeType::Element { .. } => {
|
||||||
let area = Rect::new(x as u16, y as u16, *width as u16, *height as u16);
|
let area = Rect::new(x as u16, y as u16, *width as u16, *height as u16);
|
||||||
|
|
||||||
// the renderer will panic if a node is rendered out of range even if the size is zero
|
// the renderer will panic if a node is rendered out of range even if the size is zero
|
||||||
@@ -76,7 +77,7 @@ pub(crate) fn render_vnode(
|
|||||||
frame.render_widget(WidgetWithContext::new(node, cfg), area);
|
frame.render_widget(WidgetWithContext::new(node, cfg), area);
|
||||||
}
|
}
|
||||||
|
|
||||||
for c in children {
|
for c in rdom.children_ids(node.node_data.node_id).unwrap() {
|
||||||
render_vnode(frame, layout, rdom, &rdom[*c], cfg, location);
|
render_vnode(frame, layout, rdom, &rdom[*c], cfg, location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,7 +85,7 @@ pub(crate) fn render_vnode(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RinkWidget for &Node {
|
impl RinkWidget for &TuiNode {
|
||||||
fn render(self, area: Rect, mut buf: RinkBuffer<'_>) {
|
fn render(self, area: Rect, mut buf: RinkBuffer<'_>) {
|
||||||
use tui::symbols::line::*;
|
use tui::symbols::line::*;
|
||||||
|
|
||||||
|
|||||||
@@ -31,8 +31,8 @@
|
|||||||
|
|
||||||
use dioxus_native_core::{
|
use dioxus_native_core::{
|
||||||
layout_attributes::parse_value,
|
layout_attributes::parse_value,
|
||||||
|
node::OwnedAttributeView,
|
||||||
node_ref::{AttributeMask, NodeMask, NodeView},
|
node_ref::{AttributeMask, NodeMask, NodeView},
|
||||||
real_dom::OwnedAttributeView,
|
|
||||||
state::ParentDepState,
|
state::ParentDepState,
|
||||||
};
|
};
|
||||||
use dioxus_native_core_macro::sorted_str_slice;
|
use dioxus_native_core_macro::sorted_str_slice;
|
||||||
@@ -48,12 +48,12 @@ pub struct StyleModifier {
|
|||||||
|
|
||||||
impl ParentDepState for StyleModifier {
|
impl ParentDepState for StyleModifier {
|
||||||
type Ctx = ();
|
type Ctx = ();
|
||||||
type DepState = Self;
|
type DepState = (Self,);
|
||||||
// todo: seperate each attribute into it's own class
|
// todo: seperate each attribute into it's own class
|
||||||
const NODE_MASK: NodeMask =
|
const NODE_MASK: NodeMask =
|
||||||
NodeMask::new_with_attrs(AttributeMask::Static(SORTED_STYLE_ATTRS)).with_element();
|
NodeMask::new_with_attrs(AttributeMask::Static(SORTED_STYLE_ATTRS)).with_element();
|
||||||
|
|
||||||
fn reduce(&mut self, node: NodeView, parent: Option<&Self::DepState>, _: &Self::Ctx) -> bool {
|
fn reduce(&mut self, node: NodeView, parent: Option<(&Self,)>, _: &Self::Ctx) -> bool {
|
||||||
let mut new = StyleModifier::default();
|
let mut new = StyleModifier::default();
|
||||||
if parent.is_some() {
|
if parent.is_some() {
|
||||||
new.core.fg = None;
|
new.core.fg = None;
|
||||||
@@ -91,7 +91,7 @@ impl ParentDepState for StyleModifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// keep the text styling from the parent element
|
// keep the text styling from the parent element
|
||||||
if let Some(parent) = parent {
|
if let Some((parent,)) = parent {
|
||||||
let mut new_style = new.core.merge(parent.core);
|
let mut new_style = new.core.merge(parent.core);
|
||||||
new_style.bg = new.core.bg;
|
new_style.bg = new.core.bg;
|
||||||
new.core = new_style;
|
new.core = new_style;
|
||||||
|
|||||||
Reference in new issue
Block a user