Skip to main content

repose_ui/layout/
types.rs

1#![allow(non_snake_case)]
2
3use std::collections::HashMap;
4use std::rc::Rc;
5use std::sync::Arc;
6
7use repose_core::*;
8use repose_tree::{NodeId, TreeStats, ViewTree};
9use rustc_hash::FxHashMap;
10use taffy::TaffyTree;
11
12pub(crate) struct ScopeLayoutTree {
13    pub(crate) taffy: TaffyTree<NodeContext>,
14    pub(crate) taffy_map: FxHashMap<NodeId, taffy::NodeId>,
15    pub(crate) reverse_map: FxHashMap<taffy::NodeId, NodeId>,
16    pub(crate) root_taffy_id: Option<taffy::NodeId>,
17    pub(crate) last_constraints:
18        Option<(taffy::Size<Option<f32>>, taffy::Size<taffy::AvailableSpace>)>,
19    pub(crate) cached_size: Option<taffy::Size<f32>>,
20    pub(crate) text_cache: FxHashMap<NodeId, TextLayout>,
21    pub(crate) valid: bool,
22}
23
24impl ScopeLayoutTree {
25    pub(crate) fn new() -> Self {
26        Self {
27            taffy: TaffyTree::new(),
28            taffy_map: FxHashMap::default(),
29            reverse_map: FxHashMap::default(),
30            root_taffy_id: None,
31            last_constraints: None,
32            cached_size: None,
33            text_cache: FxHashMap::default(),
34            valid: false,
35        }
36    }
37}
38
39pub struct LayoutEngine {
40    /// Persistent view tree.
41    pub(crate) tree: ViewTree,
42
43    /// Root Taffy layout tree (inter-scope layout + non-scope nodes).
44    pub(crate) taffy: TaffyTree<NodeContext>,
45
46    /// Map from ViewTree NodeId to root Taffy NodeId.
47    pub(crate) taffy_map: FxHashMap<NodeId, taffy::NodeId>,
48
49    /// Reverse map: root Taffy NodeId to ViewTree NodeId.
50    pub(crate) reverse_map: FxHashMap<taffy::NodeId, NodeId>,
51
52    /// Per-scope TaffyTrees for scope! macro isolation.
53    pub(crate) scope_trees: HashMap<String, ScopeLayoutTree>,
54
55    /// ViewTree NodeId -> scope key for scope boundary root nodes.
56    pub(crate) scope_root_map: FxHashMap<NodeId, String>,
57
58    /// ViewTree NodeId -> scope key for ALL nodes belonging to a scope.
59    pub(crate) node_to_scope: FxHashMap<NodeId, String>,
60
61    /// Cached text layouts for non-scope nodes (persists across frames).
62    pub(crate) text_cache: FxHashMap<NodeId, TextLayout>,
63
64    /// Last window size used for layout.
65    pub(crate) last_size_px: Option<(u32, u32)>,
66
67    /// Whether root Taffy has a valid computed layout for `last_size_px`.
68    pub(crate) layout_valid: bool,
69
70    /// Repaint-boundary cache (SceneNodes + hits + semantics).
71    pub(crate) paint_cache: FxHashMap<NodeId, PaintCacheEntry>,
72
73    /// Statistics from the last frame.
74    pub stats: LayoutStats,
75
76    /// Tracks the previously focused view ID to detect focus changes.
77    pub(crate) prev_focused: Option<u64>,
78
79    /// Callbacks registered via `on_focus_changed` modifier, keyed by view ID.
80    pub(crate) focus_callbacks: FxHashMap<u64, Rc<dyn Fn(bool)>>,
81
82    /// Last "locals" stamp used for layout decisions (density/text scale/dir).
83    pub(crate) last_locals_stamp: Option<u64>,
84
85    /// Stable, unique ViewId per ViewTree NodeId.
86    pub(crate) view_ids: FxHashMap<NodeId, u64>,
87    pub(crate) next_view_id: u64,
88
89    /// Monotonic counter for graphics layer ids, assigned during paint.
90    pub(crate) layer_id_counter: u32,
91
92    /// Previous absolute rects for `on_globally_positioned` / `on_size_changed` callbacks.
93    pub(crate) prev_observed_rects: FxHashMap<u64, repose_core::Rect>,
94
95    /// Stack of active focus group IDs. When non-empty, newly created hit regions
96    /// get `focus_group_id` set to the top of this stack. A focus group is entered
97    /// when a node with `modifier.focus_group == true` is traversed.
98    pub(crate) focus_group_stack: Vec<u64>,
99
100    /// InteractionSources that should receive Focus/Unfocus for the current paint tree,
101    /// keyed by view ID.
102    pub(crate) focus_interaction_sources: FxHashMap<u64, InteractionSource>,
103}
104
105/// Statistics about layout performance.
106#[derive(Clone, Debug, Default)]
107pub struct LayoutStats {
108    /// Stats from tree reconciliation.
109    pub tree: TreeStats,
110
111    /// Taffy nodes created this frame.
112    pub taffy_created: usize,
113
114    /// Taffy nodes reused this frame.
115    pub taffy_reused: usize,
116
117    /// Layout cache hits.
118    pub layout_hits: usize,
119
120    /// Layout cache misses.
121    pub layout_misses: usize,
122
123    /// Paint cache hits (repaint boundaries).
124    pub paint_cache_hits: usize,
125
126    /// Paint cache misses (repaint boundaries).
127    pub paint_cache_misses: usize,
128
129    /// Nodes skipped due to clip/viewport culling.
130    pub paint_culled: usize,
131
132    /// Total time for layout+paint (ms).
133    pub layout_time_ms: f32,
134
135    /// Total time spent in the paint pass only (ms).
136    pub paint_time_ms: f32,
137}
138
139#[derive(Clone)]
140pub(crate) struct PaintCacheEntry {
141    pub(crate) subtree_hash: u64,
142    pub(crate) stamp: u64,
143    pub(crate) rect: repose_core::Rect,
144    pub(crate) parent_offset_px: (f32, f32),
145    pub(crate) sem_parent: Option<u64>,
146    pub(crate) alpha_q: u8,
147    pub(crate) nodes: Arc<Vec<SceneNode>>,
148    pub(crate) hits: Arc<Vec<HitRegion>>,
149    pub(crate) sems: Arc<Vec<SemNode>>,
150}
151
152/// Selects the intrinsic sizing mode for [`LayoutEngine::intrinsic_size`].
153#[derive(Clone, Copy, Debug, PartialEq, Eq)]
154pub enum IntrinsicSizeMode {
155    /// Smallest size at which the view's content does not overflow.
156    MinContent,
157    /// Largest size the view's content would take if unconstrained.
158    MaxContent,
159}
160
161/// Context stored with each Taffy node.
162#[derive(Clone)]
163pub(crate) enum NodeContext {
164    Text {
165        text: String,
166        font_dp: f32,
167        soft_wrap: bool,
168        max_lines: Option<usize>,
169        overflow: TextOverflow,
170        font_family: Option<&'static str>,
171        font_weight: FontWeight,
172        font_style: FontStyle,
173        letter_spacing: f32,
174        line_height: f32,
175        font_variation_settings: Option<Arc<str>>,
176    },
177    Container,
178    ScrollContainer,
179    TextInput {
180        multiline: bool,
181    },
182}
183
184#[derive(Clone)]
185pub(crate) struct TextLayout {
186    pub(crate) lines: Vec<String>,
187    /// Byte ranges into the original text for each line (used for annotation splitting).
188    pub(crate) line_ranges: Vec<(usize, usize)>,
189    pub(crate) size_px: f32,
190    pub(crate) line_h_px: f32,
191    /// Pre-measured width per line, in the same order as `lines`.
192    pub(crate) line_widths: Vec<f32>,
193}