tessera_ui/component_tree/
node.rs

1use std::{
2    any::TypeId,
3    collections::HashMap,
4    ops::{Add, AddAssign},
5    sync::Arc,
6    time::Instant,
7};
8
9use dashmap::DashMap;
10use indextree::NodeId;
11use parking_lot::RwLock;
12use rayon::prelude::*;
13use smallvec::SmallVec;
14use tracing::debug;
15use winit::window::CursorIcon;
16
17use crate::{
18    Clipboard, ComputeCommand, ComputeResourceManager, DrawCommand, Px,
19    accessibility::{AccessibilityActionHandler, AccessibilityNode},
20    cursor::CursorEvent,
21    px::{PxPosition, PxSize},
22    renderer::Command,
23};
24
25use super::constraint::{Constraint, DimensionValue};
26
27/// A guard that manages accessibility node building and automatically
28/// commits the result to the metadata when dropped.
29pub struct AccessibilityBuilderGuard<'a> {
30    node_id: NodeId,
31    metadatas: &'a ComponentNodeMetaDatas,
32    node: AccessibilityNode,
33}
34
35impl<'a> AccessibilityBuilderGuard<'a> {
36    /// Sets the role of this node.
37    pub fn role(mut self, role: accesskit::Role) -> Self {
38        self.node.role = Some(role);
39        self
40    }
41
42    /// Sets the label of this node.
43    pub fn label(mut self, label: impl Into<String>) -> Self {
44        self.node.label = Some(label.into());
45        self
46    }
47
48    /// Sets the description of this node.
49    pub fn description(mut self, description: impl Into<String>) -> Self {
50        self.node.description = Some(description.into());
51        self
52    }
53
54    /// Sets the value of this node.
55    pub fn value(mut self, value: impl Into<String>) -> Self {
56        self.node.value = Some(value.into());
57        self
58    }
59
60    /// Sets the numeric value of this node.
61    pub fn numeric_value(mut self, value: f64) -> Self {
62        self.node.numeric_value = Some(value);
63        self
64    }
65
66    /// Sets the numeric range of this node.
67    pub fn numeric_range(mut self, min: f64, max: f64) -> Self {
68        self.node.min_numeric_value = Some(min);
69        self.node.max_numeric_value = Some(max);
70        self
71    }
72
73    /// Marks this node as focusable.
74    pub fn focusable(mut self) -> Self {
75        self.node.focusable = true;
76        self
77    }
78
79    /// Marks this node as focused.
80    pub fn focused(mut self) -> Self {
81        self.node.focused = true;
82        self
83    }
84
85    /// Sets the toggled state of this node.
86    pub fn toggled(mut self, toggled: accesskit::Toggled) -> Self {
87        self.node.toggled = Some(toggled);
88        self
89    }
90
91    /// Marks this node as disabled.
92    pub fn disabled(mut self) -> Self {
93        self.node.disabled = true;
94        self
95    }
96
97    /// Marks this node as hidden from accessibility.
98    pub fn hidden(mut self) -> Self {
99        self.node.hidden = true;
100        self
101    }
102
103    /// Adds an action that this node supports.
104    pub fn action(mut self, action: accesskit::Action) -> Self {
105        self.node.actions.push(action);
106        self
107    }
108
109    /// Adds multiple actions that this node supports.
110    pub fn actions(mut self, actions: impl IntoIterator<Item = accesskit::Action>) -> Self {
111        self.node.actions.extend(actions);
112        self
113    }
114
115    /// Sets a custom accessibility key for stable ID generation.
116    pub fn key(mut self, key: impl Into<String>) -> Self {
117        self.node.key = Some(key.into());
118        self
119    }
120
121    /// Explicitly commits the accessibility information.
122    pub fn commit(self) {
123        // The Drop impl will handle the actual commit
124        drop(self);
125    }
126}
127
128impl Drop for AccessibilityBuilderGuard<'_> {
129    fn drop(&mut self) {
130        // Copy the accessibility data to metadata
131        if let Some(mut metadata) = self.metadatas.get_mut(&self.node_id) {
132            metadata.accessibility = Some(self.node.clone());
133        }
134    }
135}
136
137/// A ComponentNode is a node in the component tree.
138/// It represents all information about a component.
139pub struct ComponentNode {
140    /// Component function's name, for debugging purposes.
141    pub fn_name: String,
142    /// Describes the component in layout.
143    /// None means using default measure policy which places children at the top-left corner
144    /// of the parent node, with no offset.
145    pub measure_fn: Option<Box<MeasureFn>>,
146    /// Describes the input handler for the component.
147    /// This is used to handle state changes.
148    pub input_handler_fn: Option<Box<InputHandlerFn>>,
149}
150
151/// Contains metadata of the component node.
152#[derive(Default)]
153pub struct ComponentNodeMetaData {
154    /// The computed data (size) of the node.
155    /// None if the node is not computed yet.
156    pub computed_data: Option<ComputedData>,
157    /// The node's start position, relative to its parent.
158    /// None if the node is not placed yet.
159    pub rel_position: Option<PxPosition>,
160    /// The node's start position, relative to the root window.
161    /// This will be computed during drawing command's generation.
162    /// None if the node is not drawn yet.
163    pub abs_position: Option<PxPosition>,
164    /// The effective clipping rectangle for this node, considering all its ancestors.
165    /// This is calculated once per frame before event handling.
166    pub event_clip_rect: Option<crate::PxRect>,
167    /// Commands associated with this node.
168    ///
169    /// This stores both draw and compute commands in a unified vector using the
170    /// new `Command` enum. Commands are collected during the measure phase and
171    /// executed during rendering. The order of commands in this vector determines
172    /// their execution order.
173    pub(crate) commands: SmallVec<[(Command, TypeId); 4]>,
174    /// Whether this node clips its children.
175    pub clips_children: bool,
176    /// Accessibility information for this node.
177    pub accessibility: Option<AccessibilityNode>,
178    /// Handler for accessibility actions on this node.
179    pub accessibility_action_handler: Option<AccessibilityActionHandler>,
180}
181
182impl ComponentNodeMetaData {
183    /// Creates a new `ComponentNodeMetaData` with default values.
184    pub fn none() -> Self {
185        Self {
186            computed_data: None,
187            rel_position: None,
188            abs_position: None,
189            event_clip_rect: None,
190            commands: SmallVec::new(),
191            clips_children: false,
192            accessibility: None,
193            accessibility_action_handler: None,
194        }
195    }
196
197    /// Pushes a draw command to the node's metadata.
198    ///
199    /// Draw commands are responsible for rendering visual content (shapes, text, images).
200    /// This method wraps the command in the unified `Command::Draw` variant and adds it
201    /// to the command queue. Commands are executed in the order they are added.
202    ///
203    /// # Example
204    /// ```rust,ignore
205    /// metadata.push_draw_command(ShapeCommand::Rect {
206    ///     color: [1.0, 0.0, 0.0, 1.0],
207    ///     corner_radius: 8.0,
208    ///     shadow: None,
209    /// });
210    /// ```
211    pub fn push_draw_command<C: DrawCommand + 'static>(&mut self, command: C) {
212        let command = Box::new(command);
213        let command = command as Box<dyn DrawCommand>;
214        let command = Command::Draw(command);
215        self.commands.push((command, TypeId::of::<C>()));
216    }
217
218    /// Pushes a compute command to the node's metadata.
219    ///
220    /// Compute commands perform GPU computation tasks (post-processing effects,
221    /// complex calculations). This method wraps the command in the unified
222    /// `Command::Compute` variant and adds it to the command queue.
223    ///
224    /// # Example
225    /// ```rust,ignore
226    /// metadata.push_compute_command(DualBlurCommand::horizontal_then_vertical(5.0, Px::new(16)));
227    /// ```
228    pub fn push_compute_command<C: ComputeCommand + 'static>(&mut self, command: C) {
229        let command = Box::new(command);
230        let command = command as Box<dyn ComputeCommand>;
231        let command = Command::Compute(command);
232        self.commands.push((command, TypeId::of::<C>()));
233    }
234}
235
236/// A tree of component nodes, using `indextree::Arena` for storage.
237pub type ComponentNodeTree = indextree::Arena<ComponentNode>;
238/// Contains all component nodes' metadatas, using a thread-safe `DashMap`.
239pub type ComponentNodeMetaDatas = DashMap<NodeId, ComponentNodeMetaData>;
240
241/// Represents errors that can occur during node measurement.
242#[derive(Debug, Clone, PartialEq)]
243pub enum MeasurementError {
244    /// Indicates that the specified node was not found in the component tree.
245    NodeNotFoundInTree,
246    /// Indicates that metadata for the specified node was not found (currently not a primary error source in measure_node).
247    NodeNotFoundInMeta,
248    /// Indicates that the custom measure function (`MeasureFn`) for a node failed.
249    /// Contains a string detailing the failure.
250    MeasureFnFailed(String),
251    /// Indicates that the measurement of a child node failed during a parent's layout calculation (e.g., in `DEFAULT_LAYOUT_DESC`).
252    /// Contains the `NodeId` of the child that failed.
253    ChildMeasurementFailed(NodeId),
254}
255
256/// A `MeasureFn` is a function that takes an input `Constraint` and its children nodes,
257/// finishes placementing inside, and returns its size (`ComputedData`) or an error.
258pub type MeasureFn =
259    dyn Fn(&MeasureInput<'_>) -> Result<ComputedData, MeasurementError> + Send + Sync;
260
261/// Input for the measure function (`MeasureFn`).
262pub struct MeasureInput<'a> {
263    /// The `NodeId` of the current node being measured.
264    pub current_node_id: indextree::NodeId,
265    /// The component tree containing all nodes.
266    pub tree: &'a ComponentNodeTree,
267    /// The effective constraint for this node, merged with its parent's constraint.
268    pub parent_constraint: &'a Constraint,
269    /// The children nodes of the current node.
270    pub children_ids: &'a [indextree::NodeId],
271    /// Metadata for all component nodes, used to access cached data and constraints.
272    pub metadatas: &'a ComponentNodeMetaDatas,
273    /// Compute resources manager
274    pub compute_resource_manager: Arc<RwLock<ComputeResourceManager>>,
275    /// Gpu device
276    pub gpu: &'a wgpu::Device,
277}
278
279impl<'a> MeasureInput<'a> {
280    /// Returns a mutable reference to the metadata of the current node.
281    ///
282    /// This is a convenience method that simplifies accessing the current node's metadata
283    /// from within a `measure` function. It encapsulates the `DashMap::get_mut` call and panics
284    /// if the metadata is not found, as it's an invariant that it must exist.
285    pub fn metadata_mut(&self) -> dashmap::mapref::one::RefMut<'_, NodeId, ComponentNodeMetaData> {
286        self.metadatas
287            .get_mut(&self.current_node_id)
288            .expect("Metadata for current node must exist during measure")
289    }
290
291    /// Measures all specified child nodes under the given constraint.
292    ///
293    /// Returns a map of each child's computed layout data, or the first measurement error encountered.
294    pub fn measure_children(
295        &self,
296        nodes_to_measure: Vec<(NodeId, Constraint)>,
297    ) -> Result<HashMap<NodeId, ComputedData>, MeasurementError> {
298        let results = measure_nodes(
299            nodes_to_measure,
300            self.tree,
301            self.metadatas,
302            self.compute_resource_manager.clone(),
303            self.gpu,
304        );
305
306        let mut successful_results = HashMap::new();
307        for (child_id, result) in results {
308            match result {
309                Ok(size) => successful_results.insert(child_id, size),
310                Err(e) => {
311                    debug!("Measurement error for child {child_id:?}: {e:?}");
312                    return Err(e);
313                }
314            };
315        }
316        Ok(successful_results)
317    }
318
319    /// Measures a single child node under the given constraint.
320    ///
321    /// Returns the computed layout data or a measurement error.
322    pub fn measure_child(
323        &self,
324        child_id: NodeId,
325        constraint: &Constraint,
326    ) -> Result<ComputedData, MeasurementError> {
327        measure_node(
328            child_id,
329            constraint,
330            self.tree,
331            self.metadatas,
332            self.compute_resource_manager.clone(),
333            self.gpu,
334        )
335    }
336
337    /// Sets the relative position of a child node.
338    pub fn place_child(&self, child_id: NodeId, position: PxPosition) {
339        place_node(child_id, position, self.metadatas);
340    }
341
342    /// Enables clipping for the current node.
343    pub fn enable_clipping(&self) {
344        // Set the clipping flag to true for this node.
345        self.metadata_mut().clips_children = true;
346    }
347
348    /// Disables clipping for the current node.
349    pub fn disable_clipping(&self) {
350        // Set the clipping flag to false for this node.
351        self.metadata_mut().clips_children = false;
352    }
353}
354
355/// A `InputHandlerFn` is a function that handles state changes for a component.
356///
357/// The rule of execution order is:
358///
359/// 1. Children's input handlers are executed earlier than parent's.
360/// 2. Newer components' input handlers are executed earlier than older ones.
361///
362/// Acutally, rule 2 includes rule 1, because a newer component is always a child of an older component :)
363pub type InputHandlerFn = dyn Fn(InputHandlerInput) + Send + Sync;
364
365/// Input for the input handler function (`InputHandlerFn`).
366///
367/// Note that you can modify the `cursor_events` and `keyboard_events` vectors
368/// for exmaple block some keyboard events or cursor events to prevent them from propagating
369/// to parent components and older brother components.
370pub struct InputHandlerInput<'a> {
371    /// The size of the component node, computed during the measure stage.
372    pub computed_data: ComputedData,
373    /// The position of the cursor, if available.
374    /// Relative to the root position of the component.
375    pub cursor_position_rel: Option<PxPosition>,
376    /// The mut ref of absolute position of the cursor in the window.
377    /// Used to block cursor fully if needed, since cursor_position_rel use this.
378    /// Not a public field for now.
379    pub(crate) cursor_position_abs: &'a mut Option<PxPosition>,
380    /// Cursor events from the event loop, if any.
381    pub cursor_events: &'a mut Vec<CursorEvent>,
382    /// Keyboard events from the event loop, if any.
383    pub keyboard_events: &'a mut Vec<winit::event::KeyEvent>,
384    /// IME events from the event loop, if any.
385    pub ime_events: &'a mut Vec<winit::event::Ime>,
386    /// The current state of the keyboard modifiers at the time of the event.
387    /// This allows for implementing keyboard shortcuts (e.g., Ctrl+C).
388    pub key_modifiers: winit::keyboard::ModifiersState,
389    /// A context for making requests to the window for the current frame.
390    pub requests: &'a mut WindowRequests,
391    /// Clipboard
392    pub clipboard: &'a mut Clipboard,
393    /// The current node ID (for accessibility setup)
394    pub(crate) current_node_id: indextree::NodeId,
395    /// Reference to component metadatas (for accessibility setup)
396    pub(crate) metadatas: &'a ComponentNodeMetaDatas,
397}
398
399impl InputHandlerInput<'_> {
400    /// Blocks the cursor to other components.
401    pub fn block_cursor(&mut self) {
402        // Block the cursor by setting its position to None.
403        self.cursor_position_abs.take();
404        // Clear all cursor events to prevent them from propagating.
405        self.cursor_events.clear();
406    }
407
408    /// Blocks the keyboard events to other components.
409    pub fn block_keyboard(&mut self) {
410        // Clear all keyboard events to prevent them from propagating.
411        self.keyboard_events.clear();
412    }
413
414    /// Blocks the IME events to other components.
415    pub fn block_ime(&mut self) {
416        // Clear all IME events to prevent them from propagating.
417        self.ime_events.clear();
418    }
419
420    /// Block all events (cursor, keyboard, IME) to other components.
421    pub fn block_all(&mut self) {
422        self.block_cursor();
423        self.block_keyboard();
424        self.block_ime();
425    }
426
427    /// Provides a fluent API for setting accessibility information for the current component.
428    ///
429    /// This method returns a builder that allows you to set various accessibility properties
430    /// like role, label, actions, and state. The accessibility information is automatically
431    /// committed when the builder is dropped or when `.commit()` is called explicitly.
432    ///
433    /// # Example
434    ///
435    /// ```
436    /// use accesskit::{Action, Role};
437    /// use tessera_ui::tessera;
438    ///
439    /// #[tessera]
440    /// fn accessible_button() {
441    ///     input_handler(Box::new(|input| {
442    ///         input.accessibility()
443    ///             .role(Role::Button)
444    ///             .label("Click me")
445    ///             .focusable()
446    ///             .action(Action::Click);
447    ///         
448    ///         // Handle clicks...
449    ///     }));
450    /// }
451    /// ```
452    ///
453    /// Note: The builder should be committed with `.commit()` or allowed to drop,
454    /// which will automatically store the accessibility information in the metadata.
455    pub fn accessibility(&self) -> AccessibilityBuilderGuard<'_> {
456        AccessibilityBuilderGuard {
457            node_id: self.current_node_id,
458            metadatas: self.metadatas,
459            node: AccessibilityNode::new(),
460        }
461    }
462
463    /// Sets an action handler for accessibility actions.
464    ///
465    /// This handler will be called when assistive technologies request actions
466    /// like clicking, focusing, or changing values.
467    ///
468    /// # Example
469    ///
470    /// ```
471    /// use accesskit::Action;
472    /// use tessera_ui::tessera;
473    ///
474    /// #[tessera]
475    /// fn interactive_button() {
476    ///     input_handler(Box::new(|input| {
477    ///         input.set_accessibility_action_handler(|action| {
478    ///             if action == Action::Click {
479    ///                 // Handle click from assistive technology
480    ///             }
481    ///         });
482    ///     }));
483    /// }
484    /// ```
485    pub fn set_accessibility_action_handler(
486        &self,
487        handler: impl Fn(accesskit::Action) + Send + Sync + 'static,
488    ) {
489        if let Some(mut metadata) = self.metadatas.get_mut(&self.current_node_id) {
490            metadata.accessibility_action_handler = Some(Box::new(handler));
491        }
492    }
493}
494
495/// A collection of requests that components can make to the windowing system for the current frame.
496/// This struct's lifecycle is confined to a single `compute` pass.
497#[derive(Default, Debug)]
498pub struct WindowRequests {
499    /// The cursor icon requested by a component. If multiple components request a cursor,
500    /// the last one to make a request in a frame "wins", since it's executed later.
501    pub cursor_icon: CursorIcon,
502    /// An Input Method Editor (IME) request.
503    /// If multiple components request IME, the one from the "newer" component (which is
504    /// processed later in the state handling pass) will overwrite previous requests.
505    pub ime_request: Option<ImeRequest>,
506}
507
508/// A request to the windowing system to open an Input Method Editor (IME).
509/// This is typically used for text input components.
510#[derive(Debug)]
511pub struct ImeRequest {
512    /// The size of the area where the IME is requested.
513    pub size: PxSize,
514    /// The absolute position where the IME should be placed.
515    /// This is set internally by the component tree during the compute pass.
516    pub(crate) position: Option<PxPosition>, // should be setted in tessera node tree compute
517}
518
519impl ImeRequest {
520    pub fn new(size: PxSize) -> Self {
521        Self {
522            size,
523            position: None, // Position will be set during the compute phase
524        }
525    }
526}
527
528/// Measures a single node recursively, returning its size or an error.
529///
530/// See [`measure_nodes`] for concurrent measurement of multiple nodes.
531/// Which is very recommended for most cases. You should only use this function
532/// when your're very sure that you only need to measure a single node.
533pub fn measure_node(
534    node_id: NodeId,
535    parent_constraint: &Constraint,
536    tree: &ComponentNodeTree,
537    component_node_metadatas: &ComponentNodeMetaDatas,
538    compute_resource_manager: Arc<RwLock<ComputeResourceManager>>,
539    gpu: &wgpu::Device,
540) -> Result<ComputedData, MeasurementError> {
541    // Make sure metadata and default value exists for the node.
542    component_node_metadatas.insert(node_id, Default::default());
543
544    let node_data_ref = tree
545        .get(node_id)
546        .ok_or(MeasurementError::NodeNotFoundInTree)?;
547    let node_data = node_data_ref.get();
548
549    let children: Vec<_> = node_id.children(tree).collect(); // No .as_ref() needed for &Arena
550    let timer = Instant::now();
551
552    debug!(
553        "Measuring node {} with {} children, parent constraint: {:?}",
554        node_data.fn_name,
555        children.len(),
556        parent_constraint
557    );
558
559    let size = if let Some(measure_fn) = &node_data.measure_fn {
560        measure_fn(&MeasureInput {
561            current_node_id: node_id,
562            tree,
563            parent_constraint,
564            children_ids: &children,
565            metadatas: component_node_metadatas,
566            compute_resource_manager,
567            gpu,
568        })
569    } else {
570        DEFAULT_LAYOUT_DESC(&MeasureInput {
571            current_node_id: node_id,
572            tree,
573            parent_constraint,
574            children_ids: &children,
575            metadatas: component_node_metadatas,
576            compute_resource_manager,
577            gpu,
578        })
579    }?;
580
581    debug!(
582        "Measured node {} in {:?} with size {:?}",
583        node_data.fn_name,
584        timer.elapsed(),
585        size
586    );
587
588    let mut metadata = component_node_metadatas.entry(node_id).or_default();
589    metadata.computed_data = Some(size);
590
591    Ok(size)
592}
593
594/// Places a node at the specified relative position within its parent.
595pub fn place_node(
596    node: indextree::NodeId,
597    rel_position: PxPosition,
598    component_node_metadatas: &ComponentNodeMetaDatas,
599) {
600    component_node_metadatas
601        .entry(node)
602        .or_default()
603        .rel_position = Some(rel_position);
604}
605
606/// A default layout descriptor (`MeasureFn`) that places children at the top-left corner ([0,0])
607/// of the parent node with no offset. Children are measured concurrently using `measure_nodes`.
608pub const DEFAULT_LAYOUT_DESC: &MeasureFn = &|input| {
609    if input.children_ids.is_empty() {
610        // If there are no children, the size depends on the parent_constraint
611        // For Fixed, it's the fixed size. For Wrap/Fill, it's typically 0 if no content.
612        // This part might need refinement based on how min constraints in Wrap/Fill should behave for empty nodes.
613        // For now, returning ZERO, assuming intrinsic size of an empty node is zero before min constraints are applied.
614        // The actual min size enforcement happens when the parent (or this node itself if it has intrinsic min)
615        // considers its own DimensionValue.
616        return Ok(ComputedData::min_from_constraint(input.parent_constraint));
617    }
618
619    let nodes_to_measure: Vec<(NodeId, Constraint)> = input
620        .children_ids
621        .iter()
622        .map(|&child_id| (child_id, *input.parent_constraint)) // Children inherit parent's effective constraint
623        .collect();
624
625    let children_results_map = measure_nodes(
626        nodes_to_measure,
627        input.tree,
628        input.metadatas,
629        input.compute_resource_manager.clone(),
630        input.gpu,
631    );
632
633    let mut aggregate_size = ComputedData::ZERO;
634    let mut first_error: Option<MeasurementError> = None;
635    let mut successful_children_data = Vec::new();
636
637    for &child_id in input.children_ids {
638        match children_results_map.get(&child_id) {
639            Some(Ok(child_size)) => {
640                successful_children_data.push((child_id, *child_size));
641            }
642            Some(Err(e)) => {
643                debug!(
644                    "Child node {child_id:?} measurement failed for parent {:?}: {e:?}",
645                    input.current_node_id
646                );
647                if first_error.is_none() {
648                    first_error = Some(MeasurementError::ChildMeasurementFailed(child_id));
649                }
650            }
651            None => {
652                debug!(
653                    "Child node {child_id:?} was not found in measure_nodes results for parent {:?}",
654                    input.current_node_id
655                );
656                if first_error.is_none() {
657                    first_error = Some(MeasurementError::MeasureFnFailed(format!(
658                        "Result for child {child_id:?} missing"
659                    )));
660                }
661            }
662        }
663    }
664
665    if let Some(error) = first_error {
666        return Err(error);
667    }
668    if successful_children_data.is_empty() && !input.children_ids.is_empty() {
669        // This case should ideally be caught by first_error if all children failed.
670        // If it's reached, it implies some logic issue.
671        return Err(MeasurementError::MeasureFnFailed(
672            "All children failed to measure or results missing in DEFAULT_LAYOUT_DESC".to_string(),
673        ));
674    }
675
676    // For default layout (stacking), the aggregate size is the max of children's sizes.
677    for (child_id, child_size) in successful_children_data {
678        aggregate_size = aggregate_size.max(child_size);
679        place_node(child_id, PxPosition::ZERO, input.metadatas); // All children at [0,0] for simple stacking
680    }
681
682    // The aggregate_size is based on children. Now apply current node's own constraints.
683    // If current node is Fixed, its size is fixed.
684    // If current node is Wrap, its size is aggregate_size (clamped by its own min/max).
685    // If current node is Fill, its size is aggregate_size (clamped by its own min/max, and parent's available space if parent was Fill).
686    // This final clamping/adjustment based on `parent_constraint` should ideally happen
687    // when `ComputedData` is returned from `measure_node` itself, or by the caller of `measure_node`.
688    // For DEFAULT_LAYOUT_DESC, it should return the size required by its children,
689    // and then `measure_node` will finalize it based on `parent_constraint`.
690
691    // Let's refine: DEFAULT_LAYOUT_DESC should calculate the "natural" size based on children.
692    // Then, `measure_node` (or its caller) would apply the `parent_constraint` to this natural size.
693    // However, `measure_node` currently directly returns the result of `DEFAULT_LAYOUT_DESC` or custom `measure_fn`.
694    // So, `DEFAULT_LAYOUT_DESC` itself needs to consider `parent_constraint` for its final size.
695
696    let mut final_width = aggregate_size.width;
697    let mut final_height = aggregate_size.height;
698
699    match input.parent_constraint.width {
700        DimensionValue::Fixed(w) => final_width = w,
701        DimensionValue::Wrap { min, max } => {
702            if let Some(min_w) = min {
703                final_width = final_width.max(min_w);
704            }
705            if let Some(max_w) = max {
706                final_width = final_width.min(max_w);
707            }
708        }
709        DimensionValue::Fill { min, max } => {
710            // Fill behaves like wrap for default layout unless children expand
711            if let Some(min_w) = min {
712                final_width = final_width.max(min_w);
713            }
714            if let Some(max_w) = max {
715                final_width = final_width.min(max_w);
716            }
717            // If parent was Fill, this node would have gotten a Fill constraint too.
718            // The actual "filling" happens because children might be Fill.
719            // If children are not Fill, this node wraps them.
720        }
721    }
722    match input.parent_constraint.height {
723        DimensionValue::Fixed(h) => final_height = h,
724        DimensionValue::Wrap { min, max } => {
725            if let Some(min_h) = min {
726                final_height = final_height.max(min_h);
727            }
728            if let Some(max_h) = max {
729                final_height = final_height.min(max_h);
730            }
731        }
732        DimensionValue::Fill { min, max } => {
733            if let Some(min_h) = min {
734                final_height = final_height.max(min_h);
735            }
736            if let Some(max_h) = max {
737                final_height = final_height.min(max_h);
738            }
739        }
740    }
741    Ok(ComputedData {
742        width: final_width,
743        height: final_height,
744    })
745};
746
747/// Concurrently measures multiple nodes using Rayon for parallelism.
748pub fn measure_nodes(
749    nodes_to_measure: Vec<(NodeId, Constraint)>,
750    tree: &ComponentNodeTree,
751    component_node_metadatas: &ComponentNodeMetaDatas,
752    compute_resource_manager: Arc<RwLock<ComputeResourceManager>>,
753    gpu: &wgpu::Device,
754) -> HashMap<NodeId, Result<ComputedData, MeasurementError>> {
755    if nodes_to_measure.is_empty() {
756        return HashMap::new();
757    }
758    // metadata must be reseted and initialized for each node to measure.
759    for (node_id, _) in &nodes_to_measure {
760        component_node_metadatas.insert(*node_id, Default::default());
761    }
762    nodes_to_measure
763        .into_par_iter()
764        .map(|(node_id, parent_constraint)| {
765            let result = measure_node(
766                node_id,
767                &parent_constraint,
768                tree,
769                component_node_metadatas,
770                compute_resource_manager.clone(),
771                gpu,
772            );
773            (node_id, result)
774        })
775        .collect::<HashMap<NodeId, Result<ComputedData, MeasurementError>>>()
776}
777
778/// Layout information computed at the measure stage, representing the size of a node.
779#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
780pub struct ComputedData {
781    pub width: Px,
782    pub height: Px,
783}
784
785impl Add for ComputedData {
786    type Output = Self;
787    fn add(self, rhs: Self) -> Self::Output {
788        Self {
789            width: self.width + rhs.width,
790            height: self.height + rhs.height,
791        }
792    }
793}
794
795impl AddAssign for ComputedData {
796    fn add_assign(&mut self, rhs: Self) {
797        *self = *self + rhs;
798    }
799}
800
801impl ComputedData {
802    pub const ZERO: Self = Self {
803        width: Px(0),
804        height: Px(0),
805    };
806
807    /// Calculates a "minimum" size based on a constraint.
808    /// For Fixed, it's the fixed value. For Wrap/Fill, it's their 'min' if Some, else 0.
809    pub fn min_from_constraint(constraint: &Constraint) -> Self {
810        let width = match constraint.width {
811            DimensionValue::Fixed(w) => w,
812            DimensionValue::Wrap { min, .. } => min.unwrap_or(Px(0)),
813            DimensionValue::Fill { min, .. } => min.unwrap_or(Px(0)),
814        };
815        let height = match constraint.height {
816            DimensionValue::Fixed(h) => h,
817            DimensionValue::Wrap { min, .. } => min.unwrap_or(Px(0)),
818            DimensionValue::Fill { min, .. } => min.unwrap_or(Px(0)),
819        };
820        Self { width, height }
821    }
822
823    pub fn min(self, rhs: Self) -> Self {
824        Self {
825            width: self.width.min(rhs.width),
826            height: self.height.min(rhs.height),
827        }
828    }
829
830    pub fn max(self, rhs: Self) -> Self {
831        Self {
832            width: self.width.max(rhs.width),
833            height: self.height.max(rhs.height),
834        }
835    }
836}