tessera_ui/component_tree/
node.rs

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