cranpose_ui/widgets/nodes/
layout_state.rs1use std::cell::Cell;
5
6use cranpose_core::{NodeId, PackedU64};
7
8use crate::{
9 modifier::{Point, Size},
10 render_state::AppContext,
11};
12
13pub(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 pub(crate) fn begin(&self) {
31 self.id.set(self.id.get() + 1);
32 self.unplaced.set(0);
33 }
34
35 pub(crate) fn with_unplaced_nodes(&self) -> Option<u64> {
37 (self.unplaced.get() > 0).then(|| self.id.get())
38 }
39
40 fn clear(&self) -> u64 {
42 self.unplaced.set(self.unplaced.get() + 1);
43 self.id.get()
44 }
45
46 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#[derive(Clone, Copy, Debug, Default)]
59pub struct LayoutState {
60 size: Size,
61 position: Point,
62 is_placed: bool,
63 node_id: Option<NodeId>,
64 cleared_in_pass: PackedU64,
66 content_offset: Point,
67 semantics_stamp: PackedU64,
68 moved_noted_in: PackedU64,
70 reshaped_noted_in: PackedU64,
72 z_index: f32,
73 places_children_with_z_index: bool,
75}
76
77#[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 pub fn size(&self) -> Size {
94 self.size
95 }
96
97 pub fn position(&self) -> Point {
99 self.position
100 }
101
102 pub fn content_offset(&self) -> Point {
105 self.content_offset
106 }
107
108 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 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 pub fn at_origin(mut self) -> Self {
127 self.position = Point::default();
128 self
129 }
130
131 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 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 pub fn z_index(&self) -> f32 {
155 self.z_index
156 }
157
158 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 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 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 pub fn place(&mut self, position: Point, placer: Option<NodeId>, context: &AppContext) {
201 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 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 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
245pub(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}