use std::any::{type_name, Any, TypeId};
use std::cell::{Cell, RefCell};
use std::fmt;
use std::hash::{Hash, Hasher};
use std::ops::{BitOr, BitOrAssign};
use std::rc::Rc;
use cranpose_core::hash::default;
use rustc_hash::FxHashMap;
pub use cranpose_ui_graphics::DrawScope;
pub use cranpose_ui_graphics::Size;
pub use cranpose_ui_layout::{Constraints, Measurable};
use crate::nodes::input::types::PointerEvent;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum InvalidationKind {
Layout,
Draw,
PointerInput,
Semantics,
Focus,
}
pub trait ModifierNodeContext {
fn invalidate(&mut self, _kind: InvalidationKind) {}
fn request_update(&mut self) {}
fn node_id(&self) -> Option<cranpose_core::NodeId> {
None
}
fn push_active_capabilities(&mut self, _capabilities: NodeCapabilities) {}
fn pop_active_capabilities(&mut self) {}
}
#[derive(Default, Debug, Clone)]
pub struct BasicModifierNodeContext {
invalidations: Vec<ModifierInvalidation>,
update_requested: bool,
active_capabilities: Vec<NodeCapabilities>,
node_id: Option<cranpose_core::NodeId>,
}
impl BasicModifierNodeContext {
pub fn new() -> Self {
Self::default()
}
pub fn invalidations(&self) -> &[ModifierInvalidation] {
&self.invalidations
}
pub fn clear_invalidations(&mut self) {
self.invalidations.clear();
}
pub fn take_invalidations(&mut self) -> Vec<ModifierInvalidation> {
std::mem::take(&mut self.invalidations)
}
pub fn update_requested(&self) -> bool {
self.update_requested
}
pub fn take_update_requested(&mut self) -> bool {
std::mem::take(&mut self.update_requested)
}
pub fn set_node_id(&mut self, id: Option<cranpose_core::NodeId>) {
self.node_id = id;
}
fn push_invalidation(&mut self, kind: InvalidationKind) {
let mut capabilities = self.current_capabilities();
capabilities.insert(NodeCapabilities::for_invalidation(kind));
if let Some(existing) = self
.invalidations
.iter_mut()
.find(|entry| entry.kind() == kind)
{
let updated = existing.capabilities() | capabilities;
*existing = ModifierInvalidation::new(kind, updated);
} else {
self.invalidations
.push(ModifierInvalidation::new(kind, capabilities));
}
}
fn current_capabilities(&self) -> NodeCapabilities {
self.active_capabilities
.last()
.copied()
.unwrap_or_else(NodeCapabilities::empty)
}
}
impl ModifierNodeContext for BasicModifierNodeContext {
fn invalidate(&mut self, kind: InvalidationKind) {
self.push_invalidation(kind);
}
fn request_update(&mut self) {
self.update_requested = true;
}
fn push_active_capabilities(&mut self, capabilities: NodeCapabilities) {
self.active_capabilities.push(capabilities);
}
fn pop_active_capabilities(&mut self) {
self.active_capabilities.pop();
}
fn node_id(&self) -> Option<cranpose_core::NodeId> {
self.node_id
}
}
const MAX_DELEGATE_DEPTH: usize = 3;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) struct NodePath {
entry: usize,
delegate_buf: [u8; MAX_DELEGATE_DEPTH],
delegate_len: u8,
}
impl NodePath {
#[inline]
fn root(entry: usize) -> Self {
Self {
entry,
delegate_buf: [0; MAX_DELEGATE_DEPTH],
delegate_len: 0,
}
}
#[inline]
fn from_slice(entry: usize, path: &[usize]) -> Self {
debug_assert!(
path.len() <= MAX_DELEGATE_DEPTH,
"delegate depth {} exceeds MAX_DELEGATE_DEPTH {}",
path.len(),
MAX_DELEGATE_DEPTH
);
debug_assert!(
path.iter().all(|&i| i <= u8::MAX as usize),
"delegate index exceeds u8 range"
);
let mut delegate_buf = [0u8; MAX_DELEGATE_DEPTH];
for (i, &v) in path.iter().enumerate().take(MAX_DELEGATE_DEPTH) {
delegate_buf[i] = v as u8;
}
Self {
entry,
delegate_buf,
delegate_len: path.len().min(MAX_DELEGATE_DEPTH) as u8,
}
}
#[inline]
fn entry(&self) -> usize {
self.entry
}
#[inline]
fn delegates(&self) -> &[u8] {
&self.delegate_buf[..self.delegate_len as usize]
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum NodeLink {
Head,
Tail,
Entry(NodePath),
}
#[derive(Debug)]
pub struct NodeState {
aggregate_child_capabilities: Cell<NodeCapabilities>,
capabilities: Cell<NodeCapabilities>,
parent: RefCell<Option<NodeLink>>,
child: RefCell<Option<NodeLink>>,
attached: Cell<bool>,
is_sentinel: bool,
}
impl Default for NodeState {
fn default() -> Self {
Self::new()
}
}
impl NodeState {
pub const fn new() -> Self {
Self {
aggregate_child_capabilities: Cell::new(NodeCapabilities::empty()),
capabilities: Cell::new(NodeCapabilities::empty()),
parent: RefCell::new(None),
child: RefCell::new(None),
attached: Cell::new(false),
is_sentinel: false,
}
}
pub const fn sentinel() -> Self {
Self {
aggregate_child_capabilities: Cell::new(NodeCapabilities::empty()),
capabilities: Cell::new(NodeCapabilities::empty()),
parent: RefCell::new(None),
child: RefCell::new(None),
attached: Cell::new(true),
is_sentinel: true,
}
}
pub fn set_capabilities(&self, capabilities: NodeCapabilities) {
self.capabilities.set(capabilities);
}
#[inline]
pub fn capabilities(&self) -> NodeCapabilities {
self.capabilities.get()
}
pub fn set_aggregate_child_capabilities(&self, capabilities: NodeCapabilities) {
self.aggregate_child_capabilities.set(capabilities);
}
#[inline]
pub fn aggregate_child_capabilities(&self) -> NodeCapabilities {
self.aggregate_child_capabilities.get()
}
pub(crate) fn set_parent_link(&self, parent: Option<NodeLink>) {
*self.parent.borrow_mut() = parent;
}
#[inline]
pub(crate) fn parent_link(&self) -> Option<NodeLink> {
*self.parent.borrow()
}
pub(crate) fn set_child_link(&self, child: Option<NodeLink>) {
*self.child.borrow_mut() = child;
}
#[inline]
pub(crate) fn child_link(&self) -> Option<NodeLink> {
*self.child.borrow()
}
pub fn set_attached(&self, attached: bool) {
self.attached.set(attached);
}
pub fn is_attached(&self) -> bool {
self.attached.get()
}
pub fn is_sentinel(&self) -> bool {
self.is_sentinel
}
}
pub trait DelegatableNode {
fn node_state(&self) -> &NodeState;
fn aggregate_child_capabilities(&self) -> NodeCapabilities {
self.node_state().aggregate_child_capabilities()
}
}
pub trait ModifierNode: Any + DelegatableNode {
fn on_attach(&mut self, _context: &mut dyn ModifierNodeContext) {}
fn on_detach(&mut self) {}
fn on_reset(&mut self) {}
fn as_draw_node(&self) -> Option<&dyn DrawModifierNode> {
None
}
fn as_draw_node_mut(&mut self) -> Option<&mut dyn DrawModifierNode> {
None
}
fn as_pointer_input_node(&self) -> Option<&dyn PointerInputNode> {
None
}
fn as_pointer_input_node_mut(&mut self) -> Option<&mut dyn PointerInputNode> {
None
}
fn as_semantics_node(&self) -> Option<&dyn SemanticsNode> {
None
}
fn as_semantics_node_mut(&mut self) -> Option<&mut dyn SemanticsNode> {
None
}
fn as_focus_node(&self) -> Option<&dyn FocusNode> {
None
}
fn as_focus_node_mut(&mut self) -> Option<&mut dyn FocusNode> {
None
}
fn as_layout_node(&self) -> Option<&dyn LayoutModifierNode> {
None
}
fn as_layout_node_mut(&mut self) -> Option<&mut dyn LayoutModifierNode> {
None
}
fn for_each_delegate<'b>(&'b self, _visitor: &mut dyn FnMut(&'b dyn ModifierNode)) {}
fn for_each_delegate_mut<'b>(&'b mut self, _visitor: &mut dyn FnMut(&'b mut dyn ModifierNode)) {
}
}
pub trait LayoutModifierNode: ModifierNode {
fn measure(
&self,
_context: &mut dyn ModifierNodeContext,
measurable: &dyn Measurable,
constraints: Constraints,
) -> cranpose_ui_layout::LayoutModifierMeasureResult {
let placeable = measurable.measure(constraints);
cranpose_ui_layout::LayoutModifierMeasureResult::with_size(Size {
width: placeable.width(),
height: placeable.height(),
})
}
fn min_intrinsic_width(&self, _measurable: &dyn Measurable, _height: f32) -> f32 {
0.0
}
fn max_intrinsic_width(&self, _measurable: &dyn Measurable, _height: f32) -> f32 {
0.0
}
fn min_intrinsic_height(&self, _measurable: &dyn Measurable, _width: f32) -> f32 {
0.0
}
fn max_intrinsic_height(&self, _measurable: &dyn Measurable, _width: f32) -> f32 {
0.0
}
fn create_measurement_proxy(
&self,
) -> Option<Box<dyn crate::measurement_proxy::MeasurementProxy>> {
None
}
}
pub trait DrawModifierNode: ModifierNode {
fn draw(&self, _draw_scope: &mut dyn DrawScope) {
}
fn create_draw_closure(
&self,
) -> Option<Rc<dyn Fn(Size) -> Vec<cranpose_ui_graphics::DrawPrimitive>>> {
None
}
}
pub trait PointerInputNode: ModifierNode {
fn on_pointer_event(
&mut self,
_context: &mut dyn ModifierNodeContext,
_event: &PointerEvent,
) -> bool {
false
}
fn hit_test(&self, _x: f32, _y: f32) -> bool {
true
}
fn pointer_input_handler(&self) -> Option<Rc<dyn Fn(PointerEvent)>> {
None
}
}
pub trait SemanticsNode: ModifierNode {
fn merge_semantics(&self, _config: &mut SemanticsConfiguration) {
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
pub enum FocusState {
Active,
ActiveParent,
Captured,
#[default]
Inactive,
}
impl FocusState {
pub fn is_focused(self) -> bool {
matches!(self, FocusState::Active | FocusState::Captured)
}
pub fn has_focus(self) -> bool {
matches!(
self,
FocusState::Active | FocusState::ActiveParent | FocusState::Captured
)
}
pub fn is_captured(self) -> bool {
matches!(self, FocusState::Captured)
}
}
pub trait FocusNode: ModifierNode {
fn focus_state(&self) -> FocusState;
fn on_focus_changed(&mut self, _context: &mut dyn ModifierNodeContext, _state: FocusState) {
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct SemanticsConfiguration {
pub content_description: Option<String>,
pub is_button: bool,
pub is_clickable: bool,
}
impl SemanticsConfiguration {
pub fn merge(&mut self, other: &SemanticsConfiguration) {
if let Some(description) = &other.content_description {
self.content_description = Some(description.clone());
}
self.is_button |= other.is_button;
self.is_clickable |= other.is_clickable;
}
}
impl fmt::Debug for dyn ModifierNode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ModifierNode").finish_non_exhaustive()
}
}
impl dyn ModifierNode {
pub fn as_any(&self) -> &dyn Any {
self
}
pub fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
pub trait ModifierNodeElement: fmt::Debug + Hash + PartialEq + 'static {
type Node: ModifierNode;
fn create(&self) -> Self::Node;
fn update(&self, node: &mut Self::Node);
fn key(&self) -> Option<u64> {
None
}
fn inspector_name(&self) -> &'static str {
type_name::<Self>()
}
fn inspector_properties(&self, _inspector: &mut dyn FnMut(&'static str, String)) {}
fn capabilities(&self) -> NodeCapabilities {
NodeCapabilities::default()
}
fn always_update(&self) -> bool {
false
}
}
pub trait ModifierElement: ModifierNodeElement {}
impl<T> ModifierElement for T where T: ModifierNodeElement {}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct NodeCapabilities(u32);
impl NodeCapabilities {
pub const NONE: Self = Self(0);
pub const LAYOUT: Self = Self(1 << 0);
pub const DRAW: Self = Self(1 << 1);
pub const POINTER_INPUT: Self = Self(1 << 2);
pub const SEMANTICS: Self = Self(1 << 3);
pub const MODIFIER_LOCALS: Self = Self(1 << 4);
pub const FOCUS: Self = Self(1 << 5);
pub const fn empty() -> Self {
Self::NONE
}
pub const fn contains(self, other: Self) -> bool {
(self.0 & other.0) == other.0
}
pub const fn intersects(self, other: Self) -> bool {
(self.0 & other.0) != 0
}
pub fn insert(&mut self, other: Self) {
self.0 |= other.0;
}
pub const fn bits(self) -> u32 {
self.0
}
pub const fn is_empty(self) -> bool {
self.0 == 0
}
pub const fn for_invalidation(kind: InvalidationKind) -> Self {
match kind {
InvalidationKind::Layout => Self::LAYOUT,
InvalidationKind::Draw => Self::DRAW,
InvalidationKind::PointerInput => Self::POINTER_INPUT,
InvalidationKind::Semantics => Self::SEMANTICS,
InvalidationKind::Focus => Self::FOCUS,
}
}
}
impl Default for NodeCapabilities {
fn default() -> Self {
Self::NONE
}
}
impl fmt::Debug for NodeCapabilities {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("NodeCapabilities")
.field("layout", &self.contains(Self::LAYOUT))
.field("draw", &self.contains(Self::DRAW))
.field("pointer_input", &self.contains(Self::POINTER_INPUT))
.field("semantics", &self.contains(Self::SEMANTICS))
.field("modifier_locals", &self.contains(Self::MODIFIER_LOCALS))
.field("focus", &self.contains(Self::FOCUS))
.finish()
}
}
impl BitOr for NodeCapabilities {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0 | rhs.0)
}
}
impl BitOrAssign for NodeCapabilities {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ModifierInvalidation {
kind: InvalidationKind,
capabilities: NodeCapabilities,
}
impl ModifierInvalidation {
pub const fn new(kind: InvalidationKind, capabilities: NodeCapabilities) -> Self {
Self { kind, capabilities }
}
pub const fn kind(self) -> InvalidationKind {
self.kind
}
pub const fn capabilities(self) -> NodeCapabilities {
self.capabilities
}
}
pub trait AnyModifierElement: fmt::Debug {
fn node_type(&self) -> TypeId;
fn element_type(&self) -> TypeId;
fn create_node(&self) -> Box<dyn ModifierNode>;
fn update_node(&self, node: &mut dyn ModifierNode);
fn key(&self) -> Option<u64>;
fn capabilities(&self) -> NodeCapabilities {
NodeCapabilities::default()
}
fn hash_code(&self) -> u64;
fn equals_element(&self, other: &dyn AnyModifierElement) -> bool;
fn inspector_name(&self) -> &'static str;
fn record_inspector_properties(&self, visitor: &mut dyn FnMut(&'static str, String));
fn requires_update(&self) -> bool;
fn as_any(&self) -> &dyn Any;
}
struct TypedModifierElement<E: ModifierNodeElement> {
element: E,
cached_hash: u64,
}
impl<E: ModifierNodeElement> TypedModifierElement<E> {
fn new(element: E) -> Self {
let mut hasher = default::new();
element.hash(&mut hasher);
Self {
element,
cached_hash: hasher.finish(),
}
}
}
impl<E> fmt::Debug for TypedModifierElement<E>
where
E: ModifierNodeElement,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TypedModifierElement")
.field("type", &type_name::<E>())
.finish()
}
}
impl<E> AnyModifierElement for TypedModifierElement<E>
where
E: ModifierNodeElement,
{
fn node_type(&self) -> TypeId {
TypeId::of::<E::Node>()
}
fn element_type(&self) -> TypeId {
TypeId::of::<E>()
}
fn create_node(&self) -> Box<dyn ModifierNode> {
Box::new(self.element.create())
}
fn update_node(&self, node: &mut dyn ModifierNode) {
let typed = node
.as_any_mut()
.downcast_mut::<E::Node>()
.expect("modifier node type mismatch");
self.element.update(typed);
}
fn key(&self) -> Option<u64> {
self.element.key()
}
fn capabilities(&self) -> NodeCapabilities {
self.element.capabilities()
}
fn hash_code(&self) -> u64 {
self.cached_hash
}
fn equals_element(&self, other: &dyn AnyModifierElement) -> bool {
other
.as_any()
.downcast_ref::<Self>()
.map(|typed| typed.element == self.element)
.unwrap_or(false)
}
fn inspector_name(&self) -> &'static str {
self.element.inspector_name()
}
fn record_inspector_properties(&self, visitor: &mut dyn FnMut(&'static str, String)) {
self.element.inspector_properties(visitor);
}
fn requires_update(&self) -> bool {
self.element.always_update()
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub fn modifier_element<E: ModifierNodeElement>(element: E) -> DynModifierElement {
Rc::new(TypedModifierElement::new(element))
}
pub type DynModifierElement = Rc<dyn AnyModifierElement>;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum TraversalDirection {
Forward,
Backward,
}
pub struct ModifierChainIter<'a> {
chain: &'a ModifierNodeChain,
cursor: usize,
remaining: usize,
direction: TraversalDirection,
}
impl<'a> ModifierChainIter<'a> {
fn forward(chain: &'a ModifierNodeChain) -> Self {
Self {
chain,
cursor: 0,
remaining: chain.ordered_nodes.len(),
direction: TraversalDirection::Forward,
}
}
fn backward(chain: &'a ModifierNodeChain) -> Self {
let len = chain.ordered_nodes.len();
Self {
chain,
cursor: len.wrapping_sub(1),
remaining: len,
direction: TraversalDirection::Backward,
}
}
}
impl<'a> Iterator for ModifierChainIter<'a> {
type Item = ModifierChainNodeRef<'a>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
return None;
}
let (link, caps, agg) = self.chain.ordered_nodes[self.cursor];
let node_ref = self.chain.make_node_ref_with_caps(link, caps, agg);
self.remaining -= 1;
match self.direction {
TraversalDirection::Forward => self.cursor += 1,
TraversalDirection::Backward => self.cursor = self.cursor.wrapping_sub(1),
}
Some(node_ref)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.remaining, Some(self.remaining))
}
}
impl<'a> ExactSizeIterator for ModifierChainIter<'a> {}
impl<'a> std::iter::FusedIterator for ModifierChainIter<'a> {}
#[derive(Debug)]
struct ModifierNodeEntry {
element_type: TypeId,
key: Option<u64>,
hash_code: u64,
element: DynModifierElement,
node: Rc<RefCell<Box<dyn ModifierNode>>>,
capabilities: NodeCapabilities,
}
impl ModifierNodeEntry {
fn new(
element_type: TypeId,
key: Option<u64>,
element: DynModifierElement,
node: Box<dyn ModifierNode>,
hash_code: u64,
capabilities: NodeCapabilities,
) -> Self {
let node_rc = Rc::new(RefCell::new(node));
let entry = Self {
element_type,
key,
hash_code,
element,
node: Rc::clone(&node_rc),
capabilities,
};
entry
.node
.borrow()
.node_state()
.set_capabilities(entry.capabilities);
entry
}
}
fn visit_node_tree_mut(
node: &mut dyn ModifierNode,
visitor: &mut dyn FnMut(&mut dyn ModifierNode),
) {
visitor(node);
node.for_each_delegate_mut(&mut |child| visit_node_tree_mut(child, visitor));
}
fn nth_delegate(node: &dyn ModifierNode, target: usize) -> Option<&dyn ModifierNode> {
let mut current = 0usize;
let mut result: Option<&dyn ModifierNode> = None;
node.for_each_delegate(&mut |child| {
if result.is_none() && current == target {
result = Some(child);
}
current += 1;
});
result
}
fn nth_delegate_mut(node: &mut dyn ModifierNode, target: usize) -> Option<&mut dyn ModifierNode> {
let mut current = 0usize;
let mut result: Option<&mut dyn ModifierNode> = None;
node.for_each_delegate_mut(&mut |child| {
if result.is_none() && current == target {
result = Some(child);
}
current += 1;
});
result
}
fn with_node_context<F, R>(
node: &mut dyn ModifierNode,
context: &mut dyn ModifierNodeContext,
f: F,
) -> R
where
F: FnOnce(&mut dyn ModifierNode, &mut dyn ModifierNodeContext) -> R,
{
context.push_active_capabilities(node.node_state().capabilities());
let result = f(node, context);
context.pop_active_capabilities();
result
}
fn request_auto_invalidations(
context: &mut dyn ModifierNodeContext,
capabilities: NodeCapabilities,
) {
if capabilities.is_empty() {
return;
}
context.push_active_capabilities(capabilities);
if capabilities.contains(NodeCapabilities::LAYOUT) {
context.invalidate(InvalidationKind::Layout);
}
if capabilities.contains(NodeCapabilities::DRAW) {
context.invalidate(InvalidationKind::Draw);
}
if capabilities.contains(NodeCapabilities::POINTER_INPUT) {
context.invalidate(InvalidationKind::PointerInput);
}
if capabilities.contains(NodeCapabilities::SEMANTICS) {
context.invalidate(InvalidationKind::Semantics);
}
if capabilities.contains(NodeCapabilities::FOCUS) {
context.invalidate(InvalidationKind::Focus);
}
context.pop_active_capabilities();
}
fn attach_node_tree(node: &mut dyn ModifierNode, context: &mut dyn ModifierNodeContext) {
visit_node_tree_mut(node, &mut |n| {
if !n.node_state().is_attached() {
n.node_state().set_attached(true);
with_node_context(n, context, |node, ctx| node.on_attach(ctx));
}
});
}
fn reset_node_tree(node: &mut dyn ModifierNode) {
visit_node_tree_mut(node, &mut |n| n.on_reset());
}
fn detach_node_tree(node: &mut dyn ModifierNode) {
visit_node_tree_mut(node, &mut |n| {
if n.node_state().is_attached() {
n.on_detach();
n.node_state().set_attached(false);
}
n.node_state().set_parent_link(None);
n.node_state().set_child_link(None);
n.node_state()
.set_aggregate_child_capabilities(NodeCapabilities::empty());
});
}
pub struct ModifierNodeChain {
entries: Vec<ModifierNodeEntry>,
aggregated_capabilities: NodeCapabilities,
head_aggregate_child_capabilities: NodeCapabilities,
head_sentinel: Box<SentinelNode>,
tail_sentinel: Box<SentinelNode>,
ordered_nodes: Vec<(NodeLink, NodeCapabilities, NodeCapabilities)>,
scratch_old_used: Vec<bool>,
scratch_match_order: Vec<Option<usize>>,
scratch_final_slots: Vec<Option<ModifierNodeEntry>>,
scratch_elements: Vec<DynModifierElement>,
}
struct SentinelNode {
state: NodeState,
}
impl SentinelNode {
fn new() -> Self {
Self {
state: NodeState::sentinel(),
}
}
}
impl DelegatableNode for SentinelNode {
fn node_state(&self) -> &NodeState {
&self.state
}
}
impl ModifierNode for SentinelNode {}
#[derive(Clone)]
pub struct ModifierChainNodeRef<'a> {
chain: &'a ModifierNodeChain,
link: NodeLink,
cached_capabilities: Option<NodeCapabilities>,
cached_aggregate_child: Option<NodeCapabilities>,
}
impl Default for ModifierNodeChain {
fn default() -> Self {
Self::new()
}
}
struct EntryIndex {
keyed: FxHashMap<(TypeId, u64), Vec<usize>>,
hashed: FxHashMap<(TypeId, u64), Vec<usize>>,
typed: FxHashMap<TypeId, Vec<usize>>,
}
impl EntryIndex {
fn build(entries: &[ModifierNodeEntry]) -> Self {
let mut keyed = FxHashMap::default();
let mut hashed = FxHashMap::default();
let mut typed = FxHashMap::default();
for (i, entry) in entries.iter().enumerate() {
if let Some(key_value) = entry.key {
keyed
.entry((entry.element_type, key_value))
.or_insert_with(Vec::new)
.push(i);
} else {
hashed
.entry((entry.element_type, entry.hash_code))
.or_insert_with(Vec::new)
.push(i);
typed
.entry(entry.element_type)
.or_insert_with(Vec::new)
.push(i);
}
}
Self {
keyed,
hashed,
typed,
}
}
fn find_match(
&self,
entries: &[ModifierNodeEntry],
used: &[bool],
element_type: TypeId,
key: Option<u64>,
hash_code: u64,
element: &DynModifierElement,
) -> Option<usize> {
if let Some(key_value) = key {
if let Some(candidates) = self.keyed.get(&(element_type, key_value)) {
for &i in candidates {
if !used[i] {
return Some(i);
}
}
}
} else {
if let Some(candidates) = self.hashed.get(&(element_type, hash_code)) {
for &i in candidates {
if !used[i] && entries[i].element.as_ref().equals_element(element.as_ref()) {
return Some(i);
}
}
}
if let Some(candidates) = self.typed.get(&element_type) {
for &i in candidates {
if !used[i] {
return Some(i);
}
}
}
}
None
}
}
impl ModifierNodeChain {
pub fn new() -> Self {
let mut chain = Self {
entries: Vec::new(),
aggregated_capabilities: NodeCapabilities::empty(),
head_aggregate_child_capabilities: NodeCapabilities::empty(),
head_sentinel: Box::new(SentinelNode::new()),
tail_sentinel: Box::new(SentinelNode::new()),
ordered_nodes: Vec::new(),
scratch_old_used: Vec::new(),
scratch_match_order: Vec::new(),
scratch_final_slots: Vec::new(),
scratch_elements: Vec::new(),
};
chain.sync_chain_links();
chain
}
pub fn detach_nodes(&mut self) {
for entry in &self.entries {
detach_node_tree(&mut **entry.node.borrow_mut());
}
}
pub fn attach_nodes(&mut self, context: &mut dyn ModifierNodeContext) {
for entry in &self.entries {
attach_node_tree(&mut **entry.node.borrow_mut(), context);
}
}
pub fn repair_chain(&mut self) {
self.sync_chain_links();
}
pub fn update_from_slice(
&mut self,
elements: &[DynModifierElement],
context: &mut dyn ModifierNodeContext,
) {
self.update_from_ref_iter(elements.iter(), context);
}
pub fn update_from_ref_iter<'a, I>(
&mut self,
elements: I,
context: &mut dyn ModifierNodeContext,
) where
I: Iterator<Item = &'a DynModifierElement>,
{
let old_len = self.entries.len();
let mut fast_path_failed_at: Option<usize> = None;
let mut elements_count = 0;
self.scratch_elements.clear();
for (idx, element) in elements.enumerate() {
elements_count = idx + 1;
if fast_path_failed_at.is_none() && idx < old_len {
let entry = &mut self.entries[idx];
let same_type = entry.element_type == element.element_type();
let same_key = entry.key == element.key();
let same_hash = entry.hash_code == element.hash_code();
let positional_update = element.requires_update();
if same_type && same_key && (same_hash || positional_update) {
let same_element = entry.element.as_ref().equals_element(element.as_ref());
let capabilities = element.capabilities();
{
let node_borrow = entry.node.borrow();
if !node_borrow.node_state().is_attached() {
drop(node_borrow);
attach_node_tree(&mut **entry.node.borrow_mut(), context);
}
}
let needs_update = !same_element || element.requires_update();
if needs_update {
element.update_node(&mut **entry.node.borrow_mut());
entry.element = element.clone();
entry.hash_code = element.hash_code();
request_auto_invalidations(context, capabilities);
}
entry.capabilities = capabilities;
entry
.node
.borrow()
.node_state()
.set_capabilities(capabilities);
continue;
}
fast_path_failed_at = Some(idx);
}
self.scratch_elements.push(element.clone());
}
if fast_path_failed_at.is_none() && self.scratch_elements.is_empty() {
if elements_count < self.entries.len() {
for entry in self.entries.drain(elements_count..) {
request_auto_invalidations(context, entry.capabilities);
detach_node_tree(&mut **entry.node.borrow_mut());
}
}
self.sync_chain_links();
return;
}
let fail_idx = fast_path_failed_at.unwrap_or(old_len);
let mut old_entries: Vec<ModifierNodeEntry> = self.entries.drain(fail_idx..).collect();
let processed_entries_len = self.entries.len();
let old_len = old_entries.len();
self.scratch_old_used.clear();
self.scratch_old_used.resize(old_len, false);
self.scratch_match_order.clear();
self.scratch_match_order.resize(old_len, None);
let index = EntryIndex::build(&old_entries);
let new_elements_count = self.scratch_elements.len();
self.scratch_final_slots.clear();
self.scratch_final_slots.reserve(new_elements_count);
for (new_pos, element) in self.scratch_elements.drain(..).enumerate() {
self.scratch_final_slots.push(None);
let element_type = element.element_type();
let key = element.key();
let hash_code = element.hash_code();
let capabilities = element.capabilities();
let matched_idx = index.find_match(
&old_entries,
&self.scratch_old_used,
element_type,
key,
hash_code,
&element,
);
if let Some(idx) = matched_idx {
self.scratch_old_used[idx] = true;
self.scratch_match_order[idx] = Some(new_pos);
let entry = &mut old_entries[idx];
let moved = idx != new_pos;
let same_element = entry.element.as_ref().equals_element(element.as_ref());
{
let node_borrow = entry.node.borrow();
if !node_borrow.node_state().is_attached() {
drop(node_borrow);
attach_node_tree(&mut **entry.node.borrow_mut(), context);
}
}
let needs_update = !same_element || element.requires_update();
if needs_update {
element.update_node(&mut **entry.node.borrow_mut());
entry.element = element;
entry.hash_code = hash_code;
request_auto_invalidations(context, capabilities);
}
if moved {
request_auto_invalidations(context, capabilities);
}
entry.key = key;
entry.element_type = element_type;
entry.capabilities = capabilities;
entry
.node
.borrow()
.node_state()
.set_capabilities(capabilities);
} else {
let entry = ModifierNodeEntry::new(
element_type,
key,
element.clone(),
element.create_node(),
hash_code,
capabilities,
);
attach_node_tree(&mut **entry.node.borrow_mut(), context);
element.update_node(&mut **entry.node.borrow_mut());
request_auto_invalidations(context, capabilities);
self.scratch_final_slots[new_pos] = Some(entry);
}
}
for (i, entry) in old_entries.into_iter().enumerate() {
if self.scratch_old_used[i] {
let pos = self.scratch_match_order[i]
.expect("Missing match order for used modifier entry");
self.scratch_final_slots[pos] = Some(entry);
} else {
request_auto_invalidations(context, entry.capabilities);
detach_node_tree(&mut **entry.node.borrow_mut());
}
}
self.entries.reserve(self.scratch_final_slots.len());
for slot in self.scratch_final_slots.drain(..) {
let entry = slot.expect("Missing modifier entry for reconciled position");
self.entries.push(entry);
}
debug_assert_eq!(
self.entries.len(),
processed_entries_len + new_elements_count
);
self.sync_chain_links();
}
pub fn update<I>(&mut self, elements: I, context: &mut dyn ModifierNodeContext)
where
I: IntoIterator<Item = DynModifierElement>,
{
let collected: Vec<DynModifierElement> = elements.into_iter().collect();
self.update_from_slice(&collected, context);
}
pub fn reset(&mut self) {
for entry in &mut self.entries {
reset_node_tree(&mut **entry.node.borrow_mut());
}
}
pub fn detach_all(&mut self) {
for entry in std::mem::take(&mut self.entries) {
detach_node_tree(&mut **entry.node.borrow_mut());
{
let node_borrow = entry.node.borrow();
let state = node_borrow.node_state();
state.set_capabilities(NodeCapabilities::empty());
}
}
self.aggregated_capabilities = NodeCapabilities::empty();
self.head_aggregate_child_capabilities = NodeCapabilities::empty();
self.ordered_nodes.clear();
self.sync_chain_links();
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn capabilities(&self) -> NodeCapabilities {
self.aggregated_capabilities
}
pub fn has_capability(&self, capability: NodeCapabilities) -> bool {
self.aggregated_capabilities.contains(capability)
}
pub fn head(&self) -> ModifierChainNodeRef<'_> {
self.make_node_ref(NodeLink::Head)
}
pub fn tail(&self) -> ModifierChainNodeRef<'_> {
self.make_node_ref(NodeLink::Tail)
}
pub fn head_to_tail(&self) -> ModifierChainIter<'_> {
ModifierChainIter::forward(self)
}
pub fn tail_to_head(&self) -> ModifierChainIter<'_> {
ModifierChainIter::backward(self)
}
pub fn for_each_forward<F>(&self, mut f: F)
where
F: FnMut(ModifierChainNodeRef<'_>),
{
for node in self.head_to_tail() {
f(node);
}
}
pub fn for_each_forward_matching<F>(&self, mask: NodeCapabilities, mut f: F)
where
F: FnMut(ModifierChainNodeRef<'_>),
{
if mask.is_empty() {
self.for_each_forward(f);
return;
}
if !self.head().aggregate_child_capabilities().intersects(mask) {
return;
}
for node in self.head_to_tail() {
if node.kind_set().intersects(mask) {
f(node);
}
}
}
pub fn for_each_node_with_capability<F>(&self, mask: NodeCapabilities, mut f: F)
where
F: FnMut(ModifierChainNodeRef<'_>, &dyn ModifierNode),
{
self.for_each_forward_matching(mask, |node_ref| {
node_ref.with_node(|node| f(node_ref.clone(), node));
});
}
pub fn for_each_backward<F>(&self, mut f: F)
where
F: FnMut(ModifierChainNodeRef<'_>),
{
for node in self.tail_to_head() {
f(node);
}
}
pub fn for_each_backward_matching<F>(&self, mask: NodeCapabilities, mut f: F)
where
F: FnMut(ModifierChainNodeRef<'_>),
{
if mask.is_empty() {
self.for_each_backward(f);
return;
}
if !self.head().aggregate_child_capabilities().intersects(mask) {
return;
}
for node in self.tail_to_head() {
if node.kind_set().intersects(mask) {
f(node);
}
}
}
pub fn node_ref_at(&self, index: usize) -> Option<ModifierChainNodeRef<'_>> {
if index >= self.entries.len() {
None
} else {
Some(self.make_node_ref(NodeLink::Entry(NodePath::root(index))))
}
}
pub fn find_node_ref(&self, node: &dyn ModifierNode) -> Option<ModifierChainNodeRef<'_>> {
fn node_data_ptr(node: &dyn ModifierNode) -> *const () {
node as *const dyn ModifierNode as *const ()
}
let target = node_data_ptr(node);
for (index, entry) in self.entries.iter().enumerate() {
if node_data_ptr(&**entry.node.borrow()) == target {
return Some(self.make_node_ref(NodeLink::Entry(NodePath::root(index))));
}
}
self.ordered_nodes.iter().find_map(|(link, _caps, _agg)| {
if matches!(link, NodeLink::Entry(path) if path.delegates().is_empty()) {
return None;
}
let matches_target = match link {
NodeLink::Head => node_data_ptr(self.head_sentinel.as_ref()) == target,
NodeLink::Tail => node_data_ptr(self.tail_sentinel.as_ref()) == target,
NodeLink::Entry(path) => {
let node_borrow = self.entries[path.entry()].node.borrow();
node_data_ptr(&**node_borrow) == target
}
};
if matches_target {
Some(self.make_node_ref(*link))
} else {
None
}
})
}
pub fn node<N: ModifierNode + 'static>(&self, index: usize) -> Option<std::cell::Ref<'_, N>> {
self.entries.get(index).and_then(|entry| {
std::cell::Ref::filter_map(entry.node.borrow(), |boxed_node| {
boxed_node.as_any().downcast_ref::<N>()
})
.ok()
})
}
pub fn node_mut<N: ModifierNode + 'static>(
&self,
index: usize,
) -> Option<std::cell::RefMut<'_, N>> {
self.entries.get(index).and_then(|entry| {
std::cell::RefMut::filter_map(entry.node.borrow_mut(), |boxed_node| {
boxed_node.as_any_mut().downcast_mut::<N>()
})
.ok()
})
}
pub fn get_node_rc(&self, index: usize) -> Option<Rc<RefCell<Box<dyn ModifierNode>>>> {
self.entries.get(index).map(|entry| Rc::clone(&entry.node))
}
pub fn has_nodes_for_invalidation(&self, kind: InvalidationKind) -> bool {
self.aggregated_capabilities
.contains(NodeCapabilities::for_invalidation(kind))
}
pub fn visit_nodes<F>(&self, mut f: F)
where
F: FnMut(&dyn ModifierNode, NodeCapabilities),
{
for (link, cached_caps, _agg) in &self.ordered_nodes {
match link {
NodeLink::Head => {
f(self.head_sentinel.as_ref(), *cached_caps);
}
NodeLink::Tail => {
f(self.tail_sentinel.as_ref(), *cached_caps);
}
NodeLink::Entry(path) => {
let node_borrow = self.entries[path.entry()].node.borrow();
if path.delegates().is_empty() {
f(&**node_borrow, *cached_caps);
} else {
let mut current: &dyn ModifierNode = &**node_borrow;
for &delegate_index in path.delegates() {
if let Some(delegate) = nth_delegate(current, delegate_index as usize) {
current = delegate;
} else {
return; }
}
f(current, *cached_caps);
}
}
}
}
}
pub fn visit_nodes_mut<F>(&mut self, mut f: F)
where
F: FnMut(&mut dyn ModifierNode, NodeCapabilities),
{
for index in 0..self.ordered_nodes.len() {
let (link, cached_caps, _agg) = self.ordered_nodes[index];
match link {
NodeLink::Head => {
f(self.head_sentinel.as_mut(), cached_caps);
}
NodeLink::Tail => {
f(self.tail_sentinel.as_mut(), cached_caps);
}
NodeLink::Entry(path) => {
let mut node_borrow = self.entries[path.entry()].node.borrow_mut();
if path.delegates().is_empty() {
f(&mut **node_borrow, cached_caps);
} else {
let mut current: &mut dyn ModifierNode = &mut **node_borrow;
for &delegate_index in path.delegates() {
if let Some(delegate) =
nth_delegate_mut(current, delegate_index as usize)
{
current = delegate;
} else {
return; }
}
f(current, cached_caps);
}
}
}
}
}
fn make_node_ref(&self, link: NodeLink) -> ModifierChainNodeRef<'_> {
ModifierChainNodeRef {
chain: self,
link,
cached_capabilities: None,
cached_aggregate_child: None,
}
}
fn make_node_ref_with_caps(
&self,
link: NodeLink,
caps: NodeCapabilities,
aggregate_child: NodeCapabilities,
) -> ModifierChainNodeRef<'_> {
ModifierChainNodeRef {
chain: self,
link,
cached_capabilities: Some(caps),
cached_aggregate_child: Some(aggregate_child),
}
}
fn sync_chain_links(&mut self) {
self.rebuild_ordered_nodes();
self.head_sentinel.node_state().set_parent_link(None);
self.tail_sentinel.node_state().set_child_link(None);
if self.ordered_nodes.is_empty() {
self.head_sentinel
.node_state()
.set_child_link(Some(NodeLink::Tail));
self.tail_sentinel
.node_state()
.set_parent_link(Some(NodeLink::Head));
self.aggregated_capabilities = NodeCapabilities::empty();
self.head_aggregate_child_capabilities = NodeCapabilities::empty();
self.head_sentinel
.node_state()
.set_aggregate_child_capabilities(NodeCapabilities::empty());
self.tail_sentinel
.node_state()
.set_aggregate_child_capabilities(NodeCapabilities::empty());
return;
}
let mut previous = NodeLink::Head;
for (link, _caps, _agg) in self.ordered_nodes.iter().copied() {
match &previous {
NodeLink::Head => self.head_sentinel.node_state().set_child_link(Some(link)),
NodeLink::Tail => self.tail_sentinel.node_state().set_child_link(Some(link)),
NodeLink::Entry(path) => {
let node_borrow = self.entries[path.entry()].node.borrow();
if path.delegates().is_empty() {
node_borrow.node_state().set_child_link(Some(link));
} else {
let mut current: &dyn ModifierNode = &**node_borrow;
for &delegate_index in path.delegates() {
if let Some(delegate) = nth_delegate(current, delegate_index as usize) {
current = delegate;
}
}
current.node_state().set_child_link(Some(link));
}
}
}
match &link {
NodeLink::Head => self
.head_sentinel
.node_state()
.set_parent_link(Some(previous)),
NodeLink::Tail => self
.tail_sentinel
.node_state()
.set_parent_link(Some(previous)),
NodeLink::Entry(path) => {
let node_borrow = self.entries[path.entry()].node.borrow();
if path.delegates().is_empty() {
node_borrow.node_state().set_parent_link(Some(previous));
} else {
let mut current: &dyn ModifierNode = &**node_borrow;
for &delegate_index in path.delegates() {
if let Some(delegate) = nth_delegate(current, delegate_index as usize) {
current = delegate;
}
}
current.node_state().set_parent_link(Some(previous));
}
}
}
previous = link;
}
match &previous {
NodeLink::Head => self
.head_sentinel
.node_state()
.set_child_link(Some(NodeLink::Tail)),
NodeLink::Tail => self
.tail_sentinel
.node_state()
.set_child_link(Some(NodeLink::Tail)),
NodeLink::Entry(path) => {
let node_borrow = self.entries[path.entry()].node.borrow();
if path.delegates().is_empty() {
node_borrow
.node_state()
.set_child_link(Some(NodeLink::Tail));
} else {
let mut current: &dyn ModifierNode = &**node_borrow;
for &delegate_index in path.delegates() {
if let Some(delegate) = nth_delegate(current, delegate_index as usize) {
current = delegate;
}
}
current.node_state().set_child_link(Some(NodeLink::Tail));
}
}
}
self.tail_sentinel
.node_state()
.set_parent_link(Some(previous));
self.tail_sentinel.node_state().set_child_link(None);
let mut aggregate = NodeCapabilities::empty();
for (link, cached_caps, cached_aggregate) in self.ordered_nodes.iter_mut().rev() {
aggregate |= *cached_caps;
*cached_aggregate = aggregate;
match link {
NodeLink::Head => {
self.head_sentinel
.node_state()
.set_aggregate_child_capabilities(aggregate);
}
NodeLink::Tail => {
self.tail_sentinel
.node_state()
.set_aggregate_child_capabilities(aggregate);
}
NodeLink::Entry(path) => {
let node_borrow = self.entries[path.entry()].node.borrow();
let state = if path.delegates().is_empty() {
node_borrow.node_state()
} else {
let mut current: &dyn ModifierNode = &**node_borrow;
for &delegate_index in path.delegates() {
if let Some(delegate) = nth_delegate(current, delegate_index as usize) {
current = delegate;
}
}
current.node_state()
};
state.set_aggregate_child_capabilities(aggregate);
}
}
}
self.aggregated_capabilities = aggregate;
self.head_aggregate_child_capabilities = aggregate;
self.head_sentinel
.node_state()
.set_aggregate_child_capabilities(aggregate);
self.tail_sentinel
.node_state()
.set_aggregate_child_capabilities(NodeCapabilities::empty());
}
fn rebuild_ordered_nodes(&mut self) {
self.ordered_nodes.clear();
let mut path_buf = [0usize; MAX_DELEGATE_DEPTH];
for (index, entry) in self.entries.iter().enumerate() {
let node_borrow = entry.node.borrow();
Self::enumerate_link_order(
&**node_borrow,
index,
&mut path_buf,
0,
&mut self.ordered_nodes,
);
}
}
fn enumerate_link_order(
node: &dyn ModifierNode,
entry: usize,
path_buf: &mut [usize; MAX_DELEGATE_DEPTH],
path_len: usize,
out: &mut Vec<(NodeLink, NodeCapabilities, NodeCapabilities)>,
) {
let caps = node.node_state().capabilities();
out.push((
NodeLink::Entry(NodePath::from_slice(entry, &path_buf[..path_len])),
caps,
NodeCapabilities::empty(),
));
let mut delegate_index = 0usize;
node.for_each_delegate(&mut |child| {
if path_len < MAX_DELEGATE_DEPTH {
path_buf[path_len] = delegate_index;
Self::enumerate_link_order(child, entry, path_buf, path_len + 1, out);
}
delegate_index += 1;
});
}
}
impl<'a> ModifierChainNodeRef<'a> {
fn with_state<R>(&self, f: impl FnOnce(&NodeState) -> R) -> R {
match &self.link {
NodeLink::Head => f(self.chain.head_sentinel.node_state()),
NodeLink::Tail => f(self.chain.tail_sentinel.node_state()),
NodeLink::Entry(path) => {
let node_borrow = self.chain.entries[path.entry()].node.borrow();
if path.delegates().is_empty() {
f(node_borrow.node_state())
} else {
let mut current: &dyn ModifierNode = &**node_borrow;
for &delegate_index in path.delegates() {
if let Some(delegate) = nth_delegate(current, delegate_index as usize) {
current = delegate;
} else {
return f(node_borrow.node_state());
}
}
f(current.node_state())
}
}
}
}
pub fn with_node<R>(&self, f: impl FnOnce(&dyn ModifierNode) -> R) -> Option<R> {
match &self.link {
NodeLink::Head => None, NodeLink::Tail => None, NodeLink::Entry(path) => {
let node_borrow = self.chain.entries[path.entry()].node.borrow();
if path.delegates().is_empty() {
Some(f(&**node_borrow))
} else {
let mut current: &dyn ModifierNode = &**node_borrow;
for &delegate_index in path.delegates() {
if let Some(delegate) = nth_delegate(current, delegate_index as usize) {
current = delegate;
} else {
return None;
}
}
Some(f(current))
}
}
}
}
#[inline]
pub fn parent(&self) -> Option<Self> {
self.with_state(|state| state.parent_link())
.map(|link| self.chain.make_node_ref(link))
}
#[inline]
pub fn child(&self) -> Option<Self> {
self.with_state(|state| state.child_link())
.map(|link| self.chain.make_node_ref(link))
}
#[inline]
pub fn kind_set(&self) -> NodeCapabilities {
if let Some(caps) = self.cached_capabilities {
return caps;
}
match &self.link {
NodeLink::Head | NodeLink::Tail => NodeCapabilities::empty(),
NodeLink::Entry(_) => self.with_state(|state| state.capabilities()),
}
}
pub fn entry_index(&self) -> Option<usize> {
match &self.link {
NodeLink::Entry(path) => Some(path.entry()),
_ => None,
}
}
pub fn delegate_depth(&self) -> usize {
match &self.link {
NodeLink::Entry(path) => path.delegates().len(),
_ => 0,
}
}
#[inline]
pub fn aggregate_child_capabilities(&self) -> NodeCapabilities {
if let Some(agg) = self.cached_aggregate_child {
return agg;
}
if self.is_tail() {
NodeCapabilities::empty()
} else {
self.with_state(|state| state.aggregate_child_capabilities())
}
}
pub fn is_head(&self) -> bool {
matches!(self.link, NodeLink::Head)
}
pub fn is_tail(&self) -> bool {
matches!(self.link, NodeLink::Tail)
}
pub fn is_sentinel(&self) -> bool {
matches!(self.link, NodeLink::Head | NodeLink::Tail)
}
pub fn has_capability(&self, mask: NodeCapabilities) -> bool {
!mask.is_empty() && self.kind_set().intersects(mask)
}
pub fn visit_descendants<F>(self, include_self: bool, mut f: F)
where
F: FnMut(ModifierChainNodeRef<'a>),
{
let mut current = if include_self {
Some(self)
} else {
self.child()
};
while let Some(node) = current {
if node.is_tail() {
break;
}
if !node.is_sentinel() {
f(node.clone());
}
current = node.child();
}
}
pub fn visit_descendants_matching<F>(self, include_self: bool, mask: NodeCapabilities, mut f: F)
where
F: FnMut(ModifierChainNodeRef<'a>),
{
if mask.is_empty() {
self.visit_descendants(include_self, f);
return;
}
if !self.aggregate_child_capabilities().intersects(mask) {
return;
}
self.visit_descendants(include_self, |node| {
if node.kind_set().intersects(mask) {
f(node);
}
});
}
pub fn visit_ancestors<F>(self, include_self: bool, mut f: F)
where
F: FnMut(ModifierChainNodeRef<'a>),
{
let mut current = if include_self {
Some(self)
} else {
self.parent()
};
while let Some(node) = current {
if node.is_head() {
break;
}
f(node.clone());
current = node.parent();
}
}
pub fn visit_ancestors_matching<F>(self, include_self: bool, mask: NodeCapabilities, mut f: F)
where
F: FnMut(ModifierChainNodeRef<'a>),
{
if mask.is_empty() {
self.visit_ancestors(include_self, f);
return;
}
self.visit_ancestors(include_self, |node| {
if node.kind_set().intersects(mask) {
f(node);
}
});
}
pub fn find_parent_focus_target(&self) -> Option<ModifierChainNodeRef<'a>> {
let mut result = None;
self.clone()
.visit_ancestors_matching(false, NodeCapabilities::FOCUS, |node| {
if result.is_none() {
result = Some(node);
}
});
result
}
pub fn find_first_focus_target(&self) -> Option<ModifierChainNodeRef<'a>> {
let mut result = None;
self.clone()
.visit_descendants_matching(false, NodeCapabilities::FOCUS, |node| {
if result.is_none() {
result = Some(node);
}
});
result
}
pub fn has_focus_capability_in_ancestors(&self) -> bool {
let mut found = false;
self.clone()
.visit_ancestors_matching(true, NodeCapabilities::FOCUS, |_| {
found = true;
});
found
}
}
#[cfg(test)]
#[path = "tests/modifier_tests.rs"]
mod tests;