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