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 pub fn push_draw_command<C: DrawCommand + 'static>(&mut self, command: C) {
203 let command = Box::new(command);
204 let command = command as Box<dyn DrawCommand>;
205 let command = Command::Draw(command);
206 self.commands.push((command, TypeId::of::<C>()));
207 }
208
209 /// Pushes a compute command to the node's metadata.
210 ///
211 /// Compute commands perform GPU computation tasks (post-processing effects,
212 /// complex calculations). This method wraps the command in the unified
213 /// `Command::Compute` variant and adds it to the command queue.
214 pub fn push_compute_command<C: ComputeCommand + 'static>(&mut self, command: C) {
215 let command = Box::new(command);
216 let command = command as Box<dyn ComputeCommand>;
217 let command = Command::Compute(command);
218 self.commands.push((command, TypeId::of::<C>()));
219 }
220}
221
222/// A tree of component nodes, using `indextree::Arena` for storage.
223pub type ComponentNodeTree = indextree::Arena<ComponentNode>;
224/// Contains all component nodes' metadatas, using a thread-safe `DashMap`.
225pub type ComponentNodeMetaDatas = DashMap<NodeId, ComponentNodeMetaData>;
226
227/// Represents errors that can occur during node measurement.
228#[derive(Debug, Clone, PartialEq)]
229pub enum MeasurementError {
230 /// Indicates that the specified node was not found in the component tree.
231 NodeNotFoundInTree,
232 /// Indicates that metadata for the specified node was not found (currently not a primary error source in measure_node).
233 NodeNotFoundInMeta,
234 /// Indicates that the custom measure function (`MeasureFn`) for a node failed.
235 /// Contains a string detailing the failure.
236 MeasureFnFailed(String),
237 /// Indicates that the measurement of a child node failed during a parent's layout calculation (e.g., in `DEFAULT_LAYOUT_DESC`).
238 /// Contains the `NodeId` of the child that failed.
239 ChildMeasurementFailed(NodeId),
240}
241
242/// A `MeasureFn` is a function that takes an input `Constraint` and its children nodes,
243/// finishes placementing inside, and returns its size (`ComputedData`) or an error.
244pub type MeasureFn =
245 dyn Fn(&MeasureInput<'_>) -> Result<ComputedData, MeasurementError> + Send + Sync;
246
247/// Input for the measure function (`MeasureFn`).
248pub struct MeasureInput<'a> {
249 /// The `NodeId` of the current node being measured.
250 pub current_node_id: indextree::NodeId,
251 /// The component tree containing all nodes.
252 pub tree: &'a ComponentNodeTree,
253 /// The effective constraint for this node, merged with its parent's constraint.
254 pub parent_constraint: &'a Constraint,
255 /// The children nodes of the current node.
256 pub children_ids: &'a [indextree::NodeId],
257 /// Metadata for all component nodes, used to access cached data and constraints.
258 pub metadatas: &'a ComponentNodeMetaDatas,
259 /// Compute resources manager
260 pub compute_resource_manager: Arc<RwLock<ComputeResourceManager>>,
261 /// Gpu device
262 pub gpu: &'a wgpu::Device,
263}
264
265impl<'a> MeasureInput<'a> {
266 /// Returns a mutable reference to the metadata of the current node.
267 ///
268 /// This is a convenience method that simplifies accessing the current node's metadata
269 /// from within a `measure` function. It encapsulates the `DashMap::get_mut` call and panics
270 /// if the metadata is not found, as it's an invariant that it must exist.
271 pub fn metadata_mut(&self) -> dashmap::mapref::one::RefMut<'_, NodeId, ComponentNodeMetaData> {
272 self.metadatas
273 .get_mut(&self.current_node_id)
274 .expect("Metadata for current node must exist during measure")
275 }
276
277 /// Measures all specified child nodes under the given constraint.
278 ///
279 /// Returns a map of each child's computed layout data, or the first measurement error encountered.
280 pub fn measure_children(
281 &self,
282 nodes_to_measure: Vec<(NodeId, Constraint)>,
283 ) -> Result<HashMap<NodeId, ComputedData>, MeasurementError> {
284 let results = measure_nodes(
285 nodes_to_measure,
286 self.tree,
287 self.metadatas,
288 self.compute_resource_manager.clone(),
289 self.gpu,
290 );
291
292 let mut successful_results = HashMap::new();
293 for (child_id, result) in results {
294 match result {
295 Ok(size) => successful_results.insert(child_id, size),
296 Err(e) => {
297 debug!("Measurement error for child {child_id:?}: {e:?}");
298 return Err(e);
299 }
300 };
301 }
302 Ok(successful_results)
303 }
304
305 /// Measures a single child node under the given constraint.
306 ///
307 /// Returns the computed layout data or a measurement error.
308 pub fn measure_child(
309 &self,
310 child_id: NodeId,
311 constraint: &Constraint,
312 ) -> Result<ComputedData, MeasurementError> {
313 measure_node(
314 child_id,
315 constraint,
316 self.tree,
317 self.metadatas,
318 self.compute_resource_manager.clone(),
319 self.gpu,
320 )
321 }
322
323 /// Sets the relative position of a child node.
324 pub fn place_child(&self, child_id: NodeId, position: PxPosition) {
325 place_node(child_id, position, self.metadatas);
326 }
327
328 /// Enables clipping for the current node.
329 pub fn enable_clipping(&self) {
330 // Set the clipping flag to true for this node.
331 self.metadata_mut().clips_children = true;
332 }
333
334 /// Disables clipping for the current node.
335 pub fn disable_clipping(&self) {
336 // Set the clipping flag to false for this node.
337 self.metadata_mut().clips_children = false;
338 }
339}
340
341/// A `InputHandlerFn` is a function that handles state changes for a component.
342///
343/// The rule of execution order is:
344///
345/// 1. Children's input handlers are executed earlier than parent's.
346/// 2. Newer components' input handlers are executed earlier than older ones.
347///
348/// Acutally, rule 2 includes rule 1, because a newer component is always a child of an older component :)
349pub type InputHandlerFn = dyn Fn(InputHandlerInput) + Send + Sync;
350
351/// Input for the input handler function (`InputHandlerFn`).
352///
353/// Note that you can modify the `cursor_events` and `keyboard_events` vectors
354/// for exmaple block some keyboard events or cursor events to prevent them from propagating
355/// to parent components and older brother components.
356pub struct InputHandlerInput<'a> {
357 /// The size of the component node, computed during the measure stage.
358 pub computed_data: ComputedData,
359 /// The position of the cursor, if available.
360 /// Relative to the root position of the component.
361 pub cursor_position_rel: Option<PxPosition>,
362 /// The mut ref of absolute position of the cursor in the window.
363 /// Used to block cursor fully if needed, since cursor_position_rel use this.
364 /// Not a public field for now.
365 pub(crate) cursor_position_abs: &'a mut Option<PxPosition>,
366 /// Cursor events from the event loop, if any.
367 pub cursor_events: &'a mut Vec<CursorEvent>,
368 /// Keyboard events from the event loop, if any.
369 pub keyboard_events: &'a mut Vec<winit::event::KeyEvent>,
370 /// IME events from the event loop, if any.
371 pub ime_events: &'a mut Vec<winit::event::Ime>,
372 /// The current state of the keyboard modifiers at the time of the event.
373 /// This allows for implementing keyboard shortcuts (e.g., Ctrl+C).
374 pub key_modifiers: winit::keyboard::ModifiersState,
375 /// A context for making requests to the window for the current frame.
376 pub requests: &'a mut WindowRequests,
377 /// Clipboard
378 pub clipboard: &'a mut Clipboard,
379 /// The current node ID (for accessibility setup)
380 pub(crate) current_node_id: indextree::NodeId,
381 /// Reference to component metadatas (for accessibility setup)
382 pub(crate) metadatas: &'a ComponentNodeMetaDatas,
383}
384
385impl InputHandlerInput<'_> {
386 /// Blocks the cursor to other components.
387 pub fn block_cursor(&mut self) {
388 // Block the cursor by setting its position to None.
389 self.cursor_position_abs.take();
390 // Clear all cursor events to prevent them from propagating.
391 self.cursor_events.clear();
392 }
393
394 /// Blocks the keyboard events to other components.
395 pub fn block_keyboard(&mut self) {
396 // Clear all keyboard events to prevent them from propagating.
397 self.keyboard_events.clear();
398 }
399
400 /// Blocks the IME events to other components.
401 pub fn block_ime(&mut self) {
402 // Clear all IME events to prevent them from propagating.
403 self.ime_events.clear();
404 }
405
406 /// Block all events (cursor, keyboard, IME) to other components.
407 pub fn block_all(&mut self) {
408 self.block_cursor();
409 self.block_keyboard();
410 self.block_ime();
411 }
412
413 /// Provides a fluent API for setting accessibility information for the current component.
414 ///
415 /// This method returns a builder that allows you to set various accessibility properties
416 /// like role, label, actions, and state. The accessibility information is automatically
417 /// committed when the builder is dropped or when `.commit()` is called explicitly.
418 ///
419 /// # Example
420 ///
421 /// ```
422 /// use accesskit::{Action, Role};
423 /// use tessera_ui::tessera;
424 ///
425 /// #[tessera]
426 /// fn accessible_button() {
427 /// input_handler(Box::new(|input| {
428 /// input.accessibility()
429 /// .role(Role::Button)
430 /// .label("Click me")
431 /// .focusable()
432 /// .action(Action::Click);
433 ///
434 /// // Handle clicks...
435 /// }));
436 /// }
437 /// ```
438 ///
439 /// Note: The builder should be committed with `.commit()` or allowed to drop,
440 /// which will automatically store the accessibility information in the metadata.
441 pub fn accessibility(&self) -> AccessibilityBuilderGuard<'_> {
442 AccessibilityBuilderGuard {
443 node_id: self.current_node_id,
444 metadatas: self.metadatas,
445 node: AccessibilityNode::new(),
446 }
447 }
448
449 /// Sets an action handler for accessibility actions.
450 ///
451 /// This handler will be called when assistive technologies request actions
452 /// like clicking, focusing, or changing values.
453 ///
454 /// # Example
455 ///
456 /// ```
457 /// use accesskit::Action;
458 /// use tessera_ui::tessera;
459 ///
460 /// #[tessera]
461 /// fn interactive_button() {
462 /// input_handler(Box::new(|input| {
463 /// input.set_accessibility_action_handler(|action| {
464 /// if action == Action::Click {
465 /// // Handle click from assistive technology
466 /// }
467 /// });
468 /// }));
469 /// }
470 /// ```
471 pub fn set_accessibility_action_handler(
472 &self,
473 handler: impl Fn(accesskit::Action) + Send + Sync + 'static,
474 ) {
475 if let Some(mut metadata) = self.metadatas.get_mut(&self.current_node_id) {
476 metadata.accessibility_action_handler = Some(Box::new(handler));
477 }
478 }
479}
480
481/// A collection of requests that components can make to the windowing system for the current frame.
482/// This struct's lifecycle is confined to a single `compute` pass.
483#[derive(Default, Debug)]
484pub struct WindowRequests {
485 /// The cursor icon requested by a component. If multiple components request a cursor,
486 /// the last one to make a request in a frame "wins", since it's executed later.
487 pub cursor_icon: CursorIcon,
488 /// An Input Method Editor (IME) request.
489 /// If multiple components request IME, the one from the "newer" component (which is
490 /// processed later in the state handling pass) will overwrite previous requests.
491 pub ime_request: Option<ImeRequest>,
492}
493
494/// A request to the windowing system to open an Input Method Editor (IME).
495/// This is typically used for text input components.
496#[derive(Debug)]
497pub struct ImeRequest {
498 /// The size of the area where the IME is requested.
499 pub size: PxSize,
500 /// The absolute position where the IME should be placed.
501 /// This is set internally by the component tree during the compute pass.
502 pub(crate) position: Option<PxPosition>, // should be setted in tessera node tree compute
503}
504
505impl ImeRequest {
506 /// Creates a new IME request with the target input area size.
507 ///
508 /// The absolute position is injected during the compute pass.
509 pub fn new(size: PxSize) -> Self {
510 Self {
511 size,
512 position: None, // Position will be set during the compute phase
513 }
514 }
515}
516
517/// Measures a single node recursively, returning its size or an error.
518///
519/// See [`measure_nodes`] for concurrent measurement of multiple nodes.
520/// Which is very recommended for most cases. You should only use this function
521/// when your're very sure that you only need to measure a single node.
522pub(crate) fn measure_node(
523 node_id: NodeId,
524 parent_constraint: &Constraint,
525 tree: &ComponentNodeTree,
526 component_node_metadatas: &ComponentNodeMetaDatas,
527 compute_resource_manager: Arc<RwLock<ComputeResourceManager>>,
528 gpu: &wgpu::Device,
529) -> Result<ComputedData, MeasurementError> {
530 // Make sure metadata and default value exists for the node.
531 component_node_metadatas.insert(node_id, Default::default());
532
533 let node_data_ref = tree
534 .get(node_id)
535 .ok_or(MeasurementError::NodeNotFoundInTree)?;
536 let node_data = node_data_ref.get();
537
538 let children: Vec<_> = node_id.children(tree).collect(); // No .as_ref() needed for &Arena
539 let timer = Instant::now();
540
541 debug!(
542 "Measuring node {} with {} children, parent constraint: {:?}",
543 node_data.fn_name,
544 children.len(),
545 parent_constraint
546 );
547
548 let size = if let Some(measure_fn) = &node_data.measure_fn {
549 measure_fn(&MeasureInput {
550 current_node_id: node_id,
551 tree,
552 parent_constraint,
553 children_ids: &children,
554 metadatas: component_node_metadatas,
555 compute_resource_manager,
556 gpu,
557 })
558 } else {
559 DEFAULT_LAYOUT_DESC(&MeasureInput {
560 current_node_id: node_id,
561 tree,
562 parent_constraint,
563 children_ids: &children,
564 metadatas: component_node_metadatas,
565 compute_resource_manager,
566 gpu,
567 })
568 }?;
569
570 debug!(
571 "Measured node {} in {:?} with size {:?}",
572 node_data.fn_name,
573 timer.elapsed(),
574 size
575 );
576
577 let mut metadata = component_node_metadatas.entry(node_id).or_default();
578 metadata.computed_data = Some(size);
579
580 Ok(size)
581}
582
583/// Places a node at the specified relative position within its parent.
584pub(crate) fn place_node(
585 node: indextree::NodeId,
586 rel_position: PxPosition,
587 component_node_metadatas: &ComponentNodeMetaDatas,
588) {
589 component_node_metadatas
590 .entry(node)
591 .or_default()
592 .rel_position = Some(rel_position);
593}
594
595/// A default layout descriptor (`MeasureFn`) that places children at the top-left corner ([0,0])
596/// of the parent node with no offset. Children are measured concurrently using `measure_nodes`.
597pub const DEFAULT_LAYOUT_DESC: &MeasureFn = &|input| {
598 if input.children_ids.is_empty() {
599 // If there are no children, the size depends on the parent_constraint
600 // For Fixed, it's the fixed size. For Wrap/Fill, it's typically 0 if no content.
601 // This part might need refinement based on how min constraints in Wrap/Fill should behave for empty nodes.
602 // For now, returning ZERO, assuming intrinsic size of an empty node is zero before min constraints are applied.
603 // The actual min size enforcement happens when the parent (or this node itself if it has intrinsic min)
604 // considers its own DimensionValue.
605 return Ok(ComputedData::min_from_constraint(input.parent_constraint));
606 }
607
608 let nodes_to_measure: Vec<(NodeId, Constraint)> = input
609 .children_ids
610 .iter()
611 .map(|&child_id| (child_id, *input.parent_constraint)) // Children inherit parent's effective constraint
612 .collect();
613
614 let children_results_map = measure_nodes(
615 nodes_to_measure,
616 input.tree,
617 input.metadatas,
618 input.compute_resource_manager.clone(),
619 input.gpu,
620 );
621
622 let mut aggregate_size = ComputedData::ZERO;
623 let mut first_error: Option<MeasurementError> = None;
624 let mut successful_children_data = Vec::new();
625
626 for &child_id in input.children_ids {
627 match children_results_map.get(&child_id) {
628 Some(Ok(child_size)) => {
629 successful_children_data.push((child_id, *child_size));
630 }
631 Some(Err(e)) => {
632 debug!(
633 "Child node {child_id:?} measurement failed for parent {:?}: {e:?}",
634 input.current_node_id
635 );
636 if first_error.is_none() {
637 first_error = Some(MeasurementError::ChildMeasurementFailed(child_id));
638 }
639 }
640 None => {
641 debug!(
642 "Child node {child_id:?} was not found in measure_nodes results for parent {:?}",
643 input.current_node_id
644 );
645 if first_error.is_none() {
646 first_error = Some(MeasurementError::MeasureFnFailed(format!(
647 "Result for child {child_id:?} missing"
648 )));
649 }
650 }
651 }
652 }
653
654 if let Some(error) = first_error {
655 return Err(error);
656 }
657 if successful_children_data.is_empty() && !input.children_ids.is_empty() {
658 // This case should ideally be caught by first_error if all children failed.
659 // If it's reached, it implies some logic issue.
660 return Err(MeasurementError::MeasureFnFailed(
661 "All children failed to measure or results missing in DEFAULT_LAYOUT_DESC".to_string(),
662 ));
663 }
664
665 // For default layout (stacking), the aggregate size is the max of children's sizes.
666 for (child_id, child_size) in successful_children_data {
667 aggregate_size = aggregate_size.max(child_size);
668 place_node(child_id, PxPosition::ZERO, input.metadatas); // All children at [0,0] for simple stacking
669 }
670
671 // The aggregate_size is based on children. Now apply current node's own constraints.
672 // If current node is Fixed, its size is fixed.
673 // If current node is Wrap, its size is aggregate_size (clamped by its own min/max).
674 // 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).
675 // This final clamping/adjustment based on `parent_constraint` should ideally happen
676 // when `ComputedData` is returned from `measure_node` itself, or by the caller of `measure_node`.
677 // For DEFAULT_LAYOUT_DESC, it should return the size required by its children,
678 // and then `measure_node` will finalize it based on `parent_constraint`.
679
680 // Let's refine: DEFAULT_LAYOUT_DESC should calculate the "natural" size based on children.
681 // Then, `measure_node` (or its caller) would apply the `parent_constraint` to this natural size.
682 // However, `measure_node` currently directly returns the result of `DEFAULT_LAYOUT_DESC` or custom `measure_fn`.
683 // So, `DEFAULT_LAYOUT_DESC` itself needs to consider `parent_constraint` for its final size.
684
685 let mut final_width = aggregate_size.width;
686 let mut final_height = aggregate_size.height;
687
688 match input.parent_constraint.width {
689 DimensionValue::Fixed(w) => final_width = w,
690 DimensionValue::Wrap { min, max } => {
691 if let Some(min_w) = min {
692 final_width = final_width.max(min_w);
693 }
694 if let Some(max_w) = max {
695 final_width = final_width.min(max_w);
696 }
697 }
698 DimensionValue::Fill { min, max } => {
699 // Fill behaves like wrap for default layout unless children expand
700 if let Some(min_w) = min {
701 final_width = final_width.max(min_w);
702 }
703 if let Some(max_w) = max {
704 final_width = final_width.min(max_w);
705 }
706 // If parent was Fill, this node would have gotten a Fill constraint too.
707 // The actual "filling" happens because children might be Fill.
708 // If children are not Fill, this node wraps them.
709 }
710 }
711 match input.parent_constraint.height {
712 DimensionValue::Fixed(h) => final_height = h,
713 DimensionValue::Wrap { min, max } => {
714 if let Some(min_h) = min {
715 final_height = final_height.max(min_h);
716 }
717 if let Some(max_h) = max {
718 final_height = final_height.min(max_h);
719 }
720 }
721 DimensionValue::Fill { min, max } => {
722 if let Some(min_h) = min {
723 final_height = final_height.max(min_h);
724 }
725 if let Some(max_h) = max {
726 final_height = final_height.min(max_h);
727 }
728 }
729 }
730 Ok(ComputedData {
731 width: final_width,
732 height: final_height,
733 })
734};
735
736/// Concurrently measures multiple nodes using Rayon for parallelism.
737pub(crate) fn measure_nodes(
738 nodes_to_measure: Vec<(NodeId, Constraint)>,
739 tree: &ComponentNodeTree,
740 component_node_metadatas: &ComponentNodeMetaDatas,
741 compute_resource_manager: Arc<RwLock<ComputeResourceManager>>,
742 gpu: &wgpu::Device,
743) -> HashMap<NodeId, Result<ComputedData, MeasurementError>> {
744 if nodes_to_measure.is_empty() {
745 return HashMap::new();
746 }
747 // metadata must be reseted and initialized for each node to measure.
748 for (node_id, _) in &nodes_to_measure {
749 component_node_metadatas.insert(*node_id, Default::default());
750 }
751 nodes_to_measure
752 .into_par_iter()
753 .map(|(node_id, parent_constraint)| {
754 let result = measure_node(
755 node_id,
756 &parent_constraint,
757 tree,
758 component_node_metadatas,
759 compute_resource_manager.clone(),
760 gpu,
761 );
762 (node_id, result)
763 })
764 .collect::<HashMap<NodeId, Result<ComputedData, MeasurementError>>>()
765}
766
767/// Layout information computed at the measure stage, representing the size of a node.
768#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
769pub struct ComputedData {
770 /// The resolved width of the node in physical pixels.
771 pub width: Px,
772 /// The resolved height of the node in physical pixels.
773 pub height: Px,
774}
775
776impl Add for ComputedData {
777 type Output = Self;
778 fn add(self, rhs: Self) -> Self::Output {
779 Self {
780 width: self.width + rhs.width,
781 height: self.height + rhs.height,
782 }
783 }
784}
785
786impl AddAssign for ComputedData {
787 fn add_assign(&mut self, rhs: Self) {
788 *self = *self + rhs;
789 }
790}
791
792impl ComputedData {
793 /// Zero-sized layout data.
794 pub const ZERO: Self = Self {
795 width: Px(0),
796 height: Px(0),
797 };
798
799 /// Calculates a "minimum" size based on a constraint.
800 /// For Fixed, it's the fixed value. For Wrap/Fill, it's their 'min' if Some, else 0.
801 pub fn min_from_constraint(constraint: &Constraint) -> Self {
802 let width = match constraint.width {
803 DimensionValue::Fixed(w) => w,
804 DimensionValue::Wrap { min, .. } => min.unwrap_or(Px(0)),
805 DimensionValue::Fill { min, .. } => min.unwrap_or(Px(0)),
806 };
807 let height = match constraint.height {
808 DimensionValue::Fixed(h) => h,
809 DimensionValue::Wrap { min, .. } => min.unwrap_or(Px(0)),
810 DimensionValue::Fill { min, .. } => min.unwrap_or(Px(0)),
811 };
812 Self { width, height }
813 }
814
815 /// Returns the component-wise minimum of two computed sizes.
816 pub fn min(self, rhs: Self) -> Self {
817 Self {
818 width: self.width.min(rhs.width),
819 height: self.height.min(rhs.height),
820 }
821 }
822
823 /// Returns the component-wise maximum of two computed sizes.
824 pub fn max(self, rhs: Self) -> Self {
825 Self {
826 width: self.width.max(rhs.width),
827 height: self.height.max(rhs.height),
828 }
829 }
830}