Skip to main content

cranpose_ui/modifier/
z_index.rs

1use std::hash::{Hash, Hasher};
2
3use cranpose_foundation::{
4    DelegatableNode, InvalidationKind, ModifierNode, ModifierNodeContext, ModifierNodeElement,
5    NodeCapabilities, NodeState,
6};
7
8use super::{Modifier, inspector_metadata};
9
10impl Modifier {
11    /// Sets where this node draws among its siblings: a sibling with a larger
12    /// `z_index` draws on top and takes a tap first; siblings with the same
13    /// one keep the order the parent placed them in. Several `z_index`
14    /// modifiers on one node add up. The order a screen reader reads in does
15    /// not change.
16    ///
17    /// This is Compose's `Modifier.zIndex(zIndex)`.
18    ///
19    /// Example: `Modifier::empty().z_index(1.0)`
20    pub fn z_index(self, z_index: f32) -> Self {
21        let modifier = Self::with_element(ZIndexElement { z_index }).with_inspector_metadata(
22            inspector_metadata("zIndex", move |info| {
23                info.add_property("zIndex", z_index.to_string());
24            }),
25        );
26        self.then(modifier)
27    }
28}
29
30/// Whether a `z_index` modifier is attached anywhere on this thread. While
31/// none is, the scene keeps every node's children in composition order
32/// without reading their z indices.
33pub fn z_index_in_use() -> bool {
34    crate::thread_state::cells(|cells| cells.z_index_nodes.get()) > 0
35}
36
37fn count_z_index_node(attached: bool) {
38    crate::thread_state::cells(|cells| {
39        let nodes = &cells.z_index_nodes;
40        nodes.set(if attached {
41            nodes.get() + 1
42        } else {
43            nodes.get().saturating_sub(1)
44        });
45    });
46}
47
48#[derive(Debug, Clone, PartialEq)]
49pub(crate) struct ZIndexElement {
50    z_index: f32,
51}
52
53impl Hash for ZIndexElement {
54    fn hash<H: Hasher>(&self, state: &mut H) {
55        state.write_u32(self.z_index.to_bits());
56    }
57}
58
59impl ModifierNodeElement for ZIndexElement {
60    type Node = ZIndexNode;
61
62    fn create(&self) -> Self::Node {
63        ZIndexNode {
64            z_index: self.z_index,
65            owner: None,
66            state: NodeState::new(),
67        }
68    }
69
70    fn update(&self, node: &mut Self::Node) {
71        if node.z_index.to_bits() != self.z_index.to_bits() {
72            node.z_index = self.z_index;
73            node.reorder();
74        }
75    }
76
77    fn capabilities(&self) -> NodeCapabilities {
78        NodeCapabilities::DRAW
79    }
80}
81
82/// The node behind [`Modifier::z_index`]. It draws nothing; the modifier
83/// slices add its value up into the node's z index.
84pub(crate) struct ZIndexNode {
85    z_index: f32,
86    /// The layout node whose chain holds this node.
87    owner: Option<cranpose_core::NodeId>,
88    state: NodeState,
89}
90
91impl ZIndexNode {
92    pub(crate) fn z_index(&self) -> f32 {
93        self.z_index
94    }
95
96    /// The order lives in the owner's parent's layer, which a change of the
97    /// owner alone does not rebuild.
98    fn reorder(&self) {
99        if let Some(owner) = self.owner {
100            crate::schedule_z_order_change(owner);
101        }
102    }
103}
104
105impl DelegatableNode for ZIndexNode {
106    fn node_state(&self) -> &NodeState {
107        &self.state
108    }
109}
110
111impl ModifierNode for ZIndexNode {
112    fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
113        context.invalidate(InvalidationKind::Draw);
114        count_z_index_node(true);
115        self.owner = context.node_id();
116        self.reorder();
117    }
118
119    fn on_detach(&mut self) {
120        count_z_index_node(false);
121        self.reorder();
122    }
123}