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, or a
31/// node's last placement gave a child a z index. While neither is so, the
32/// scene keeps every node's children in composition order without reading
33/// their z indices.
34pub fn z_index_in_use() -> bool {
35    crate::thread_state::cells(|cells| cells.z_order_reasons.get()) > 0
36}
37
38/// Whether a `z_index` modifier is attached anywhere on this thread. While
39/// none is, only a node whose last placement gave a child a z index orders
40/// its children by z index.
41pub fn z_index_modifier_in_use() -> bool {
42    crate::thread_state::cells(|cells| cells.z_index_nodes.get()) > 0
43}
44
45fn count(counter: &std::cell::Cell<usize>, added: bool) {
46    counter.set(if added {
47        counter.get() + 1
48    } else {
49        counter.get().saturating_sub(1)
50    });
51}
52
53fn count_z_index_node(attached: bool) {
54    crate::thread_state::cells(|cells| {
55        count(&cells.z_index_nodes, attached);
56        count(&cells.z_order_reasons, attached);
57    });
58}
59
60#[derive(Debug, Clone, PartialEq)]
61pub(crate) struct ZIndexElement {
62    z_index: f32,
63}
64
65impl Hash for ZIndexElement {
66    fn hash<H: Hasher>(&self, state: &mut H) {
67        state.write_u32(self.z_index.to_bits());
68    }
69}
70
71impl ModifierNodeElement for ZIndexElement {
72    type Node = ZIndexNode;
73
74    fn create(&self) -> Self::Node {
75        ZIndexNode {
76            z_index: self.z_index,
77            owner: None,
78            state: NodeState::new(),
79        }
80    }
81
82    fn update(&self, node: &mut Self::Node) {
83        if node.z_index.to_bits() != self.z_index.to_bits() {
84            node.z_index = self.z_index;
85            node.reorder();
86        }
87    }
88
89    fn capabilities(&self) -> NodeCapabilities {
90        NodeCapabilities::DRAW
91    }
92}
93
94/// The node behind [`Modifier::z_index`]. It draws nothing; the modifier
95/// slices add its value up into the node's z index.
96pub(crate) struct ZIndexNode {
97    z_index: f32,
98    /// The layout node whose chain holds this node.
99    owner: Option<cranpose_core::NodeId>,
100    state: NodeState,
101}
102
103impl ZIndexNode {
104    pub(crate) fn z_index(&self) -> f32 {
105        self.z_index
106    }
107
108    /// The order lives in the owner's parent's layer, which a change of the
109    /// owner alone does not rebuild.
110    fn reorder(&self) {
111        if let Some(owner) = self.owner {
112            crate::schedule_z_order_change(owner);
113        }
114    }
115}
116
117impl DelegatableNode for ZIndexNode {
118    fn node_state(&self) -> &NodeState {
119        &self.state
120    }
121}
122
123impl ModifierNode for ZIndexNode {
124    fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
125        context.invalidate(InvalidationKind::Draw);
126        count_z_index_node(true);
127        self.owner = context.node_id();
128        self.reorder();
129    }
130
131    fn on_detach(&mut self) {
132        count_z_index_node(false);
133        self.reorder();
134    }
135}