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
144#[derive(Default)]
146pub struct WindowRootRoutingScratch {
147 owners: HashMap<NodeId, Option<NodeId>>,
148 path: Vec<NodeId>,
149}
150
151pub fn nearest_window_roots(applier: &mut MemoryApplier, nodes: &[NodeId]) -> Vec<Option<NodeId>> {
155 let mut output = Vec::new();
156 nearest_window_roots_into(
157 applier,
158 nodes.iter().copied(),
159 &mut output,
160 &mut WindowRootRoutingScratch::default(),
161 );
162 output
163}
164
165pub fn nearest_window_roots_into(
182 applier: &mut MemoryApplier,
183 nodes: impl IntoIterator<Item = NodeId>,
184 output: &mut Vec<Option<NodeId>>,
185 scratch: &mut WindowRootRoutingScratch,
186) {
187 let nodes = nodes.into_iter();
188 output.clear();
189 scratch.owners.clear();
190 scratch.path.clear();
191 let (lower_bound, upper_bound) = nodes.size_hint();
192 if lower_bound == 0 && upper_bound == Some(0) {
193 return;
194 }
195 output.reserve(lower_bound);
196 if current_app_context().is_some_and(|context| context.window_roots().is_empty()) {
197 output.extend(nodes.map(|_| None));
198 return;
199 }
200 for node in nodes {
201 let mut current = node;
202 scratch.path.clear();
203 let owner = loop {
204 if let Some(known) = scratch.owners.get(¤t) {
205 break *known;
206 }
207 scratch.path.push(current);
208 if is_window_root(applier, current) {
209 break Some(current);
210 }
211 match applier.get_mut(current).ok().and_then(|node| node.parent()) {
212 Some(parent) if scratch.path.len() < 100_000 => current = parent,
213 _ => break None,
214 }
215 };
216 for visited in scratch.path.drain(..) {
217 scratch.owners.insert(visited, owner);
218 }
219 output.push(owner);
220 }
221}
222
223pub struct WindowRootNode {
227 descriptor: Rc<dyn WindowRootDescriptor>,
228 node_id: Cell<Option<NodeId>>,
229 owner: Cell<Option<AppContextId>>,
230 state: NodeState,
231}
232
233impl fmt::Debug for WindowRootNode {
234 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
235 f.debug_struct("WindowRootNode")
236 .field("node_id", &self.node_id.get())
237 .finish()
238 }
239}
240
241impl WindowRootNode {
242 pub fn new(descriptor: Rc<dyn WindowRootDescriptor>) -> Self {
245 Self {
246 descriptor,
247 node_id: Cell::new(None),
248 owner: Cell::new(None),
249 state: NodeState::new(),
250 }
251 }
252
253 pub fn set_descriptor(&mut self, descriptor: Rc<dyn WindowRootDescriptor>) {
256 self.descriptor = descriptor;
257 self.register();
258 }
259
260 fn entry(&self, node: NodeId) -> WindowRootEntry {
261 WindowRootEntry {
262 node,
263 descriptor: Rc::clone(&self.descriptor),
264 }
265 }
266
267 fn register(&self) {
268 let Some(node) = self.node_id.get() else {
269 return;
270 };
271 let Some(context) = current_app_context() else {
272 log::debug!("window root {node} attached outside an app context");
273 return;
274 };
275 self.owner.set(Some(context.id()));
276 context.window_roots().register(self.entry(node));
277 }
278
279 fn unregister(&self) {
280 let (Some(node), Some(owner)) = (self.node_id.get(), self.owner.take()) else {
281 return;
282 };
283 with_app_context_by_id(owner, |context| context.window_roots().unregister(node));
284 }
285}
286
287impl DelegatableNode for WindowRootNode {
288 fn node_state(&self) -> &NodeState {
289 &self.state
290 }
291}
292
293impl ModifierNode for WindowRootNode {
294 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
295 self.node_id.set(context.node_id());
296 self.register();
297 context.invalidate(InvalidationKind::Layout);
298 }
299
300 fn on_detach(&mut self) {
301 self.unregister();
302 }
303
304 fn as_layout_node(&self) -> Option<&dyn LayoutModifierNode> {
305 Some(self)
306 }
307
308 fn as_layout_node_mut(&mut self) -> Option<&mut dyn LayoutModifierNode> {
309 Some(self)
310 }
311}
312
313impl LayoutModifierNode for WindowRootNode {
314 fn measure(
315 &self,
316 _context: &mut dyn ModifierNodeContext,
317 measurable: &dyn Measurable,
318 _constraints: Constraints,
319 ) -> LayoutModifierMeasureResult {
320 let size = self.descriptor.layout_size();
321 let size = Size::new(size.width.max(0.0), size.height.max(0.0));
322 let _content = measurable.measure(Constraints {
323 min_width: 0.0,
324 max_width: size.width,
325 min_height: 0.0,
326 max_height: size.height,
327 });
328 LayoutModifierMeasureResult::with_size(size)
329 }
330
331 fn min_intrinsic_width(
332 &self,
333 _measurable: &dyn Measurable,
334 _height: f32,
335 _density: f32,
336 ) -> f32 {
337 0.0
338 }
339
340 fn max_intrinsic_width(
341 &self,
342 _measurable: &dyn Measurable,
343 _height: f32,
344 _density: f32,
345 ) -> f32 {
346 0.0
347 }
348
349 fn min_intrinsic_height(
350 &self,
351 _measurable: &dyn Measurable,
352 _width: f32,
353 _density: f32,
354 ) -> f32 {
355 0.0
356 }
357
358 fn max_intrinsic_height(
359 &self,
360 _measurable: &dyn Measurable,
361 _width: f32,
362 _density: f32,
363 ) -> f32 {
364 0.0
365 }
366}
367
368#[derive(Clone)]
370pub struct WindowRootElement {
371 descriptor: Rc<dyn WindowRootDescriptor>,
372}
373
374impl WindowRootElement {
375 pub fn new(descriptor: Rc<dyn WindowRootDescriptor>) -> Self {
377 Self { descriptor }
378 }
379
380 fn descriptor_address(&self) -> usize {
381 Rc::as_ptr(&self.descriptor).cast::<()>() as usize
382 }
383}
384
385impl fmt::Debug for WindowRootElement {
386 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
387 f.debug_struct("WindowRootElement").finish()
388 }
389}
390
391impl PartialEq for WindowRootElement {
392 fn eq(&self, other: &Self) -> bool {
393 Rc::ptr_eq(&self.descriptor, &other.descriptor)
394 }
395}
396
397impl Hash for WindowRootElement {
398 fn hash<H: Hasher>(&self, state: &mut H) {
399 self.descriptor_address().hash(state);
400 }
401}
402
403impl ModifierNodeElement for WindowRootElement {
404 type Node = WindowRootNode;
405
406 fn create(&self) -> Self::Node {
407 WindowRootNode::new(Rc::clone(&self.descriptor))
408 }
409
410 fn update(&self, node: &mut Self::Node) {
411 node.set_descriptor(Rc::clone(&self.descriptor));
412 }
413
414 fn capabilities(&self) -> NodeCapabilities {
415 NodeCapabilities::LAYOUT | NodeCapabilities::WINDOW_ROOT
416 }
417
418 fn inspector_name(&self) -> &'static str {
419 "windowRoot"
420 }
421}
422
423impl Modifier {
424 pub fn window_root(self, descriptor: Rc<dyn WindowRootDescriptor>) -> Self {
430 self.then(Self::with_element(WindowRootElement::new(descriptor)))
431 }
432}