1use std::{cell::RefCell, fmt::Debug, rc::Rc};
14
15use cranpose_core::{MemoryApplier, NodeId, collections::map::HashSet};
16use cranpose_foundation::{PointerButton, PointerButtons, PointerSource, RotaryScrollEvent};
17use cranpose_render_common::Renderer;
18use cranpose_ui::{
19 LayoutNode, LayoutTree, MovedSceneNode, PlatformTextInputHandler, SemanticsTree,
20 pointer_icon_session::PointerIconState,
21};
22use cranpose_ui_graphics::{Point, PointerIcon, Size};
23use web_time::Instant;
24
25use crate::{
26 AppShell, DevOverlayControl, FramePacingMode, FrameRatePreference, FrameSchedule,
27 FrameScheduler, FrameUpdateResult, PlatformFrameDriver, ShellApp, SurfaceRoutingScratch,
28 hit_path_tracker::{HitPathTracker, PointerId},
29};
30
31#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
35pub enum RootId {
36 Primary,
38 Window(u64),
40}
41
42pub struct RootSurface<R: Renderer> {
50 pub(crate) id: RootId,
51 pub(crate) root: Option<NodeId>,
52 pub(crate) renderer: R,
53 pub(crate) cursor: (f32, f32),
54 pub(crate) viewport: (f32, f32),
55 pub(crate) buffer_size: (u32, u32),
56 pub(crate) layout_tree: Option<LayoutTree>,
57 pub(crate) semantics_tree: Option<SemanticsTree>,
58 semantics_moved: bool,
61 pub(crate) modal_focus: Vec<(NodeId, Option<NodeId>)>,
62 pub(crate) frame_rate_preference: FrameRatePreference,
63 pub(crate) scene_dirty: bool,
64 pub(crate) scoped_layout_scene_nodes: Vec<NodeId>,
65 pub(crate) scoped_moved_scene_nodes: Vec<NodeId>,
67 pub(crate) scoped_draw_nodes: Vec<NodeId>,
68 pub(crate) scoped_layer_property_nodes: Vec<NodeId>,
69 pub(crate) structural_scene_nodes: Vec<NodeId>,
70 pub(crate) partial_scene_nodes: Vec<NodeId>,
71 pub(crate) redraw_walk: Vec<NodeId>,
74 pub(crate) retained_visual_nodes: HashSet<NodeId>,
75 pub(crate) is_dirty: bool,
76 pub(crate) buttons_pressed: PointerButtons,
77 pub(crate) pointer_source: PointerSource,
78 pub(crate) hit_path_tracker: HitPathTracker,
79 pub(crate) hovered_nodes: Vec<NodeId>,
80 pub(crate) on_rotary_scroll: Option<Rc<dyn Fn(RotaryScrollEvent) -> bool>>,
81 pub(crate) dev_overlay_controls: Vec<DevOverlayControl>,
82 pub(crate) inspector: crate::inspector::DeveloperInspector,
83 pub(crate) dev_overlay_text: String,
84 pub(crate) dev_overlay_last_refresh: Option<Instant>,
85 pub(crate) dev_overlay_viewport: Option<Size>,
86 pub(crate) frame_scheduler: FrameScheduler,
87 pub(crate) pointer_icon: PointerIconState,
88 pub(crate) last_update: FrameUpdateResult,
89 pub(crate) frame_owed: bool,
90 pub(crate) screen_origin: Option<Point>,
91}
92
93impl<R: Renderer> RootSurface<R> {
94 pub(crate) fn new(
95 id: RootId,
96 renderer: R,
97 buffer_size: (u32, u32),
98 viewport: (f32, f32),
99 ) -> Self {
100 Self {
101 id,
102 root: None,
103 renderer,
104 cursor: (0.0, 0.0),
105 viewport,
106 buffer_size,
107 layout_tree: None,
108 semantics_tree: None,
109 semantics_moved: false,
110 modal_focus: Vec::new(),
111 frame_rate_preference: FrameRatePreference::default(),
112 scene_dirty: true,
113 scoped_layout_scene_nodes: Vec::new(),
114 scoped_moved_scene_nodes: Vec::new(),
115 scoped_draw_nodes: Vec::new(),
116 scoped_layer_property_nodes: Vec::new(),
117 structural_scene_nodes: Vec::new(),
118 partial_scene_nodes: Vec::new(),
119 redraw_walk: Vec::new(),
120 retained_visual_nodes: HashSet::default(),
121 is_dirty: true,
122 buttons_pressed: PointerButtons::NONE,
123 pointer_source: PointerSource::Unknown,
124 hit_path_tracker: HitPathTracker::new(),
125 hovered_nodes: Vec::new(),
126 on_rotary_scroll: None,
127 dev_overlay_controls: Vec::new(),
128 inspector: crate::inspector::DeveloperInspector::default(),
129 dev_overlay_text: String::new(),
130 dev_overlay_last_refresh: None,
131 dev_overlay_viewport: None,
132 frame_scheduler: FrameScheduler::default(),
133 pointer_icon: PointerIconState::new(),
134 last_update: FrameUpdateResult::default(),
135 frame_owed: false,
136 screen_origin: None,
137 }
138 }
139
140 pub(crate) fn root_node(&self, app: &ShellApp) -> Option<NodeId> {
141 match self.id {
142 RootId::Primary => app.composition.root(),
143 RootId::Window(_) => self.root,
144 }
145 }
146
147 pub(crate) fn owns_nodes_under(&self, window_root: Option<NodeId>) -> bool {
148 match self.id {
149 RootId::Primary => window_root.is_none(),
150 RootId::Window(_) => self.root.is_some() && self.root == window_root,
151 }
152 }
153
154 pub(crate) fn viewport_size(&self) -> Size {
155 Size {
156 width: self.viewport.0,
157 height: self.viewport.1,
158 }
159 }
160
161 pub(crate) fn screen_point_inside(&self, screen: Point) -> Option<Point> {
162 let origin = self.screen_origin?;
163 let local = Point {
164 x: screen.x - origin.x,
165 y: screen.y - origin.y,
166 };
167 (local.x >= 0.0
168 && local.y >= 0.0
169 && local.x <= self.viewport.0
170 && local.y <= self.viewport.1)
171 .then_some(local)
172 }
173
174 pub(crate) fn set_root(&mut self, root: Option<NodeId>) {
175 if self.root == root {
176 return;
177 }
178 self.root = root;
179 self.forget_snapshots();
180 self.invalidate_scene_root(root);
181 self.retained_visual_nodes.clear();
182 self.hit_path_tracker.clear();
183 self.hovered_nodes.clear();
184 self.buttons_pressed = PointerButtons::NONE;
185 self.is_dirty = true;
186 }
187
188 pub(crate) fn invalidate_scene_root(&mut self, root: Option<NodeId>) {
189 self.scoped_layout_scene_nodes.clear();
190 self.scoped_layout_scene_nodes.extend(root);
191 self.scoped_moved_scene_nodes.clear();
192 self.scoped_draw_nodes.clear();
193 self.scoped_layer_property_nodes.clear();
194 self.structural_scene_nodes.clear();
195 self.partial_scene_nodes.clear();
196 self.scene_dirty = true;
197 }
198
199 pub(crate) fn forget_snapshots(&mut self) {
202 self.layout_tree = None;
203 self.semantics_moved = true;
204 }
205
206 pub(crate) fn has_active_pointer_gesture(&self) -> bool {
207 self.buttons_pressed != PointerButtons::NONE
208 && self.hit_path_tracker.has_path(PointerId::PRIMARY)
209 }
210
211 pub(crate) fn has_active_primary_gesture(&self) -> bool {
212 self.buttons_pressed.contains(PointerButton::Primary)
213 && self.hit_path_tracker.has_path(PointerId::PRIMARY)
214 }
215
216 pub(crate) fn renderer_warmup_due(&self, app: &ShellApp) -> bool {
217 self.renderer.needs_frame_warmup() && !app.composition.runtime().has_frame_callbacks()
218 }
219
220 pub(crate) fn needs_redraw_in_context(&self, app: &ShellApp) -> bool {
221 app.has_stale_work_in_context()
222 || app.composition.should_recompose()
223 || self.is_dirty
224 || self.scene_dirty
225 || self.renderer_warmup_due(app)
226 }
227
228 pub(crate) fn compute_frame_schedule(
229 &self,
230 app: &ShellApp,
231 surfaces_dirty: bool,
232 ) -> FrameSchedule {
233 let (needs_update, needs_frame) = app.app_context.enter(|| {
234 let needs_frame = self.is_dirty
235 || self.scene_dirty
236 || app.wants_frame_in_context()
237 || self.has_active_pointer_gesture()
238 || self.renderer_warmup_due(app);
239 (app.needs_ui_update_in_context(surfaces_dirty), needs_frame)
240 });
241 FrameSchedule {
242 needs_update,
243 needs_frame,
244 next_deadline: app.next_event_time(),
245 }
246 }
247
248 pub(crate) fn invalidate_dev_overlay_text(&mut self) {
249 self.dev_overlay_text.clear();
250 self.dev_overlay_last_refresh = None;
251 self.dev_overlay_viewport = None;
252 }
253
254 pub(crate) fn dev_overlay_control_center(&self, mode: FramePacingMode) -> Option<(f32, f32)> {
255 self.dev_overlay_controls
256 .iter()
257 .find(|control| control.mode == mode)
258 .map(|control| {
259 (
260 control.bounds.x + control.bounds.width * 0.5,
261 control.bounds.y + control.bounds.height * 0.5,
262 )
263 })
264 }
265
266 pub(crate) fn layout_tree_in_context(&mut self, app: &mut ShellApp) -> Option<&LayoutTree> {
267 if self.layout_tree.is_none() {
268 let root = self.root_node(app)?;
269 let mut applier = app.composition.applier_mut();
270 match cranpose_ui::build_layout_tree_from_applier(&mut applier, root) {
271 Ok(layout_tree) => {
272 self.layout_tree = layout_tree;
273 }
274 Err(err) => {
275 log::debug!("failed to build layout snapshot: {err}");
276 return None;
277 }
278 }
279 }
280 self.layout_tree.as_ref()
281 }
282
283 pub(crate) fn top_modal(&self, app: &mut ShellApp) -> Option<NodeId> {
286 let root = self.root_node(app)?;
287 let mut applier = app.composition.applier_mut();
288 cranpose_ui::top_modal_from_applier(&mut applier, root).unwrap_or_else(|err| {
289 log::debug!("failed to find the top modal under root #{root}: {err}");
290 None
291 })
292 }
293
294 pub(crate) fn semantics_tree_in_context(
295 &mut self,
296 app: &mut ShellApp,
297 ) -> Option<&SemanticsTree> {
298 if !app.semantics_enabled && !self.inspector.state.open {
299 return None;
300 }
301 self.semantics_tree_for_input(app)
302 }
303
304 pub(crate) fn semantics_tree_for_input(
305 &mut self,
306 app: &mut ShellApp,
307 ) -> Option<&SemanticsTree> {
308 app.flush_semantics_invalidations();
309 let root = self.root_node(app)?;
310 let semantics_dirty = {
311 let mut applier = app.composition.applier_mut();
312 cranpose_ui::tree_needs_semantics(&mut *applier, root).unwrap_or_else(|err| {
313 log::debug!("failed to check semantics dirty status for root #{root}: {err}");
314 true
315 })
316 };
317 if self.semantics_tree.is_none() || self.semantics_moved || semantics_dirty {
318 let mut applier = app.composition.applier_mut();
319 match cranpose_ui::update_semantics_tree_from_applier(
320 &mut applier,
321 root,
322 &mut self.semantics_tree,
323 ) {
324 Ok(()) => {
325 #[cfg(debug_assertions)]
326 crate::semantics_check::assert_matches_rebuild(
327 &mut applier,
328 root,
329 self.semantics_tree.as_ref(),
330 );
331 self.semantics_moved = false;
332 app.semantics_snapshot_revision =
333 app.semantics_snapshot_revision.wrapping_add(1);
334 }
335 Err(err) => {
336 log::debug!("failed to build semantics snapshot: {err}");
337 return None;
338 }
339 }
340 }
341 self.semantics_tree.as_ref()
342 }
343}
344
345#[derive(Default)]
346pub(crate) struct TextInputRoutes {
347 active: Option<RootId>,
348 shown: Option<RootId>,
349 handlers: Vec<(RootId, Rc<dyn PlatformTextInputHandler>)>,
350}
351
352impl TextInputRoutes {
353 pub(crate) fn active(&self) -> RootId {
354 self.active.unwrap_or(RootId::Primary)
355 }
356
357 pub(crate) fn set_active(&mut self, root: RootId) {
358 self.active = Some(root);
359 }
360
361 pub(crate) fn set_handler(&mut self, root: RootId, handler: Rc<dyn PlatformTextInputHandler>) {
362 self.remove(root);
363 self.handlers.push((root, handler));
364 }
365
366 pub(crate) fn remove(&mut self, root: RootId) {
367 self.handlers.retain(|(id, _)| *id != root);
368 if self.shown == Some(root) {
369 self.shown = None;
370 }
371 }
372
373 fn handler(&self, root: RootId) -> Option<Rc<dyn PlatformTextInputHandler>> {
374 self.handlers
375 .iter()
376 .find(|(id, _)| *id == root)
377 .map(|(_, handler)| Rc::clone(handler))
378 }
379
380 fn take_show_target(&mut self) -> Option<Rc<dyn PlatformTextInputHandler>> {
381 let active = self.active();
382 let handler = self.handler(active);
383 if handler.is_some() {
384 self.shown = Some(active);
385 }
386 handler
387 }
388
389 fn take_hide_target(&mut self) -> Option<Rc<dyn PlatformTextInputHandler>> {
390 let target = self.shown.take().unwrap_or_else(|| self.active());
391 self.handler(target)
392 }
393}
394
395pub(crate) struct TextInputRouter {
396 pub(crate) routes: Rc<RefCell<TextInputRoutes>>,
397}
398
399impl PlatformTextInputHandler for TextInputRouter {
400 fn show_keyboard(&self) {
401 let handler = self.routes.borrow_mut().take_show_target();
402 if let Some(handler) = handler {
403 handler.show_keyboard();
404 }
405 }
406
407 fn hide_keyboard(&self) {
408 let handler = self.routes.borrow_mut().take_hide_target();
409 if let Some(handler) = handler {
410 handler.hide_keyboard();
411 }
412 }
413}
414
415#[derive(Clone, Copy)]
416pub(crate) enum SurfaceDirtyLane {
417 Layout,
418 Moved,
419 Draw,
420 LayerProperties,
421 Structural,
422}
423
424fn append_surface_dirty_node(
425 surface: &mut RootSurface<impl Renderer>,
426 surface_index: usize,
427 node: NodeId,
428 lane: SurfaceDirtyLane,
429 seen: &mut HashSet<(usize, NodeId)>,
430) {
431 match lane {
435 SurfaceDirtyLane::Layout => {
436 surface.scene_dirty = true;
437 surface.scoped_layout_scene_nodes.push(node);
438 }
439 SurfaceDirtyLane::Moved => {
440 surface.scene_dirty = true;
441 surface.scoped_moved_scene_nodes.push(node);
442 }
443 SurfaceDirtyLane::Draw => surface.scoped_draw_nodes.push(node),
444 SurfaceDirtyLane::LayerProperties => surface.scoped_layer_property_nodes.push(node),
445 SurfaceDirtyLane::Structural => {
446 if seen.insert((surface_index, node)) {
447 surface.structural_scene_nodes.push(node);
448 }
449 }
450 }
451}
452
453pub(crate) fn route_nodes_by_surface<R: Renderer>(
454 app: &mut ShellApp,
455 surfaces: &mut [RootSurface<R>],
456 nodes: impl IntoIterator<Item = NodeId>,
457 lane: SurfaceDirtyLane,
458 scratch: &mut SurfaceRoutingScratch,
459) {
460 let Some(primary_root) = app.composition.root() else {
461 scratch.attached.clear();
462 scratch.owners.clear();
463 return;
464 };
465 let mut applier = app.composition.applier_mut();
466 applier.scene_nodes_attached_to_into(
467 nodes,
468 primary_root,
469 &mut scratch.attached,
470 &mut scratch.attachment,
471 );
472 if scratch.attached.is_empty() {
473 scratch.owners.clear();
474 return;
475 }
476 scratch.seen.clear();
477 if app.app_context.window_roots().is_empty() {
478 scratch.owners.clear();
479 if let Some(index) = surfaces
480 .iter()
481 .position(|surface| surface.id == RootId::Primary)
482 {
483 let surface = &mut surfaces[index];
484 for node in scratch.attached.iter().flatten().copied() {
485 append_surface_dirty_node(surface, index, node, lane, &mut scratch.seen);
486 }
487 }
488 return;
489 }
490 cranpose_ui::nearest_window_roots_into(
491 &mut applier,
492 scratch.attached.iter().flatten().copied(),
493 &mut scratch.owners,
494 &mut scratch.window_roots,
495 );
496 for (node, owner) in scratch.attached.iter().flatten().zip(&scratch.owners) {
497 let node = *node;
498 if let Some(index) = surfaces
499 .iter()
500 .position(|surface| surface.owns_nodes_under(*owner))
501 {
502 let surface = &mut surfaces[index];
503 append_surface_dirty_node(surface, index, node, lane, &mut scratch.seen);
504 }
505 }
506}
507
508pub(crate) fn route_geometry_scene_nodes<R: Renderer>(
514 app: &mut ShellApp,
515 surfaces: &mut [RootSurface<R>],
516 laid_out: impl IntoIterator<Item = NodeId>,
517 moved: &[MovedSceneNode],
518 scratch: &mut SurfaceRoutingScratch,
519) {
520 if moved.is_empty() {
521 route_nodes_by_surface(app, surfaces, laid_out, SurfaceDirtyLane::Layout, scratch);
522 return;
523 }
524 scratch.laid_out_starts.clear();
525 scratch.laid_out_starts.extend(
526 surfaces
527 .iter()
528 .map(|surface| surface.scoped_layout_scene_nodes.len()),
529 );
530 route_nodes_by_surface(app, surfaces, laid_out, SurfaceDirtyLane::Layout, scratch);
531 let mut routed = std::mem::take(&mut scratch.moved);
532 routed.clear();
533 {
534 let applier = app.composition.applier_mut();
535 scratch.laid_out.reset(&applier);
536 for (surface, &start) in surfaces.iter().zip(&scratch.laid_out_starts) {
537 for &node in &surface.scoped_layout_scene_nodes[start..] {
538 scratch.laid_out.set(&applier, node, 1);
539 }
540 }
541 let marks = &scratch.laid_out;
542 let window_roots = !app.app_context.window_roots().is_empty();
543 let mut last_placer = None;
546 for &moved in moved {
547 let placed_again = moved.placer.is_some_and(|placer| match last_placer {
548 Some((last, laid_out)) if last == placer => laid_out,
549 _ => {
550 let laid_out = marks.get(&applier, placer) != 0;
551 last_placer = Some((placer, laid_out));
552 laid_out
553 }
554 });
555 if !placed_again || window_roots && is_window_root(&applier, moved.node) {
556 routed.push(moved.node);
557 }
558 }
559 }
560 route_nodes_by_surface(
561 app,
562 surfaces,
563 routed.iter().copied(),
564 SurfaceDirtyLane::Moved,
565 scratch,
566 );
567 scratch.moved = routed;
568}
569
570fn is_window_root(applier: &MemoryApplier, node: NodeId) -> bool {
572 applier.get_ref(node).is_ok_and(|node| {
573 let node: &dyn std::any::Any = node;
574 node.downcast_ref::<LayoutNode>()
575 .is_some_and(LayoutNode::is_window_root)
576 })
577}
578
579pub struct SurfaceMut<'a, R: Renderer> {
586 pub(crate) shell: &'a mut AppShell<R>,
587 pub(crate) index: usize,
588}
589
590impl<'a, R> SurfaceMut<'a, R>
591where
592 R: Renderer,
593 R::Error: Debug,
594{
595 pub(crate) fn new(shell: &'a mut AppShell<R>, index: usize) -> Self {
596 Self { shell, index }
597 }
598
599 pub fn shell(&mut self) -> &mut AppShell<R> {
602 self.shell
603 }
604
605 pub(crate) fn shell_app(&mut self) -> &mut ShellApp {
606 &mut self.shell.app
607 }
608
609 pub(crate) fn shell_app_ref(&self) -> &ShellApp {
610 &self.shell.app
611 }
612
613 pub(crate) fn surface(&self) -> &RootSurface<R> {
614 &self.shell.surfaces[self.index]
615 }
616
617 pub(crate) fn surface_mut(&mut self) -> &mut RootSurface<R> {
618 &mut self.shell.surfaces[self.index]
619 }
620
621 pub(crate) fn parts(&mut self) -> (&mut ShellApp, &mut RootSurface<R>) {
622 let shell = &mut *self.shell;
623 (&mut shell.app, &mut shell.surfaces[self.index])
624 }
625
626 pub fn id(&self) -> RootId {
628 self.surface().id
629 }
630
631 pub fn root(&self) -> Option<NodeId> {
633 let surface = self.surface();
634 surface.root_node(self.shell_app_ref())
635 }
636
637 pub fn renderer(&mut self) -> &mut R {
639 &mut self.surface_mut().renderer
640 }
641
642 pub fn with_renderer<T>(&mut self, block: impl FnOnce(&mut R) -> T) -> T {
645 let (app, surface) = self.parts();
646 app.app_context.enter(|| block(&mut surface.renderer))
647 }
648
649 pub fn scene(&self) -> &R::Scene {
651 self.surface().renderer.scene()
652 }
653
654 pub fn set_viewport(&mut self, width: f32, height: f32) {
662 self.surface_mut().viewport = (width, height);
663 match self.id() {
664 RootId::Primary => self.shell_app().request_forced_layout_pass(),
665 RootId::Window(_) => {
666 if let Some(root) = self.root() {
667 self.shell_app_ref()
668 .app_context
669 .enter(|| cranpose_ui::schedule_measure_repass(root));
670 }
671 self.shell_app().request_layout_pass();
672 }
673 }
674 let root = self.root();
675 self.surface_mut().invalidate_scene_root(root);
676 self.mark_dirty();
677 }
678
679 pub fn set_screen_origin(&mut self, origin: Option<Point>) {
685 self.surface_mut().screen_origin = origin;
686 }
687
688 pub fn screen_origin(&self) -> Option<Point> {
691 self.surface().screen_origin
692 }
693
694 pub fn viewport_size(&self) -> (f32, f32) {
696 self.surface().viewport
697 }
698
699 pub fn set_buffer_size(&mut self, width: u32, height: u32) {
701 self.surface_mut().buffer_size = (width, height);
702 }
703
704 pub fn buffer_size(&self) -> (u32, u32) {
706 self.surface().buffer_size
707 }
708
709 pub fn mark_dirty(&mut self) {
711 self.surface_mut().is_dirty = true;
712 }
713
714 pub fn needs_redraw(&self) -> bool {
718 self.shell_app_ref()
719 .app_context
720 .enter(|| self.surface().needs_redraw_in_context(self.shell_app_ref()))
721 }
722
723 pub fn has_active_pointer_gesture(&self) -> bool {
726 self.surface().has_active_pointer_gesture()
727 }
728
729 pub fn has_active_primary_gesture(&self) -> bool {
734 self.surface().has_active_primary_gesture()
735 }
736
737 pub fn last_update_result(&self) -> FrameUpdateResult {
740 self.surface().last_update
741 }
742
743 pub fn frame_owed(&self) -> bool {
748 self.surface().frame_owed
749 }
750
751 pub fn take_frame_owed(&mut self) -> bool {
753 std::mem::take(&mut self.surface_mut().frame_owed)
754 }
755
756 fn compute_frame_schedule(&self) -> FrameSchedule {
757 self.surface()
758 .compute_frame_schedule(self.shell_app_ref(), self.shell.any_surface_dirty())
759 }
760
761 pub fn frame_schedule(&self) -> FrameSchedule {
764 let schedule = self.compute_frame_schedule();
765 self.surface().frame_scheduler.record(schedule);
766 schedule
767 }
768
769 pub fn schedule_platform_frame<D>(&self, driver: &D) -> FrameSchedule
771 where
772 D: PlatformFrameDriver + ?Sized,
773 {
774 let schedule = self.compute_frame_schedule();
775 self.surface().frame_scheduler.schedule(schedule, driver);
776 schedule
777 }
778
779 pub fn frame_scheduler_snapshot(&self) -> FrameSchedule {
781 self.surface().frame_scheduler.snapshot()
782 }
783
784 pub fn set_frame_rate_preference(&mut self, preference: FrameRatePreference) {
787 self.surface_mut().frame_rate_preference = preference;
788 }
789
790 pub fn frame_rate_preference(&self) -> FrameRatePreference {
792 self.surface().frame_rate_preference
793 }
794
795 pub fn dev_overlay_control_center(&self, mode: FramePacingMode) -> Option<(f32, f32)> {
798 self.surface().dev_overlay_control_center(mode)
799 }
800
801 pub(crate) fn dev_overlay_press(&mut self, x: f32, y: f32) -> bool {
802 if !self.shell_app_ref().dev_options.frame_pacing_controls {
803 return false;
804 }
805 let Some(mode) = self
806 .surface()
807 .dev_overlay_controls
808 .iter()
809 .find(|control| control.bounds.contains(x, y))
810 .map(|control| control.mode)
811 else {
812 return false;
813 };
814 self.shell().set_frame_pacing_mode(mode);
815 true
816 }
817
818 pub fn with_layout_tree<T>(&mut self, block: impl FnOnce(Option<&LayoutTree>) -> T) -> T {
820 let (app, surface) = self.parts();
821 let _app_context = app.app_context.enter_scope();
822 block(surface.layout_tree_in_context(app))
823 }
824
825 pub fn with_semantics_tree<T>(&mut self, block: impl FnOnce(Option<&SemanticsTree>) -> T) -> T {
828 let (app, surface) = self.parts();
829 let _app_context = app.app_context.enter_scope();
830 block(surface.semantics_tree_in_context(app))
831 }
832
833 pub fn take_pointer_icon_change(&self) -> Option<PointerIcon> {
836 self.surface().pointer_icon.take_change()
837 }
838
839 pub fn refresh_pointer_icon(&self) {
842 self.surface().pointer_icon.refresh();
843 }
844
845 pub fn set_platform_text_input(&mut self, handler: Rc<dyn PlatformTextInputHandler>) {
850 let id = self.id();
851 let app = self.shell_app();
852 app.text_input_routes.borrow_mut().set_handler(id, handler);
853 app.install_text_input_router();
854 }
855
856 pub fn activate(&mut self) {
859 let id = self.id();
860 self.shell_app()
861 .text_input_routes
862 .borrow_mut()
863 .set_active(id);
864 }
865}