1use std::{
13 any::Any,
14 cell::{Cell, RefCell},
15 collections::HashMap,
16 fmt,
17 hash::{Hash, Hasher},
18 rc::Rc,
19};
20
21use cranpose_core::{Applier, MemoryApplier, NodeId};
22use cranpose_foundation::{
23 Constraints, DelegatableNode, InvalidationKind, LayoutModifierNode, Measurable, ModifierNode,
24 ModifierNodeContext, ModifierNodeElement, NodeCapabilities, NodeState, Size,
25};
26use cranpose_ui_layout::LayoutModifierMeasureResult;
27
28use super::Modifier;
29use crate::{
30 render_state::{AppContextId, current_app_context, with_app_context_by_id},
31 widgets::nodes::layout_node::LayoutNode,
32};
33
34pub trait WindowRootDescriptor: Any {
39 fn layout_size(&self) -> Size;
41
42 fn as_any(&self) -> &dyn Any;
44}
45
46#[derive(Clone)]
48pub struct WindowRootEntry {
49 pub node: NodeId,
52 pub descriptor: Rc<dyn WindowRootDescriptor>,
54}
55
56impl fmt::Debug for WindowRootEntry {
57 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58 f.debug_struct("WindowRootEntry")
59 .field("node", &self.node)
60 .finish()
61 }
62}
63
64#[derive(Default)]
67pub struct WindowRootRegistry {
68 entries: RefCell<Vec<WindowRootEntry>>,
69 revision: Cell<u64>,
70}
71
72impl WindowRootRegistry {
73 fn register(&self, entry: WindowRootEntry) {
74 let mut entries = self.entries.borrow_mut();
75 if let Some(existing) = entries.iter_mut().find(|known| known.node == entry.node) {
76 *existing = entry;
77 } else {
78 entries.push(entry);
79 }
80 self.bump();
81 }
82
83 fn unregister(&self, node: NodeId) {
84 let mut entries = self.entries.borrow_mut();
85 let before = entries.len();
86 entries.retain(|entry| entry.node != node);
87 if entries.len() != before {
88 self.bump();
89 }
90 }
91
92 fn bump(&self) {
93 self.revision.set(self.revision.get().wrapping_add(1));
94 }
95
96 pub fn is_empty(&self) -> bool {
98 self.entries.borrow().is_empty()
99 }
100
101 pub fn entries(&self) -> Vec<WindowRootEntry> {
103 self.entries.borrow().clone()
104 }
105
106 pub fn revision(&self) -> u64 {
108 self.revision.get()
109 }
110}
111
112pub fn window_roots() -> Vec<WindowRootEntry> {
114 current_app_context().map_or_else(Vec::new, |context| context.window_roots().entries())
115}
116
117pub fn window_roots_revision() -> u64 {
120 current_app_context().map_or(0, |context| context.window_roots().revision())
121}
122
123pub fn is_window_root(applier: &mut MemoryApplier, node: NodeId) -> bool {
125 applier
126 .with_node::<LayoutNode, _>(node, |layout_node| layout_node.is_window_root())
127 .unwrap_or(false)
128}
129
130pub fn nearest_window_root(applier: &mut MemoryApplier, node: NodeId) -> Option<NodeId> {
134 let mut current = node;
135 for _ in 0..100_000 {
136 if is_window_root(applier, current) {
137 return Some(current);
138 }
139 current = applier.get_mut(current).ok()?.parent()?;
140 }
141 None
142}
143
144pub fn nearest_window_roots(applier: &mut MemoryApplier, nodes: &[NodeId]) -> Vec<Option<NodeId>> {
148 if current_app_context().is_some_and(|context| context.window_roots().is_empty()) {
149 return vec![None; nodes.len()];
150 }
151 let mut owners: HashMap<NodeId, Option<NodeId>> = HashMap::new();
152 let mut path = Vec::new();
153 nodes
154 .iter()
155 .map(|&node| {
156 let mut current = node;
157 path.clear();
158 let owner = loop {
159 if let Some(known) = owners.get(¤t) {
160 break *known;
161 }
162 path.push(current);
163 if is_window_root(applier, current) {
164 break Some(current);
165 }
166 match applier.get_mut(current).ok().and_then(|node| node.parent()) {
167 Some(parent) if path.len() < 100_000 => current = parent,
168 _ => break None,
169 }
170 };
171 for visited in path.drain(..) {
172 owners.insert(visited, owner);
173 }
174 owner
175 })
176 .collect()
177}
178
179pub struct WindowRootNode {
183 descriptor: Rc<dyn WindowRootDescriptor>,
184 node_id: Cell<Option<NodeId>>,
185 owner: Cell<Option<AppContextId>>,
186 state: NodeState,
187}
188
189impl fmt::Debug for WindowRootNode {
190 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191 f.debug_struct("WindowRootNode")
192 .field("node_id", &self.node_id.get())
193 .finish()
194 }
195}
196
197impl WindowRootNode {
198 pub fn new(descriptor: Rc<dyn WindowRootDescriptor>) -> Self {
201 Self {
202 descriptor,
203 node_id: Cell::new(None),
204 owner: Cell::new(None),
205 state: NodeState::new(),
206 }
207 }
208
209 pub fn set_descriptor(&mut self, descriptor: Rc<dyn WindowRootDescriptor>) {
212 self.descriptor = descriptor;
213 self.register();
214 }
215
216 fn entry(&self, node: NodeId) -> WindowRootEntry {
217 WindowRootEntry {
218 node,
219 descriptor: Rc::clone(&self.descriptor),
220 }
221 }
222
223 fn register(&self) {
224 let Some(node) = self.node_id.get() else {
225 return;
226 };
227 let Some(context) = current_app_context() else {
228 log::debug!("window root {node} attached outside an app context");
229 return;
230 };
231 self.owner.set(Some(context.id()));
232 context.window_roots().register(self.entry(node));
233 }
234
235 fn unregister(&self) {
236 let (Some(node), Some(owner)) = (self.node_id.get(), self.owner.take()) else {
237 return;
238 };
239 with_app_context_by_id(owner, |context| context.window_roots().unregister(node));
240 }
241}
242
243impl DelegatableNode for WindowRootNode {
244 fn node_state(&self) -> &NodeState {
245 &self.state
246 }
247}
248
249impl ModifierNode for WindowRootNode {
250 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
251 self.node_id.set(context.node_id());
252 self.register();
253 context.invalidate(InvalidationKind::Layout);
254 }
255
256 fn on_detach(&mut self) {
257 self.unregister();
258 }
259
260 fn as_layout_node(&self) -> Option<&dyn LayoutModifierNode> {
261 Some(self)
262 }
263
264 fn as_layout_node_mut(&mut self) -> Option<&mut dyn LayoutModifierNode> {
265 Some(self)
266 }
267}
268
269impl LayoutModifierNode for WindowRootNode {
270 fn measure(
271 &self,
272 _context: &mut dyn ModifierNodeContext,
273 measurable: &dyn Measurable,
274 _constraints: Constraints,
275 ) -> LayoutModifierMeasureResult {
276 let size = self.descriptor.layout_size();
277 let size = Size::new(size.width.max(0.0), size.height.max(0.0));
278 let _content = measurable.measure(Constraints {
279 min_width: 0.0,
280 max_width: size.width,
281 min_height: 0.0,
282 max_height: size.height,
283 });
284 LayoutModifierMeasureResult::with_size(size)
285 }
286
287 fn min_intrinsic_width(
288 &self,
289 _measurable: &dyn Measurable,
290 _height: f32,
291 _density: f32,
292 ) -> f32 {
293 0.0
294 }
295
296 fn max_intrinsic_width(
297 &self,
298 _measurable: &dyn Measurable,
299 _height: f32,
300 _density: f32,
301 ) -> f32 {
302 0.0
303 }
304
305 fn min_intrinsic_height(
306 &self,
307 _measurable: &dyn Measurable,
308 _width: f32,
309 _density: f32,
310 ) -> f32 {
311 0.0
312 }
313
314 fn max_intrinsic_height(
315 &self,
316 _measurable: &dyn Measurable,
317 _width: f32,
318 _density: f32,
319 ) -> f32 {
320 0.0
321 }
322}
323
324#[derive(Clone)]
326pub struct WindowRootElement {
327 descriptor: Rc<dyn WindowRootDescriptor>,
328}
329
330impl WindowRootElement {
331 pub fn new(descriptor: Rc<dyn WindowRootDescriptor>) -> Self {
333 Self { descriptor }
334 }
335
336 fn descriptor_address(&self) -> usize {
337 Rc::as_ptr(&self.descriptor).cast::<()>() as usize
338 }
339}
340
341impl fmt::Debug for WindowRootElement {
342 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
343 f.debug_struct("WindowRootElement").finish()
344 }
345}
346
347impl PartialEq for WindowRootElement {
348 fn eq(&self, other: &Self) -> bool {
349 Rc::ptr_eq(&self.descriptor, &other.descriptor)
350 }
351}
352
353impl Hash for WindowRootElement {
354 fn hash<H: Hasher>(&self, state: &mut H) {
355 self.descriptor_address().hash(state);
356 }
357}
358
359impl ModifierNodeElement for WindowRootElement {
360 type Node = WindowRootNode;
361
362 fn create(&self) -> Self::Node {
363 WindowRootNode::new(Rc::clone(&self.descriptor))
364 }
365
366 fn update(&self, node: &mut Self::Node) {
367 node.set_descriptor(Rc::clone(&self.descriptor));
368 }
369
370 fn capabilities(&self) -> NodeCapabilities {
371 NodeCapabilities::LAYOUT | NodeCapabilities::WINDOW_ROOT
372 }
373
374 fn inspector_name(&self) -> &'static str {
375 "windowRoot"
376 }
377}
378
379impl Modifier {
380 pub fn window_root(self, descriptor: Rc<dyn WindowRootDescriptor>) -> Self {
386 self.then(Self::with_element(WindowRootElement::new(descriptor)))
387 }
388}