1use std::{cell::RefCell, fmt::Debug, rc::Rc};
14
15use cranpose_core::{NodeId, collections::map::HashSet};
16use cranpose_foundation::{PointerButtons, PointerSource, RotaryScrollEvent};
17use cranpose_render_common::Renderer;
18use cranpose_ui::{
19 LayoutTree, PlatformTextInputHandler, SemanticsTree, pointer_icon_session::PointerIconState,
20};
21use cranpose_ui_graphics::{Point, PointerIcon, Size};
22use web_time::Instant;
23
24use crate::{
25 AppShell, DevOverlayControl, FramePacingMode, FrameRatePreference, FrameSchedule,
26 FrameScheduler, FrameUpdateResult, PlatformFrameDriver, ShellApp,
27 hit_path_tracker::{HitPathTracker, PointerId},
28};
29
30#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
34pub enum RootId {
35 Primary,
37 Window(u64),
39}
40
41pub struct RootSurface<R: Renderer> {
49 pub(crate) id: RootId,
50 pub(crate) root: Option<NodeId>,
51 pub(crate) renderer: R,
52 pub(crate) cursor: (f32, f32),
53 pub(crate) viewport: (f32, f32),
54 pub(crate) buffer_size: (u32, u32),
55 pub(crate) layout_tree: Option<LayoutTree>,
56 pub(crate) semantics_tree: Option<SemanticsTree>,
57 semantics_moved: bool,
60 pub(crate) modal_focus: Vec<(NodeId, Option<NodeId>)>,
61 pub(crate) frame_rate_preference: FrameRatePreference,
62 pub(crate) scene_dirty: bool,
63 pub(crate) scoped_layout_scene_nodes: Vec<NodeId>,
64 pub(crate) retained_visual_nodes: HashSet<NodeId>,
65 pub(crate) is_dirty: bool,
66 pub(crate) buttons_pressed: PointerButtons,
67 pub(crate) pointer_source: PointerSource,
68 pub(crate) hit_path_tracker: HitPathTracker,
69 pub(crate) hovered_nodes: Vec<NodeId>,
70 pub(crate) on_rotary_scroll: Option<Rc<dyn Fn(RotaryScrollEvent) -> bool>>,
71 pub(crate) dev_overlay_controls: Vec<DevOverlayControl>,
72 pub(crate) inspector: crate::inspector::DeveloperInspector,
73 pub(crate) dev_overlay_text: String,
74 pub(crate) dev_overlay_last_refresh: Option<Instant>,
75 pub(crate) dev_overlay_viewport: Option<Size>,
76 pub(crate) frame_scheduler: FrameScheduler,
77 pub(crate) pointer_icon: PointerIconState,
78 pub(crate) last_update: FrameUpdateResult,
79 pub(crate) frame_owed: bool,
80 pub(crate) screen_origin: Option<Point>,
81}
82
83impl<R: Renderer> RootSurface<R> {
84 pub(crate) fn new(
85 id: RootId,
86 renderer: R,
87 buffer_size: (u32, u32),
88 viewport: (f32, f32),
89 ) -> Self {
90 Self {
91 id,
92 root: None,
93 renderer,
94 cursor: (0.0, 0.0),
95 viewport,
96 buffer_size,
97 layout_tree: None,
98 semantics_tree: None,
99 semantics_moved: false,
100 modal_focus: Vec::new(),
101 frame_rate_preference: FrameRatePreference::default(),
102 scene_dirty: true,
103 scoped_layout_scene_nodes: Vec::new(),
104 retained_visual_nodes: HashSet::default(),
105 is_dirty: true,
106 buttons_pressed: PointerButtons::NONE,
107 pointer_source: PointerSource::Unknown,
108 hit_path_tracker: HitPathTracker::new(),
109 hovered_nodes: Vec::new(),
110 on_rotary_scroll: None,
111 dev_overlay_controls: Vec::new(),
112 inspector: crate::inspector::DeveloperInspector::default(),
113 dev_overlay_text: String::new(),
114 dev_overlay_last_refresh: None,
115 dev_overlay_viewport: None,
116 frame_scheduler: FrameScheduler::default(),
117 pointer_icon: PointerIconState::new(),
118 last_update: FrameUpdateResult::default(),
119 frame_owed: false,
120 screen_origin: None,
121 }
122 }
123
124 pub(crate) fn root_node(&self, app: &ShellApp) -> Option<NodeId> {
125 match self.id {
126 RootId::Primary => app.composition.root(),
127 RootId::Window(_) => self.root,
128 }
129 }
130
131 pub(crate) fn owns_nodes_under(&self, window_root: Option<NodeId>) -> bool {
132 match self.id {
133 RootId::Primary => window_root.is_none(),
134 RootId::Window(_) => self.root.is_some() && self.root == window_root,
135 }
136 }
137
138 pub(crate) fn viewport_size(&self) -> Size {
139 Size {
140 width: self.viewport.0,
141 height: self.viewport.1,
142 }
143 }
144
145 pub(crate) fn screen_point_inside(&self, screen: Point) -> Option<Point> {
146 let origin = self.screen_origin?;
147 let local = Point {
148 x: screen.x - origin.x,
149 y: screen.y - origin.y,
150 };
151 (local.x >= 0.0
152 && local.y >= 0.0
153 && local.x <= self.viewport.0
154 && local.y <= self.viewport.1)
155 .then_some(local)
156 }
157
158 pub(crate) fn set_root(&mut self, root: Option<NodeId>) {
159 if self.root == root {
160 return;
161 }
162 self.root = root;
163 self.forget_snapshots();
164 self.scoped_layout_scene_nodes.clear();
165 self.retained_visual_nodes.clear();
166 self.hit_path_tracker.clear();
167 self.hovered_nodes.clear();
168 self.buttons_pressed = PointerButtons::NONE;
169 self.scene_dirty = true;
170 self.is_dirty = true;
171 }
172
173 pub(crate) fn forget_snapshots(&mut self) {
176 self.layout_tree = None;
177 self.semantics_moved = true;
178 }
179
180 pub(crate) fn has_active_pointer_gesture(&self) -> bool {
181 self.buttons_pressed != PointerButtons::NONE
182 && self.hit_path_tracker.has_path(PointerId::PRIMARY)
183 }
184
185 pub(crate) fn renderer_warmup_due(&self, app: &ShellApp) -> bool {
186 self.renderer.needs_frame_warmup() && !app.runtime.runtime_handle().has_frame_callbacks()
187 }
188
189 pub(crate) fn needs_redraw_in_context(&self, app: &ShellApp) -> bool {
190 app.has_stale_work_in_context()
191 || self.is_dirty
192 || self.scene_dirty
193 || self.renderer_warmup_due(app)
194 }
195
196 pub(crate) fn compute_frame_schedule(
197 &self,
198 app: &ShellApp,
199 surfaces_dirty: bool,
200 ) -> FrameSchedule {
201 let app_context = Rc::clone(&app.app_context);
202 let (needs_update, needs_frame) = app_context.enter(|| {
203 let needs_frame = self.is_dirty
204 || self.scene_dirty
205 || app.wants_frame_in_context()
206 || self.has_active_pointer_gesture()
207 || self.renderer_warmup_due(app);
208 (app.needs_ui_update_in_context(surfaces_dirty), needs_frame)
209 });
210 FrameSchedule {
211 needs_update,
212 needs_frame,
213 next_deadline: app.next_event_time(),
214 }
215 }
216
217 pub(crate) fn invalidate_dev_overlay_text(&mut self) {
218 self.dev_overlay_text.clear();
219 self.dev_overlay_last_refresh = None;
220 self.dev_overlay_viewport = None;
221 }
222
223 pub(crate) fn dev_overlay_control_center(&self, mode: FramePacingMode) -> Option<(f32, f32)> {
224 self.dev_overlay_controls
225 .iter()
226 .find(|control| control.mode == mode)
227 .map(|control| {
228 (
229 control.bounds.x + control.bounds.width * 0.5,
230 control.bounds.y + control.bounds.height * 0.5,
231 )
232 })
233 }
234
235 pub(crate) fn layout_tree_in_context(&mut self, app: &mut ShellApp) -> Option<&LayoutTree> {
236 if self.layout_tree.is_none() {
237 let root = self.root_node(app)?;
238 let mut applier = app.composition.applier_mut();
239 match cranpose_ui::build_layout_tree_from_applier(&mut applier, root) {
240 Ok(layout_tree) => {
241 self.layout_tree = layout_tree;
242 }
243 Err(err) => {
244 log::debug!("failed to build layout snapshot: {err}");
245 return None;
246 }
247 }
248 }
249 self.layout_tree.as_ref()
250 }
251
252 pub(crate) fn top_modal(&self, app: &mut ShellApp) -> Option<NodeId> {
255 let root = self.root_node(app)?;
256 let mut applier = app.composition.applier_mut();
257 cranpose_ui::top_modal_from_applier(&mut applier, root).unwrap_or_else(|err| {
258 log::debug!("failed to find the top modal under root #{root}: {err}");
259 None
260 })
261 }
262
263 pub(crate) fn semantics_tree_in_context(
264 &mut self,
265 app: &mut ShellApp,
266 ) -> Option<&SemanticsTree> {
267 if !app.semantics_enabled && !self.inspector.state.open {
268 return None;
269 }
270 self.semantics_tree_for_input(app)
271 }
272
273 pub(crate) fn semantics_tree_for_input(
274 &mut self,
275 app: &mut ShellApp,
276 ) -> Option<&SemanticsTree> {
277 let root = self.root_node(app)?;
278 let semantics_dirty = {
279 let mut applier = app.composition.applier_mut();
280 cranpose_ui::tree_needs_semantics(&mut *applier, root).unwrap_or_else(|err| {
281 log::debug!("failed to check semantics dirty status for root #{root}: {err}");
282 true
283 })
284 };
285 if self.semantics_tree.is_none() || self.semantics_moved || semantics_dirty {
286 let mut applier = app.composition.applier_mut();
287 match cranpose_ui::update_semantics_tree_from_applier(
288 &mut applier,
289 root,
290 &mut self.semantics_tree,
291 ) {
292 Ok(()) => {
293 self.semantics_moved = false;
294 app.semantics_snapshot_revision =
295 app.semantics_snapshot_revision.wrapping_add(1);
296 }
297 Err(err) => {
298 log::debug!("failed to build semantics snapshot: {err}");
299 return None;
300 }
301 }
302 }
303 self.semantics_tree.as_ref()
304 }
305}
306
307#[derive(Default)]
308pub(crate) struct TextInputRoutes {
309 active: Option<RootId>,
310 shown: Option<RootId>,
311 handlers: Vec<(RootId, Rc<dyn PlatformTextInputHandler>)>,
312}
313
314impl TextInputRoutes {
315 pub(crate) fn active(&self) -> RootId {
316 self.active.unwrap_or(RootId::Primary)
317 }
318
319 pub(crate) fn set_active(&mut self, root: RootId) {
320 self.active = Some(root);
321 }
322
323 pub(crate) fn set_handler(&mut self, root: RootId, handler: Rc<dyn PlatformTextInputHandler>) {
324 self.remove(root);
325 self.handlers.push((root, handler));
326 }
327
328 pub(crate) fn remove(&mut self, root: RootId) {
329 self.handlers.retain(|(id, _)| *id != root);
330 if self.shown == Some(root) {
331 self.shown = None;
332 }
333 }
334
335 fn handler(&self, root: RootId) -> Option<Rc<dyn PlatformTextInputHandler>> {
336 self.handlers
337 .iter()
338 .find(|(id, _)| *id == root)
339 .map(|(_, handler)| Rc::clone(handler))
340 }
341
342 fn take_show_target(&mut self) -> Option<Rc<dyn PlatformTextInputHandler>> {
343 let active = self.active();
344 let handler = self.handler(active);
345 if handler.is_some() {
346 self.shown = Some(active);
347 }
348 handler
349 }
350
351 fn take_hide_target(&mut self) -> Option<Rc<dyn PlatformTextInputHandler>> {
352 let target = self.shown.take().unwrap_or_else(|| self.active());
353 self.handler(target)
354 }
355}
356
357pub(crate) struct TextInputRouter {
358 pub(crate) routes: Rc<RefCell<TextInputRoutes>>,
359}
360
361impl PlatformTextInputHandler for TextInputRouter {
362 fn show_keyboard(&self) {
363 let handler = self.routes.borrow_mut().take_show_target();
364 if let Some(handler) = handler {
365 handler.show_keyboard();
366 }
367 }
368
369 fn hide_keyboard(&self) {
370 let handler = self.routes.borrow_mut().take_hide_target();
371 if let Some(handler) = handler {
372 handler.hide_keyboard();
373 }
374 }
375}
376
377pub(crate) fn partition_nodes_by_surface<R: Renderer>(
378 app: &mut ShellApp,
379 surfaces: &[RootSurface<R>],
380 nodes: Vec<NodeId>,
381) -> Vec<Vec<NodeId>> {
382 let mut buckets: Vec<Vec<NodeId>> = surfaces.iter().map(|_| Vec::new()).collect();
383 let Some(primary_root) = app.composition.root() else {
384 return buckets;
385 };
386 let mut applier = app.composition.applier_mut();
387 let attached: Vec<NodeId> = applier
388 .scene_nodes_attached_to(nodes, primary_root)
389 .into_iter()
390 .flatten()
391 .collect();
392 let owners = cranpose_ui::nearest_window_roots(&mut applier, &attached);
393 for (node, owner) in attached.into_iter().zip(owners) {
394 if let Some(index) = surfaces
395 .iter()
396 .position(|surface| surface.owns_nodes_under(owner))
397 {
398 buckets[index].push(node);
399 }
400 }
401 buckets
402}
403
404pub struct SurfaceMut<'a, R: Renderer> {
411 pub(crate) shell: &'a mut AppShell<R>,
412 pub(crate) index: usize,
413}
414
415impl<'a, R> SurfaceMut<'a, R>
416where
417 R: Renderer,
418 R::Error: Debug,
419{
420 pub(crate) fn new(shell: &'a mut AppShell<R>, index: usize) -> Self {
421 Self { shell, index }
422 }
423
424 pub fn shell(&mut self) -> &mut AppShell<R> {
427 self.shell
428 }
429
430 pub(crate) fn shell_app(&mut self) -> &mut ShellApp {
431 &mut self.shell.app
432 }
433
434 pub(crate) fn shell_app_ref(&self) -> &ShellApp {
435 &self.shell.app
436 }
437
438 pub(crate) fn surface(&self) -> &RootSurface<R> {
439 &self.shell.surfaces[self.index]
440 }
441
442 pub(crate) fn surface_mut(&mut self) -> &mut RootSurface<R> {
443 &mut self.shell.surfaces[self.index]
444 }
445
446 pub(crate) fn parts(&mut self) -> (&mut ShellApp, &mut RootSurface<R>) {
447 let shell = &mut *self.shell;
448 (&mut shell.app, &mut shell.surfaces[self.index])
449 }
450
451 pub fn id(&self) -> RootId {
453 self.surface().id
454 }
455
456 pub fn root(&self) -> Option<NodeId> {
458 let surface = self.surface();
459 surface.root_node(self.shell_app_ref())
460 }
461
462 pub fn renderer(&mut self) -> &mut R {
464 &mut self.surface_mut().renderer
465 }
466
467 pub fn scene(&self) -> &R::Scene {
469 self.surface().renderer.scene()
470 }
471
472 pub fn set_viewport(&mut self, width: f32, height: f32) {
480 self.surface_mut().viewport = (width, height);
481 match self.id() {
482 RootId::Primary => self.shell_app().request_forced_layout_pass(),
483 RootId::Window(_) => {
484 if let Some(root) = self.root() {
485 let app_context = Rc::clone(&self.shell_app_ref().app_context);
486 app_context.enter(|| cranpose_ui::schedule_measure_repass(root));
487 }
488 self.shell_app().request_layout_pass();
489 }
490 }
491 self.surface_mut().scene_dirty = true;
492 self.mark_dirty();
493 }
494
495 pub fn set_screen_origin(&mut self, origin: Option<Point>) {
501 self.surface_mut().screen_origin = origin;
502 }
503
504 pub fn screen_origin(&self) -> Option<Point> {
507 self.surface().screen_origin
508 }
509
510 pub fn viewport_size(&self) -> (f32, f32) {
512 self.surface().viewport
513 }
514
515 pub fn set_buffer_size(&mut self, width: u32, height: u32) {
517 self.surface_mut().buffer_size = (width, height);
518 }
519
520 pub fn buffer_size(&self) -> (u32, u32) {
522 self.surface().buffer_size
523 }
524
525 pub fn mark_dirty(&mut self) {
527 self.surface_mut().is_dirty = true;
528 }
529
530 pub fn needs_redraw(&self) -> bool {
534 let app_context = Rc::clone(&self.shell_app_ref().app_context);
535 app_context.enter(|| self.surface().needs_redraw_in_context(self.shell_app_ref()))
536 }
537
538 pub fn has_active_pointer_gesture(&self) -> bool {
541 self.surface().has_active_pointer_gesture()
542 }
543
544 pub fn last_update_result(&self) -> FrameUpdateResult {
547 self.surface().last_update
548 }
549
550 pub fn frame_owed(&self) -> bool {
555 self.surface().frame_owed
556 }
557
558 pub fn take_frame_owed(&mut self) -> bool {
560 std::mem::take(&mut self.surface_mut().frame_owed)
561 }
562
563 fn compute_frame_schedule(&self) -> FrameSchedule {
564 self.surface()
565 .compute_frame_schedule(self.shell_app_ref(), self.shell.any_surface_dirty())
566 }
567
568 pub fn frame_schedule(&self) -> FrameSchedule {
571 let schedule = self.compute_frame_schedule();
572 self.surface().frame_scheduler.record(schedule);
573 schedule
574 }
575
576 pub fn schedule_platform_frame<D>(&self, driver: &D) -> FrameSchedule
578 where
579 D: PlatformFrameDriver + ?Sized,
580 {
581 let schedule = self.compute_frame_schedule();
582 self.surface().frame_scheduler.schedule(schedule, driver);
583 schedule
584 }
585
586 pub fn frame_scheduler_snapshot(&self) -> FrameSchedule {
588 self.surface().frame_scheduler.snapshot()
589 }
590
591 pub fn set_frame_rate_preference(&mut self, preference: FrameRatePreference) {
594 self.surface_mut().frame_rate_preference = preference;
595 }
596
597 pub fn frame_rate_preference(&self) -> FrameRatePreference {
599 self.surface().frame_rate_preference
600 }
601
602 pub fn dev_overlay_control_center(&self, mode: FramePacingMode) -> Option<(f32, f32)> {
605 self.surface().dev_overlay_control_center(mode)
606 }
607
608 pub(crate) fn dev_overlay_press(&mut self, x: f32, y: f32) -> bool {
609 if !self.shell_app_ref().dev_options.frame_pacing_controls {
610 return false;
611 }
612 let Some(mode) = self
613 .surface()
614 .dev_overlay_controls
615 .iter()
616 .find(|control| control.bounds.contains(x, y))
617 .map(|control| control.mode)
618 else {
619 return false;
620 };
621 self.shell().set_frame_pacing_mode(mode);
622 true
623 }
624
625 pub fn with_layout_tree<T>(&mut self, block: impl FnOnce(Option<&LayoutTree>) -> T) -> T {
627 let (app, surface) = self.parts();
628 let app_context = Rc::clone(&app.app_context);
629 app_context.enter(|| block(surface.layout_tree_in_context(app)))
630 }
631
632 pub fn with_semantics_tree<T>(&mut self, block: impl FnOnce(Option<&SemanticsTree>) -> T) -> T {
635 let (app, surface) = self.parts();
636 let app_context = Rc::clone(&app.app_context);
637 app_context.enter(|| block(surface.semantics_tree_in_context(app)))
638 }
639
640 pub fn take_pointer_icon_change(&self) -> Option<PointerIcon> {
643 self.surface().pointer_icon.take_change()
644 }
645
646 pub fn refresh_pointer_icon(&self) {
649 self.surface().pointer_icon.refresh();
650 }
651
652 pub fn set_platform_text_input(&mut self, handler: Rc<dyn PlatformTextInputHandler>) {
657 let id = self.id();
658 let app = self.shell_app();
659 app.text_input_routes.borrow_mut().set_handler(id, handler);
660 app.install_text_input_router();
661 }
662
663 pub fn activate(&mut self) {
666 let id = self.id();
667 self.shell_app()
668 .text_input_routes
669 .borrow_mut()
670 .set_active(id);
671 }
672}