Skip to main content

cranpose_ui/widgets/nodes/
layout_state.rs

1//! Where a layout node's parent placed it and what size it measured, and
2//! the placement bookkeeping of a layout pass.
3
4use std::cell::Cell;
5
6use cranpose_core::{NodeId, PackedU64};
7
8use crate::{
9    modifier::{Point, Size},
10    render_state::AppContext,
11};
12
13/// The running layout pass and how many placed nodes it has cleared and not
14/// placed again yet: the nodes it unplaces, unless their parents place them
15/// before it ends. Ids start past 0, the pass a node no pass cleared names.
16pub(crate) struct PlacementPass {
17    id: Cell<u64>,
18    unplaced: Cell<usize>,
19}
20
21impl PlacementPass {
22    pub(crate) const fn new() -> Self {
23        Self {
24            id: Cell::new(1),
25            unplaced: Cell::new(0),
26        }
27    }
28
29    /// Starts the placement bookkeeping of a layout pass.
30    pub(crate) fn begin(&self) {
31        self.id.set(self.id.get() + 1);
32        self.unplaced.set(0);
33    }
34
35    /// The running pass, when it has left a node unplaced that it found placed.
36    pub(crate) fn with_unplaced_nodes(&self) -> Option<u64> {
37        (self.unplaced.get() > 0).then(|| self.id.get())
38    }
39
40    /// Counts a placed node the running pass cleared, and returns the pass.
41    fn clear(&self) -> u64 {
42        self.unplaced.set(self.unplaced.get() + 1);
43        self.id.get()
44    }
45
46    /// Whether a node cleared in pass `cleared_in` was cleared by the running
47    /// pass, which then counts it placed again.
48    fn restore(&self, cleared_in: u64) -> bool {
49        let restored = cleared_in == self.id.get();
50        if restored {
51            self.unplaced.set(self.unplaced.get().saturating_sub(1));
52        }
53        restored
54    }
55}
56
57/// The measured size and placed position that a layout node kept from its latest layout pass.
58#[derive(Clone, Copy, Debug, Default)]
59pub struct LayoutState {
60    size: Size,
61    position: Point,
62    is_placed: bool,
63    node_id: Option<NodeId>,
64    /// The layout pass that cleared the placed flag while the node was placed.
65    cleared_in_pass: PackedU64,
66    content_offset: Point,
67    semantics_stamp: PackedU64,
68    /// The take of the moved-node list this node was last noted in.
69    moved_noted_in: PackedU64,
70    /// The take of the reshaped-node list this node was last noted in.
71    reshaped_noted_in: PackedU64,
72    z_index: f32,
73    // Whether the node's last placement gave a child a z index other than 0.
74    places_children_with_z_index: bool,
75}
76
77// Where a parent placed a child: its position in the parent, and the z
78// index the placement gave it among its siblings.
79#[derive(Clone, Copy, Default)]
80pub(crate) struct ChildPlacement {
81    position: Point,
82    z_index: f32,
83}
84
85impl ChildPlacement {
86    pub(crate) fn new(position: Point, z_index: f32) -> Self {
87        Self { position, z_index }
88    }
89}
90
91impl LayoutState {
92    /// Returns the measured size, in logical pixels.
93    pub fn size(&self) -> Size {
94        self.size
95    }
96
97    /// Returns the placed position relative to the parent, in logical pixels.
98    pub fn position(&self) -> Point {
99        self.position
100    }
101
102    /// Offset of the content box relative to the node origin (e.g. due to
103    /// padding).
104    pub fn content_offset(&self) -> Point {
105        self.content_offset
106    }
107
108    /// Writes the content offset, reporting an actual change to the
109    /// semantics tree.
110    pub fn set_content_offset(&mut self, offset: Point) {
111        if self.content_offset != offset {
112            self.content_offset = offset;
113            self.note_semantics_change();
114        }
115    }
116
117    /// Notes to the scene phase of `context` that the layout pass changed
118    /// the geometry of this node, `node_id`.
119    pub(crate) fn note_geometry_change(&mut self, node_id: NodeId, context: &AppContext) {
120        context.note_geometry_scene_node(node_id, &mut self.reshaped_noted_in);
121    }
122
123    /// The same state placed at the origin: what a window root's own scene
124    /// starts from, since the position its parent gave it belongs to the
125    /// parent's window.
126    pub fn at_origin(mut self) -> Self {
127        self.position = Point::default();
128        self
129    }
130
131    /// Returns whether layout has placed the node.
132    pub fn is_placed(&self) -> bool {
133        self.is_placed
134    }
135
136    pub(crate) fn set_node_id(&mut self, node_id: NodeId) {
137        self.node_id = Some(node_id);
138    }
139
140    /// Writes the measured size, self-reporting an actual change to the
141    /// scene phase of `context`, the node's app context.
142    pub fn set_size(&mut self, size: Size, context: &AppContext) {
143        if self.size != size {
144            if let Some(id) = self.node_id {
145                context.note_geometry_scene_node(id, &mut self.reshaped_noted_in);
146            }
147            self.size = size;
148            self.note_semantics_change();
149        }
150    }
151
152    /// The z index the node's parent last placed it with. It adds to the
153    /// node's `Modifier::z_index` to order the node among its siblings.
154    pub fn z_index(&self) -> f32 {
155        self.z_index
156    }
157
158    /// Whether the node's last placement gave one of its children a z index
159    /// other than 0: the scene then orders the children by z index.
160    pub fn places_children_with_z_index(&self) -> bool {
161        self.places_children_with_z_index
162    }
163
164    pub(crate) fn set_places_children_with_z_index(&mut self, places: bool) {
165        if self.places_children_with_z_index != places {
166            self.places_children_with_z_index = places;
167            count_z_index_placer(places);
168        }
169    }
170
171    // Places the node where `at` says, with its z index.
172    pub(crate) fn place_child(
173        &mut self,
174        at: ChildPlacement,
175        placer: Option<NodeId>,
176        context: &AppContext,
177    ) {
178        self.place(at.position, placer, context);
179        self.set_z_index(at.z_index);
180    }
181
182    // Sets the z index the parent placed the node with. A change reorders
183    // the node among its siblings, which its parent's layer holds.
184    pub(crate) fn set_z_index(&mut self, z_index: f32) {
185        if self.z_index.to_bits() == z_index.to_bits() {
186            return;
187        }
188        self.z_index = z_index;
189        if let Some(id) = self.node_id {
190            crate::schedule_z_order_change(id);
191        }
192    }
193
194    /// Writes the placed position and marks the node placed, self-reporting
195    /// to the scene phase of `context`, the node's app context, an actual
196    /// move, and a placement of a node the scene does not draw: one the
197    /// running pass did not find placed. A move is reported apart, with
198    /// `placer`, the node whose placement put the node there, so the scene
199    /// can keep what it drew for the node.
200    pub fn place(&mut self, position: Point, placer: Option<NodeId>, context: &AppContext) {
201        // A node the running pass cleared while it was placed changes
202        // nothing the scene draws by being placed again.
203        let newly_placed =
204            !self.is_placed && !context.placement_pass().restore(self.cleared_in_pass.get());
205        let moved = self.position != position;
206        if let Some(id) = self.node_id {
207            if newly_placed {
208                context.note_geometry_scene_node(id, &mut self.reshaped_noted_in);
209            } else if moved {
210                context.record_moved_scene_node(id, placer, &mut self.moved_noted_in);
211            }
212        }
213        self.position = position;
214        if moved || !self.is_placed {
215            self.is_placed = true;
216            self.note_semantics_change();
217        }
218    }
219
220    /// Clears the placed flag at the start of a layout pass of `context`.
221    /// Until its parent places it again the pass counts the node unplaced,
222    /// and the pass names it to the scene phase if it ends that way.
223    pub fn clear_placed(&mut self, context: &AppContext) {
224        if !self.is_placed {
225            return;
226        }
227        self.is_placed = false;
228        self.cleared_in_pass = PackedU64::new(context.placement_pass().clear());
229        self.note_semantics_change();
230    }
231
232    /// Whether `pass` found this node placed and left it unplaced.
233    pub(crate) fn unplaced_in(&self, pass: u64) -> bool {
234        !self.is_placed && self.cleared_in_pass.get() == pass
235    }
236
237    #[inline]
238    pub(crate) fn note_semantics_change(&mut self) {
239        if let Some(id) = self.node_id {
240            crate::semantics_layout_log::record_layout_change(id, &mut self.semantics_stamp);
241        }
242    }
243}
244
245// Counts a layout node whose last placement gave a child a z index among the
246// thread's reasons to order siblings, when `places`, or takes it out.
247pub(super) fn count_z_index_placer(places: bool) {
248    crate::thread_state::cells(|cells| {
249        let reasons = &cells.z_order_reasons;
250        reasons.set(if places {
251            reasons.get() + 1
252        } else {
253            reasons.get().saturating_sub(1)
254        });
255    });
256}