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