Skip to main content

cranpose_app_shell/
surface.rs

1//! Root surfaces: what the shell keeps per window.
2//!
3//! The composition is one tree. Each window the app shows is a root in that
4//! tree: the composition root for the primary window, a node carrying
5//! `Modifier::window_root` for every other. A [`RootSurface`] is the shell's
6//! bookkeeping for one such root: the renderer that draws it, the viewport it
7//! draws into, the pointer that hovers it, the gesture it tracks, the
8//! snapshots a test or a platform reads for it, and the dirt that says whether
9//! it owes the display a frame. A [`SurfaceMut`] borrows one surface together
10//! with the app, which is how a platform delivers a window's events and reads
11//! a window's frame.
12
13use 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, SurfaceRoutingScratch,
27    hit_path_tracker::{HitPathTracker, PointerId},
28};
29
30/// Names a root surface of the app: the primary window, or a window root
31/// declared with [`Modifier::window_root`](cranpose_ui::Modifier::window_root)
32/// by the id given there.
33#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
34pub enum RootId {
35    /// The composition root, drawn in the app's first window.
36    Primary,
37    /// The node carrying `Modifier::window_root` with this id.
38    Window(u64),
39}
40
41/// The shell's bookkeeping for one root.
42///
43/// The primary surface draws the composition root. A window surface draws the
44/// window root node registered under its id, and draws nothing while that
45/// node is not in the tree. Everything here is what an OS owns per window:
46/// the framebuffer, the viewport, the pointer, the cursor image, the soft
47/// keyboard, the frame the window is asked for.
48pub 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    /// A layout pass ran since `semantics_tree` was brought up to date, so
58    /// its bounds may be out of date though no node marked its semantics.
59    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    /// Nodes the layout pass only moved: the scene moves their layers.
65    pub(crate) scoped_moved_scene_nodes: Vec<NodeId>,
66    pub(crate) scoped_draw_nodes: Vec<NodeId>,
67    pub(crate) scoped_layer_property_nodes: Vec<NodeId>,
68    pub(crate) structural_scene_nodes: Vec<NodeId>,
69    pub(crate) partial_scene_nodes: Vec<NodeId>,
70    pub(crate) retained_visual_nodes: HashSet<NodeId>,
71    pub(crate) is_dirty: bool,
72    pub(crate) buttons_pressed: PointerButtons,
73    pub(crate) pointer_source: PointerSource,
74    pub(crate) hit_path_tracker: HitPathTracker,
75    pub(crate) hovered_nodes: Vec<NodeId>,
76    pub(crate) on_rotary_scroll: Option<Rc<dyn Fn(RotaryScrollEvent) -> bool>>,
77    pub(crate) dev_overlay_controls: Vec<DevOverlayControl>,
78    pub(crate) inspector: crate::inspector::DeveloperInspector,
79    pub(crate) dev_overlay_text: String,
80    pub(crate) dev_overlay_last_refresh: Option<Instant>,
81    pub(crate) dev_overlay_viewport: Option<Size>,
82    pub(crate) frame_scheduler: FrameScheduler,
83    pub(crate) pointer_icon: PointerIconState,
84    pub(crate) last_update: FrameUpdateResult,
85    pub(crate) frame_owed: bool,
86    pub(crate) screen_origin: Option<Point>,
87}
88
89impl<R: Renderer> RootSurface<R> {
90    pub(crate) fn new(
91        id: RootId,
92        renderer: R,
93        buffer_size: (u32, u32),
94        viewport: (f32, f32),
95    ) -> Self {
96        Self {
97            id,
98            root: None,
99            renderer,
100            cursor: (0.0, 0.0),
101            viewport,
102            buffer_size,
103            layout_tree: None,
104            semantics_tree: None,
105            semantics_moved: false,
106            modal_focus: Vec::new(),
107            frame_rate_preference: FrameRatePreference::default(),
108            scene_dirty: true,
109            scoped_layout_scene_nodes: Vec::new(),
110            scoped_moved_scene_nodes: Vec::new(),
111            scoped_draw_nodes: Vec::new(),
112            scoped_layer_property_nodes: Vec::new(),
113            structural_scene_nodes: Vec::new(),
114            partial_scene_nodes: Vec::new(),
115            retained_visual_nodes: HashSet::default(),
116            is_dirty: true,
117            buttons_pressed: PointerButtons::NONE,
118            pointer_source: PointerSource::Unknown,
119            hit_path_tracker: HitPathTracker::new(),
120            hovered_nodes: Vec::new(),
121            on_rotary_scroll: None,
122            dev_overlay_controls: Vec::new(),
123            inspector: crate::inspector::DeveloperInspector::default(),
124            dev_overlay_text: String::new(),
125            dev_overlay_last_refresh: None,
126            dev_overlay_viewport: None,
127            frame_scheduler: FrameScheduler::default(),
128            pointer_icon: PointerIconState::new(),
129            last_update: FrameUpdateResult::default(),
130            frame_owed: false,
131            screen_origin: None,
132        }
133    }
134
135    pub(crate) fn root_node(&self, app: &ShellApp) -> Option<NodeId> {
136        match self.id {
137            RootId::Primary => app.composition.root(),
138            RootId::Window(_) => self.root,
139        }
140    }
141
142    pub(crate) fn owns_nodes_under(&self, window_root: Option<NodeId>) -> bool {
143        match self.id {
144            RootId::Primary => window_root.is_none(),
145            RootId::Window(_) => self.root.is_some() && self.root == window_root,
146        }
147    }
148
149    pub(crate) fn viewport_size(&self) -> Size {
150        Size {
151            width: self.viewport.0,
152            height: self.viewport.1,
153        }
154    }
155
156    pub(crate) fn screen_point_inside(&self, screen: Point) -> Option<Point> {
157        let origin = self.screen_origin?;
158        let local = Point {
159            x: screen.x - origin.x,
160            y: screen.y - origin.y,
161        };
162        (local.x >= 0.0
163            && local.y >= 0.0
164            && local.x <= self.viewport.0
165            && local.y <= self.viewport.1)
166            .then_some(local)
167    }
168
169    pub(crate) fn set_root(&mut self, root: Option<NodeId>) {
170        if self.root == root {
171            return;
172        }
173        self.root = root;
174        self.forget_snapshots();
175        self.invalidate_scene_root(root);
176        self.retained_visual_nodes.clear();
177        self.hit_path_tracker.clear();
178        self.hovered_nodes.clear();
179        self.buttons_pressed = PointerButtons::NONE;
180        self.is_dirty = true;
181    }
182
183    pub(crate) fn invalidate_scene_root(&mut self, root: Option<NodeId>) {
184        self.scoped_layout_scene_nodes.clear();
185        self.scoped_layout_scene_nodes.extend(root);
186        self.scoped_moved_scene_nodes.clear();
187        self.scoped_draw_nodes.clear();
188        self.scoped_layer_property_nodes.clear();
189        self.structural_scene_nodes.clear();
190        self.partial_scene_nodes.clear();
191        self.scene_dirty = true;
192    }
193
194    /// Drops the layout snapshot and marks the semantics tree for an update,
195    /// which keeps what did not change.
196    pub(crate) fn forget_snapshots(&mut self) {
197        self.layout_tree = None;
198        self.semantics_moved = true;
199    }
200
201    pub(crate) fn has_active_pointer_gesture(&self) -> bool {
202        self.buttons_pressed != PointerButtons::NONE
203            && self.hit_path_tracker.has_path(PointerId::PRIMARY)
204    }
205
206    pub(crate) fn renderer_warmup_due(&self, app: &ShellApp) -> bool {
207        self.renderer.needs_frame_warmup() && !app.runtime.runtime_handle().has_frame_callbacks()
208    }
209
210    pub(crate) fn needs_redraw_in_context(&self, app: &ShellApp) -> bool {
211        app.has_stale_work_in_context()
212            || app.composition.should_recompose()
213            || self.is_dirty
214            || self.scene_dirty
215            || self.renderer_warmup_due(app)
216    }
217
218    pub(crate) fn compute_frame_schedule(
219        &self,
220        app: &ShellApp,
221        surfaces_dirty: bool,
222    ) -> FrameSchedule {
223        let app_context = Rc::clone(&app.app_context);
224        let (needs_update, needs_frame) = app_context.enter(|| {
225            let needs_frame = self.is_dirty
226                || self.scene_dirty
227                || app.wants_frame_in_context()
228                || self.has_active_pointer_gesture()
229                || self.renderer_warmup_due(app);
230            (app.needs_ui_update_in_context(surfaces_dirty), needs_frame)
231        });
232        FrameSchedule {
233            needs_update,
234            needs_frame,
235            next_deadline: app.next_event_time(),
236        }
237    }
238
239    pub(crate) fn invalidate_dev_overlay_text(&mut self) {
240        self.dev_overlay_text.clear();
241        self.dev_overlay_last_refresh = None;
242        self.dev_overlay_viewport = None;
243    }
244
245    pub(crate) fn dev_overlay_control_center(&self, mode: FramePacingMode) -> Option<(f32, f32)> {
246        self.dev_overlay_controls
247            .iter()
248            .find(|control| control.mode == mode)
249            .map(|control| {
250                (
251                    control.bounds.x + control.bounds.width * 0.5,
252                    control.bounds.y + control.bounds.height * 0.5,
253                )
254            })
255    }
256
257    pub(crate) fn layout_tree_in_context(&mut self, app: &mut ShellApp) -> Option<&LayoutTree> {
258        if self.layout_tree.is_none() {
259            let root = self.root_node(app)?;
260            let mut applier = app.composition.applier_mut();
261            match cranpose_ui::build_layout_tree_from_applier(&mut applier, root) {
262                Ok(layout_tree) => {
263                    self.layout_tree = layout_tree;
264                }
265                Err(err) => {
266                    log::debug!("failed to build layout snapshot: {err}");
267                    return None;
268                }
269            }
270        }
271        self.layout_tree.as_ref()
272    }
273
274    /// The node an open modal takes this surface over with, the root its
275    /// semantics tree would have, found without building the tree.
276    pub(crate) fn top_modal(&self, app: &mut ShellApp) -> Option<NodeId> {
277        let root = self.root_node(app)?;
278        let mut applier = app.composition.applier_mut();
279        cranpose_ui::top_modal_from_applier(&mut applier, root).unwrap_or_else(|err| {
280            log::debug!("failed to find the top modal under root #{root}: {err}");
281            None
282        })
283    }
284
285    pub(crate) fn semantics_tree_in_context(
286        &mut self,
287        app: &mut ShellApp,
288    ) -> Option<&SemanticsTree> {
289        if !app.semantics_enabled && !self.inspector.state.open {
290            return None;
291        }
292        self.semantics_tree_for_input(app)
293    }
294
295    pub(crate) fn semantics_tree_for_input(
296        &mut self,
297        app: &mut ShellApp,
298    ) -> Option<&SemanticsTree> {
299        app.flush_semantics_invalidations();
300        let root = self.root_node(app)?;
301        let semantics_dirty = {
302            let mut applier = app.composition.applier_mut();
303            cranpose_ui::tree_needs_semantics(&mut *applier, root).unwrap_or_else(|err| {
304                log::debug!("failed to check semantics dirty status for root #{root}: {err}");
305                true
306            })
307        };
308        if self.semantics_tree.is_none() || self.semantics_moved || semantics_dirty {
309            let mut applier = app.composition.applier_mut();
310            match cranpose_ui::update_semantics_tree_from_applier(
311                &mut applier,
312                root,
313                &mut self.semantics_tree,
314            ) {
315                Ok(()) => {
316                    #[cfg(debug_assertions)]
317                    crate::semantics_check::assert_matches_rebuild(
318                        &mut applier,
319                        root,
320                        self.semantics_tree.as_ref(),
321                    );
322                    self.semantics_moved = false;
323                    app.semantics_snapshot_revision =
324                        app.semantics_snapshot_revision.wrapping_add(1);
325                }
326                Err(err) => {
327                    log::debug!("failed to build semantics snapshot: {err}");
328                    return None;
329                }
330            }
331        }
332        self.semantics_tree.as_ref()
333    }
334}
335
336#[derive(Default)]
337pub(crate) struct TextInputRoutes {
338    active: Option<RootId>,
339    shown: Option<RootId>,
340    handlers: Vec<(RootId, Rc<dyn PlatformTextInputHandler>)>,
341}
342
343impl TextInputRoutes {
344    pub(crate) fn active(&self) -> RootId {
345        self.active.unwrap_or(RootId::Primary)
346    }
347
348    pub(crate) fn set_active(&mut self, root: RootId) {
349        self.active = Some(root);
350    }
351
352    pub(crate) fn set_handler(&mut self, root: RootId, handler: Rc<dyn PlatformTextInputHandler>) {
353        self.remove(root);
354        self.handlers.push((root, handler));
355    }
356
357    pub(crate) fn remove(&mut self, root: RootId) {
358        self.handlers.retain(|(id, _)| *id != root);
359        if self.shown == Some(root) {
360            self.shown = None;
361        }
362    }
363
364    fn handler(&self, root: RootId) -> Option<Rc<dyn PlatformTextInputHandler>> {
365        self.handlers
366            .iter()
367            .find(|(id, _)| *id == root)
368            .map(|(_, handler)| Rc::clone(handler))
369    }
370
371    fn take_show_target(&mut self) -> Option<Rc<dyn PlatformTextInputHandler>> {
372        let active = self.active();
373        let handler = self.handler(active);
374        if handler.is_some() {
375            self.shown = Some(active);
376        }
377        handler
378    }
379
380    fn take_hide_target(&mut self) -> Option<Rc<dyn PlatformTextInputHandler>> {
381        let target = self.shown.take().unwrap_or_else(|| self.active());
382        self.handler(target)
383    }
384}
385
386pub(crate) struct TextInputRouter {
387    pub(crate) routes: Rc<RefCell<TextInputRoutes>>,
388}
389
390impl PlatformTextInputHandler for TextInputRouter {
391    fn show_keyboard(&self) {
392        let handler = self.routes.borrow_mut().take_show_target();
393        if let Some(handler) = handler {
394            handler.show_keyboard();
395        }
396    }
397
398    fn hide_keyboard(&self) {
399        let handler = self.routes.borrow_mut().take_hide_target();
400        if let Some(handler) = handler {
401            handler.hide_keyboard();
402        }
403    }
404}
405
406#[derive(Clone, Copy)]
407pub(crate) enum SurfaceDirtyLane {
408    Layout,
409    Moved,
410    Draw,
411    LayerProperties,
412    Structural,
413}
414
415fn append_surface_dirty_node(
416    surface: &mut RootSurface<impl Renderer>,
417    surface_index: usize,
418    node: NodeId,
419    lane: SurfaceDirtyLane,
420    seen: &mut HashSet<(usize, NodeId)>,
421) {
422    match lane {
423        SurfaceDirtyLane::Layout => {
424            surface.scene_dirty = true;
425            if seen.insert((surface_index, node)) {
426                surface.scoped_layout_scene_nodes.push(node);
427            }
428        }
429        SurfaceDirtyLane::Moved => {
430            surface.scene_dirty = true;
431            if seen.insert((surface_index, node)) {
432                surface.scoped_moved_scene_nodes.push(node);
433            }
434        }
435        SurfaceDirtyLane::Draw => surface.scoped_draw_nodes.push(node),
436        SurfaceDirtyLane::LayerProperties => surface.scoped_layer_property_nodes.push(node),
437        SurfaceDirtyLane::Structural => {
438            if seen.insert((surface_index, node)) {
439                surface.structural_scene_nodes.push(node);
440            }
441        }
442    }
443}
444
445/// The nodes `lane` already queued on `surface`, which routing must not
446/// queue twice.
447fn queued_scene_nodes<R: Renderer>(surface: &RootSurface<R>, lane: SurfaceDirtyLane) -> &[NodeId] {
448    match lane {
449        SurfaceDirtyLane::Layout => &surface.scoped_layout_scene_nodes,
450        SurfaceDirtyLane::Moved => &surface.scoped_moved_scene_nodes,
451        SurfaceDirtyLane::Draw
452        | SurfaceDirtyLane::LayerProperties
453        | SurfaceDirtyLane::Structural => &[],
454    }
455}
456
457pub(crate) fn route_nodes_by_surface<R: Renderer>(
458    app: &mut ShellApp,
459    surfaces: &mut [RootSurface<R>],
460    nodes: impl IntoIterator<Item = NodeId>,
461    lane: SurfaceDirtyLane,
462    scratch: &mut SurfaceRoutingScratch,
463) {
464    let Some(primary_root) = app.composition.root() else {
465        scratch.attached.clear();
466        scratch.owners.clear();
467        return;
468    };
469    let mut applier = app.composition.applier_mut();
470    applier.scene_nodes_attached_to_into(
471        nodes,
472        primary_root,
473        &mut scratch.attached,
474        &mut scratch.attachment,
475    );
476    if scratch.attached.is_empty() {
477        scratch.owners.clear();
478        return;
479    }
480    scratch.seen.clear();
481    for (index, surface) in surfaces.iter().enumerate() {
482        scratch.seen.extend(
483            queued_scene_nodes(surface, lane)
484                .iter()
485                .map(|node| (index, *node)),
486        );
487    }
488    if app.app_context.window_roots().is_empty() {
489        scratch.owners.clear();
490        if let Some(index) = surfaces
491            .iter()
492            .position(|surface| surface.id == RootId::Primary)
493        {
494            let surface = &mut surfaces[index];
495            for node in scratch.attached.iter().flatten().copied() {
496                append_surface_dirty_node(surface, index, node, lane, &mut scratch.seen);
497            }
498        }
499        return;
500    }
501    cranpose_ui::nearest_window_roots_into(
502        &mut applier,
503        scratch.attached.iter().flatten().copied(),
504        &mut scratch.owners,
505        &mut scratch.window_roots,
506    );
507    for (node, owner) in scratch.attached.iter().flatten().zip(&scratch.owners) {
508        let node = *node;
509        if let Some(index) = surfaces
510            .iter()
511            .position(|surface| surface.owns_nodes_under(*owner))
512        {
513            let surface = &mut surfaces[index];
514            append_surface_dirty_node(surface, index, node, lane, &mut scratch.seen);
515        }
516    }
517}
518
519/// One surface borrowed together with its shell: the handle a platform
520/// delivers a window's events through and reads a window's frame from.
521///
522/// [`AppShell::surface`] hands one out per root. Every method of [`AppShell`]
523/// that names no root acts on the primary surface through the same code, so
524/// a single-window platform never sees this type.
525pub struct SurfaceMut<'a, R: Renderer> {
526    pub(crate) shell: &'a mut AppShell<R>,
527    pub(crate) index: usize,
528}
529
530impl<'a, R> SurfaceMut<'a, R>
531where
532    R: Renderer,
533    R::Error: Debug,
534{
535    pub(crate) fn new(shell: &'a mut AppShell<R>, index: usize) -> Self {
536        Self { shell, index }
537    }
538
539    /// The whole app, for what a window's event needs beyond its surface:
540    /// the clipboard, the dev options, a debug report.
541    pub fn shell(&mut self) -> &mut AppShell<R> {
542        self.shell
543    }
544
545    pub(crate) fn shell_app(&mut self) -> &mut ShellApp {
546        &mut self.shell.app
547    }
548
549    pub(crate) fn shell_app_ref(&self) -> &ShellApp {
550        &self.shell.app
551    }
552
553    pub(crate) fn surface(&self) -> &RootSurface<R> {
554        &self.shell.surfaces[self.index]
555    }
556
557    pub(crate) fn surface_mut(&mut self) -> &mut RootSurface<R> {
558        &mut self.shell.surfaces[self.index]
559    }
560
561    pub(crate) fn parts(&mut self) -> (&mut ShellApp, &mut RootSurface<R>) {
562        let shell = &mut *self.shell;
563        (&mut shell.app, &mut shell.surfaces[self.index])
564    }
565
566    /// Which root this surface draws.
567    pub fn id(&self) -> RootId {
568        self.surface().id
569    }
570
571    /// The node this surface draws from, when it has one.
572    pub fn root(&self) -> Option<NodeId> {
573        let surface = self.surface();
574        surface.root_node(self.shell_app_ref())
575    }
576
577    /// The renderer that draws this surface.
578    pub fn renderer(&mut self) -> &mut R {
579        &mut self.surface_mut().renderer
580    }
581
582    /// Runs a presentation operation with this surface's app context active.
583    /// Software text rasterization uses the context's font measurement services.
584    pub fn with_renderer<T>(&mut self, block: impl FnOnce(&mut R) -> T) -> T {
585        let (app, surface) = self.parts();
586        app.app_context.enter(|| block(&mut surface.renderer))
587    }
588
589    /// The scene this surface last built.
590    pub fn scene(&self) -> &R::Scene {
591        self.surface().renderer.scene()
592    }
593
594    /// Sets the logical size this surface lays out and draws into.
595    ///
596    /// The primary surface's viewport is the composition root's constraints.
597    /// A window surface's viewport is what its renderer draws into; the
598    /// window root lays out to the size its descriptor reports, which the
599    /// platform keeps equal to this. The next update lays out and renders;
600    /// [`AppShell::set_viewport`] additionally runs that frame at once.
601    pub fn set_viewport(&mut self, width: f32, height: f32) {
602        self.surface_mut().viewport = (width, height);
603        match self.id() {
604            RootId::Primary => self.shell_app().request_forced_layout_pass(),
605            RootId::Window(_) => {
606                if let Some(root) = self.root() {
607                    let app_context = Rc::clone(&self.shell_app_ref().app_context);
608                    app_context.enter(|| cranpose_ui::schedule_measure_repass(root));
609                }
610                self.shell_app().request_layout_pass();
611            }
612        }
613        let root = self.root();
614        self.surface_mut().invalidate_scene_root(root);
615        self.mark_dirty();
616    }
617
618    /// Tells the shell where the window drawing this surface sits on the
619    /// screen, in logical pixels, so pointer events can carry a
620    /// [`screen_position`](cranpose_foundation::PointerEvent::screen_position).
621    /// A platform sets it when the window moves and before it delivers a
622    /// pointer sample; `None` says the platform does not know.
623    pub fn set_screen_origin(&mut self, origin: Option<Point>) {
624        self.surface_mut().screen_origin = origin;
625    }
626
627    /// Where the window drawing this surface sits on the screen, as the
628    /// platform last said.
629    pub fn screen_origin(&self) -> Option<Point> {
630        self.surface().screen_origin
631    }
632
633    /// The logical size this surface draws into.
634    pub fn viewport_size(&self) -> (f32, f32) {
635        self.surface().viewport
636    }
637
638    /// Sets the physical size of this surface's framebuffer.
639    pub fn set_buffer_size(&mut self, width: u32, height: u32) {
640        self.surface_mut().buffer_size = (width, height);
641    }
642
643    /// The physical size of this surface's framebuffer.
644    pub fn buffer_size(&self) -> (u32, u32) {
645        self.surface().buffer_size
646    }
647
648    /// Marks this surface as needing a redraw.
649    pub fn mark_dirty(&mut self) {
650        self.surface_mut().is_dirty = true;
651    }
652
653    /// Whether this surface owes the display a frame: stale pixels, or a
654    /// renderer that has not warmed its swapchain yet. See
655    /// [`AppShell::needs_redraw`].
656    pub fn needs_redraw(&self) -> bool {
657        let app_context = Rc::clone(&self.shell_app_ref().app_context);
658        app_context.enter(|| self.surface().needs_redraw_in_context(self.shell_app_ref()))
659    }
660
661    /// Whether a primary-button gesture that started on this surface is
662    /// still in progress.
663    pub fn has_active_pointer_gesture(&self) -> bool {
664        self.surface().has_active_pointer_gesture()
665    }
666
667    /// What the update and frame produced for this surface the last time
668    /// the app updated.
669    pub fn last_update_result(&self) -> FrameUpdateResult {
670        self.surface().last_update
671    }
672
673    /// Whether an update since the platform last presented this surface
674    /// changed its pixels. An update runs for the whole app, so the update a
675    /// platform ran for one window may have drawn another; this is how the
676    /// other window learns it has a frame to show.
677    pub fn frame_owed(&self) -> bool {
678        self.surface().frame_owed
679    }
680
681    /// [`Self::frame_owed`], cleared: the platform is about to present.
682    pub fn take_frame_owed(&mut self) -> bool {
683        std::mem::take(&mut self.surface_mut().frame_owed)
684    }
685
686    fn compute_frame_schedule(&self) -> FrameSchedule {
687        self.surface()
688            .compute_frame_schedule(self.shell_app_ref(), self.shell.any_surface_dirty())
689    }
690
691    /// The frame this surface asks its platform for, recorded for
692    /// [`Self::frame_scheduler_snapshot`].
693    pub fn frame_schedule(&self) -> FrameSchedule {
694        let schedule = self.compute_frame_schedule();
695        self.surface().frame_scheduler.record(schedule);
696        schedule
697    }
698
699    /// Computes this surface's frame schedule and applies it to `driver`.
700    pub fn schedule_platform_frame<D>(&self, driver: &D) -> FrameSchedule
701    where
702        D: PlatformFrameDriver + ?Sized,
703    {
704        let schedule = self.compute_frame_schedule();
705        self.surface().frame_scheduler.schedule(schedule, driver);
706        schedule
707    }
708
709    /// The schedule this surface last recorded.
710    pub fn frame_scheduler_snapshot(&self) -> FrameSchedule {
711        self.surface().frame_scheduler.snapshot()
712    }
713
714    /// Sets how the platform should vote the display's frame rate for the
715    /// window showing this surface. See [`AppShell::set_frame_rate_preference`].
716    pub fn set_frame_rate_preference(&mut self, preference: FrameRatePreference) {
717        self.surface_mut().frame_rate_preference = preference;
718    }
719
720    /// This surface's display frame-rate preference.
721    pub fn frame_rate_preference(&self) -> FrameRatePreference {
722        self.surface().frame_rate_preference
723    }
724
725    /// Where this surface's dev overlay draws the control for `mode`, in
726    /// logical pixels. See [`AppShell::dev_overlay_control_center`].
727    pub fn dev_overlay_control_center(&self, mode: FramePacingMode) -> Option<(f32, f32)> {
728        self.surface().dev_overlay_control_center(mode)
729    }
730
731    pub(crate) fn dev_overlay_press(&mut self, x: f32, y: f32) -> bool {
732        if !self.shell_app_ref().dev_options.frame_pacing_controls {
733            return false;
734        }
735        let Some(mode) = self
736            .surface()
737            .dev_overlay_controls
738            .iter()
739            .find(|control| control.bounds.contains(x, y))
740            .map(|control| control.mode)
741        else {
742            return false;
743        };
744        self.shell().set_frame_pacing_mode(mode);
745        true
746    }
747
748    /// Runs `block` with this surface's layout snapshot, built on demand.
749    pub fn with_layout_tree<T>(&mut self, block: impl FnOnce(Option<&LayoutTree>) -> T) -> T {
750        let (app, surface) = self.parts();
751        let app_context = Rc::clone(&app.app_context);
752        app_context.enter(|| block(surface.layout_tree_in_context(app)))
753    }
754
755    /// Runs `block` with this surface's semantics snapshot, built on demand;
756    /// `None` while semantics are disabled.
757    pub fn with_semantics_tree<T>(&mut self, block: impl FnOnce(Option<&SemanticsTree>) -> T) -> T {
758        let (app, surface) = self.parts();
759        let app_context = Rc::clone(&app.app_context);
760        app_context.enter(|| block(surface.semantics_tree_in_context(app)))
761    }
762
763    /// The pointer icon the platform has not applied to this surface's
764    /// window yet. See [`AppShell::take_pointer_icon_change`].
765    pub fn take_pointer_icon_change(&self) -> Option<PointerIcon> {
766        self.surface().pointer_icon.take_change()
767    }
768
769    /// Offers this surface's pointer icon to the platform again. See
770    /// [`AppShell::refresh_pointer_icon`].
771    pub fn refresh_pointer_icon(&self) {
772        self.surface().pointer_icon.refresh();
773    }
774
775    /// Installs the platform text input for the window showing this
776    /// surface. Keyboard requests reach the handler of the surface the
777    /// platform last called active, and a hide reaches the handler that
778    /// showed. See [`AppShell::set_platform_text_input`].
779    pub fn set_platform_text_input(&mut self, handler: Rc<dyn PlatformTextInputHandler>) {
780        let id = self.id();
781        let app = self.shell_app();
782        app.text_input_routes.borrow_mut().set_handler(id, handler);
783        app.install_text_input_router();
784    }
785
786    /// Makes this the surface the platform considers focused: the one the
787    /// soft keyboard belongs to. Pointer presses do this on their own.
788    pub fn activate(&mut self) {
789        let id = self.id();
790        self.shell_app()
791            .text_input_routes
792            .borrow_mut()
793            .set_active(id);
794    }
795}