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