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 entries(&self) -> Vec<WindowRootEntry> {
98 self.entries.borrow().clone()
99 }
100
101 pub fn revision(&self) -> u64 {
103 self.revision.get()
104 }
105}
106
107pub fn window_roots() -> Vec<WindowRootEntry> {
109 current_app_context().map_or_else(Vec::new, |context| context.window_roots().entries())
110}
111
112pub fn window_roots_revision() -> u64 {
115 current_app_context().map_or(0, |context| context.window_roots().revision())
116}
117
118pub fn is_window_root(applier: &mut MemoryApplier, node: NodeId) -> bool {
120 applier
121 .with_node::<LayoutNode, _>(node, |layout_node| layout_node.is_window_root())
122 .unwrap_or(false)
123}
124
125pub fn nearest_window_root(applier: &mut MemoryApplier, node: NodeId) -> Option<NodeId> {
129 let mut current = node;
130 for _ in 0..100_000 {
131 if is_window_root(applier, current) {
132 return Some(current);
133 }
134 current = applier.get_mut(current).ok()?.parent()?;
135 }
136 None
137}
138
139pub fn nearest_window_roots(applier: &mut MemoryApplier, nodes: &[NodeId]) -> Vec<Option<NodeId>> {
142 let mut owners: HashMap<NodeId, Option<NodeId>> = HashMap::new();
143 let mut path = Vec::new();
144 nodes
145 .iter()
146 .map(|&node| {
147 let mut current = node;
148 path.clear();
149 let owner = loop {
150 if let Some(known) = owners.get(¤t) {
151 break *known;
152 }
153 path.push(current);
154 if is_window_root(applier, current) {
155 break Some(current);
156 }
157 match applier.get_mut(current).ok().and_then(|node| node.parent()) {
158 Some(parent) if path.len() < 100_000 => current = parent,
159 _ => break None,
160 }
161 };
162 for visited in path.drain(..) {
163 owners.insert(visited, owner);
164 }
165 owner
166 })
167 .collect()
168}
169
170pub struct WindowRootNode {
174 descriptor: Rc<dyn WindowRootDescriptor>,
175 node_id: Cell<Option<NodeId>>,
176 owner: Cell<Option<AppContextId>>,
177 state: NodeState,
178}
179
180impl fmt::Debug for WindowRootNode {
181 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182 f.debug_struct("WindowRootNode")
183 .field("node_id", &self.node_id.get())
184 .finish()
185 }
186}
187
188impl WindowRootNode {
189 pub fn new(descriptor: Rc<dyn WindowRootDescriptor>) -> Self {
192 Self {
193 descriptor,
194 node_id: Cell::new(None),
195 owner: Cell::new(None),
196 state: NodeState::new(),
197 }
198 }
199
200 pub fn set_descriptor(&mut self, descriptor: Rc<dyn WindowRootDescriptor>) {
203 self.descriptor = descriptor;
204 self.register();
205 }
206
207 fn entry(&self, node: NodeId) -> WindowRootEntry {
208 WindowRootEntry {
209 node,
210 descriptor: Rc::clone(&self.descriptor),
211 }
212 }
213
214 fn register(&self) {
215 let Some(node) = self.node_id.get() else {
216 return;
217 };
218 let Some(context) = current_app_context() else {
219 log::debug!("window root {node} attached outside an app context");
220 return;
221 };
222 self.owner.set(Some(context.id()));
223 context.window_roots().register(self.entry(node));
224 }
225
226 fn unregister(&self) {
227 let (Some(node), Some(owner)) = (self.node_id.get(), self.owner.take()) else {
228 return;
229 };
230 with_app_context_by_id(owner, |context| context.window_roots().unregister(node));
231 }
232}
233
234impl DelegatableNode for WindowRootNode {
235 fn node_state(&self) -> &NodeState {
236 &self.state
237 }
238}
239
240impl ModifierNode for WindowRootNode {
241 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
242 self.node_id.set(context.node_id());
243 self.register();
244 context.invalidate(InvalidationKind::Layout);
245 }
246
247 fn on_detach(&mut self) {
248 self.unregister();
249 }
250
251 fn as_layout_node(&self) -> Option<&dyn LayoutModifierNode> {
252 Some(self)
253 }
254
255 fn as_layout_node_mut(&mut self) -> Option<&mut dyn LayoutModifierNode> {
256 Some(self)
257 }
258}
259
260impl LayoutModifierNode for WindowRootNode {
261 fn measure(
262 &self,
263 _context: &mut dyn ModifierNodeContext,
264 measurable: &dyn Measurable,
265 _constraints: Constraints,
266 ) -> LayoutModifierMeasureResult {
267 let size = self.descriptor.layout_size();
268 let size = Size::new(size.width.max(0.0), size.height.max(0.0));
269 let _content = measurable.measure(Constraints {
270 min_width: 0.0,
271 max_width: size.width,
272 min_height: 0.0,
273 max_height: size.height,
274 });
275 LayoutModifierMeasureResult::with_size(size)
276 }
277
278 fn min_intrinsic_width(&self, _measurable: &dyn Measurable, _height: f32) -> f32 {
279 0.0
280 }
281
282 fn max_intrinsic_width(&self, _measurable: &dyn Measurable, _height: f32) -> f32 {
283 0.0
284 }
285
286 fn min_intrinsic_height(&self, _measurable: &dyn Measurable, _width: f32) -> f32 {
287 0.0
288 }
289
290 fn max_intrinsic_height(&self, _measurable: &dyn Measurable, _width: f32) -> f32 {
291 0.0
292 }
293}
294
295#[derive(Clone)]
297pub struct WindowRootElement {
298 descriptor: Rc<dyn WindowRootDescriptor>,
299}
300
301impl WindowRootElement {
302 pub fn new(descriptor: Rc<dyn WindowRootDescriptor>) -> Self {
304 Self { descriptor }
305 }
306
307 fn descriptor_address(&self) -> usize {
308 Rc::as_ptr(&self.descriptor).cast::<()>() as usize
309 }
310}
311
312impl fmt::Debug for WindowRootElement {
313 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314 f.debug_struct("WindowRootElement").finish()
315 }
316}
317
318impl PartialEq for WindowRootElement {
319 fn eq(&self, other: &Self) -> bool {
320 Rc::ptr_eq(&self.descriptor, &other.descriptor)
321 }
322}
323
324impl Hash for WindowRootElement {
325 fn hash<H: Hasher>(&self, state: &mut H) {
326 self.descriptor_address().hash(state);
327 }
328}
329
330impl ModifierNodeElement for WindowRootElement {
331 type Node = WindowRootNode;
332
333 fn create(&self) -> Self::Node {
334 WindowRootNode::new(Rc::clone(&self.descriptor))
335 }
336
337 fn update(&self, node: &mut Self::Node) {
338 node.set_descriptor(Rc::clone(&self.descriptor));
339 }
340
341 fn capabilities(&self) -> NodeCapabilities {
342 NodeCapabilities::LAYOUT | NodeCapabilities::WINDOW_ROOT
343 }
344
345 fn inspector_name(&self) -> &'static str {
346 "windowRoot"
347 }
348}
349
350impl Modifier {
351 pub fn window_root(self, descriptor: Rc<dyn WindowRootDescriptor>) -> Self {
357 self.then(Self::with_element(WindowRootElement::new(descriptor)))
358 }
359}