cranpose_ui/modifier/
window_root.rs1use std::{
13 any::Any,
14 cell::{Cell, RefCell},
15 fmt,
16 hash::{Hash, Hasher},
17 rc::Rc,
18};
19
20use cranpose_core::{Applier, MemoryApplier, NodeId};
21use cranpose_foundation::{
22 Constraints, DelegatableNode, InvalidationKind, LayoutModifierNode, Measurable, ModifierNode,
23 ModifierNodeContext, ModifierNodeElement, NodeCapabilities, NodeState, Size,
24};
25use cranpose_ui_layout::LayoutModifierMeasureResult;
26
27use super::Modifier;
28use crate::{
29 render_state::{AppContextId, current_app_context, with_app_context_by_id},
30 widgets::nodes::layout_node::LayoutNode,
31};
32
33pub trait WindowRootDescriptor: Any {
38 fn layout_size(&self) -> Size;
40
41 fn as_any(&self) -> &dyn Any;
43}
44
45#[derive(Clone)]
47pub struct WindowRootEntry {
48 pub node: NodeId,
51 pub descriptor: Rc<dyn WindowRootDescriptor>,
53}
54
55impl fmt::Debug for WindowRootEntry {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 f.debug_struct("WindowRootEntry")
58 .field("node", &self.node)
59 .finish()
60 }
61}
62
63#[derive(Default)]
66pub struct WindowRootRegistry {
67 entries: RefCell<Vec<WindowRootEntry>>,
68 revision: Cell<u64>,
69}
70
71impl WindowRootRegistry {
72 fn register(&self, entry: WindowRootEntry) {
73 let mut entries = self.entries.borrow_mut();
74 if let Some(existing) = entries.iter_mut().find(|known| known.node == entry.node) {
75 *existing = entry;
76 } else {
77 entries.push(entry);
78 }
79 self.bump();
80 }
81
82 fn unregister(&self, node: NodeId) {
83 let mut entries = self.entries.borrow_mut();
84 let before = entries.len();
85 entries.retain(|entry| entry.node != node);
86 if entries.len() != before {
87 self.bump();
88 }
89 }
90
91 fn bump(&self) {
92 self.revision.set(self.revision.get().wrapping_add(1));
93 }
94
95 pub fn entries(&self) -> Vec<WindowRootEntry> {
97 self.entries.borrow().clone()
98 }
99
100 pub fn revision(&self) -> u64 {
102 self.revision.get()
103 }
104}
105
106pub fn window_roots() -> Vec<WindowRootEntry> {
108 current_app_context().map_or_else(Vec::new, |context| context.window_roots().entries())
109}
110
111pub fn window_roots_revision() -> u64 {
114 current_app_context().map_or(0, |context| context.window_roots().revision())
115}
116
117pub fn is_window_root(applier: &mut MemoryApplier, node: NodeId) -> bool {
119 applier
120 .with_node::<LayoutNode, _>(node, |layout_node| layout_node.is_window_root())
121 .unwrap_or(false)
122}
123
124pub fn nearest_window_root(applier: &mut MemoryApplier, node: NodeId) -> Option<NodeId> {
128 let mut current = node;
129 for _ in 0..100_000 {
130 let is_root = applier
131 .with_node::<LayoutNode, _>(current, |layout_node| layout_node.is_window_root())
132 .unwrap_or(false);
133 if is_root {
134 return Some(current);
135 }
136 current = applier.get_mut(current).ok()?.parent()?;
137 }
138 None
139}
140
141pub struct WindowRootNode {
145 descriptor: Rc<dyn WindowRootDescriptor>,
146 node_id: Cell<Option<NodeId>>,
147 owner: Cell<Option<AppContextId>>,
148 state: NodeState,
149}
150
151impl fmt::Debug for WindowRootNode {
152 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153 f.debug_struct("WindowRootNode")
154 .field("node_id", &self.node_id.get())
155 .finish()
156 }
157}
158
159impl WindowRootNode {
160 pub fn new(descriptor: Rc<dyn WindowRootDescriptor>) -> Self {
163 Self {
164 descriptor,
165 node_id: Cell::new(None),
166 owner: Cell::new(None),
167 state: NodeState::new(),
168 }
169 }
170
171 pub fn set_descriptor(&mut self, descriptor: Rc<dyn WindowRootDescriptor>) {
174 self.descriptor = descriptor;
175 self.register();
176 }
177
178 fn entry(&self, node: NodeId) -> WindowRootEntry {
179 WindowRootEntry {
180 node,
181 descriptor: Rc::clone(&self.descriptor),
182 }
183 }
184
185 fn register(&self) {
186 let Some(node) = self.node_id.get() else {
187 return;
188 };
189 let Some(context) = current_app_context() else {
190 log::debug!("window root {node} attached outside an app context");
191 return;
192 };
193 self.owner.set(Some(context.id()));
194 context.window_roots().register(self.entry(node));
195 }
196
197 fn unregister(&self) {
198 let (Some(node), Some(owner)) = (self.node_id.get(), self.owner.take()) else {
199 return;
200 };
201 with_app_context_by_id(owner, |context| context.window_roots().unregister(node));
202 }
203}
204
205impl DelegatableNode for WindowRootNode {
206 fn node_state(&self) -> &NodeState {
207 &self.state
208 }
209}
210
211impl ModifierNode for WindowRootNode {
212 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
213 self.node_id.set(context.node_id());
214 self.register();
215 context.invalidate(InvalidationKind::Layout);
216 }
217
218 fn on_detach(&mut self) {
219 self.unregister();
220 }
221
222 fn as_layout_node(&self) -> Option<&dyn LayoutModifierNode> {
223 Some(self)
224 }
225
226 fn as_layout_node_mut(&mut self) -> Option<&mut dyn LayoutModifierNode> {
227 Some(self)
228 }
229}
230
231impl LayoutModifierNode for WindowRootNode {
232 fn measure(
233 &self,
234 _context: &mut dyn ModifierNodeContext,
235 measurable: &dyn Measurable,
236 _constraints: Constraints,
237 ) -> LayoutModifierMeasureResult {
238 let size = self.descriptor.layout_size();
239 let size = Size::new(size.width.max(0.0), size.height.max(0.0));
240 let _content = measurable.measure(Constraints {
241 min_width: 0.0,
242 max_width: size.width,
243 min_height: 0.0,
244 max_height: size.height,
245 });
246 LayoutModifierMeasureResult::with_size(size)
247 }
248
249 fn min_intrinsic_width(&self, _measurable: &dyn Measurable, _height: f32) -> f32 {
250 0.0
251 }
252
253 fn max_intrinsic_width(&self, _measurable: &dyn Measurable, _height: f32) -> f32 {
254 0.0
255 }
256
257 fn min_intrinsic_height(&self, _measurable: &dyn Measurable, _width: f32) -> f32 {
258 0.0
259 }
260
261 fn max_intrinsic_height(&self, _measurable: &dyn Measurable, _width: f32) -> f32 {
262 0.0
263 }
264}
265
266#[derive(Clone)]
268pub struct WindowRootElement {
269 descriptor: Rc<dyn WindowRootDescriptor>,
270}
271
272impl WindowRootElement {
273 pub fn new(descriptor: Rc<dyn WindowRootDescriptor>) -> Self {
275 Self { descriptor }
276 }
277
278 fn descriptor_address(&self) -> usize {
279 Rc::as_ptr(&self.descriptor).cast::<()>() as usize
280 }
281}
282
283impl fmt::Debug for WindowRootElement {
284 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
285 f.debug_struct("WindowRootElement").finish()
286 }
287}
288
289impl PartialEq for WindowRootElement {
290 fn eq(&self, other: &Self) -> bool {
291 Rc::ptr_eq(&self.descriptor, &other.descriptor)
292 }
293}
294
295impl Hash for WindowRootElement {
296 fn hash<H: Hasher>(&self, state: &mut H) {
297 self.descriptor_address().hash(state);
298 }
299}
300
301impl ModifierNodeElement for WindowRootElement {
302 type Node = WindowRootNode;
303
304 fn create(&self) -> Self::Node {
305 WindowRootNode::new(Rc::clone(&self.descriptor))
306 }
307
308 fn update(&self, node: &mut Self::Node) {
309 node.set_descriptor(Rc::clone(&self.descriptor));
310 }
311
312 fn capabilities(&self) -> NodeCapabilities {
313 NodeCapabilities::LAYOUT | NodeCapabilities::WINDOW_ROOT
314 }
315
316 fn inspector_name(&self) -> &'static str {
317 "windowRoot"
318 }
319}
320
321impl Modifier {
322 pub fn window_root(self, descriptor: Rc<dyn WindowRootDescriptor>) -> Self {
328 self.then(Self::with_element(WindowRootElement::new(descriptor)))
329 }
330}