1mod constraint;
2mod node;
3
4use std::{any::TypeId, num::NonZero, sync::Arc, time::Instant};
5
6use parking_lot::RwLock;
7use rayon::prelude::*;
8use tracing::{debug, warn};
9
10use crate::{
11 Clipboard, ComputeResourceManager, Px, PxRect,
12 component_tree::node::measure_node,
13 cursor::CursorEvent,
14 px::{PxPosition, PxSize},
15 renderer::Command,
16};
17
18pub use constraint::{Constraint, DimensionValue};
19pub use node::{
20 ComponentNode, ComponentNodeMetaData, ComponentNodeMetaDatas, ComponentNodeTree, ComputedData,
21 ImeRequest, InputHandlerFn, InputHandlerInput, MeasureFn, MeasureInput, MeasurementError,
22 WindowRequests,
23};
24
25#[derive(Debug, Clone, Copy)]
26struct ScreenSize {
27 width: i32,
28 height: i32,
29}
30
31pub struct ComputeParams<'a> {
33 pub screen_size: PxSize,
34 pub cursor_position: Option<PxPosition>,
35 pub cursor_events: Vec<CursorEvent>,
36 pub keyboard_events: Vec<winit::event::KeyEvent>,
37 pub ime_events: Vec<winit::event::Ime>,
38 pub modifiers: winit::keyboard::ModifiersState,
39 pub compute_resource_manager: Arc<RwLock<ComputeResourceManager>>,
40 pub gpu: &'a wgpu::Device,
41 pub clipboard: &'a mut Clipboard,
42}
43
44pub struct ComponentTree {
46 tree: indextree::Arena<ComponentNode>,
48 metadatas: ComponentNodeMetaDatas,
50 node_queue: Vec<indextree::NodeId>,
52}
53
54impl Default for ComponentTree {
55 fn default() -> Self {
56 Self::new()
57 }
58}
59
60impl ComponentTree {
61 pub fn new() -> Self {
63 let tree = indextree::Arena::new();
64 let node_queue = Vec::new();
65 let metadatas = ComponentNodeMetaDatas::new();
66 Self {
67 tree,
68 node_queue,
69 metadatas,
70 }
71 }
72
73 pub fn clear(&mut self) {
75 self.tree.clear();
76 self.metadatas.clear();
77 self.node_queue.clear();
78 }
79
80 pub fn get(&self, node_id: indextree::NodeId) -> Option<&ComponentNode> {
82 self.tree.get(node_id).map(|n| n.get())
83 }
84
85 pub fn get_mut(&mut self, node_id: indextree::NodeId) -> Option<&mut ComponentNode> {
87 self.tree.get_mut(node_id).map(|n| n.get_mut())
88 }
89
90 pub fn current_node(&self) -> Option<&ComponentNode> {
92 self.node_queue
93 .last()
94 .and_then(|node_id| self.get(*node_id))
95 }
96
97 pub fn current_node_mut(&mut self) -> Option<&mut ComponentNode> {
99 let node_id = self.node_queue.last()?;
100 self.get_mut(*node_id)
101 }
102
103 pub fn add_node(&mut self, node_component: ComponentNode) {
107 let new_node_id = self.tree.new_node(node_component);
108 if let Some(current_node_id) = self.node_queue.last_mut() {
109 current_node_id.append(new_node_id, &mut self.tree);
110 }
111 let metadata = ComponentNodeMetaData::none();
112 self.metadatas.insert(new_node_id, metadata);
113 self.node_queue.push(new_node_id);
114 }
115
116 pub fn pop_node(&mut self) {
118 self.node_queue.pop();
119 }
120
121 pub(crate) fn tree(&self) -> &indextree::Arena<ComponentNode> {
125 &self.tree
126 }
127
128 pub(crate) fn metadatas(&self) -> &ComponentNodeMetaDatas {
132 &self.metadatas
133 }
134
135 #[tracing::instrument(level = "debug", skip(self, params))]
145 pub fn compute(
146 &mut self,
147 params: ComputeParams<'_>,
148 ) -> (Vec<(Command, TypeId, PxSize, PxPosition)>, WindowRequests) {
149 let ComputeParams {
150 screen_size,
151 mut cursor_position,
152 mut cursor_events,
153 mut keyboard_events,
154 mut ime_events,
155 modifiers,
156 compute_resource_manager,
157 gpu,
158 clipboard,
159 } = params;
160 let Some(root_node) = self
161 .tree
162 .get_node_id_at(NonZero::new(1).expect("root node index must be non-zero"))
163 else {
164 return (vec![], WindowRequests::default());
165 };
166 let screen_constraint = Constraint::new(
167 DimensionValue::Fixed(screen_size.width),
168 DimensionValue::Fixed(screen_size.height),
169 );
170
171 let measure_timer = Instant::now();
172 debug!("Start measuring the component tree...");
173
174 match measure_node(
177 root_node,
178 &screen_constraint,
179 &self.tree,
180 &self.metadatas,
181 compute_resource_manager,
182 gpu,
183 ) {
184 Ok(_root_computed_data) => {
185 debug!("Component tree measured in {:?}", measure_timer.elapsed());
186 }
187 Err(e) => {
188 panic!(
189 "Root node ({root_node:?}) measurement failed: {e:?}. Aborting draw command computation."
190 );
191 }
192 }
193
194 let compute_draw_timer = Instant::now();
195 debug!("Start computing draw commands...");
196 let commands = compute_draw_commands_parallel(
199 root_node,
200 &self.tree,
201 &self.metadatas,
202 screen_size.width.0,
203 screen_size.height.0,
204 );
205 debug!(
206 "Draw commands computed in {:?}, total commands: {}",
207 compute_draw_timer.elapsed(),
208 commands.len()
209 );
210
211 let input_handler_timer = Instant::now();
212 let mut window_requests = WindowRequests::default();
213 debug!("Start executing input handlers...");
214
215 for node_id in root_node
216 .reverse_traverse(&self.tree)
217 .filter_map(|edge| match edge {
218 indextree::NodeEdge::Start(id) => Some(id),
219 indextree::NodeEdge::End(_) => None,
220 })
221 {
222 let Some(input_handler) = self
223 .tree
224 .get(node_id)
225 .and_then(|n| n.get().input_handler_fn.as_ref())
226 else {
227 continue;
228 };
229
230 let Some(metadata) = self.metadatas.get(&node_id) else {
231 warn!(
232 "Input handler metadata missing for node {node_id:?}; skipping input handling"
233 );
234 continue;
235 };
236 let Some(abs_pos) = metadata.abs_position else {
237 warn!("Absolute position missing for node {node_id:?}; skipping input handling");
238 continue;
239 };
240 let event_clip_rect = metadata.event_clip_rect;
241 let node_computed_data = metadata.computed_data;
242 drop(metadata); let mut cursor_position_ref = &mut cursor_position;
245 let mut dummy_cursor_position = None;
246 let mut cursor_events_ref = &mut cursor_events;
247 let mut empty_dummy_cursor_events = Vec::new();
248 if let (Some(cursor_pos), Some(clip_rect)) = (*cursor_position_ref, event_clip_rect) {
249 if !clip_rect.contains(cursor_pos) {
251 cursor_position_ref = &mut dummy_cursor_position;
253 cursor_events_ref = &mut empty_dummy_cursor_events;
254 }
255 }
256 let current_cursor_position = cursor_position_ref.map(|pos| pos - abs_pos);
257
258 if let Some(node_computed_data) = node_computed_data {
259 let input = InputHandlerInput {
260 computed_data: node_computed_data,
261 cursor_position_rel: current_cursor_position,
262 cursor_position_abs: cursor_position_ref,
263 cursor_events: cursor_events_ref,
264 keyboard_events: &mut keyboard_events,
265 ime_events: &mut ime_events,
266 key_modifiers: modifiers,
267 requests: &mut window_requests,
268 clipboard,
269 current_node_id: node_id,
270 metadatas: &self.metadatas,
271 };
272 input_handler(input);
273 if let Some(ref mut ime_request) = window_requests.ime_request
275 && ime_request.position.is_none()
276 {
277 ime_request.position = Some(abs_pos);
278 }
279 } else {
280 warn!(
281 "Computed data not found for node {:?} during input handler execution.",
282 node_id
283 );
284 }
285 }
286
287 debug!(
288 "Input Handlers executed in {:?}",
289 input_handler_timer.elapsed()
290 );
291 (commands, window_requests)
292 }
293}
294
295#[tracing::instrument(level = "trace", skip(tree, metadatas))]
304fn compute_draw_commands_parallel(
305 node_id: indextree::NodeId,
306 tree: &ComponentNodeTree,
307 metadatas: &ComponentNodeMetaDatas,
308 screen_width: i32,
309 screen_height: i32,
310) -> Vec<(Command, TypeId, PxSize, PxPosition)> {
311 compute_draw_commands_inner_parallel(
312 PxPosition::ZERO,
313 true,
314 node_id,
315 tree,
316 metadatas,
317 ScreenSize {
318 width: screen_width,
319 height: screen_height,
320 },
321 None,
322 )
323}
324
325#[tracing::instrument(level = "trace", skip(tree, metadatas))]
326fn compute_draw_commands_inner_parallel(
327 start_pos: PxPosition,
328 is_root: bool,
329 node_id: indextree::NodeId,
330 tree: &ComponentNodeTree,
331 metadatas: &ComponentNodeMetaDatas,
332 screen_size: ScreenSize,
333 clip_rect: Option<PxRect>,
334) -> Vec<(Command, TypeId, PxSize, PxPosition)> {
335 let mut local_commands = Vec::new();
336
337 let Some(mut metadata) = metadatas.get_mut(&node_id) else {
339 warn!("Missing metadata for node {node_id:?}; skipping draw computation");
340 return local_commands;
341 };
342 let rel_pos = match metadata.rel_position {
343 Some(pos) => pos,
344 None if is_root => PxPosition::ZERO,
345 _ => return local_commands, };
347 let self_pos = start_pos + rel_pos;
348 metadata.abs_position = Some(self_pos);
349
350 let size = metadata
351 .computed_data
352 .map(|d| PxSize {
353 width: d.width,
354 height: d.height,
355 })
356 .unwrap_or_default();
357
358 let node_rect = PxRect {
359 x: self_pos.x,
360 y: self_pos.y,
361 width: size.width,
362 height: size.height,
363 };
364
365 let mut clip_rect = clip_rect;
366 if let Some(clip_rect) = clip_rect {
367 metadata.event_clip_rect = Some(clip_rect);
368 }
369
370 let clips_children = metadata.clips_children;
371 if clips_children {
373 let new_clip_rect = if let Some(existing_clip) = clip_rect {
374 existing_clip
375 .intersection(&node_rect)
376 .unwrap_or(PxRect::ZERO)
377 } else {
378 node_rect
379 };
380
381 clip_rect = Some(new_clip_rect);
382
383 local_commands.push((
384 Command::ClipPush(new_clip_rect),
385 TypeId::of::<Command>(),
386 size,
387 self_pos,
388 ));
389 }
390
391 let screen_rect = PxRect {
393 x: Px(0),
394 y: Px(0),
395 width: Px(screen_size.width),
396 height: Px(screen_size.height),
397 };
398
399 if size.width.0 > 0 && size.height.0 > 0 && !node_rect.is_orthogonal(&screen_rect) {
401 for (cmd, type_id) in metadata.commands.drain(..) {
402 local_commands.push((cmd, type_id, size, self_pos));
403 }
404 }
405
406 drop(metadata); let children: Vec<_> = node_id.children(tree).collect();
410 let child_results: Vec<Vec<_>> = children
411 .into_par_iter()
412 .filter_map(|child| {
413 let parent_abs_pos = {
415 let Some(parent_meta) = metadatas.get(&node_id) else {
416 warn!(
417 "Missing parent metadata for node {node_id:?}; skipping child {child:?}"
418 );
419 return None;
420 };
421 let Some(pos) = parent_meta.abs_position else {
422 warn!(
423 "Missing parent absolute position for node {node_id:?}; skipping child {child:?}"
424 );
425 return None;
426 };
427 pos
428 };
429
430 Some(compute_draw_commands_inner_parallel(
431 parent_abs_pos, false,
433 child,
434 tree,
435 metadatas,
436 screen_size,
437 clip_rect,
438 ))
439 })
440 .collect();
441
442 for child_cmds in child_results {
443 local_commands.extend(child_cmds);
444 }
445
446 if clips_children {
448 local_commands.push((Command::ClipPop, TypeId::of::<Command>(), size, self_pos));
449 }
450
451 local_commands
452}