Files
dioxus/packages/native-core/src/state.rs
T

312 lines
10 KiB
Rust

use std::{any::TypeId, fmt::Debug};
use anymap::AnyMap;
use dioxus_core::{Attribute, ElementId, VElement, VNode, VText};
pub(crate) fn union_ordered_iter<T: Ord + Debug>(
s_iter: impl Iterator<Item = T>,
o_iter: impl Iterator<Item = T>,
new_len_guess: usize,
) -> Vec<T> {
let mut s_peekable = s_iter.peekable();
let mut o_peekable = o_iter.peekable();
let mut v = Vec::with_capacity(new_len_guess);
while let Some(s_i) = s_peekable.peek() {
loop {
if let Some(o_i) = o_peekable.peek() {
if o_i > s_i {
break;
} else if o_i < s_i {
v.push(o_peekable.next().unwrap());
}
} else {
break;
}
}
v.push(s_peekable.next().unwrap());
}
while let Some(o_i) = o_peekable.next() {
v.push(o_i);
}
for w in v.windows(2) {
debug_assert!(w[1] > w[0]);
}
v
}
#[derive(Debug)]
pub struct NodeView<'a> {
inner: &'a VNode<'a>,
mask: NodeMask,
}
impl<'a> NodeView<'a> {
pub fn new(vnode: &'a VNode<'a>, view: NodeMask) -> Self {
Self {
inner: vnode,
mask: view,
}
}
pub fn id(&self) -> ElementId {
self.inner.mounted_id()
}
pub fn tag(&self) -> Option<&'a str> {
self.mask.tag.then(|| self.el().map(|el| el.tag)).flatten()
}
pub fn namespace(&self) -> Option<&'a str> {
self.mask
.namespace
.then(|| self.el().map(|el| el.namespace).flatten())
.flatten()
}
pub fn attributes(&self) -> impl Iterator<Item = &Attribute<'a>> {
self.el()
.map(|el| el.attributes)
.unwrap_or_default()
.iter()
.filter(|a| self.mask.attritutes.contains_attribute(&a.name))
}
pub fn text(&self) -> Option<&str> {
self.mask
.text
.then(|| self.txt().map(|txt| txt.text))
.flatten()
}
fn el(&self) -> Option<&'a VElement<'a>> {
if let VNode::Element(el) = &self.inner {
Some(el)
} else {
None
}
}
fn txt(&self) -> Option<&'a VText<'a>> {
if let VNode::Text(txt) = &self.inner {
Some(txt)
} else {
None
}
}
}
#[derive(PartialEq, Clone, Debug)]
pub enum AttributeMask {
All,
Dynamic(Vec<&'static str>),
Static(&'static [&'static str]),
}
impl AttributeMask {
pub const NONE: Self = Self::Static(&[]);
fn contains_attribute(&self, attr: &'static str) -> bool {
match self {
AttributeMask::All => true,
AttributeMask::Dynamic(l) => l.binary_search(&attr).is_ok(),
AttributeMask::Static(l) => l.binary_search(&attr).is_ok(),
}
}
pub fn single(new: &'static str) -> Self {
Self::Dynamic(vec![new])
}
pub fn verify(&self) {
match &self {
AttributeMask::Static(attrs) => debug_assert!(
attrs.windows(2).all(|w| w[0] < w[1]),
"attritutes must be increasing"
),
AttributeMask::Dynamic(attrs) => debug_assert!(
attrs.windows(2).all(|w| w[0] < w[1]),
"attritutes must be increasing"
),
_ => (),
}
}
pub fn union(&self, other: &Self) -> Self {
let new = match (self, other) {
(AttributeMask::Dynamic(s), AttributeMask::Dynamic(o)) => AttributeMask::Dynamic(
union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()),
),
(AttributeMask::Static(s), AttributeMask::Dynamic(o)) => AttributeMask::Dynamic(
union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()),
),
(AttributeMask::Dynamic(s), AttributeMask::Static(o)) => AttributeMask::Dynamic(
union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()),
),
(AttributeMask::Static(s), AttributeMask::Static(o)) => AttributeMask::Dynamic(
union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()),
),
_ => AttributeMask::All,
};
new.verify();
new
}
fn overlaps(&self, other: &Self) -> bool {
fn overlaps_iter(
mut self_iter: impl Iterator<Item = &'static str>,
mut other_iter: impl Iterator<Item = &'static str>,
) -> bool {
if let Some(mut other_attr) = other_iter.next() {
while let Some(self_attr) = self_iter.next() {
while other_attr < self_attr {
if let Some(attr) = other_iter.next() {
other_attr = attr;
} else {
return false;
}
}
if other_attr == self_attr {
return true;
}
}
}
false
}
match (self, other) {
(AttributeMask::All, AttributeMask::All) => true,
(AttributeMask::All, AttributeMask::Dynamic(v)) => !v.is_empty(),
(AttributeMask::All, AttributeMask::Static(s)) => !s.is_empty(),
(AttributeMask::Dynamic(v), AttributeMask::All) => !v.is_empty(),
(AttributeMask::Static(s), AttributeMask::All) => !s.is_empty(),
(AttributeMask::Dynamic(v1), AttributeMask::Dynamic(v2)) => {
overlaps_iter(v1.iter().copied(), v2.iter().copied())
}
(AttributeMask::Dynamic(v), AttributeMask::Static(s)) => {
overlaps_iter(v.iter().copied(), s.iter().copied())
}
(AttributeMask::Static(s), AttributeMask::Dynamic(v)) => {
overlaps_iter(v.iter().copied(), s.iter().copied())
}
(AttributeMask::Static(s1), AttributeMask::Static(s2)) => {
overlaps_iter(s1.iter().copied(), s2.iter().copied())
}
}
}
}
impl Default for AttributeMask {
fn default() -> Self {
AttributeMask::Static(&[])
}
}
#[derive(Default, PartialEq, Clone, Debug)]
pub struct NodeMask {
// must be sorted
attritutes: AttributeMask,
tag: bool,
namespace: bool,
text: bool,
}
impl NodeMask {
pub const NONE: Self = Self::new(AttributeMask::Static(&[]), false, false, false);
pub const ALL: Self = Self::new(AttributeMask::All, true, true, true);
/// attritutes must be sorted!
pub const fn new(attritutes: AttributeMask, tag: bool, namespace: bool, text: bool) -> Self {
Self {
attritutes,
tag,
namespace,
text,
}
}
pub fn overlaps(&self, other: &Self) -> bool {
(self.tag && other.tag)
|| (self.namespace && other.namespace)
|| self.attritutes.overlaps(&other.attritutes)
|| (self.text && other.text)
}
pub fn union(&self, other: &Self) -> Self {
Self {
attritutes: self.attritutes.union(&other.attritutes),
tag: self.tag | other.tag,
namespace: self.namespace | other.namespace,
text: self.text | other.text,
}
}
}
/// This state is derived from children. For example a node's size could be derived from the size of children.
/// Called when the current node's node properties are modified, a child's [BubbledUpState] is modified or a child is removed.
/// Called at most once per update.
pub trait ChildDepState {
/// The context is passed to the [PushedDownState::reduce] when it is pushed down.
/// This is sometimes nessisary for lifetime purposes.
type Ctx;
type DepState: ChildDepState;
const NODE_MASK: NodeMask = NodeMask::NONE;
fn reduce<'a>(
&mut self,
node: NodeView,
children: impl Iterator<Item = &'a Self::DepState>,
ctx: &Self::Ctx,
) -> bool
where
Self::DepState: 'a;
}
/// This state that is passed down to children. For example text properties (`<b>` `<i>` `<u>`) would be passed to children.
/// Called when the current node's node properties are modified or a parrent's [PushedDownState] is modified.
/// Called at most once per update.
pub trait ParentDepState {
/// The context is passed to the [PushedDownState::reduce] when it is pushed down.
/// This is sometimes nessisary for lifetime purposes.
type Ctx;
type DepState: ParentDepState;
const NODE_MASK: NodeMask = NodeMask::NONE;
fn reduce(&mut self, node: NodeView, parent: Option<&Self::DepState>, ctx: &Self::Ctx) -> bool;
}
/// This state that is upadated lazily. For example any propertys that do not effect other parts of the dom like bg-color.
/// Called when the current node's node properties are modified or a parrent's [PushedDownState] is modified.
/// Called at most once per update.
pub trait NodeDepState {
type Ctx;
const NODE_MASK: NodeMask = NodeMask::NONE;
fn reduce(&mut self, node: NodeView, ctx: &Self::Ctx) -> bool;
}
pub trait State: Default + Clone {
fn update_node_dep_state<'a>(
&'a mut self,
ty: TypeId,
node: &'a VNode<'a>,
ctx: &AnyMap,
) -> bool;
/// This must be a valid resolution order. (no nodes updated before a state they rely on)
fn child_dep_types(&self, mask: &NodeMask) -> Vec<TypeId>;
fn update_parent_dep_state<'a>(
&'a mut self,
ty: TypeId,
node: &'a VNode<'a>,
parent: Option<&Self>,
ctx: &AnyMap,
) -> bool;
/// This must be a valid resolution order. (no nodes updated before a state they rely on)
fn parent_dep_types(&self, mask: &NodeMask) -> Vec<TypeId>;
fn update_child_dep_state<'a>(
&'a mut self,
ty: TypeId,
node: &'a VNode<'a>,
children: &[&Self],
ctx: &AnyMap,
) -> bool;
/// This must be a valid resolution order. (no nodes updated before a state they rely on)
fn node_dep_types(&self, mask: &NodeMask) -> Vec<TypeId>;
}