document every item in native-core
This commit is contained in:
1 parent
f34fa5d974
commit
daac9f540f
23 files changed
+479
-181
No files matched your search
@@ -9,10 +9,8 @@ use std::{
|
|||||||
|
|
||||||
use dioxus_core::{Component, ElementId, VirtualDom};
|
use dioxus_core::{Component, ElementId, VirtualDom};
|
||||||
use dioxus_html::EventData;
|
use dioxus_html::EventData;
|
||||||
use dioxus_native_core::{
|
use dioxus_native_core::dioxus::{DioxusState, NodeImmutableDioxusExt};
|
||||||
dioxus::{DioxusState, NodeImmutableDioxusExt},
|
use dioxus_native_core::prelude::*;
|
||||||
NodeId, RealDom,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use rink::Config;
|
pub use rink::Config;
|
||||||
use rink::{query::Query, render, Renderer, TuiContext};
|
use rink::{query::Query, render, Renderer, TuiContext};
|
||||||
|
|||||||
@@ -300,19 +300,18 @@ pub fn partial_derive_state(_: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
#defaultness #unsafety #impl_token #generics #trait_ #for_ #self_ty {
|
#defaultness #unsafety #impl_token #generics #trait_ #for_ #self_ty {
|
||||||
#(#items)*
|
#(#items)*
|
||||||
|
|
||||||
fn workload_system(type_id: std::any::TypeId, dependants: dioxus_native_core::exports::FxHashSet<std::any::TypeId>, pass_direction: dioxus_native_core::PassDirection) -> dioxus_native_core::exports::shipyard::WorkloadSystem {
|
fn workload_system(type_id: std::any::TypeId, dependants: dioxus_native_core::exports::FxHashSet<std::any::TypeId>, pass_direction: dioxus_native_core::prelude::PassDirection) -> dioxus_native_core::exports::shipyard::WorkloadSystem {
|
||||||
use dioxus_native_core::exports::shipyard::{IntoWorkloadSystem, Get, AddComponent};
|
use dioxus_native_core::exports::shipyard::{IntoWorkloadSystem, Get, AddComponent};
|
||||||
use dioxus_native_core::tree::TreeRef;
|
use dioxus_native_core::tree::TreeRef;
|
||||||
use dioxus_native_core::prelude::{NodeType, NodeView};
|
use dioxus_native_core::prelude::{NodeType, NodeView};
|
||||||
|
|
||||||
let node_mask = Self::NODE_MASK.build();
|
let node_mask = Self::NODE_MASK.build();
|
||||||
|
|
||||||
(move |data: #combined_dependencies_quote, run_view: dioxus_native_core::RunPassView #trait_generics| {
|
(move |data: #combined_dependencies_quote, run_view: dioxus_native_core::prelude::RunPassView #trait_generics| {
|
||||||
let (#(#split_views,)*) = data;
|
let (#(#split_views,)*) = data;
|
||||||
let (tree, types, _, _, _) = &run_view;
|
let tree = run_view.tree.clone();
|
||||||
let tree = tree.clone();
|
let node_types = run_view.node_type.clone();
|
||||||
let node_types = types.clone();
|
dioxus_native_core::prelude::run_pass(type_id, dependants.clone(), pass_direction, run_view, |id, context| {
|
||||||
dioxus_native_core::run_pass(type_id, dependants.clone(), pass_direction, run_view, |id, context| {
|
|
||||||
let node_data: &NodeType<_> = node_types.get(id).unwrap_or_else(|err| panic!("Failed to get node type {:?}", err));
|
let node_data: &NodeType<_> = node_types.get(id).unwrap_or_else(|err| panic!("Failed to get node type {:?}", err));
|
||||||
// get all of the states from the tree view
|
// get all of the states from the tree view
|
||||||
// Safety: No node has itself as a parent or child.
|
// Safety: No node has itself as a parent or child.
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//! Integration between Dioxus and the RealDom
|
||||||
|
|
||||||
use crate::tree::TreeMut;
|
use crate::tree::TreeMut;
|
||||||
use dioxus_core::{BorrowedAttributeValue, ElementId, Mutations, TemplateNode};
|
use dioxus_core::{BorrowedAttributeValue, ElementId, Mutations, TemplateNode};
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
@@ -8,14 +10,15 @@ use crate::{
|
|||||||
ElementNode, FromAnyValue, NodeType, OwnedAttributeDiscription, OwnedAttributeValue,
|
ElementNode, FromAnyValue, NodeType, OwnedAttributeDiscription, OwnedAttributeValue,
|
||||||
TextNode,
|
TextNode,
|
||||||
},
|
},
|
||||||
prelude::NodeImmutable,
|
prelude::*,
|
||||||
real_dom::NodeTypeMut,
|
real_dom::NodeTypeMut,
|
||||||
NodeId, NodeMut, RealDom,
|
NodeId,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
struct ElementIdComponent(ElementId);
|
struct ElementIdComponent(ElementId);
|
||||||
|
|
||||||
|
/// The state of the Dioxus integration with the RealDom
|
||||||
pub struct DioxusState {
|
pub struct DioxusState {
|
||||||
templates: FxHashMap<String, Vec<NodeId>>,
|
templates: FxHashMap<String, Vec<NodeId>>,
|
||||||
stack: Vec<NodeId>,
|
stack: Vec<NodeId>,
|
||||||
@@ -23,6 +26,7 @@ pub struct DioxusState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DioxusState {
|
impl DioxusState {
|
||||||
|
/// Initialize the DioxusState in the RealDom
|
||||||
pub fn create(rdom: &mut RealDom) -> Self {
|
pub fn create(rdom: &mut RealDom) -> Self {
|
||||||
let root_id = rdom.root_id();
|
let root_id = rdom.root_id();
|
||||||
let mut root = rdom.get_mut(root_id).unwrap();
|
let mut root = rdom.get_mut(root_id).unwrap();
|
||||||
@@ -34,10 +38,12 @@ impl DioxusState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert an ElementId to a NodeId
|
||||||
pub fn element_to_node_id(&self, element_id: ElementId) -> NodeId {
|
pub fn element_to_node_id(&self, element_id: ElementId) -> NodeId {
|
||||||
self.try_element_to_node_id(element_id).unwrap()
|
self.try_element_to_node_id(element_id).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempt to convert an ElementId to a NodeId. This will return None if the ElementId is not in the RealDom.
|
||||||
pub fn try_element_to_node_id(&self, element_id: ElementId) -> Option<NodeId> {
|
pub fn try_element_to_node_id(&self, element_id: ElementId) -> Option<NodeId> {
|
||||||
self.node_id_mapping.get(element_id.0).copied().flatten()
|
self.node_id_mapping.get(element_id.0).copied().flatten()
|
||||||
}
|
}
|
||||||
@@ -51,7 +57,7 @@ impl DioxusState {
|
|||||||
self.node_id_mapping[element_id.0] = Some(node_id);
|
self.node_id_mapping[element_id.0] = Some(node_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_child(&self, rdom: &RealDom, path: &[u8]) -> NodeId {
|
fn load_child(&self, rdom: &RealDom, path: &[u8]) -> NodeId {
|
||||||
let mut current = rdom.get(*self.stack.last().unwrap()).unwrap();
|
let mut current = rdom.get(*self.stack.last().unwrap()).unwrap();
|
||||||
for i in path {
|
for i in path {
|
||||||
let new_id = current.child_ids()[*i as usize];
|
let new_id = current.child_ids()[*i as usize];
|
||||||
@@ -170,7 +176,7 @@ impl DioxusState {
|
|||||||
let mut node_type_mut = node.node_type_mut();
|
let mut node_type_mut = node.node_type_mut();
|
||||||
if let NodeTypeMut::Element(element) = &mut node_type_mut {
|
if let NodeTypeMut::Element(element) = &mut node_type_mut {
|
||||||
if let BorrowedAttributeValue::None = &value {
|
if let BorrowedAttributeValue::None = &value {
|
||||||
element.remove_attributes(&OwnedAttributeDiscription {
|
element.remove_attribute(&OwnedAttributeDiscription {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
namespace: ns.map(|s| s.to_string()),
|
namespace: ns.map(|s| s.to_string()),
|
||||||
});
|
});
|
||||||
@@ -266,7 +272,10 @@ fn create_template_node(rdom: &mut RealDom, node: &TemplateNode) -> NodeId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A trait that extends the `NodeImmutable` trait with methods that are useful for dioxus.
|
||||||
pub trait NodeImmutableDioxusExt<V: FromAnyValue + Send + Sync>: NodeImmutable<V> {
|
pub trait NodeImmutableDioxusExt<V: FromAnyValue + Send + Sync>: NodeImmutable<V> {
|
||||||
|
/// Returns the id of the element that this node is mounted to.
|
||||||
|
/// Not all nodes are mounted to an element, only nodes with dynamic content that have been renderered will have an id.
|
||||||
fn mounted_id(&self) -> Option<ElementId> {
|
fn mounted_id(&self) -> Option<ElementId> {
|
||||||
let id = self.get::<ElementIdComponent>();
|
let id = self.get::<ElementIdComponent>();
|
||||||
id.map(|id| id.0)
|
id.map(|id| id.0)
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//! Utility functions for applying layout attributes to taffy layout
|
||||||
|
|
||||||
/*
|
/*
|
||||||
- [ ] pub display: Display, ----> taffy doesnt support all display types
|
- [ ] pub display: Display, ----> taffy doesnt support all display types
|
||||||
- [x] pub position_type: PositionType, --> taffy doesnt support everything
|
- [x] pub position_type: PositionType, --> taffy doesnt support everything
|
||||||
@@ -46,12 +48,14 @@ use taffy::{
|
|||||||
style::{FlexDirection, PositionType},
|
style::{FlexDirection, PositionType},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Default values for layout attributes
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct LayoutConfigeration {
|
pub struct LayoutConfigeration {
|
||||||
/// the default border widths to use
|
/// the default border widths to use
|
||||||
pub border_widths: BorderWidths,
|
pub border_widths: BorderWidths,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Default border widths
|
||||||
pub struct BorderWidths {
|
pub struct BorderWidths {
|
||||||
/// the default border width to use for thin borders
|
/// the default border width to use for thin borders
|
||||||
pub thin: f32,
|
pub thin: f32,
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
|
#![doc = include_str!("../README.md")]
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::hash::BuildHasherDefault;
|
use std::hash::BuildHasherDefault;
|
||||||
|
|
||||||
pub use node_ref::NodeMask;
|
use node_ref::NodeMask;
|
||||||
pub use passes::AnyMapLike;
|
|
||||||
pub use passes::{run_pass, Dependancy, PassDirection, RunPassView, State, TypeErasedPass};
|
|
||||||
pub use real_dom::{NodeMut, NodeRef, RealDom};
|
|
||||||
use rustc_hash::FxHasher;
|
use rustc_hash::FxHasher;
|
||||||
|
|
||||||
#[cfg(feature = "dioxus")]
|
#[cfg(feature = "dioxus")]
|
||||||
@@ -19,22 +19,29 @@ pub mod tree;
|
|||||||
pub mod utils;
|
pub mod utils;
|
||||||
pub use shipyard::EntityId as NodeId;
|
pub use shipyard::EntityId as NodeId;
|
||||||
|
|
||||||
// exported for the macro
|
|
||||||
pub mod exports {
|
pub mod exports {
|
||||||
|
//! Important dependencies that are used by the rest of the library
|
||||||
|
//! Feel free to just add the dependencies in your own Crates.toml
|
||||||
|
// exported for the macro
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub use rustc_hash::FxHashSet;
|
pub use rustc_hash::FxHashSet;
|
||||||
pub use shipyard;
|
pub use shipyard;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A prelude of commonly used items
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::node::{ElementNode, FromAnyValue, NodeType, OwnedAttributeView, TextNode};
|
pub use crate::node::{ElementNode, FromAnyValue, NodeType, OwnedAttributeView, TextNode};
|
||||||
pub use crate::node_ref::{AttributeMaskBuilder, NodeMaskBuilder, NodeView};
|
pub use crate::node_ref::{AttributeMaskBuilder, NodeMaskBuilder, NodeView};
|
||||||
pub use crate::passes::{AnyState, Dependancy, DependancyView, State};
|
pub use crate::passes::{run_pass, PassDirection, RunPassView, TypeErasedState};
|
||||||
|
pub use crate::passes::{Dependancy, DependancyView, State};
|
||||||
pub use crate::real_dom::{NodeImmutable, NodeMut, NodeRef, RealDom};
|
pub use crate::real_dom::{NodeImmutable, NodeMut, NodeRef, RealDom};
|
||||||
pub use crate::NodeId;
|
pub use crate::NodeId;
|
||||||
pub use crate::SendAnyMap;
|
pub use crate::SendAnyMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A map that can be sent between threads
|
||||||
pub type FxDashMap<K, V> = dashmap::DashMap<K, V, BuildHasherDefault<FxHasher>>;
|
pub type FxDashMap<K, V> = dashmap::DashMap<K, V, BuildHasherDefault<FxHasher>>;
|
||||||
|
/// A set that can be sent between threads
|
||||||
pub type FxDashSet<K> = dashmap::DashSet<K, BuildHasherDefault<FxHasher>>;
|
pub type FxDashSet<K> = dashmap::DashSet<K, BuildHasherDefault<FxHasher>>;
|
||||||
|
/// A map of types that can be sent between threads
|
||||||
pub type SendAnyMap = anymap::Map<dyn Any + Send + Sync + 'static>;
|
pub type SendAnyMap = anymap::Map<dyn Any + Send + Sync + 'static>;
|
||||||
@@ -1,22 +1,36 @@
|
|||||||
|
//! Items related to Nodes in the RealDom
|
||||||
|
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use shipyard::Component;
|
use shipyard::Component;
|
||||||
use std::{any::Any, fmt::Debug};
|
use std::{
|
||||||
|
any::Any,
|
||||||
|
fmt::{Debug, Display},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A element node in the RealDom
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct ElementNode<V: FromAnyValue = ()> {
|
pub struct ElementNode<V: FromAnyValue = ()> {
|
||||||
|
/// The [tag](https://developer.mozilla.org/en-US/docs/Web/API/Element/tagName) of the element
|
||||||
pub tag: String,
|
pub tag: String,
|
||||||
|
/// The [namespace](https://developer.mozilla.org/en-US/docs/Web/API/Element/namespaceURI) of the element
|
||||||
pub namespace: Option<String>,
|
pub namespace: Option<String>,
|
||||||
|
/// The attributes of the element
|
||||||
pub attributes: FxHashMap<OwnedAttributeDiscription, OwnedAttributeValue<V>>,
|
pub attributes: FxHashMap<OwnedAttributeDiscription, OwnedAttributeValue<V>>,
|
||||||
|
/// The events the element is listening for
|
||||||
pub listeners: FxHashSet<String>,
|
pub listeners: FxHashSet<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A text node in the RealDom
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct TextNode {
|
pub struct TextNode {
|
||||||
|
/// The text of the node
|
||||||
pub text: String,
|
pub text: String,
|
||||||
|
/// The events the node is listening for
|
||||||
pub listeners: FxHashSet<String>,
|
pub listeners: FxHashSet<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextNode {
|
impl TextNode {
|
||||||
|
/// Create a new text node
|
||||||
pub fn new(text: String) -> Self {
|
pub fn new(text: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
text,
|
text,
|
||||||
@@ -25,17 +39,26 @@ impl TextNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type of node with data specific to the node type. The types are a subset of the [VNode] types.
|
/// A type of node with data specific to the node type.
|
||||||
#[derive(Debug, Clone, Component)]
|
#[derive(Debug, Clone, Component)]
|
||||||
pub enum NodeType<V: FromAnyValue = ()> {
|
pub enum NodeType<V: FromAnyValue = ()> {
|
||||||
|
/// A text node
|
||||||
Text(TextNode),
|
Text(TextNode),
|
||||||
|
/// An element node
|
||||||
Element(ElementNode<V>),
|
Element(ElementNode<V>),
|
||||||
|
/// A placeholder node. This can be used as a cheaper placeholder for a node that will be created later
|
||||||
Placeholder,
|
Placeholder,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A discription of an attribute on a DOM node, such as `id` or `href`.
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||||
pub struct OwnedAttributeDiscription {
|
pub struct OwnedAttributeDiscription {
|
||||||
|
/// The name of the attribute.
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
/// The namespace of the attribute used to identify what kind of attribute it is.
|
||||||
|
///
|
||||||
|
/// For renderers that use HTML, this can be used to identify if the attribute is a style attribute.
|
||||||
|
/// Instead of parsing the style attribute every time a style is changed, you can set an attribute with the `style` namespace.
|
||||||
pub namespace: Option<String>,
|
pub namespace: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,16 +73,24 @@ pub struct OwnedAttributeView<'a, V: FromAnyValue = ()> {
|
|||||||
pub value: &'a OwnedAttributeValue<V>,
|
pub value: &'a OwnedAttributeValue<V>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The value of an attribute on a DOM node. This contains non-text values to allow users to skip parsing attribute values in some cases.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum OwnedAttributeValue<V: FromAnyValue = ()> {
|
pub enum OwnedAttributeValue<V: FromAnyValue = ()> {
|
||||||
|
/// A string value. This is the most common type of attribute.
|
||||||
Text(String),
|
Text(String),
|
||||||
|
/// A floating point value.
|
||||||
Float(f64),
|
Float(f64),
|
||||||
|
/// An integer value.
|
||||||
Int(i64),
|
Int(i64),
|
||||||
|
/// A boolean value.
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
|
/// A custom value specific to the renderer
|
||||||
Custom(V),
|
Custom(V),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Something that can be converted from a borrowed [Any] value.
|
||||||
pub trait FromAnyValue: Clone + 'static {
|
pub trait FromAnyValue: Clone + 'static {
|
||||||
|
/// Convert from an [Any] value.
|
||||||
fn from_any_value(value: &dyn Any) -> Self;
|
fn from_any_value(value: &dyn Any) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,6 +110,18 @@ impl<V: FromAnyValue> Debug for OwnedAttributeValue<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<V: FromAnyValue + Display> Display for OwnedAttributeValue<V> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Text(arg0) => f.write_str(arg0),
|
||||||
|
Self::Float(arg0) => f.write_str(&arg0.to_string()),
|
||||||
|
Self::Int(arg0) => f.write_str(&arg0.to_string()),
|
||||||
|
Self::Bool(arg0) => f.write_str(&arg0.to_string()),
|
||||||
|
Self::Custom(arg0) => f.write_str(&arg0.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "dioxus")]
|
#[cfg(feature = "dioxus")]
|
||||||
impl<V: FromAnyValue> From<dioxus_core::BorrowedAttributeValue<'_>> for OwnedAttributeValue<V> {
|
impl<V: FromAnyValue> From<dioxus_core::BorrowedAttributeValue<'_>> for OwnedAttributeValue<V> {
|
||||||
fn from(value: dioxus_core::BorrowedAttributeValue<'_>) -> Self {
|
fn from(value: dioxus_core::BorrowedAttributeValue<'_>) -> Self {
|
||||||
@@ -94,6 +137,7 @@ impl<V: FromAnyValue> From<dioxus_core::BorrowedAttributeValue<'_>> for OwnedAtt
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<V: FromAnyValue> OwnedAttributeValue<V> {
|
impl<V: FromAnyValue> OwnedAttributeValue<V> {
|
||||||
|
/// Attempt to convert the attribute value to a string.
|
||||||
pub fn as_text(&self) -> Option<&str> {
|
pub fn as_text(&self) -> Option<&str> {
|
||||||
match self {
|
match self {
|
||||||
OwnedAttributeValue::Text(text) => Some(text),
|
OwnedAttributeValue::Text(text) => Some(text),
|
||||||
@@ -101,20 +145,25 @@ impl<V: FromAnyValue> OwnedAttributeValue<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempt to convert the attribute value to a float.
|
||||||
pub fn as_float(&self) -> Option<f64> {
|
pub fn as_float(&self) -> Option<f64> {
|
||||||
match self {
|
match self {
|
||||||
OwnedAttributeValue::Float(float) => Some(*float),
|
OwnedAttributeValue::Float(float) => Some(*float),
|
||||||
|
OwnedAttributeValue::Int(int) => Some(*int as f64),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempt to convert the attribute value to an integer.
|
||||||
pub fn as_int(&self) -> Option<i64> {
|
pub fn as_int(&self) -> Option<i64> {
|
||||||
match self {
|
match self {
|
||||||
|
OwnedAttributeValue::Float(float) => Some(*float as i64),
|
||||||
OwnedAttributeValue::Int(int) => Some(*int),
|
OwnedAttributeValue::Int(int) => Some(*int),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempt to convert the attribute value to a boolean.
|
||||||
pub fn as_bool(&self) -> Option<bool> {
|
pub fn as_bool(&self) -> Option<bool> {
|
||||||
match self {
|
match self {
|
||||||
OwnedAttributeValue::Bool(bool) => Some(*bool),
|
OwnedAttributeValue::Bool(bool) => Some(*bool),
|
||||||
@@ -122,6 +171,7 @@ impl<V: FromAnyValue> OwnedAttributeValue<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempt to convert the attribute value to a custom value.
|
||||||
pub fn as_custom(&self) -> Option<&V> {
|
pub fn as_custom(&self) -> Option<&V> {
|
||||||
match self {
|
match self {
|
||||||
OwnedAttributeValue::Custom(custom) => Some(custom),
|
OwnedAttributeValue::Custom(custom) => Some(custom),
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//! Utilities that provide limited access to nodes
|
||||||
|
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -5,7 +7,7 @@ use crate::{
|
|||||||
NodeId,
|
NodeId,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A view into a [VNode] with limited access.
|
/// A view into a [NodeType] with a mask that determines what is visible.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct NodeView<'a, V: FromAnyValue = ()> {
|
pub struct NodeView<'a, V: FromAnyValue = ()> {
|
||||||
id: NodeId,
|
id: NodeId,
|
||||||
@@ -98,11 +100,14 @@ impl<'a, V: FromAnyValue> NodeView<'a, V> {
|
|||||||
/// A mask that contains a list of attributes that are visible.
|
/// A mask that contains a list of attributes that are visible.
|
||||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||||
pub enum AttributeMask {
|
pub enum AttributeMask {
|
||||||
|
/// All attributes are visible
|
||||||
All,
|
All,
|
||||||
|
/// Only the given attributes are visible
|
||||||
Some(FxHashSet<Box<str>>),
|
Some(FxHashSet<Box<str>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AttributeMask {
|
impl AttributeMask {
|
||||||
|
/// Check if the given attribute is visible
|
||||||
fn contains_attribute(&self, attr: &str) -> bool {
|
fn contains_attribute(&self, attr: &str) -> bool {
|
||||||
match self {
|
match self {
|
||||||
AttributeMask::All => true,
|
AttributeMask::All => true,
|
||||||
@@ -203,9 +208,12 @@ impl NodeMask {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A builder for a mask that controls what attributes are visible.
|
||||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||||
pub enum AttributeMaskBuilder<'a> {
|
pub enum AttributeMaskBuilder<'a> {
|
||||||
|
/// All attributes are visible
|
||||||
All,
|
All,
|
||||||
|
/// Only the given attributes are visible
|
||||||
Some(&'a [&'a str]),
|
Some(&'a [&'a str]),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
use crate::{node::FromAnyValue, NodeMut};
|
//! Helpers for watching for changes in the DOM tree.
|
||||||
|
|
||||||
|
use crate::{node::FromAnyValue, prelude::*};
|
||||||
|
|
||||||
/// A trait for watching for changes in the DOM tree.
|
/// A trait for watching for changes in the DOM tree.
|
||||||
pub trait NodeWatcher<V: FromAnyValue + Send + Sync> {
|
pub trait NodeWatcher<V: FromAnyValue + Send + Sync> {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use anymap::AnyMap;
|
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use shipyard::{Component, Unique, UniqueView, View, WorkloadSystem};
|
use shipyard::{Borrow, BorrowInfo, Component, Unique, UniqueView, View, WorkloadSystem};
|
||||||
use std::any::{Any, TypeId};
|
use std::any::{Any, TypeId};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
@@ -104,6 +103,7 @@ impl DirtyNodeStates {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A state that is automatically inserted in a node with dependencies.
|
||||||
pub trait State<V: FromAnyValue + Send + Sync = ()>: Any + Send + Sync {
|
pub trait State<V: FromAnyValue + Send + Sync = ()>: Any + Send + Sync {
|
||||||
/// This is a tuple of (T: State, ..) of states read from the parent required to run this pass
|
/// This is a tuple of (T: State, ..) of states read from the parent required to run this pass
|
||||||
type ParentDependencies: Dependancy;
|
type ParentDependencies: Dependancy;
|
||||||
@@ -139,24 +139,77 @@ pub trait State<V: FromAnyValue + Send + Sync = ()>: Any + Send + Sync {
|
|||||||
dependants: FxHashSet<TypeId>,
|
dependants: FxHashSet<TypeId>,
|
||||||
pass_direction: PassDirection,
|
pass_direction: PassDirection,
|
||||||
) -> WorkloadSystem;
|
) -> WorkloadSystem;
|
||||||
|
|
||||||
|
/// Converts to a type erased version of the trait
|
||||||
|
fn to_type_erased() -> TypeErasedState<V>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let node_mask = Self::NODE_MASK.build();
|
||||||
|
TypeErasedState {
|
||||||
|
this_type_id: TypeId::of::<Self>(),
|
||||||
|
combined_dependancy_type_ids: all_dependanices::<V, Self>().iter().copied().collect(),
|
||||||
|
parent_dependant: !Self::ParentDependencies::type_ids().is_empty(),
|
||||||
|
child_dependant: !Self::ChildDependencies::type_ids().is_empty(),
|
||||||
|
dependants: FxHashSet::default(),
|
||||||
|
mask: node_mask,
|
||||||
|
pass_direction: pass_direction::<V, Self>(),
|
||||||
|
workload: Self::workload_system,
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type RunPassView<'a, V = ()> = (
|
fn pass_direction<V: FromAnyValue + Send + Sync, S: State<V>>() -> PassDirection {
|
||||||
TreeRefView<'a>,
|
if S::ChildDependencies::type_ids()
|
||||||
View<'a, NodeType<V>>,
|
.iter()
|
||||||
UniqueView<'a, DirtyNodesResult>,
|
.any(|type_id| *type_id == TypeId::of::<S>())
|
||||||
UniqueView<'a, DirtyNodeStates>,
|
{
|
||||||
UniqueView<'a, SendAnyMapWrapper>,
|
PassDirection::ChildToParent
|
||||||
);
|
} else if S::ParentDependencies::type_ids()
|
||||||
|
.iter()
|
||||||
|
.any(|type_id| *type_id == TypeId::of::<S>())
|
||||||
|
{
|
||||||
|
PassDirection::ParentToChild
|
||||||
|
} else {
|
||||||
|
PassDirection::AnyOrder
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn run_pass<V: FromAnyValue>(
|
fn all_dependanices<V: FromAnyValue + Send + Sync, S: State<V>>() -> Box<[TypeId]> {
|
||||||
|
let mut dependencies = S::ParentDependencies::type_ids().to_vec();
|
||||||
|
dependencies.extend(S::ChildDependencies::type_ids().iter());
|
||||||
|
dependencies.extend(S::NodeDependencies::type_ids().iter());
|
||||||
|
dependencies.into_boxed_slice()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[derive(Borrow, BorrowInfo)]
|
||||||
|
pub struct RunPassView<'a, V: FromAnyValue + Send + Sync = ()> {
|
||||||
|
pub tree: TreeRefView<'a>,
|
||||||
|
pub node_type: View<'a, NodeType<V>>,
|
||||||
|
dirty_nodes_result: UniqueView<'a, DirtyNodesResult>,
|
||||||
|
node_states: UniqueView<'a, DirtyNodeStates>,
|
||||||
|
any_map: UniqueView<'a, SendAnyMapWrapper>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is used by the macro
|
||||||
|
/// Updates the given pass, marking any nodes that were changed
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn run_pass<V: FromAnyValue + Send + Sync>(
|
||||||
type_id: TypeId,
|
type_id: TypeId,
|
||||||
dependants: FxHashSet<TypeId>,
|
dependants: FxHashSet<TypeId>,
|
||||||
pass_direction: PassDirection,
|
pass_direction: PassDirection,
|
||||||
view: RunPassView<V>,
|
view: RunPassView<V>,
|
||||||
mut update_node: impl FnMut(NodeId, &SendAnyMap) -> bool,
|
mut update_node: impl FnMut(NodeId, &SendAnyMap) -> bool,
|
||||||
) {
|
) {
|
||||||
let (tree, _, nodes_updated, dirty, ctx, ..) = view;
|
let RunPassView {
|
||||||
|
tree,
|
||||||
|
dirty_nodes_result: nodes_updated,
|
||||||
|
node_states: dirty,
|
||||||
|
any_map: ctx,
|
||||||
|
..
|
||||||
|
} = view;
|
||||||
let ctx = ctx.as_ref();
|
let ctx = ctx.as_ref();
|
||||||
match pass_direction {
|
match pass_direction {
|
||||||
PassDirection::ParentToChild => {
|
PassDirection::ParentToChild => {
|
||||||
@@ -199,64 +252,8 @@ pub fn run_pass<V: FromAnyValue>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait AnyState<V: FromAnyValue + Send + Sync = ()>: State<V> {
|
/// A type erased version of [`State`] that can be added to the [`crate::prelude::RealDom`] with [`crate::prelude::RealDom::new`]
|
||||||
fn to_type_erased() -> TypeErasedPass<V>
|
pub struct TypeErasedState<V: FromAnyValue + Send = ()> {
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
let node_mask = Self::NODE_MASK.build();
|
|
||||||
TypeErasedPass {
|
|
||||||
this_type_id: TypeId::of::<Self>(),
|
|
||||||
combined_dependancy_type_ids: Self::all_dependanices().iter().copied().collect(),
|
|
||||||
parent_dependant: !Self::parent_type_ids().is_empty(),
|
|
||||||
child_dependant: !Self::child_type_ids().is_empty(),
|
|
||||||
dependants: FxHashSet::default(),
|
|
||||||
mask: node_mask,
|
|
||||||
pass_direction: Self::pass_direction(),
|
|
||||||
workload: Self::workload_system,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parent_type_ids() -> Box<[TypeId]> {
|
|
||||||
Self::ParentDependencies::type_ids()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn child_type_ids() -> Box<[TypeId]> {
|
|
||||||
Self::ChildDependencies::type_ids()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn node_type_ids() -> Box<[TypeId]> {
|
|
||||||
Self::NodeDependencies::type_ids()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn all_dependanices() -> Box<[TypeId]> {
|
|
||||||
let mut dependencies = Self::parent_type_ids().to_vec();
|
|
||||||
dependencies.extend(Self::child_type_ids().iter());
|
|
||||||
dependencies.extend(Self::node_type_ids().iter());
|
|
||||||
dependencies.into_boxed_slice()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pass_direction() -> PassDirection {
|
|
||||||
if Self::child_type_ids()
|
|
||||||
.iter()
|
|
||||||
.any(|type_id| *type_id == TypeId::of::<Self>())
|
|
||||||
{
|
|
||||||
PassDirection::ChildToParent
|
|
||||||
} else if Self::parent_type_ids()
|
|
||||||
.iter()
|
|
||||||
.any(|type_id| *type_id == TypeId::of::<Self>())
|
|
||||||
{
|
|
||||||
PassDirection::ParentToChild
|
|
||||||
} else {
|
|
||||||
PassDirection::AnyOrder
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: FromAnyValue + Send + Sync, S: State<V>> AnyState<V> for S {}
|
|
||||||
|
|
||||||
pub struct TypeErasedPass<V: FromAnyValue + Send = ()> {
|
|
||||||
pub(crate) this_type_id: TypeId,
|
pub(crate) this_type_id: TypeId,
|
||||||
pub(crate) parent_dependant: bool,
|
pub(crate) parent_dependant: bool,
|
||||||
pub(crate) child_dependant: bool,
|
pub(crate) child_dependant: bool,
|
||||||
@@ -268,7 +265,7 @@ pub struct TypeErasedPass<V: FromAnyValue + Send = ()> {
|
|||||||
phantom: PhantomData<V>,
|
phantom: PhantomData<V>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: FromAnyValue + Send> TypeErasedPass<V> {
|
impl<V: FromAnyValue + Send> TypeErasedState<V> {
|
||||||
pub(crate) fn create_workload(&self) -> WorkloadSystem {
|
pub(crate) fn create_workload(&self) -> WorkloadSystem {
|
||||||
(self.workload)(
|
(self.workload)(
|
||||||
self.this_type_id,
|
self.this_type_id,
|
||||||
@@ -278,32 +275,23 @@ impl<V: FromAnyValue + Send> TypeErasedPass<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The direction that a pass should be run in
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum PassDirection {
|
pub enum PassDirection {
|
||||||
|
/// The pass should be run from the root to the leaves
|
||||||
ParentToChild,
|
ParentToChild,
|
||||||
|
/// The pass should be run from the leaves to the root
|
||||||
ChildToParent,
|
ChildToParent,
|
||||||
|
/// The pass can be run in any order
|
||||||
AnyOrder,
|
AnyOrder,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait AnyMapLike<'a> {
|
/// A trait that is implemented for all the dependancies of a [`State`]
|
||||||
fn get<T: Any + Sync + Send>(self) -> Option<&'a T>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> AnyMapLike<'a> for &'a AnyMap {
|
|
||||||
fn get<T: Any + Sync + Send>(self) -> Option<&'a T> {
|
|
||||||
self.get()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> AnyMapLike<'a> for &'a SendAnyMap {
|
|
||||||
fn get<T: Any + Sync + Send>(self) -> Option<&'a T> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Dependancy {
|
pub trait Dependancy {
|
||||||
|
/// A tuple with all the elements of the dependancy as [`DependancyView`]
|
||||||
type ElementBorrowed<'a>;
|
type ElementBorrowed<'a>;
|
||||||
|
|
||||||
|
/// Returns a list of all the [`TypeId`]s of the elements in the dependancy
|
||||||
fn type_ids() -> Box<[TypeId]> {
|
fn type_ids() -> Box<[TypeId]> {
|
||||||
Box::new([])
|
Box::new([])
|
||||||
}
|
}
|
||||||
@@ -323,6 +311,7 @@ macro_rules! impl_dependancy {
|
|||||||
|
|
||||||
// TODO: track what components are actually read to update subscriptions
|
// TODO: track what components are actually read to update subscriptions
|
||||||
// making this a wrapper makes it possible to implement that optimization without a breaking change
|
// making this a wrapper makes it possible to implement that optimization without a breaking change
|
||||||
|
/// A immutable view of a [`State`]
|
||||||
pub struct DependancyView<'a, T> {
|
pub struct DependancyView<'a, T> {
|
||||||
inner: &'a T,
|
inner: &'a T,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//! A Dom that can sync with the VirtualDom mutations intended for use in lazy renderers.
|
||||||
|
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use shipyard::error::GetStorage;
|
use shipyard::error::GetStorage;
|
||||||
use shipyard::track::Untracked;
|
use shipyard::track::Untracked;
|
||||||
@@ -13,14 +15,15 @@ use crate::node::{
|
|||||||
};
|
};
|
||||||
use crate::node_ref::{NodeMask, NodeMaskBuilder};
|
use crate::node_ref::{NodeMask, NodeMaskBuilder};
|
||||||
use crate::node_watcher::NodeWatcher;
|
use crate::node_watcher::NodeWatcher;
|
||||||
use crate::passes::{DirtyNodeStates, TypeErasedPass};
|
use crate::passes::{DirtyNodeStates, TypeErasedState};
|
||||||
use crate::prelude::AttributeMaskBuilder;
|
use crate::prelude::AttributeMaskBuilder;
|
||||||
use crate::tree::{TreeMut, TreeMutView, TreeRef, TreeRefView};
|
use crate::tree::{TreeMut, TreeMutView, TreeRef, TreeRefView};
|
||||||
use crate::NodeId;
|
use crate::NodeId;
|
||||||
use crate::{FxDashSet, SendAnyMap};
|
use crate::{FxDashSet, SendAnyMap};
|
||||||
|
|
||||||
|
/// The context passes can receive when they are executed
|
||||||
#[derive(Unique)]
|
#[derive(Unique)]
|
||||||
pub struct SendAnyMapWrapper(SendAnyMap);
|
pub(crate) struct SendAnyMapWrapper(SendAnyMap);
|
||||||
|
|
||||||
impl Deref for SendAnyMapWrapper {
|
impl Deref for SendAnyMapWrapper {
|
||||||
type Target = SendAnyMap;
|
type Target = SendAnyMap;
|
||||||
@@ -30,8 +33,9 @@ impl Deref for SendAnyMapWrapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The nodes that were changed when updating the state of the RealDom
|
||||||
#[derive(Unique, Default)]
|
#[derive(Unique, Default)]
|
||||||
pub struct DirtyNodesResult(FxDashSet<NodeId>);
|
pub(crate) struct DirtyNodesResult(FxDashSet<NodeId>);
|
||||||
|
|
||||||
impl Deref for DirtyNodesResult {
|
impl Deref for DirtyNodesResult {
|
||||||
type Target = FxDashSet<NodeId>;
|
type Target = FxDashSet<NodeId>;
|
||||||
@@ -41,13 +45,15 @@ impl Deref for DirtyNodesResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct NodesDirty<V: FromAnyValue + Send + Sync> {
|
/// The nodes that have been marked as dirty in the RealDom
|
||||||
|
pub(crate) struct NodesDirty<V: FromAnyValue + Send + Sync> {
|
||||||
passes_updated: FxHashMap<NodeId, FxHashSet<TypeId>>,
|
passes_updated: FxHashMap<NodeId, FxHashSet<TypeId>>,
|
||||||
nodes_updated: FxHashMap<NodeId, NodeMask>,
|
nodes_updated: FxHashMap<NodeId, NodeMask>,
|
||||||
pub(crate) passes: Box<[TypeErasedPass<V>]>,
|
pub(crate) passes: Box<[TypeErasedState<V>]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: FromAnyValue + Send + Sync> NodesDirty<V> {
|
impl<V: FromAnyValue + Send + Sync> NodesDirty<V> {
|
||||||
|
/// Mark a node as dirty
|
||||||
fn mark_dirty(&mut self, node_id: NodeId, mask: NodeMask) {
|
fn mark_dirty(&mut self, node_id: NodeId, mask: NodeMask) {
|
||||||
self.passes_updated.entry(node_id).or_default().extend(
|
self.passes_updated.entry(node_id).or_default().extend(
|
||||||
self.passes
|
self.passes
|
||||||
@@ -62,6 +68,7 @@ impl<V: FromAnyValue + Send + Sync> NodesDirty<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mark a node as added or removed from the tree
|
||||||
fn mark_parent_added_or_removed(&mut self, node_id: NodeId) {
|
fn mark_parent_added_or_removed(&mut self, node_id: NodeId) {
|
||||||
let hm = self.passes_updated.entry(node_id).or_default();
|
let hm = self.passes_updated.entry(node_id).or_default();
|
||||||
for pass in &*self.passes {
|
for pass in &*self.passes {
|
||||||
@@ -71,6 +78,7 @@ impl<V: FromAnyValue + Send + Sync> NodesDirty<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mark a node as having a child added or removed
|
||||||
fn mark_child_changed(&mut self, node_id: NodeId) {
|
fn mark_child_changed(&mut self, node_id: NodeId) {
|
||||||
let hm = self.passes_updated.entry(node_id).or_default();
|
let hm = self.passes_updated.entry(node_id).or_default();
|
||||||
for pass in &*self.passes {
|
for pass in &*self.passes {
|
||||||
@@ -85,9 +93,13 @@ type NodeWatchers<V> = Arc<RwLock<Vec<Box<dyn NodeWatcher<V> + Send + Sync>>>>;
|
|||||||
|
|
||||||
/// A Dom that can sync with the VirtualDom mutations intended for use in lazy renderers.
|
/// A Dom that can sync with the VirtualDom mutations intended for use in lazy renderers.
|
||||||
/// The render state passes from parent to children and or accumulates state from children to parents.
|
/// The render state passes from parent to children and or accumulates state from children to parents.
|
||||||
/// To get started implement [crate::state::ParentDepState], [crate::state::NodeDepState], or [crate::state::ChildDepState] and call [RealDom::apply_mutations] to update the dom and [RealDom::update_state] to update the state of the nodes.
|
/// To get started:
|
||||||
|
/// 1) Implement [crate::passes::State] for each part of your state that you want to compute incrementally
|
||||||
|
/// 2) Create a RealDom [RealDom::new], passing in each state you created
|
||||||
|
/// 3) Update the state of the RealDom by adding and modifying nodes
|
||||||
|
/// 4) Call [RealDom::update_state] to update the state of incrementally computed values on each node
|
||||||
///
|
///
|
||||||
/// # Custom values
|
/// # Custom attribute values
|
||||||
/// To allow custom values to be passed into attributes implement FromAnyValue on a type that can represent your custom value and specify the V generic to be that type. If you have many different custom values, it can be useful to use a enum type to represent the varients.
|
/// To allow custom values to be passed into attributes implement FromAnyValue on a type that can represent your custom value and specify the V generic to be that type. If you have many different custom values, it can be useful to use a enum type to represent the varients.
|
||||||
pub struct RealDom<V: FromAnyValue + Send + Sync = ()> {
|
pub struct RealDom<V: FromAnyValue + Send + Sync = ()> {
|
||||||
pub(crate) world: World,
|
pub(crate) world: World,
|
||||||
@@ -100,10 +112,11 @@ pub struct RealDom<V: FromAnyValue + Send + Sync = ()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<V: FromAnyValue + Send + Sync> RealDom<V> {
|
impl<V: FromAnyValue + Send + Sync> RealDom<V> {
|
||||||
pub fn new(mut passes: Box<[TypeErasedPass<V>]>) -> RealDom<V> {
|
/// Create a new RealDom with the given states that will be inserted and updated when needed
|
||||||
|
pub fn new(mut tracked_states: Box<[TypeErasedState<V>]>) -> RealDom<V> {
|
||||||
// resolve dependants for each pass
|
// resolve dependants for each pass
|
||||||
for i in 1..passes.len() {
|
for i in 1..tracked_states.len() {
|
||||||
let (before, after) = passes.split_at_mut(i);
|
let (before, after) = tracked_states.split_at_mut(i);
|
||||||
let (current, before) = before.split_last_mut().unwrap();
|
let (current, before) = before.split_last_mut().unwrap();
|
||||||
for pass in before.iter_mut().chain(after.iter_mut()) {
|
for pass in before.iter_mut().chain(after.iter_mut()) {
|
||||||
if current
|
if current
|
||||||
@@ -114,7 +127,7 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let workload = construct_workload(&mut passes);
|
let workload = construct_workload(&mut tracked_states);
|
||||||
let (workload, _) = workload.build().unwrap();
|
let (workload, _) = workload.build().unwrap();
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
let root_node: NodeType<V> = NodeType::Element(ElementNode {
|
let root_node: NodeType<V> = NodeType::Element(ElementNode {
|
||||||
@@ -132,7 +145,10 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
|
|||||||
let mut passes_updated = FxHashMap::default();
|
let mut passes_updated = FxHashMap::default();
|
||||||
let mut nodes_updated = FxHashMap::default();
|
let mut nodes_updated = FxHashMap::default();
|
||||||
|
|
||||||
passes_updated.insert(root_id, passes.iter().map(|x| x.this_type_id).collect());
|
passes_updated.insert(
|
||||||
|
root_id,
|
||||||
|
tracked_states.iter().map(|x| x.this_type_id).collect(),
|
||||||
|
);
|
||||||
nodes_updated.insert(root_id, NodeMaskBuilder::ALL.build());
|
nodes_updated.insert(root_id, NodeMaskBuilder::ALL.build());
|
||||||
|
|
||||||
RealDom {
|
RealDom {
|
||||||
@@ -141,7 +157,7 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
|
|||||||
dirty_nodes: NodesDirty {
|
dirty_nodes: NodesDirty {
|
||||||
passes_updated,
|
passes_updated,
|
||||||
nodes_updated,
|
nodes_updated,
|
||||||
passes,
|
passes: tracked_states,
|
||||||
},
|
},
|
||||||
node_watchers: Default::default(),
|
node_watchers: Default::default(),
|
||||||
workload,
|
workload,
|
||||||
@@ -150,14 +166,17 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tree_mut(&self) -> TreeMutView {
|
/// Get a reference to the tree.
|
||||||
self.world.borrow().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tree_ref(&self) -> TreeRefView {
|
pub fn tree_ref(&self) -> TreeRefView {
|
||||||
self.world.borrow().unwrap()
|
self.world.borrow().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a mutable reference to the tree.
|
||||||
|
pub fn tree_mut(&self) -> TreeMutView {
|
||||||
|
self.world.borrow().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new node of the given type in the dom and return a mutable reference to it.
|
||||||
pub fn create_node(&mut self, node: NodeType<V>) -> NodeMut<'_, V> {
|
pub fn create_node(&mut self, node: NodeType<V>) -> NodeMut<'_, V> {
|
||||||
let id = self.world.add_entity(node);
|
let id = self.world.add_entity(node);
|
||||||
self.tree_mut().create_node(id);
|
self.tree_mut().create_node(id);
|
||||||
@@ -196,17 +215,20 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
|
|||||||
self.root_id
|
self.root_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a reference to a node.
|
||||||
pub fn get(&self, id: NodeId) -> Option<NodeRef<'_, V>> {
|
pub fn get(&self, id: NodeId) -> Option<NodeRef<'_, V>> {
|
||||||
self.tree_ref()
|
self.tree_ref()
|
||||||
.contains(id)
|
.contains(id)
|
||||||
.then_some(NodeRef { id, dom: self })
|
.then_some(NodeRef { id, dom: self })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a mutable reference to a node.
|
||||||
pub fn get_mut(&mut self, id: NodeId) -> Option<NodeMut<'_, V>> {
|
pub fn get_mut(&mut self, id: NodeId) -> Option<NodeMut<'_, V>> {
|
||||||
let contains = self.tree_ref().contains(id);
|
let contains = self.tree_ref().contains(id);
|
||||||
contains.then(|| NodeMut::new(id, self))
|
contains.then(|| NodeMut::new(id, self))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Borrow a component from the world without updating the dirty nodes.
|
||||||
fn borrow_raw<'a, B: IntoBorrow>(&'a self) -> Result<B, GetStorage>
|
fn borrow_raw<'a, B: IntoBorrow>(&'a self) -> Result<B, GetStorage>
|
||||||
where
|
where
|
||||||
B::Borrow: shipyard::Borrow<'a, View = B>,
|
B::Borrow: shipyard::Borrow<'a, View = B>,
|
||||||
@@ -214,6 +236,7 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
|
|||||||
self.world.borrow()
|
self.world.borrow()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Borrow a component from the world without updating the dirty nodes.
|
||||||
fn borrow_node_type_mut(&self) -> Result<ViewMut<NodeType<V>>, GetStorage> {
|
fn borrow_node_type_mut(&self) -> Result<ViewMut<NodeType<V>>, GetStorage> {
|
||||||
self.world.borrow()
|
self.world.borrow()
|
||||||
}
|
}
|
||||||
@@ -250,6 +273,7 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
|
|||||||
(dirty.0, nodes_updated)
|
(dirty.0, nodes_updated)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Traverses the dom in a depth first manner, calling the provided function on each node.
|
||||||
pub fn traverse_depth_first(&self, mut f: impl FnMut(NodeRef<V>)) {
|
pub fn traverse_depth_first(&self, mut f: impl FnMut(NodeRef<V>)) {
|
||||||
let mut stack = vec![self.root_id()];
|
let mut stack = vec![self.root_id()];
|
||||||
let tree = self.tree_ref();
|
let tree = self.tree_ref();
|
||||||
@@ -262,6 +286,7 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Traverses the dom in a breadth first manner, calling the provided function on each node.
|
||||||
pub fn traverse_breadth_first(&self, mut f: impl FnMut(NodeRef<V>)) {
|
pub fn traverse_breadth_first(&self, mut f: impl FnMut(NodeRef<V>)) {
|
||||||
let mut queue = VecDeque::new();
|
let mut queue = VecDeque::new();
|
||||||
queue.push_back(self.root_id());
|
queue.push_back(self.root_id());
|
||||||
@@ -277,6 +302,7 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Traverses the dom in a depth first manner mutably, calling the provided function on each node.
|
||||||
pub fn traverse_depth_first_mut(&mut self, mut f: impl FnMut(NodeMut<V>)) {
|
pub fn traverse_depth_first_mut(&mut self, mut f: impl FnMut(NodeMut<V>)) {
|
||||||
let mut stack = vec![self.root_id()];
|
let mut stack = vec![self.root_id()];
|
||||||
while let Some(id) = stack.pop() {
|
while let Some(id) = stack.pop() {
|
||||||
@@ -292,6 +318,7 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Traverses the dom in a breadth first manner mutably, calling the provided function on each node.
|
||||||
pub fn traverse_breadth_first_mut(&mut self, mut f: impl FnMut(NodeMut<V>)) {
|
pub fn traverse_breadth_first_mut(&mut self, mut f: impl FnMut(NodeMut<V>)) {
|
||||||
let mut queue = VecDeque::new();
|
let mut queue = VecDeque::new();
|
||||||
queue.push_back(self.root_id());
|
queue.push_back(self.root_id());
|
||||||
@@ -308,6 +335,7 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a [`NodeWatcher`] to the dom. Node watchers are called whenever a node is created or removed.
|
||||||
pub fn add_node_watcher(&mut self, watcher: impl NodeWatcher<V> + 'static + Send + Sync) {
|
pub fn add_node_watcher(&mut self, watcher: impl NodeWatcher<V> + 'static + Send + Sync) {
|
||||||
self.node_watchers.write().unwrap().push(Box::new(watcher));
|
self.node_watchers.write().unwrap().push(Box::new(watcher));
|
||||||
}
|
}
|
||||||
@@ -323,6 +351,7 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A reference to a tracked component in a node.
|
||||||
pub struct ViewEntry<'a, V: Component + Send + Sync> {
|
pub struct ViewEntry<'a, V: Component + Send + Sync> {
|
||||||
view: View<'a, V>,
|
view: View<'a, V>,
|
||||||
id: NodeId,
|
id: NodeId,
|
||||||
@@ -342,6 +371,7 @@ impl<'a, V: Component + Send + Sync> Deref for ViewEntry<'a, V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A mutable reference to a tracked component in a node.
|
||||||
pub struct ViewEntryMut<'a, V: Component<Tracking = Untracked> + Send + Sync> {
|
pub struct ViewEntryMut<'a, V: Component<Tracking = Untracked> + Send + Sync> {
|
||||||
view: ViewMut<'a, V, Untracked>,
|
view: ViewMut<'a, V, Untracked>,
|
||||||
id: NodeId,
|
id: NodeId,
|
||||||
@@ -367,16 +397,21 @@ impl<'a, V: Component<Tracking = Untracked> + Send + Sync> DerefMut for ViewEntr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A immutable view of a node
|
||||||
pub trait NodeImmutable<V: FromAnyValue + Send + Sync>: Sized {
|
pub trait NodeImmutable<V: FromAnyValue + Send + Sync>: Sized {
|
||||||
|
/// Get the real dom this node was created in
|
||||||
fn real_dom(&self) -> &RealDom<V>;
|
fn real_dom(&self) -> &RealDom<V>;
|
||||||
|
|
||||||
|
/// Get the id of the current node
|
||||||
fn id(&self) -> NodeId;
|
fn id(&self) -> NodeId;
|
||||||
|
|
||||||
|
/// Get the type of the current node
|
||||||
#[inline]
|
#[inline]
|
||||||
fn node_type(&self) -> ViewEntry<NodeType<V>> {
|
fn node_type(&self) -> ViewEntry<NodeType<V>> {
|
||||||
self.get().unwrap()
|
self.get().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a component from the current node
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get<'a, T: Component + Sync + Send>(&'a self) -> Option<ViewEntry<'a, T>> {
|
fn get<'a, T: Component + Sync + Send>(&'a self) -> Option<ViewEntry<'a, T>> {
|
||||||
// self.real_dom().tree.get(self.id())
|
// self.real_dom().tree.get(self.id())
|
||||||
@@ -385,11 +420,13 @@ pub trait NodeImmutable<V: FromAnyValue + Send + Sync>: Sized {
|
|||||||
.then(|| ViewEntry::new(view, self.id()))
|
.then(|| ViewEntry::new(view, self.id()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the ids of the children of the current node
|
||||||
#[inline]
|
#[inline]
|
||||||
fn child_ids(&self) -> Vec<NodeId> {
|
fn child_ids(&self) -> Vec<NodeId> {
|
||||||
self.real_dom().tree_ref().children_ids(self.id())
|
self.real_dom().tree_ref().children_ids(self.id())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the children of the current node
|
||||||
#[inline]
|
#[inline]
|
||||||
fn children(&self) -> Vec<NodeRef<V>> {
|
fn children(&self) -> Vec<NodeRef<V>> {
|
||||||
self.child_ids()
|
self.child_ids()
|
||||||
@@ -401,11 +438,13 @@ pub trait NodeImmutable<V: FromAnyValue + Send + Sync>: Sized {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the id of the parent of the current node
|
||||||
#[inline]
|
#[inline]
|
||||||
fn parent_id(&self) -> Option<NodeId> {
|
fn parent_id(&self) -> Option<NodeId> {
|
||||||
self.real_dom().tree_ref().parent_id(self.id())
|
self.real_dom().tree_ref().parent_id(self.id())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the parent of the current node
|
||||||
#[inline]
|
#[inline]
|
||||||
fn parent(&self) -> Option<NodeRef<V>> {
|
fn parent(&self) -> Option<NodeRef<V>> {
|
||||||
self.parent_id().map(|id| NodeRef {
|
self.parent_id().map(|id| NodeRef {
|
||||||
@@ -414,6 +453,7 @@ pub trait NodeImmutable<V: FromAnyValue + Send + Sync>: Sized {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the node after the current node
|
||||||
#[inline]
|
#[inline]
|
||||||
fn next(&self) -> Option<NodeRef<V>> {
|
fn next(&self) -> Option<NodeRef<V>> {
|
||||||
let parent = self.parent_id()?;
|
let parent = self.parent_id()?;
|
||||||
@@ -429,6 +469,7 @@ pub trait NodeImmutable<V: FromAnyValue + Send + Sync>: Sized {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the node before the current node
|
||||||
#[inline]
|
#[inline]
|
||||||
fn prev(&self) -> Option<NodeRef<V>> {
|
fn prev(&self) -> Option<NodeRef<V>> {
|
||||||
let parent = self.parent_id()?;
|
let parent = self.parent_id()?;
|
||||||
@@ -444,12 +485,14 @@ pub trait NodeImmutable<V: FromAnyValue + Send + Sync>: Sized {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the height of the current node in the tree (the number of nodes between the current node and the root)
|
||||||
#[inline]
|
#[inline]
|
||||||
fn height(&self) -> u16 {
|
fn height(&self) -> u16 {
|
||||||
self.real_dom().tree_ref().height(self.id()).unwrap()
|
self.real_dom().tree_ref().height(self.id()).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An immutable reference to a node in a RealDom
|
||||||
pub struct NodeRef<'a, V: FromAnyValue + Send + Sync = ()> {
|
pub struct NodeRef<'a, V: FromAnyValue + Send + Sync = ()> {
|
||||||
id: NodeId,
|
id: NodeId,
|
||||||
dom: &'a RealDom<V>,
|
dom: &'a RealDom<V>,
|
||||||
@@ -478,12 +521,14 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeImmutable<V> for NodeRef<'a, V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A mutable refrence to a node in the RealDom that tracks what States need to be updated
|
||||||
pub struct NodeMut<'a, V: FromAnyValue + Send + Sync = ()> {
|
pub struct NodeMut<'a, V: FromAnyValue + Send + Sync = ()> {
|
||||||
id: NodeId,
|
id: NodeId,
|
||||||
dom: &'a mut RealDom<V>,
|
dom: &'a mut RealDom<V>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> {
|
impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> {
|
||||||
|
/// Create a new mutable refrence to a node in a RealDom
|
||||||
pub fn new(id: NodeId, dom: &'a mut RealDom<V>) -> Self {
|
pub fn new(id: NodeId, dom: &'a mut RealDom<V>) -> Self {
|
||||||
Self { id, dom }
|
Self { id, dom }
|
||||||
}
|
}
|
||||||
@@ -502,16 +547,19 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeImmutable<V> for NodeMut<'a, V> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> {
|
impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> {
|
||||||
|
/// Get the real dom this node was created in mutably
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn real_dom_mut(&mut self) -> &mut RealDom<V> {
|
pub fn real_dom_mut(&mut self) -> &mut RealDom<V> {
|
||||||
self.dom
|
self.dom
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the parent of this node mutably
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parent_mut(&mut self) -> Option<NodeMut<V>> {
|
pub fn parent_mut(&mut self) -> Option<NodeMut<V>> {
|
||||||
self.parent_id().map(|id| NodeMut { id, dom: self.dom })
|
self.parent_id().map(|id| NodeMut { id, dom: self.dom })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a component from the current node mutably
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_mut<T: Component<Tracking = Untracked> + Sync + Send>(
|
pub fn get_mut<T: Component<Tracking = Untracked> + Sync + Send>(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -527,6 +575,9 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> {
|
|||||||
Some(ViewEntryMut::new(view_mut, self.id))
|
Some(ViewEntryMut::new(view_mut, self.id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Insert a custom component into this node
|
||||||
|
///
|
||||||
|
/// Note: Components that implement State and are added when the RealDom is created will automatically be created
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn insert<T: Component + Sync + Send>(&mut self, value: T) {
|
pub fn insert<T: Component + Sync + Send>(&mut self, value: T) {
|
||||||
// mark the node state as dirty
|
// mark the node state as dirty
|
||||||
@@ -539,6 +590,7 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> {
|
|||||||
self.dom.world.add_component(self.id, value);
|
self.dom.world.add_component(self.id, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the next node
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn next_mut(self) -> Option<NodeMut<'a, V>> {
|
pub fn next_mut(self) -> Option<NodeMut<'a, V>> {
|
||||||
let parent = self.parent_id()?;
|
let parent = self.parent_id()?;
|
||||||
@@ -551,6 +603,7 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the previous node
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn prev_mut(self) -> Option<NodeMut<'a, V>> {
|
pub fn prev_mut(self) -> Option<NodeMut<'a, V>> {
|
||||||
let parent = self.parent_id()?;
|
let parent = self.parent_id()?;
|
||||||
@@ -563,6 +616,7 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add the given node to the end of this nodes children
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn add_child(&mut self, child: NodeId) {
|
pub fn add_child(&mut self, child: NodeId) {
|
||||||
self.dom.dirty_nodes.mark_child_changed(self.id);
|
self.dom.dirty_nodes.mark_child_changed(self.id);
|
||||||
@@ -571,6 +625,7 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> {
|
|||||||
NodeMut::new(child, self.dom).mark_moved();
|
NodeMut::new(child, self.dom).mark_moved();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Insert this node after the given node
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn insert_after(&mut self, old: NodeId) {
|
pub fn insert_after(&mut self, old: NodeId) {
|
||||||
let id = self.id();
|
let id = self.id();
|
||||||
@@ -583,6 +638,7 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> {
|
|||||||
self.mark_moved();
|
self.mark_moved();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Insert this node before the given node
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn insert_before(&mut self, old: NodeId) {
|
pub fn insert_before(&mut self, old: NodeId) {
|
||||||
let id = self.id();
|
let id = self.id();
|
||||||
@@ -595,6 +651,7 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> {
|
|||||||
self.mark_moved();
|
self.mark_moved();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Remove this node from the RealDom
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn remove(&mut self) {
|
pub fn remove(&mut self) {
|
||||||
let id = self.id();
|
let id = self.id();
|
||||||
@@ -629,8 +686,9 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> {
|
|||||||
self.dom.tree_mut().remove_single(id);
|
self.dom.tree_mut().remove_single(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Replace this node with a different node
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn replace(&mut self, new: NodeId) {
|
pub fn replace(mut self, new: NodeId) {
|
||||||
self.mark_removed();
|
self.mark_removed();
|
||||||
if let Some(parent_id) = self.parent_id() {
|
if let Some(parent_id) = self.parent_id() {
|
||||||
self.real_dom_mut()
|
self.real_dom_mut()
|
||||||
@@ -644,6 +702,7 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> {
|
|||||||
self.dom.tree_mut().replace(id, new);
|
self.dom.tree_mut().replace(id, new);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add an event listener
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn add_event_listener(&mut self, event: &str) {
|
pub fn add_event_listener(&mut self, event: &str) {
|
||||||
let id = self.id();
|
let id = self.id();
|
||||||
@@ -673,6 +732,7 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Remove an event listener
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn remove_event_listener(&mut self, event: &str) {
|
pub fn remove_event_listener(&mut self, event: &str) {
|
||||||
let id = self.id();
|
let id = self.id();
|
||||||
@@ -694,6 +754,7 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// mark that this node was removed for the incremental system
|
||||||
fn mark_removed(&mut self) {
|
fn mark_removed(&mut self) {
|
||||||
let watchers = self.dom.node_watchers.clone();
|
let watchers = self.dom.node_watchers.clone();
|
||||||
for watcher in &*watchers.read().unwrap() {
|
for watcher in &*watchers.read().unwrap() {
|
||||||
@@ -701,6 +762,7 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// mark that this node was moved for the incremental system
|
||||||
fn mark_moved(&mut self) {
|
fn mark_moved(&mut self) {
|
||||||
let watchers = self.dom.node_watchers.clone();
|
let watchers = self.dom.node_watchers.clone();
|
||||||
for watcher in &*watchers.read().unwrap() {
|
for watcher in &*watchers.read().unwrap() {
|
||||||
@@ -708,6 +770,7 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a mutable reference to the type of the current node
|
||||||
pub fn node_type_mut(&mut self) -> NodeTypeMut<'_, V> {
|
pub fn node_type_mut(&mut self) -> NodeTypeMut<'_, V> {
|
||||||
let id = self.id();
|
let id = self.id();
|
||||||
let RealDom {
|
let RealDom {
|
||||||
@@ -730,6 +793,7 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the type of the current node
|
||||||
pub fn set_type(&mut self, new: NodeType<V>) {
|
pub fn set_type(&mut self, new: NodeType<V>) {
|
||||||
{
|
{
|
||||||
let mut view: ViewMut<NodeType<V>> = self.dom.borrow_node_type_mut().unwrap();
|
let mut view: ViewMut<NodeType<V>> = self.dom.borrow_node_type_mut().unwrap();
|
||||||
@@ -740,6 +804,8 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> {
|
|||||||
.mark_dirty(self.id, NodeMaskBuilder::ALL.build())
|
.mark_dirty(self.id, NodeMaskBuilder::ALL.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clone a node and it's children and returns the id of the new node.
|
||||||
|
/// This is more effecient than creating the node from scratch because it can pre-allocate the memory required.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn clone_node(&mut self) -> NodeId {
|
pub fn clone_node(&mut self) -> NodeId {
|
||||||
let new_node = self.node_type().clone();
|
let new_node = self.node_type().clone();
|
||||||
@@ -757,12 +823,17 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A mutable refrence to the type of a node in the RealDom
|
||||||
pub enum NodeTypeMut<'a, V: FromAnyValue + Send + Sync = ()> {
|
pub enum NodeTypeMut<'a, V: FromAnyValue + Send + Sync = ()> {
|
||||||
|
/// An element node
|
||||||
Element(ElementNodeMut<'a, V>),
|
Element(ElementNodeMut<'a, V>),
|
||||||
|
/// A text node
|
||||||
Text(TextNodeMut<'a, V>),
|
Text(TextNodeMut<'a, V>),
|
||||||
|
/// A placeholder node
|
||||||
Placeholder,
|
Placeholder,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A mutable refrence to a text node in the RealDom
|
||||||
pub struct TextNodeMut<'a, V: FromAnyValue + Send + Sync = ()> {
|
pub struct TextNodeMut<'a, V: FromAnyValue + Send + Sync = ()> {
|
||||||
id: NodeId,
|
id: NodeId,
|
||||||
text: ViewEntryMut<'a, NodeType<V>>,
|
text: ViewEntryMut<'a, NodeType<V>>,
|
||||||
@@ -770,6 +841,7 @@ pub struct TextNodeMut<'a, V: FromAnyValue + Send + Sync = ()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<V: FromAnyValue + Send + Sync> TextNodeMut<'_, V> {
|
impl<V: FromAnyValue + Send + Sync> TextNodeMut<'_, V> {
|
||||||
|
/// Get the underlying test of the node
|
||||||
pub fn text(&self) -> &str {
|
pub fn text(&self) -> &str {
|
||||||
match &*self.text {
|
match &*self.text {
|
||||||
NodeType::Text(text) => &text.text,
|
NodeType::Text(text) => &text.text,
|
||||||
@@ -777,6 +849,7 @@ impl<V: FromAnyValue + Send + Sync> TextNodeMut<'_, V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the underlying text mutably
|
||||||
pub fn text_mut(&mut self) -> &mut String {
|
pub fn text_mut(&mut self) -> &mut String {
|
||||||
self.dirty_nodes
|
self.dirty_nodes
|
||||||
.mark_dirty(self.id, NodeMaskBuilder::new().with_text().build());
|
.mark_dirty(self.id, NodeMaskBuilder::new().with_text().build());
|
||||||
@@ -804,6 +877,7 @@ impl<V: FromAnyValue + Send + Sync> DerefMut for TextNodeMut<'_, V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A mutable refrence to a text Element node in the RealDom
|
||||||
pub struct ElementNodeMut<'a, V: FromAnyValue + Send + Sync = ()> {
|
pub struct ElementNodeMut<'a, V: FromAnyValue + Send + Sync = ()> {
|
||||||
id: NodeId,
|
id: NodeId,
|
||||||
element: ViewEntryMut<'a, NodeType<V>>,
|
element: ViewEntryMut<'a, NodeType<V>>,
|
||||||
@@ -811,6 +885,7 @@ pub struct ElementNodeMut<'a, V: FromAnyValue + Send + Sync = ()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<V: FromAnyValue + Send + Sync> ElementNodeMut<'_, V> {
|
impl<V: FromAnyValue + Send + Sync> ElementNodeMut<'_, V> {
|
||||||
|
/// Get the current element
|
||||||
fn element(&self) -> &ElementNode<V> {
|
fn element(&self) -> &ElementNode<V> {
|
||||||
match &*self.element {
|
match &*self.element {
|
||||||
NodeType::Element(element) => element,
|
NodeType::Element(element) => element,
|
||||||
@@ -818,6 +893,7 @@ impl<V: FromAnyValue + Send + Sync> ElementNodeMut<'_, V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the current element mutably (does not mark anything as dirty)
|
||||||
fn element_mut(&mut self) -> &mut ElementNode<V> {
|
fn element_mut(&mut self) -> &mut ElementNode<V> {
|
||||||
match &mut *self.element {
|
match &mut *self.element {
|
||||||
NodeType::Element(element) => element,
|
NodeType::Element(element) => element,
|
||||||
@@ -825,30 +901,36 @@ impl<V: FromAnyValue + Send + Sync> ElementNodeMut<'_, V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the tag of the element
|
||||||
pub fn tag(&self) -> &str {
|
pub fn tag(&self) -> &str {
|
||||||
&self.element().tag
|
&self.element().tag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a mutable reference to the tag of the element
|
||||||
pub fn tag_mut(&mut self) -> &mut String {
|
pub fn tag_mut(&mut self) -> &mut String {
|
||||||
self.dirty_nodes
|
self.dirty_nodes
|
||||||
.mark_dirty(self.id, NodeMaskBuilder::new().with_tag().build());
|
.mark_dirty(self.id, NodeMaskBuilder::new().with_tag().build());
|
||||||
&mut self.element_mut().tag
|
&mut self.element_mut().tag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a reference to the namespace the element is in
|
||||||
pub fn namespace(&self) -> Option<&str> {
|
pub fn namespace(&self) -> Option<&str> {
|
||||||
self.element().namespace.as_deref()
|
self.element().namespace.as_deref()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a mutable reference to the namespace the element is in
|
||||||
pub fn namespace_mut(&mut self) -> &mut Option<String> {
|
pub fn namespace_mut(&mut self) -> &mut Option<String> {
|
||||||
self.dirty_nodes
|
self.dirty_nodes
|
||||||
.mark_dirty(self.id, NodeMaskBuilder::new().with_namespace().build());
|
.mark_dirty(self.id, NodeMaskBuilder::new().with_namespace().build());
|
||||||
&mut self.element_mut().namespace
|
&mut self.element_mut().namespace
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a reference to all of the attributes currently set on the element
|
||||||
pub fn attributes(&self) -> &FxHashMap<OwnedAttributeDiscription, OwnedAttributeValue<V>> {
|
pub fn attributes(&self) -> &FxHashMap<OwnedAttributeDiscription, OwnedAttributeValue<V>> {
|
||||||
&self.element().attributes
|
&self.element().attributes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set an attribute in the element
|
||||||
pub fn set_attribute(
|
pub fn set_attribute(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: OwnedAttributeDiscription,
|
name: OwnedAttributeDiscription,
|
||||||
@@ -863,7 +945,8 @@ impl<V: FromAnyValue + Send + Sync> ElementNodeMut<'_, V> {
|
|||||||
self.element_mut().attributes.insert(name, value)
|
self.element_mut().attributes.insert(name, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_attributes(
|
/// Remove an attribute from the element
|
||||||
|
pub fn remove_attribute(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &OwnedAttributeDiscription,
|
name: &OwnedAttributeDiscription,
|
||||||
) -> Option<OwnedAttributeValue<V>> {
|
) -> Option<OwnedAttributeValue<V>> {
|
||||||
@@ -876,6 +959,7 @@ impl<V: FromAnyValue + Send + Sync> ElementNodeMut<'_, V> {
|
|||||||
self.element_mut().attributes.remove(name)
|
self.element_mut().attributes.remove(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get an attribute of the element
|
||||||
pub fn get_attribute_mut(
|
pub fn get_attribute_mut(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &OwnedAttributeDiscription,
|
name: &OwnedAttributeDiscription,
|
||||||
@@ -889,13 +973,18 @@ impl<V: FromAnyValue + Send + Sync> ElementNodeMut<'_, V> {
|
|||||||
self.element_mut().attributes.get_mut(name)
|
self.element_mut().attributes.get_mut(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the set of all events the element is listening to
|
||||||
pub fn listeners(&self) -> &FxHashSet<String> {
|
pub fn listeners(&self) -> &FxHashSet<String> {
|
||||||
&self.element().listeners
|
&self.element().listeners
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn construct_workload<V: FromAnyValue + Send + Sync>(passes: &mut [TypeErasedPass<V>]) -> Workload {
|
// Create a workload from all of the passes. This orders the passes so that each pass will only run at most once.
|
||||||
|
fn construct_workload<V: FromAnyValue + Send + Sync>(
|
||||||
|
passes: &mut [TypeErasedState<V>],
|
||||||
|
) -> Workload {
|
||||||
let mut workload = Workload::new("Main Workload");
|
let mut workload = Workload::new("Main Workload");
|
||||||
|
// Assign a unique index to keep track of each pass
|
||||||
let mut unresloved_workloads = passes
|
let mut unresloved_workloads = passes
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
@@ -922,6 +1011,7 @@ fn construct_workload<V: FromAnyValue + Send + Sync>(passes: &mut [TypeErasedPas
|
|||||||
.map(|workload| workload.after_all(dependancy_id.to_string()));
|
.map(|workload| workload.after_all(dependancy_id.to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Add all of the passes
|
||||||
for (_, _, mut workload_system) in unresloved_workloads {
|
for (_, _, mut workload_system) in unresloved_workloads {
|
||||||
workload = workload.with_system(workload_system.take().unwrap());
|
workload = workload.with_system(workload_system.take().unwrap());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
|
//! A tree of nodes intigated with shipyard
|
||||||
|
|
||||||
use crate::NodeId;
|
use crate::NodeId;
|
||||||
use shipyard::{Component, EntitiesViewMut, Get, View, ViewMut};
|
use shipyard::{Component, EntitiesViewMut, Get, View, ViewMut};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
/// A node in a tree.
|
||||||
#[derive(PartialEq, Eq, Clone, Debug, Component)]
|
#[derive(PartialEq, Eq, Clone, Debug, Component)]
|
||||||
pub struct Node {
|
pub struct Node {
|
||||||
parent: Option<NodeId>,
|
parent: Option<NodeId>,
|
||||||
@@ -9,24 +12,38 @@ pub struct Node {
|
|||||||
height: u16,
|
height: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A view of a tree.
|
||||||
pub type TreeRefView<'a> = View<'a, Node>;
|
pub type TreeRefView<'a> = View<'a, Node>;
|
||||||
|
/// A mutable view of a tree.
|
||||||
pub type TreeMutView<'a> = (EntitiesViewMut<'a>, ViewMut<'a, Node>);
|
pub type TreeMutView<'a> = (EntitiesViewMut<'a>, ViewMut<'a, Node>);
|
||||||
|
|
||||||
|
/// A immutable view of a tree.
|
||||||
pub trait TreeRef {
|
pub trait TreeRef {
|
||||||
|
/// The parent id of the node.
|
||||||
fn parent_id(&self, id: NodeId) -> Option<NodeId>;
|
fn parent_id(&self, id: NodeId) -> Option<NodeId>;
|
||||||
|
/// The children ids of the node.
|
||||||
fn children_ids(&self, id: NodeId) -> Vec<NodeId>;
|
fn children_ids(&self, id: NodeId) -> Vec<NodeId>;
|
||||||
|
/// The height of the node.
|
||||||
fn height(&self, id: NodeId) -> Option<u16>;
|
fn height(&self, id: NodeId) -> Option<u16>;
|
||||||
|
/// Returns true if the node exists.
|
||||||
fn contains(&self, id: NodeId) -> bool;
|
fn contains(&self, id: NodeId) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A mutable view of a tree.
|
||||||
pub trait TreeMut: TreeRef {
|
pub trait TreeMut: TreeRef {
|
||||||
|
/// Removes the node and all of its children.
|
||||||
fn remove(&mut self, id: NodeId);
|
fn remove(&mut self, id: NodeId);
|
||||||
|
/// Removes the node and all of its children.
|
||||||
fn remove_single(&mut self, id: NodeId);
|
fn remove_single(&mut self, id: NodeId);
|
||||||
fn set_height(&mut self, node: NodeId, height: u16);
|
/// Adds a new node to the tree.
|
||||||
fn create_node(&mut self, id: NodeId);
|
fn create_node(&mut self, id: NodeId);
|
||||||
|
/// Adds a child to the node.
|
||||||
fn add_child(&mut self, parent: NodeId, new: NodeId);
|
fn add_child(&mut self, parent: NodeId, new: NodeId);
|
||||||
|
/// Replaces the node with a new node.
|
||||||
fn replace(&mut self, old_id: NodeId, new_id: NodeId);
|
fn replace(&mut self, old_id: NodeId, new_id: NodeId);
|
||||||
|
/// Inserts a node before another node.
|
||||||
fn insert_before(&mut self, old_id: NodeId, new_id: NodeId);
|
fn insert_before(&mut self, old_id: NodeId, new_id: NodeId);
|
||||||
|
/// Inserts a node after another node.
|
||||||
fn insert_after(&mut self, old_id: NodeId, new_id: NodeId);
|
fn insert_after(&mut self, old_id: NodeId, new_id: NodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,18 +96,6 @@ impl<'a> TreeMut for TreeMutView<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_height(&mut self, node: NodeId, height: u16) {
|
|
||||||
let children = {
|
|
||||||
let mut node_data_mut = &mut self.1;
|
|
||||||
let mut node = (&mut node_data_mut).get(node).unwrap();
|
|
||||||
node.height = height;
|
|
||||||
node.children.clone()
|
|
||||||
};
|
|
||||||
for child in children {
|
|
||||||
self.set_height(child, height + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_node(&mut self, id: NodeId) {
|
fn create_node(&mut self, id: NodeId) {
|
||||||
let (entities, node_data_mut) = self;
|
let (entities, node_data_mut) = self;
|
||||||
entities.add_component(
|
entities.add_component(
|
||||||
@@ -113,7 +118,7 @@ impl<'a> TreeMut for TreeMutView<'a> {
|
|||||||
parent.children.push(new);
|
parent.children.push(new);
|
||||||
height = parent.height + 1;
|
height = parent.height + 1;
|
||||||
}
|
}
|
||||||
self.set_height(new, height);
|
set_height(self, new, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace(&mut self, old_id: NodeId, new_id: NodeId) {
|
fn replace(&mut self, old_id: NodeId, new_id: NodeId) {
|
||||||
@@ -129,7 +134,7 @@ impl<'a> TreeMut for TreeMutView<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let height = parent.height + 1;
|
let height = parent.height + 1;
|
||||||
self.set_height(new_id, height);
|
set_height(self, new_id, height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// remove the old node
|
// remove the old node
|
||||||
@@ -149,7 +154,7 @@ impl<'a> TreeMut for TreeMutView<'a> {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
parent.children.insert(index, new_id);
|
parent.children.insert(index, new_id);
|
||||||
let height = parent.height + 1;
|
let height = parent.height + 1;
|
||||||
self.set_height(new_id, height);
|
set_height(self, new_id, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_after(&mut self, old_id: NodeId, new_id: NodeId) {
|
fn insert_after(&mut self, old_id: NodeId, new_id: NodeId) {
|
||||||
@@ -165,7 +170,20 @@ impl<'a> TreeMut for TreeMutView<'a> {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
parent.children.insert(index + 1, new_id);
|
parent.children.insert(index + 1, new_id);
|
||||||
let height = parent.height + 1;
|
let height = parent.height + 1;
|
||||||
self.set_height(new_id, height);
|
set_height(self, new_id, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the height of a node and updates the height of all its children
|
||||||
|
fn set_height(tree: &mut TreeMutView<'_>, node: NodeId, height: u16) {
|
||||||
|
let children = {
|
||||||
|
let mut node_data_mut = &mut tree.1;
|
||||||
|
let mut node = (&mut node_data_mut).get(node).unwrap();
|
||||||
|
node.height = height;
|
||||||
|
node.children.clone()
|
||||||
|
};
|
||||||
|
for child in children {
|
||||||
|
set_height(tree, child, height + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//! A cursor implementation that can be used to navigate and edit text.
|
||||||
|
|
||||||
use std::{cmp::Ordering, ops::Range};
|
use std::{cmp::Ordering, ops::Range};
|
||||||
|
|
||||||
use keyboard_types::{Code, Key, Modifiers};
|
use keyboard_types::{Code, Key, Modifiers};
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
//! # Utilities for renders using the RealDOM
|
||||||
|
//!
|
||||||
|
//! This includes an iterator that can be used to iterate over the children of a node that persists changes in the struture of the DOM, and a cursor for text editing.
|
||||||
|
|
||||||
mod persistant_iterator;
|
mod persistant_iterator;
|
||||||
pub use persistant_iterator::*;
|
pub use persistant_iterator::*;
|
||||||
pub mod cursor;
|
pub mod cursor;
|
||||||
@@ -3,27 +3,53 @@ use smallvec::SmallVec;
|
|||||||
use crate::{
|
use crate::{
|
||||||
node::FromAnyValue,
|
node::FromAnyValue,
|
||||||
node_watcher::NodeWatcher,
|
node_watcher::NodeWatcher,
|
||||||
|
prelude::{NodeMut, NodeRef},
|
||||||
real_dom::{NodeImmutable, RealDom},
|
real_dom::{NodeImmutable, RealDom},
|
||||||
NodeId, NodeRef,
|
NodeId,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
/// The element produced by the iterator
|
||||||
pub enum ElementProduced {
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
/// The iterator produced an element by progressing to the next node in a depth first order.
|
pub struct ElementProduced {
|
||||||
Progressed(NodeId),
|
id: NodeId,
|
||||||
|
movement: IteratorMovement,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
/// The method by which the iterator produced an element
|
||||||
|
pub enum IteratorMovement {
|
||||||
|
/// The iterator produced an element by progressing to the next node
|
||||||
|
Progressed,
|
||||||
/// The iterator reached the end of the tree and looped back to the root
|
/// The iterator reached the end of the tree and looped back to the root
|
||||||
Looped(NodeId),
|
Looped,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementProduced {
|
impl ElementProduced {
|
||||||
|
/// Get the id of the element produced
|
||||||
pub fn id(&self) -> NodeId {
|
pub fn id(&self) -> NodeId {
|
||||||
match self {
|
self.id
|
||||||
ElementProduced::Progressed(id) => *id,
|
}
|
||||||
ElementProduced::Looped(id) => *id,
|
|
||||||
|
/// The movement the iterator made to produce the element
|
||||||
|
pub fn movement(&self) -> &IteratorMovement {
|
||||||
|
&self.movement
|
||||||
|
}
|
||||||
|
|
||||||
|
fn looped(id: NodeId) -> Self {
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
movement: IteratorMovement::Looped,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn progressed(id: NodeId) -> Self {
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
movement: IteratorMovement::Progressed,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -34,7 +60,7 @@ struct PersistantElementIterUpdater<V> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<V: FromAnyValue + Sync + Send> NodeWatcher<V> for PersistantElementIterUpdater<V> {
|
impl<V: FromAnyValue + Sync + Send> NodeWatcher<V> for PersistantElementIterUpdater<V> {
|
||||||
fn on_node_moved(&self, node: crate::NodeMut<V>) {
|
fn on_node_moved(&self, node: NodeMut<V>) {
|
||||||
// if any element is moved, update its parents in the stack
|
// if any element is moved, update its parents in the stack
|
||||||
let mut stack = self.stack.lock().unwrap();
|
let mut stack = self.stack.lock().unwrap();
|
||||||
let moved = node.id();
|
let moved = node.id();
|
||||||
@@ -52,7 +78,7 @@ impl<V: FromAnyValue + Sync + Send> NodeWatcher<V> for PersistantElementIterUpda
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_node_removed(&self, node: crate::NodeMut<V>) {
|
fn on_node_removed(&self, node: NodeMut<V>) {
|
||||||
// if any element is removed in the chain, remove it and its children from the stack
|
// if any element is removed in the chain, remove it and its children from the stack
|
||||||
let mut stack = self.stack.lock().unwrap();
|
let mut stack = self.stack.lock().unwrap();
|
||||||
let removed = node.id();
|
let removed = node.id();
|
||||||
@@ -62,9 +88,9 @@ impl<V: FromAnyValue + Sync + Send> NodeWatcher<V> for PersistantElementIterUpda
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Focus systems need a iterator that can persist through changes in the [dioxus_core::VirtualDom].
|
/// Focus systems need a iterator that can persist through changes in the [crate::prelude::RealDom]
|
||||||
/// This iterator traverses the tree depth first.
|
/// This iterator traverses the tree depth first.
|
||||||
/// Iterate through it with [PersistantElementIter::next] [PersistantElementIter::prev], and update it with [PersistantElementIter::prune] (with data from [`dioxus_core::prelude::VirtualDom::work_with_deadline`]).
|
/// You can iterate through it with [PersistantElementIter::next] and [PersistantElementIter::prev].
|
||||||
/// The iterator loops around when it reaches the end or the beginning.
|
/// The iterator loops around when it reaches the end or the beginning.
|
||||||
pub struct PersistantElementIter {
|
pub struct PersistantElementIter {
|
||||||
// stack of elements and fragments, the last element is the last element that was yielded
|
// stack of elements and fragments, the last element is the last element that was yielded
|
||||||
@@ -72,6 +98,7 @@ pub struct PersistantElementIter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PersistantElementIter {
|
impl PersistantElementIter {
|
||||||
|
/// Create a new iterator in the RealDom
|
||||||
pub fn create<V: FromAnyValue + Send + Sync>(rdom: &mut RealDom<V>) -> Self {
|
pub fn create<V: FromAnyValue + Send + Sync>(rdom: &mut RealDom<V>) -> Self {
|
||||||
let inner = Arc::new(Mutex::new(smallvec::smallvec![rdom.root_id()]));
|
let inner = Arc::new(Mutex::new(smallvec::smallvec![rdom.root_id()]));
|
||||||
|
|
||||||
@@ -90,7 +117,7 @@ impl PersistantElementIter {
|
|||||||
let id = rdom.root_id();
|
let id = rdom.root_id();
|
||||||
let new = id;
|
let new = id;
|
||||||
stack.push(new);
|
stack.push(new);
|
||||||
ElementProduced::Looped(id)
|
ElementProduced::looped(id)
|
||||||
} else {
|
} else {
|
||||||
let mut look_in_children = true;
|
let mut look_in_children = true;
|
||||||
loop {
|
loop {
|
||||||
@@ -100,7 +127,7 @@ impl PersistantElementIter {
|
|||||||
if let Some(first) = current.children().first() {
|
if let Some(first) = current.children().first() {
|
||||||
let new = first.id();
|
let new = first.id();
|
||||||
stack.push(new);
|
stack.push(new);
|
||||||
return ElementProduced::Progressed(new);
|
return ElementProduced::progressed(new);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stack.pop();
|
stack.pop();
|
||||||
@@ -108,7 +135,7 @@ impl PersistantElementIter {
|
|||||||
// the next element exists, add it to the stack and return it
|
// the next element exists, add it to the stack and return it
|
||||||
let new = new.id();
|
let new = new.id();
|
||||||
stack.push(new);
|
stack.push(new);
|
||||||
return ElementProduced::Progressed(new);
|
return ElementProduced::progressed(new);
|
||||||
}
|
}
|
||||||
// otherwise, continue the loop and go to the parent
|
// otherwise, continue the loop and go to the parent
|
||||||
} else {
|
} else {
|
||||||
@@ -116,7 +143,7 @@ impl PersistantElementIter {
|
|||||||
let new = rdom.root_id();
|
let new = rdom.root_id();
|
||||||
stack.clear();
|
stack.clear();
|
||||||
stack.push(new);
|
stack.push(new);
|
||||||
return ElementProduced::Looped(new);
|
return ElementProduced::looped(new);
|
||||||
}
|
}
|
||||||
look_in_children = false;
|
look_in_children = false;
|
||||||
}
|
}
|
||||||
@@ -141,28 +168,28 @@ impl PersistantElementIter {
|
|||||||
if stack.is_empty() {
|
if stack.is_empty() {
|
||||||
let id = rdom.root_id();
|
let id = rdom.root_id();
|
||||||
let last = push_back(&mut stack, rdom.get(id).unwrap());
|
let last = push_back(&mut stack, rdom.get(id).unwrap());
|
||||||
ElementProduced::Looped(last)
|
ElementProduced::looped(last)
|
||||||
} else if let Some(current) = stack.pop().and_then(|last| rdom.get(last)) {
|
} else if let Some(current) = stack.pop().and_then(|last| rdom.get(last)) {
|
||||||
if let Some(new) = current.prev() {
|
if let Some(new) = current.prev() {
|
||||||
// the next element exists, add it to the stack and return it
|
// the next element exists, add it to the stack and return it
|
||||||
let new = push_back(&mut stack, new);
|
let new = push_back(&mut stack, new);
|
||||||
ElementProduced::Progressed(new)
|
ElementProduced::progressed(new)
|
||||||
}
|
}
|
||||||
// otherwise, yeild the parent
|
// otherwise, yeild the parent
|
||||||
else if let Some(parent) = stack.last() {
|
else if let Some(parent) = stack.last() {
|
||||||
// if there is a parent, return it
|
// if there is a parent, return it
|
||||||
ElementProduced::Progressed(*parent)
|
ElementProduced::progressed(*parent)
|
||||||
} else {
|
} else {
|
||||||
// if there is no parent, loop back to the root
|
// if there is no parent, loop back to the root
|
||||||
let id = rdom.root_id();
|
let id = rdom.root_id();
|
||||||
let last = push_back(&mut stack, rdom.get(id).unwrap());
|
let last = push_back(&mut stack, rdom.get(id).unwrap());
|
||||||
ElementProduced::Looped(last)
|
ElementProduced::looped(last)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// if there is no parent, loop back to the root
|
// if there is no parent, loop back to the root
|
||||||
let id = rdom.root_id();
|
let id = rdom.root_id();
|
||||||
let last = push_back(&mut stack, rdom.get(id).unwrap());
|
let last = push_back(&mut stack, rdom.get(id).unwrap());
|
||||||
ElementProduced::Looped(last)
|
ElementProduced::looped(last)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
use dioxus::prelude::*;
|
||||||
|
use dioxus_html::FormData;
|
||||||
|
use dioxus_tui::prelude::*;
|
||||||
|
use dioxus_tui::Config;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
dioxus_tui::launch_cfg(app, Config::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn app(cx: Scope) -> Element {
|
||||||
|
let bg_green = use_state(cx, || false);
|
||||||
|
|
||||||
|
let color = if *bg_green.get() { "green" } else { "red" };
|
||||||
|
cx.render(rsx! {
|
||||||
|
div{
|
||||||
|
width: "100%",
|
||||||
|
background_color: "{color}",
|
||||||
|
flex_direction: "column",
|
||||||
|
align_items: "center",
|
||||||
|
justify_content: "center",
|
||||||
|
|
||||||
|
Input{
|
||||||
|
oninput: |data: FormData| if &data.value == "good"{
|
||||||
|
bg_green.set(true);
|
||||||
|
} else{
|
||||||
|
bg_green.set(false);
|
||||||
|
},
|
||||||
|
r#type: "checkbox",
|
||||||
|
value: "good",
|
||||||
|
width: "50%",
|
||||||
|
height: "10%",
|
||||||
|
checked: "true",
|
||||||
|
}
|
||||||
|
Input{
|
||||||
|
oninput: |data: FormData| if &data.value == "hello world"{
|
||||||
|
bg_green.set(true);
|
||||||
|
} else{
|
||||||
|
bg_green.set(false);
|
||||||
|
},
|
||||||
|
width: "50%",
|
||||||
|
height: "10%",
|
||||||
|
maxlength: "11",
|
||||||
|
}
|
||||||
|
Input{
|
||||||
|
oninput: |data: FormData| {
|
||||||
|
if (data.value.parse::<f32>().unwrap() - 40.0).abs() < 5.0 {
|
||||||
|
bg_green.set(true);
|
||||||
|
} else{
|
||||||
|
bg_green.set(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
r#type: "range",
|
||||||
|
width: "50%",
|
||||||
|
height: "10%",
|
||||||
|
min: "20",
|
||||||
|
max: "80",
|
||||||
|
}
|
||||||
|
Input{
|
||||||
|
oninput: |data: FormData| {
|
||||||
|
if data.value == "10"{
|
||||||
|
bg_green.set(true);
|
||||||
|
} else{
|
||||||
|
bg_green.set(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
r#type: "number",
|
||||||
|
width: "50%",
|
||||||
|
height: "10%",
|
||||||
|
maxlength: "4",
|
||||||
|
}
|
||||||
|
Input{
|
||||||
|
oninput: |data: FormData| {
|
||||||
|
if data.value == "hello world"{
|
||||||
|
bg_green.set(true);
|
||||||
|
} else{
|
||||||
|
bg_green.set(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
r#type: "password",
|
||||||
|
width: "50%",
|
||||||
|
height: "10%",
|
||||||
|
maxlength: "11",
|
||||||
|
}
|
||||||
|
Input{
|
||||||
|
onclick: |_: FormData| bg_green.set(true),
|
||||||
|
r#type: "button",
|
||||||
|
value: "green",
|
||||||
|
width: "50%",
|
||||||
|
height: "10%",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -2,9 +2,9 @@ use crate::prevent_default::PreventDefault;
|
|||||||
|
|
||||||
use dioxus_native_core::{
|
use dioxus_native_core::{
|
||||||
node_ref::{AttributeMaskBuilder, NodeMaskBuilder},
|
node_ref::{AttributeMaskBuilder, NodeMaskBuilder},
|
||||||
|
prelude::*,
|
||||||
real_dom::NodeImmutable,
|
real_dom::NodeImmutable,
|
||||||
utils::{ElementProduced, PersistantElementIter},
|
utils::{IteratorMovement, PersistantElementIter},
|
||||||
Dependancy, NodeId, RealDom, SendAnyMap, State,
|
|
||||||
};
|
};
|
||||||
use dioxus_native_core_macro::partial_derive_state;
|
use dioxus_native_core_macro::partial_derive_state;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
@@ -178,7 +178,7 @@ impl FocusState {
|
|||||||
self.focus_iter.prev(rdom)
|
self.focus_iter.prev(rdom)
|
||||||
};
|
};
|
||||||
let new_id = new.id();
|
let new_id = new.id();
|
||||||
if let ElementProduced::Looped(_) = new {
|
if let IteratorMovement::Looped = new.movement() {
|
||||||
let mut closest_level = None;
|
let mut closest_level = None;
|
||||||
|
|
||||||
if forward {
|
if forward {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use crossterm::event::{
|
use crossterm::event::{
|
||||||
Event as TermEvent, KeyCode as TermKeyCode, KeyModifiers, MouseButton, MouseEventKind,
|
Event as TermEvent, KeyCode as TermKeyCode, KeyModifiers, MouseButton, MouseEventKind,
|
||||||
};
|
};
|
||||||
|
use dioxus_native_core::prelude::*;
|
||||||
use dioxus_native_core::real_dom::NodeImmutable;
|
use dioxus_native_core::real_dom::NodeImmutable;
|
||||||
use dioxus_native_core::{NodeId, NodeRef, RealDom};
|
|
||||||
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};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use dioxus_native_core::layout_attributes::{
|
|||||||
};
|
};
|
||||||
use dioxus_native_core::node::OwnedAttributeView;
|
use dioxus_native_core::node::OwnedAttributeView;
|
||||||
use dioxus_native_core::node_ref::{AttributeMaskBuilder, NodeMaskBuilder, NodeView};
|
use dioxus_native_core::node_ref::{AttributeMaskBuilder, NodeMaskBuilder, NodeView};
|
||||||
use dioxus_native_core::{Dependancy, SendAnyMap, State};
|
use dioxus_native_core::prelude::*;
|
||||||
use dioxus_native_core_macro::partial_derive_state;
|
use dioxus_native_core_macro::partial_derive_state;
|
||||||
use taffy::prelude::*;
|
use taffy::prelude::*;
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use crossterm::{
|
|||||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||||
};
|
};
|
||||||
use dioxus_html::EventData;
|
use dioxus_html::EventData;
|
||||||
use dioxus_native_core::{node_ref::NodeMaskBuilder, prelude::AnyState, real_dom::NodeImmutable};
|
use dioxus_native_core::prelude::*;
|
||||||
use dioxus_native_core::{real_dom::RealDom, FxDashSet, NodeId, SendAnyMap};
|
use dioxus_native_core::{real_dom::RealDom, FxDashSet, NodeId, SendAnyMap};
|
||||||
use focus::FocusState;
|
use focus::FocusState;
|
||||||
use futures::{channel::mpsc::UnboundedSender, pin_mut, Future, StreamExt};
|
use futures::{channel::mpsc::UnboundedSender, pin_mut, Future, StreamExt};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use dioxus_native_core::{node_ref::NodeView, Dependancy, SendAnyMap, State};
|
use dioxus_native_core::prelude::*;
|
||||||
use dioxus_native_core_macro::partial_derive_state;
|
use dioxus_native_core_macro::partial_derive_state;
|
||||||
use shipyard::Component;
|
use shipyard::Component;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::sync::{Arc, Mutex, MutexGuard, RwLock, RwLockReadGuard};
|
use std::sync::{Arc, Mutex, MutexGuard, RwLock, RwLockReadGuard};
|
||||||
|
|
||||||
use dioxus_native_core::{real_dom::NodeImmutable, NodeId, RealDom};
|
use dioxus_native_core::prelude::*;
|
||||||
use taffy::{
|
use taffy::{
|
||||||
geometry::Point,
|
geometry::Point,
|
||||||
prelude::{Layout, Size},
|
prelude::{Layout, Size},
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use dioxus_native_core::{real_dom::NodeImmutable, NodeRef};
|
use dioxus_native_core::prelude::*;
|
||||||
use std::io::Stdout;
|
use std::io::Stdout;
|
||||||
use taffy::{
|
use taffy::{
|
||||||
geometry::Point,
|
geometry::Point,
|
||||||
@@ -26,8 +26,6 @@ pub(crate) fn render_vnode(
|
|||||||
cfg: Config,
|
cfg: Config,
|
||||||
parent_location: Point<f32>,
|
parent_location: Point<f32>,
|
||||||
) {
|
) {
|
||||||
use dioxus_native_core::node::NodeType;
|
|
||||||
|
|
||||||
if let NodeType::Placeholder = &*node.node_type() {
|
if let NodeType::Placeholder = &*node.node_type() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ use dioxus_native_core::{
|
|||||||
layout_attributes::parse_value,
|
layout_attributes::parse_value,
|
||||||
node::OwnedAttributeView,
|
node::OwnedAttributeView,
|
||||||
node_ref::{AttributeMaskBuilder, NodeMaskBuilder, NodeView},
|
node_ref::{AttributeMaskBuilder, NodeMaskBuilder, NodeView},
|
||||||
Dependancy, SendAnyMap, State,
|
prelude::*,
|
||||||
};
|
};
|
||||||
use dioxus_native_core_macro::partial_derive_state;
|
use dioxus_native_core_macro::partial_derive_state;
|
||||||
use shipyard::Component;
|
use shipyard::Component;
|
||||||
|
|||||||
Reference in new issue
Block a user