Skip to main content

incular_runtime/
frame.rs

1use crate::application_types::{
2    ApplicationLifecycle, LifecycleTransition, RuntimeErrorReport, WindowError,
3};
4#[cfg(feature = "devtools")]
5use crate::environment::DEV_TASK_COMPLETION;
6#[cfg(feature = "devtools")]
7use crate::environment::ReactiveRootId;
8use crate::environment::{
9    BuildScope, BuildScopeGuard, InitialBuildDependencies, ReactiveQueue, environment_change_mask,
10    install_focus_scope_watch,
11};
12use crate::profiling;
13use crate::profiling::FrameTimings;
14use crate::tasks::{
15    self, RuntimeDiagnostics, RuntimeSpawner, RuntimeWake, Task, TaskFailure, TaskHandle,
16    TaskScope, TokioHandle, UiDispatcher,
17};
18use crate::undo::UndoHistoryController;
19use crate::window_commands::{WindowHandle, WindowOpener};
20use crate::window_state::WindowManager;
21use incular_config::{Constraints, RuntimeEnvironment};
22use incular_core::{
23    Code, ImeEvent, InputEvent, KeyboardEvent, Modifiers, Offset, PRIMARY_POINTER_BUTTON,
24    PointerPhase, PointerSampleMetadata, Rect,
25};
26use incular_platform::{
27    Clipboard, ClipboardCapabilities, ClipboardError, ClipboardWriteReport, DataTransfer,
28    ExternalDragEvent, ExternalDragResponse, MemoryClipboard, PlatformEvent, PlatformLifecycle,
29    TextInputAction, TextInputClientId, TextInputCommand, TextInputConfiguration, TextInputState,
30    TextInputType, TransferReadRequest, WindowId, WindowMetrics, WindowOperation, WindowOptions,
31};
32use incular_rendering::DisplayList;
33use incular_semantics::{SemanticAction, SemanticNodeId};
34#[cfg(feature = "devtools")]
35use incular_widgets::internal::InvalidationCause;
36use incular_widgets::internal::{
37    ActionId, Diagnostics, ElementId, MouseCursor, PlatformMenuBinding, PointerDeviceKind,
38    PointerEvent, RawPointerEvent, TextEditingController, TextRange, TextSelection, TreeError,
39    WidgetTree, WindowInteraction,
40};
41use incular_widgets::{TextInputActionHint, TextInputTypeHint, Widget};
42use std::{
43    cell::{Cell, RefCell},
44    collections::{HashMap, VecDeque},
45    rc::{Rc, Weak},
46    sync::atomic::{AtomicU64, Ordering},
47    time::Instant,
48};
49
50/// Sequence numbering runtime driver domains so outlet ownership errors
51/// can name the runtimes involved.
52static RUNTIME_SEQUENCE: AtomicU64 = AtomicU64::new(1);
53
54#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
55pub struct FrameStats {
56    pub updated_elements: usize,
57    pub rebuilt_elements: u64,
58    pub laid_out_render_objects: u64,
59    pub repainted_render_objects: u64,
60    pub composited: u64,
61    pub active_animations: u64,
62    pub display_list_commands: usize,
63    pub requested_another_frame: bool,
64    /// Monotonic per-phase CPU durations. Phases never nest.
65    pub timings: FrameTimings,
66}
67#[derive(Clone, Copy, Debug, PartialEq, Eq)]
68pub struct EventTarget {
69    pub element: ElementId,
70    pub action: Option<ActionId>,
71}
72/// Debug-facing focus state without exposing native event-loop details.
73#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
74pub struct FocusDiagnostics {
75    pub focused_element: Option<ElementId>,
76    pub text_pointer_capture: Option<ElementId>,
77}
78#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
79pub struct EditingDiagnostics {
80    pub key_down_received: u64,
81    pub key_up_received: u64,
82    pub backspace_commands: u64,
83    pub delete_commands: u64,
84    pub text_commits: u64,
85    pub ime_events: u64,
86}
87
88/// Open IME composition. Native IME events carry no session identity,
89/// so the field and controller that received the latest Preedit are
90/// the only routing authority for the matching Commit/End. The
91/// controller handle is retained so cleanup always addresses the
92/// composition's own editor, never the currently focused one.
93///
94/// Preedit → update → Commit → End: Commit retires the owner; End is ignored.
95/// Preedit → End: cancellation; the owner's preedit clears, no text changes.
96/// Preedit → focus moves: focus change cancels first; later events start fresh.
97/// Preedit → controller replaced, field unmounted, or field no longer
98///   editable: the owner stops validating and is cancelled, never committed.
99/// Commit without Preedit: ownerless, applies to the focused field directly.
100struct ImeCompositionOwner {
101    field: ElementId,
102    controller: TextEditingController,
103}
104#[derive(Clone, Copy, PartialEq, Eq)]
105enum InitialFocus {
106    AwaitingLayout,
107    Settled,
108}
109
110/// Retained application execution, input dispatch and frame coordination.
111pub struct Runtime {
112    pub(crate) tree: WidgetTree,
113    /// Input-dispatch time accumulated since the last frame consumed it.
114    pub(crate) pending_event_processing_us: u32,
115    pub(crate) pending: HashMap<ElementId, Widget>,
116    pub(crate) order: VecDeque<ElementId>,
117    pub(crate) reactive: Rc<RefCell<ReactiveQueue>>,
118    pub(crate) builders: HashMap<ElementId, Box<dyn FnMut() -> Widget>>,
119    pub(crate) handlers: HashMap<ActionId, Rc<dyn Fn()>>,
120    /// Set when handlers were bound or elements unmounted, so the next
121    /// prune scans the tree for live actions; otherwise none can be stale.
122    handlers_stale: bool,
123    pub(crate) legacy_action_handler: Option<Box<dyn FnMut(ActionId)>>,
124    pub(crate) hovered_button: Option<ElementId>,
125    pub(crate) pressed_button: Option<ElementId>,
126    /// The contact currently allowed to drive single-contact controls. Other
127    /// contacts still reach retained gesture regions for scale/pan input.
128    pub(crate) legacy_pointer: Option<(u64, u64)>,
129    pub(crate) last_pointer: Offset,
130    pub(crate) pointer_inside: bool,
131    pub(crate) pointer_position_known: bool,
132    pub(crate) focused: Option<ElementId>,
133    initial_focus: InitialFocus,
134    pub(crate) captured_text_field: Option<ElementId>,
135    pub(crate) captured_selectable_text: Option<ElementId>,
136    ime_composition: Option<ImeCompositionOwner>,
137    pub(crate) text_histories: HashMap<ElementId, UndoHistoryController>,
138    pub(crate) text_input_commands: VecDeque<TextInputCommand>,
139    pub(crate) text_input_client: Option<TextInputClientId>,
140    pub(crate) text_input_configuration: Option<TextInputConfiguration>,
141    pub(crate) text_input_state: Option<TextInputState>,
142    pub(crate) text_input_caret_rect: Option<Rect>,
143    pub(crate) clipboard: Box<dyn Clipboard>,
144    pub(crate) editing_diagnostics: EditingDiagnostics,
145    pub(crate) frame_requested: bool,
146    pub(crate) scheduler: Rc<RefCell<tasks::TaskScheduler>>,
147    pub(crate) environment: Rc<RefCell<RuntimeEnvironment>>,
148    pub(crate) lifecycle: ApplicationLifecycle,
149    pub(crate) lifecycle_observers: Vec<Rc<dyn Fn(LifecycleTransition)>>,
150    pub(crate) error_observers: Vec<Rc<dyn Fn(RuntimeErrorReport)>>,
151    pub(crate) environment_generation: u64,
152    pub(crate) environment_dependencies: Rc<Cell<u16>>,
153    pub(crate) application_root: Option<ElementId>,
154    pub(crate) owner_scopes: HashMap<ElementId, TaskScope>,
155    pub(crate) window_id: Option<WindowId>,
156    pub(crate) window_scope: TaskScope,
157    pub(crate) window_manager: Option<WindowManager>,
158    /// Stable driver identity shared with outlet ownership claims: the id
159    /// names this runtime in [`OutletError`](super::route_outlet::OutletError)s,
160    /// while the shared ownership doubles as a liveness token — dropping
161    /// the runtime releases every claim without a global registry.
162    driver_domain: Rc<u64>,
163}
164
165pub(crate) fn diagnostic_input_trigger(event: &InputEvent) -> String {
166    match event {
167        InputEvent::Pointer { phase, position } => {
168            format!("pointer({phase:?}, {:.1}, {:.1})", position.x, position.y)
169        }
170        InputEvent::PointerWithId {
171            pointer,
172            phase,
173            position,
174        } => format!(
175            "pointer({pointer}, {phase:?}, {:.1}, {:.1})",
176            position.x, position.y
177        ),
178        InputEvent::PointerWithMetadata {
179            pointer,
180            device,
181            kind,
182            buttons,
183            button,
184            sample: _,
185            phase,
186            position,
187        } => format!(
188            "pointer({pointer}, device={device}, {kind:?}, buttons={buttons:#x}, changed={button:?}, {phase:?}, {:.1}, {:.1})",
189            position.x, position.y
190        ),
191        InputEvent::TrackpadGesture(event) => format!(
192            "trackpad(device={}, kind={:?}, phase={:?})",
193            event.device(),
194            event.kind(),
195            event.phase()
196        ),
197        InputEvent::Scroll { delta } => format!("scroll({:.1}, {:.1})", delta.x, delta.y),
198        InputEvent::Key(event) => format!("key({:?}, {:?})", event.state, event.code),
199        InputEvent::Text(text) => format!("text_input(chars={})", text.chars().count()),
200        InputEvent::Ime(ImeEvent::Preedit { text, .. }) => {
201            format!("ime_preedit(chars={})", text.chars().count())
202        }
203        InputEvent::Ime(ImeEvent::Commit(text)) => {
204            format!("ime_commit(chars={})", text.chars().count())
205        }
206        InputEvent::Ime(ImeEvent::End) => "ime_end".to_owned(),
207        InputEvent::WindowResized { size, scale_factor } => format!(
208            "window_resize({:.1}x{:.1}, scale={scale_factor:.2})",
209            size.width, size.height
210        ),
211    }
212}
213
214impl Runtime {
215    pub fn new(root: Widget) -> Result<Self, TreeError> {
216        Self::with_scheduler(root, tasks::TaskScheduler::new())
217    }
218    fn with_scheduler(
219        root: Widget,
220        scheduler: Rc<RefCell<tasks::TaskScheduler>>,
221    ) -> Result<Self, TreeError> {
222        let window_scope = tasks::TaskScheduler::spawner(&scheduler).scope();
223        Self::with_window(root, scheduler, None, window_scope, None)
224    }
225
226    pub(crate) fn with_window(
227        root: Widget,
228        scheduler: Rc<RefCell<tasks::TaskScheduler>>,
229        window_id: Option<WindowId>,
230        window_scope: TaskScope,
231        window_manager: Option<WindowManager>,
232    ) -> Result<Self, TreeError> {
233        Self::with_window_and_reactive(
234            root,
235            scheduler,
236            window_id,
237            window_scope,
238            window_manager,
239            ReactiveQueue::new(),
240        )
241    }
242    pub(crate) fn with_window_and_reactive(
243        mut root: Widget,
244        scheduler: Rc<RefCell<tasks::TaskScheduler>>,
245        window_id: Option<WindowId>,
246        window_scope: TaskScope,
247        window_manager: Option<WindowManager>,
248        reactive: Rc<RefCell<ReactiveQueue>>,
249    ) -> Result<Self, TreeError> {
250        let mut tree = WidgetTree::new();
251        let mut handlers = HashMap::new();
252        root.bind_callbacks(&mut |callback| {
253            let action = tree.allocate_action();
254            handlers.insert(action, callback);
255            action
256        });
257        tree.mount(root)?;
258        for (action, handler) in tree.take_pending_handlers() {
259            handlers.insert(action, handler);
260        }
261        let mut runtime = Self {
262            pending_event_processing_us: 0,
263            tree,
264            pending: HashMap::new(),
265            order: VecDeque::new(),
266            reactive,
267            builders: HashMap::new(),
268            handlers,
269            handlers_stale: false,
270            legacy_action_handler: None,
271            hovered_button: None,
272            pressed_button: None,
273            legacy_pointer: None,
274            last_pointer: Offset::ZERO,
275            pointer_inside: false,
276            pointer_position_known: false,
277            focused: None,
278            initial_focus: InitialFocus::AwaitingLayout,
279            captured_text_field: None,
280            captured_selectable_text: None,
281            ime_composition: None,
282            text_histories: HashMap::new(),
283            text_input_commands: VecDeque::new(),
284            text_input_client: None,
285            text_input_configuration: None,
286            text_input_state: None,
287            text_input_caret_rect: None,
288            clipboard: Box::new(MemoryClipboard::default()),
289            editing_diagnostics: EditingDiagnostics::default(),
290            frame_requested: true,
291            scheduler,
292            environment: Rc::new(RefCell::new(RuntimeEnvironment::default())),
293            lifecycle: ApplicationLifecycle::Starting,
294            lifecycle_observers: Vec::new(),
295            error_observers: Vec::new(),
296            environment_generation: 0,
297            environment_dependencies: Rc::new(Cell::new(0)),
298            application_root: None,
299            owner_scopes: HashMap::new(),
300            window_id,
301            window_scope,
302            window_manager,
303            driver_domain: Rc::new(
304                RUNTIME_SEQUENCE
305                    .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |id| id.checked_add(1))
306                    .expect("runtime driver identity space exhausted"),
307            ),
308        };
309        // Focus nodes live outside the retained tree so they may be shared by
310        // rebuildable descriptors. Resolve the first mounted autofocus listener
311        // once the element IDs exist, then keep runtime and node focus mirrored
312        // for subsequent keyboard traversal.
313        if let Some(autofocus) = runtime.tree.autofocus_element() {
314            runtime.set_focus(Some(autofocus));
315        }
316        Ok(runtime)
317    }
318    /// Stable identity of this runtime's driver domain, for outlet
319    /// ownership diagnostics: pairs with the outlet id to name both sides
320    /// of a driver conflict.
321    #[must_use]
322    pub fn id(&self) -> u64 {
323        *self.driver_domain
324    }
325
326    /// Liveness token for outlet driver claims. Dies with the runtime, so
327    /// teardown releases outlet ownership without any registry.
328    pub(crate) fn driver_token(&self) -> Weak<u64> {
329        Rc::downgrade(&self.driver_domain)
330    }
331
332    /// Starts work owned by this retained root. Component code should prefer
333    /// the build context's spawn API so completion lifetime follows its owner.
334    #[must_use]
335    pub fn spawner(&self) -> RuntimeSpawner {
336        self.window_id.map_or_else(
337            || tasks::TaskScheduler::spawner(&self.scheduler),
338            |window_id| tasks::TaskScheduler::spawner_for(&self.scheduler, window_id),
339        )
340    }
341    /// Sends a `Send` callback from any worker thread to the UI owner. The
342    /// callback is never run concurrently with retained-tree mutation.
343    #[must_use]
344    pub fn dispatcher(&self) -> UiDispatcher {
345        self.scheduler.borrow().dispatcher_for(self.window_id)
346    }
347    /// Returns the application's Tokio handle for advanced libraries. Direct
348    /// Tokio tasks have Tokio/application lifetime and are not component-owned.
349    #[must_use]
350    pub fn tokio_handle(&self) -> TokioHandle {
351        self.scheduler.borrow().tokio_handle()
352    }
353    pub fn set_wake_handler(&mut self, wake: std::sync::Arc<dyn RuntimeWake>) {
354        self.scheduler.borrow_mut().set_wake(wake);
355    }
356
357    /// Returns the owning normalized window when this runtime is managed by a
358    /// multi-window application. Standalone runtimes remain windowless.
359    #[must_use]
360    pub const fn window_id(&self) -> Option<WindowId> {
361        self.window_id
362    }
363
364    /// Returns default application-menu bridges retained by this window root.
365    /// Native desktop adapters use these after native window creation; normal
366    /// application code should use `PlatformMenuBar` instead.
367    #[doc(hidden)]
368    #[must_use]
369    pub fn platform_menu_bindings(&self) -> Vec<PlatformMenuBinding> {
370        self.tree.platform_menu_bindings()
371    }
372
373    /// Marks only this retained root for a future presentation.
374    pub fn request_frame(&mut self) {
375        self.frame_requested = true;
376    }
377
378    /// Drains native text-input commands produced since the last platform
379    /// event-loop turn. A desktop runner applies these to Winit; mobile hosts
380    /// can translate the same data to their platform text services.
381    pub fn take_text_input_commands(&mut self) -> Vec<TextInputCommand> {
382        self.text_input_commands.drain(..).collect()
383    }
384
385    /// Opens a second retained root through the owning application. This can
386    /// be called from a build or a UI completion; native creation is queued
387    /// for the desktop event-loop callback.
388    pub fn open_window(
389        &mut self,
390        options: WindowOptions,
391        root: Widget,
392    ) -> Result<WindowHandle, WindowError> {
393        self.window_manager
394            .as_ref()
395            .ok_or(WindowError::ApplicationStopped)?
396            .open_window(options, root)
397    }
398
399    /// Returns a retained UI capability that callbacks may use to open a
400    /// window after this build has returned.
401    #[must_use]
402    pub fn window_opener(&self) -> Option<WindowOpener> {
403        self.window_manager
404            .as_ref()
405            .cloned()
406            .map(|manager| WindowOpener { manager })
407    }
408
409    /// Returns a command handle for this retained root's owning native window.
410    #[must_use]
411    pub fn window_handle(&self) -> Option<WindowHandle> {
412        let id = self.window_id?;
413        self.window_manager.as_ref()?.handle(id)
414    }
415
416    /// Returns the scope cancelled as soon as this window closes. Child
417    /// component scopes inherit from it automatically.
418    #[must_use]
419    pub fn window_task_scope(&self) -> TaskScope {
420        self.window_scope.clone()
421    }
422
423    /// Starts bounded blocking work. Its completion callback runs later on the
424    /// UI thread, so it may safely update a local `Signal` or schedule a build.
425    pub fn spawn_blocking<T>(
426        &self,
427        work: impl FnOnce() -> T + Send + 'static,
428        complete: impl FnOnce(Result<T, TaskFailure>, &mut Runtime) + 'static,
429    ) -> TaskHandle
430    where
431        T: Send + 'static,
432    {
433        self.spawner()
434            .spawn_blocking_in(&self.window_scope, work, complete)
435    }
436    /// Starts Tokio work cancelled with this window. Use a `BuildContext` for
437    /// work whose completion must be tied to a mounted declarative owner.
438    pub fn spawn<F, T>(&self, future: F) -> Task<T>
439    where
440        F: std::future::Future<Output = T> + Send + 'static,
441        T: Send + 'static,
442    {
443        self.spawner().spawn_in(&self.window_scope, future)
444    }
445    /// Starts window-scoped Tokio work with a UI-thread completion.
446    pub fn spawn_into<F, T>(
447        &self,
448        future: F,
449        complete: impl FnOnce(Result<T, TaskFailure>, &mut Runtime) + 'static,
450    ) -> TaskHandle
451    where
452        F: std::future::Future<Output = T> + Send + 'static,
453        T: Send + 'static,
454    {
455        self.spawner()
456            .spawn_into_in(&self.window_scope, future, complete)
457    }
458    /// Processes a bounded batch of UI-relevant Tokio completion messages and
459    /// dispatch callbacks. Native runners call this only from their user-event
460    /// path; it never polls Tokio or implies a redraw.
461    pub fn process_runtime_work_at(&mut self, _now: Instant) {
462        let scheduler = self.scheduler.clone();
463        // Take message-owned work while borrowing the local scheduler, then
464        // release that borrow before any UI callback runs. A completion may
465        // safely start its next Tokio task without re-entering `RefCell`.
466        let work = scheduler.borrow_mut().take_turn();
467        for work in work {
468            self.process_ui_work(&scheduler, work);
469        }
470        if self.reactive.borrow().has_work() || !self.pending.is_empty() {
471            self.frame_requested = true;
472        }
473    }
474
475    pub(crate) fn process_ui_work(
476        &mut self,
477        scheduler: &Rc<RefCell<tasks::TaskScheduler>>,
478        work: tasks::UiWork,
479    ) {
480        let mut failures = Vec::new();
481        match work {
482            tasks::UiWork::Dispatch { callback, .. } => {
483                if scheduler.borrow().accepting()
484                    && std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| callback(self)))
485                        .is_err()
486                {
487                    failures.push(TaskFailure::Panicked);
488                }
489            }
490            tasks::UiWork::Task(pending) => {
491                scheduler.borrow().detach_scope(&pending.control);
492                let stale_owner = pending.control.owner_is_stale(self);
493                if !scheduler.borrow().accepting() || pending.control.is_cancelled() || stale_owner
494                {
495                    (pending.discard)();
496                    scheduler
497                        .borrow_mut()
498                        .record_discard(pending.blocking, stale_owner);
499                } else {
500                    #[cfg(feature = "devtools")]
501                    let previous_task_context = DEV_TASK_COMPLETION.with(|context| {
502                        let previous = context.get();
503                        context.set(true);
504                        previous
505                    });
506                    let failure = (pending.finish)(self);
507                    #[cfg(feature = "devtools")]
508                    DEV_TASK_COMPLETION.with(|context| context.set(previous_task_context));
509                    if let Some(failure) = scheduler
510                        .borrow_mut()
511                        .record_completion(pending.blocking, failure)
512                    {
513                        failures.push(failure);
514                    }
515                }
516            }
517        }
518        for failure in failures {
519            for observer in &self.error_observers {
520                observer(RuntimeErrorReport {
521                    failure: failure.clone(),
522                });
523            }
524        }
525        if self.reactive.borrow().has_work() || !self.pending.is_empty() {
526            self.frame_requested = true;
527        }
528    }
529    pub fn process_runtime_work(&mut self) {
530        self.process_runtime_work_at(Instant::now());
531    }
532    #[must_use]
533    pub fn runtime_diagnostics(&self) -> RuntimeDiagnostics {
534        self.scheduler.borrow().diagnostics()
535    }
536    /// Compact ownership-facing state for framework diagnostics. Tokio's own
537    /// tracing ecosystem remains the source of executor-level telemetry.
538    #[must_use]
539    pub fn debug_dump(&self) -> String {
540        let diagnostics = self.runtime_diagnostics();
541        let (application_tasks, owner_tasks) = self.scheduler.borrow().active_ownership();
542        format!(
543            "Lifecycle: {:?}\nTokio tasks tracked: {}\n  application: {}\n  owner-scoped: {}\nPending UI messages: {}\nEnvironment generation: {}",
544            self.lifecycle,
545            diagnostics.active_tracked_tasks,
546            application_tasks,
547            owner_tasks,
548            diagnostics
549                .ui_messages_enqueued
550                .saturating_sub(diagnostics.ui_messages_processed),
551            self.environment_generation,
552        )
553    }
554    #[must_use]
555    pub fn environment(&self) -> RuntimeEnvironment {
556        self.environment.borrow().clone()
557    }
558    #[must_use]
559    pub const fn lifecycle(&self) -> ApplicationLifecycle {
560        self.lifecycle
561    }
562    #[must_use]
563    pub const fn environment_generation(&self) -> u64 {
564        self.environment_generation
565    }
566    /// Replaces normalized runtime values from a platform event. Rebuild is
567    /// requested only when the declarative root read a changed value.
568    pub fn set_environment(&mut self, environment: RuntimeEnvironment) -> bool {
569        let environment = environment.normalized();
570        let previous = self.environment.borrow().clone();
571        if previous == environment {
572            return false;
573        }
574        let changed = environment_change_mask(&previous, &environment);
575        *self.environment.borrow_mut() = environment.clone();
576        // WidgetTree owns renderer-neutral runtime geometry and direction used
577        // by retained policies such as SafeArea and transient placement. Keep
578        // that snapshot synchronized even when no declarative environment
579        // dependency requires rebuilding the application root.
580        let retained_environment_dirty = self.tree.set_environment(environment);
581        self.environment_generation = self
582            .environment_generation
583            .checked_add(1)
584            .expect("runtime environment generation exhausted");
585        if retained_environment_dirty {
586            self.frame_requested = true;
587        }
588        if self.environment_dependencies.get() & changed != 0
589            && let Some(root) = self.application_root
590        {
591            #[cfg(feature = "devtools")]
592            self.tree
593                .note_invalidation(root, InvalidationCause::EnvironmentChanged);
594            let _ = self.rebuild_from_builder(root);
595            self.frame_requested = true;
596        }
597        true
598    }
599    pub fn transition_lifecycle(&mut self, lifecycle: ApplicationLifecycle) -> bool {
600        if self.lifecycle == lifecycle {
601            return false;
602        }
603        let transition = LifecycleTransition {
604            previous: self.lifecycle,
605            current: lifecycle,
606        };
607        self.lifecycle = lifecycle;
608        for observer in &self.lifecycle_observers {
609            observer(transition);
610        }
611        if matches!(
612            lifecycle,
613            ApplicationLifecycle::Stopping | ApplicationLifecycle::Terminated
614        ) {
615            self.reactive.borrow_mut().clear();
616            self.scheduler.borrow_mut().shutdown();
617        }
618        true
619    }
620    pub fn observe_lifecycle(&mut self, observer: impl Fn(LifecycleTransition) + 'static) {
621        self.lifecycle_observers.push(Rc::new(observer));
622    }
623    pub fn observe_runtime_error(&mut self, observer: impl Fn(RuntimeErrorReport) + 'static) {
624        self.error_observers.push(Rc::new(observer));
625    }
626    /// Cancels runtime-owned work and prevents later queued callbacks from
627    /// touching retained state. Native runners should call this before their
628    /// renderer/window teardown.
629    pub fn shutdown(&mut self) {
630        if self.focused.is_some() {
631            self.set_focus(None);
632        } else {
633            self.cancel_ime_composition();
634            self.sync_text_input_client();
635        }
636        self.tree.cancel_all_gesture_streams();
637        self.captured_text_field = None;
638        self.captured_selectable_text = None;
639        self.hovered_button = None;
640        self.pressed_button = None;
641        self.legacy_pointer = None;
642        self.text_histories.clear();
643        self.tree.release_edit_subscriptions();
644        let _ = self.transition_lifecycle(ApplicationLifecycle::Stopping);
645        for (_, scope) in self.owner_scopes.drain() {
646            scope.cancel();
647        }
648        self.scheduler.borrow_mut().shutdown();
649        self.lifecycle = ApplicationLifecycle::Terminated;
650    }
651
652    /// Releases window-owned subscriptions and callback state without
653    /// stopping the shared application scheduler. `Application` calls this
654    /// before dropping a closed retained root.
655    pub(crate) fn dispose_window(&mut self) {
656        if self.focused.is_some() {
657            self.set_focus(None);
658        } else {
659            self.cancel_ime_composition();
660            self.sync_text_input_client();
661        }
662        self.tree.cancel_all_gesture_streams();
663        self.tree.release_edit_subscriptions();
664        self.legacy_pointer = None;
665        self.window_scope.cancel();
666        for (_, scope) in self.owner_scopes.drain() {
667            scope.cancel();
668        }
669        self.reactive.borrow_mut().clear();
670        self.builders.clear();
671        self.pending.clear();
672        self.order.clear();
673        self.handlers.clear();
674        self.legacy_action_handler = None;
675        self.text_histories.clear();
676        self.text_input_commands.clear();
677        self.text_input_client = None;
678        self.text_input_configuration = None;
679        self.text_input_state = None;
680        self.text_input_caret_rect = None;
681        self.captured_text_field = None;
682        self.captured_selectable_text = None;
683        self.hovered_button = None;
684        self.pressed_button = None;
685    }
686    #[must_use]
687    pub fn tree(&self) -> &WidgetTree {
688        &self.tree
689    }
690
691    #[cfg(feature = "devtools")]
692    pub(crate) fn devtools_reactive_root(&self) -> ReactiveRootId {
693        self.reactive.borrow().root
694    }
695    #[must_use]
696    pub fn tree_mut(&mut self) -> &mut WidgetTree {
697        &mut self.tree
698    }
699
700    /// Returns the effective capture-protection policy contributed by the
701    /// mounted retained tree. The window/application boundary is responsible
702    /// for forwarding changes to its native adapter.
703    #[must_use]
704    pub fn content_sensitivity(&self) -> incular_config::ContentSensitivity {
705        self.tree.content_sensitivity()
706    }
707
708    /// Visible transient portals after the latest layout. Desktop adapters use
709    /// this retained snapshot to bind popup presentation to the owning window
710    /// without coupling controls to a native event loop.
711    #[must_use]
712    pub fn transient_surfaces(&self) -> Vec<incular_widgets::TransientSurfaceSnapshot> {
713        self.tree.transient_surfaces()
714    }
715
716    /// Updates the parent-local logical rectangle that confirmed native
717    /// transient hosts may use for collision placement.
718    pub fn set_native_transient_bounds(&mut self, bounds: Option<incular_core::Rect>) -> bool {
719        let changed = self.tree.set_native_transient_bounds(bounds);
720        self.frame_requested |= changed;
721        changed
722    }
723
724    /// Updates the set of retained transients whose native hosts have completed
725    /// negotiation. A presentation change requests a frame so placement,
726    /// semantics, hit testing, and rendering switch coordinate spaces together.
727    pub fn set_native_transient_presentations(
728        &mut self,
729        ids: impl IntoIterator<Item = incular_widgets::TransientSurfaceId>,
730    ) -> bool {
731        let changed = self.tree.set_native_transient_presentations(ids);
732        self.frame_requested |= changed;
733        changed
734    }
735
736    /// Applies a framework-level transient dismissal cause without changing
737    /// native focus ownership. Controllers receive the callback and the normal
738    /// retained rebuild path removes their popup subtree.
739    pub fn dismiss_transients(&mut self, reason: incular_widgets::TransientDismissReason) -> bool {
740        let dismissed = self.tree.dismiss_transients(reason) > 0;
741        self.frame_requested |= dismissed;
742        dismissed
743    }
744    #[cfg(feature = "devtools")]
745    pub(crate) fn devtools_edit_property(
746        &mut self,
747        id: incular_devtools_protocol::DevWidgetId,
748        name: &str,
749        value: &incular_devtools_protocol::DebugValue,
750    ) -> bool {
751        let edited = self.tree.devtools_edit_property(id, name, value);
752        self.frame_requested |= edited;
753        edited
754    }
755    /// Sets the animation-only time scale for this retained root. Runtime
756    /// messages, Tokio tasks and profiler wall-clock measurements remain on
757    /// the platform monotonic clock.
758    pub fn set_animation_time_scale(&mut self, scale: f32) {
759        self.tree.set_animation_time_scale(scale);
760    }
761    #[must_use]
762    pub fn focused_element(&self) -> Option<ElementId> {
763        self.focused
764    }
765    /// Dispatches an owned semantic action through the same control state used
766    /// by pointer and keyboard input. Native adapters queue these requests;
767    /// they never borrow mutable element storage. Eligibility reuses the
768    /// existing owners: explicit semantic callbacks always execute;
769    /// built-in Activate additionally requires the advertisement predicate
770    /// (enabled button with a bound callback), matching the pointer and
771    /// keyboard paths, which never see a disabled button past their own
772    /// enabled gate. SetText requires an editable field while SetSelection
773    /// requires only a controller — selection stays available in read-only
774    /// and disabled fields on every input path. Focus accepts only targets
775    /// the tree's own focusability owner admits (`can_request_focus`:
776    /// enabled controls, selectable text, or a focus node, unobstructed by
777    /// `exclude_focus` or a hidden IndexedStack slot), with an
778    /// already-focused valid target still reporting success; `set_focus`
779    /// remains the single focus owner for all paths.
780    pub fn dispatch_semantic_action(
781        &mut self,
782        node: SemanticNodeId,
783        action: SemanticAction,
784    ) -> bool {
785        let Some(element) = self.tree.element_for_semantic_node(node) else {
786            return false;
787        };
788        self.tree.note_semantic_action();
789        let handled = match action {
790            SemanticAction::Focus => {
791                if self.tree.can_request_focus(element) {
792                    self.set_focus(Some(element));
793                    true
794                } else {
795                    false
796                }
797            }
798            SemanticAction::Activate => {
799                if let Some(callback) = self.tree.semantic_action_callback(
800                    element,
801                    incular_semantics::SemanticActionKind::Activate,
802                ) {
803                    callback();
804                    true
805                } else if self.tree.semantic_activate_executable(element) {
806                    self.tree
807                        .action_for_element(element)
808                        .map(|action| self.activate_action(action))
809                        .unwrap_or(false)
810                } else {
811                    false
812                }
813            }
814            SemanticAction::SetText(text) => {
815                if !self.tree.text_field_is_editable(element) {
816                    false
817                } else {
818                    self.tree
819                        .text_controller(element)
820                        .map(|controller| {
821                            controller.set_text(text);
822                            true
823                        })
824                        .unwrap_or(false)
825                }
826            }
827            SemanticAction::SetSelection { base, extent } => self
828                .tree
829                .text_controller(element)
830                .map(|controller| {
831                    let length = controller.text().len();
832                    controller.set_selection(TextSelection {
833                        base: base.min(length),
834                        extent: extent.min(length),
835                    });
836                    true
837                })
838                .unwrap_or(false),
839            SemanticAction::ScrollForward => self
840                .tree
841                .semantic_action_callback(
842                    element,
843                    incular_semantics::SemanticActionKind::ScrollForward,
844                )
845                .map(|callback| {
846                    callback();
847                    true
848                })
849                .unwrap_or_else(|| self.tree.semantic_scroll(element, true)),
850            SemanticAction::ScrollBackward => self
851                .tree
852                .semantic_action_callback(
853                    element,
854                    incular_semantics::SemanticActionKind::ScrollBackward,
855                )
856                .map(|callback| {
857                    callback();
858                    true
859                })
860                .unwrap_or_else(|| self.tree.semantic_scroll(element, false)),
861            SemanticAction::Increment => self
862                .tree
863                .semantic_action_callback(element, incular_semantics::SemanticActionKind::Increment)
864                .map(|callback| {
865                    callback();
866                    true
867                })
868                .unwrap_or_else(|| self.tree.dispatch_semantic_increment(element, true)),
869            SemanticAction::Decrement => self
870                .tree
871                .semantic_action_callback(element, incular_semantics::SemanticActionKind::Decrement)
872                .map(|callback| {
873                    callback();
874                    true
875                })
876                .unwrap_or_else(|| self.tree.dispatch_semantic_increment(element, false)),
877        };
878        if handled {
879            self.frame_requested = true;
880        }
881        handled
882    }
883    #[must_use]
884    pub fn focus_diagnostics(&self) -> FocusDiagnostics {
885        FocusDiagnostics {
886            focused_element: self.focused,
887            text_pointer_capture: self.captured_text_field,
888        }
889    }
890    #[must_use]
891    pub const fn editing_diagnostics(&self) -> EditingDiagnostics {
892        self.editing_diagnostics
893    }
894    pub fn set_clipboard(&mut self, clipboard: Box<dyn Clipboard>) {
895        self.clipboard = clipboard;
896    }
897    /// Returns the capabilities of the clipboard backend currently installed
898    /// for this retained window.
899    #[must_use]
900    pub fn clipboard_capabilities(&self) -> ClipboardCapabilities {
901        self.clipboard.capabilities()
902    }
903    /// Reads rich clipboard data without collapsing it to plain text.
904    pub fn read_clipboard(
905        &mut self,
906        request: TransferReadRequest,
907    ) -> Result<DataTransfer, ClipboardError> {
908        self.clipboard.read(request)
909    }
910    /// Writes one rich clipboard transfer. The report distinguishes formats
911    /// written atomically by the backend from representations it skipped.
912    pub fn write_clipboard(
913        &mut self,
914        transfer: DataTransfer,
915    ) -> Result<ClipboardWriteReport, ClipboardError> {
916        self.clipboard.write(transfer)
917    }
918    pub fn set_clipboard_text(&mut self, text: impl Into<String>) {
919        self.clipboard.set_text(text.into());
920    }
921    /// Routes platform-owned transfer data through external drop targets. This
922    /// path is intentionally independent of the local typed gesture arena.
923    #[must_use]
924    pub fn handle_external_drag(&mut self, event: ExternalDragEvent) -> ExternalDragResponse {
925        let response = self.tree.dispatch_external_drag(event);
926        self.frame_requested = true;
927        response
928    }
929    /// Processes a normalized platform event without exposing native window or
930    /// event-loop types to the runtime API.
931    pub fn handle_platform_event(&mut self, event: PlatformEvent) -> Option<EventTarget> {
932        match event {
933            PlatformEvent::Input(input) => self.handle_input(input),
934            PlatformEvent::TextInputAction(action) => {
935                self.handle_text_input_action(action);
936                None
937            }
938            PlatformEvent::Metrics(metrics) => {
939                self.update_window_metrics(metrics);
940                None
941            }
942            PlatformEvent::Environment(environment) => {
943                self.set_environment(environment);
944                None
945            }
946            PlatformEvent::Lifecycle(lifecycle) => {
947                self.transition_lifecycle(match lifecycle {
948                    PlatformLifecycle::Active => ApplicationLifecycle::Active,
949                    PlatformLifecycle::Inactive => ApplicationLifecycle::Inactive,
950                    PlatformLifecycle::Suspended => ApplicationLifecycle::Suspended,
951                    PlatformLifecycle::Resumed => ApplicationLifecycle::Active,
952                    PlatformLifecycle::Stopping => ApplicationLifecycle::Stopping,
953                });
954                None
955            }
956            PlatformEvent::ExternalDrag(event) => {
957                let _ = self.handle_external_drag(event);
958                None
959            }
960            PlatformEvent::CloseRequested => {
961                self.shutdown();
962                None
963            }
964        }
965    }
966    /// Applies the authoritative logical/physical conversion supplied by the
967    /// platform metrics owner. Existing environment values such as locale are
968    /// preserved while size and scale change together.
969    pub fn update_window_metrics(&mut self, metrics: WindowMetrics) -> bool {
970        let mut environment = self.environment();
971        environment.viewport = metrics.logical_size();
972        environment.physical_width = metrics.physical_size.width;
973        environment.physical_height = metrics.physical_size.height;
974        environment.scale_factor = metrics.scale_factor;
975        self.set_environment(environment)
976    }
977    pub fn register_builder(
978        &mut self,
979        id: ElementId,
980        builder: impl FnMut() -> Widget + 'static,
981    ) -> Result<(), TreeError> {
982        self.register_builder_without_rebuild(id, builder)?;
983        self.rebuild_from_builder(id)
984    }
985    pub(crate) fn register_builder_without_rebuild(
986        &mut self,
987        id: ElementId,
988        builder: impl FnMut() -> Widget + 'static,
989    ) -> Result<(), TreeError> {
990        if !self.tree.element_exists(id) {
991            return Err(TreeError::MissingElement(id));
992        }
993        self.builders.insert(id, Box::new(builder));
994        Ok(())
995    }
996    pub(crate) fn install_initial_dependencies(
997        &mut self,
998        id: ElementId,
999        initial_dependencies: Rc<RefCell<InitialBuildDependencies>>,
1000    ) {
1001        let initial_dependencies = std::mem::take(&mut *initial_dependencies.borrow_mut());
1002        let queue = self.reactive.clone();
1003        let root = queue.borrow().root;
1004        let queue_weak = Rc::downgrade(&queue);
1005        for dependency in initial_dependencies.signals {
1006            let tracked =
1007                crate::reactive::subscribe_element(dependency, root, id, queue_weak.clone());
1008            queue.borrow_mut().record(id, tracked);
1009        }
1010        for scope in initial_dependencies.focus_scopes {
1011            install_focus_scope_watch(&queue, id, &scope);
1012        }
1013    }
1014    pub fn schedule_update(&mut self, id: ElementId, widget: Widget) -> Result<(), TreeError> {
1015        if !self.tree.element_exists(id) {
1016            return Err(TreeError::MissingElement(id));
1017        }
1018        #[cfg(feature = "devtools")]
1019        self.tree
1020            .note_invalidation(id, InvalidationCause::WidgetConfigurationChanged);
1021        self.tree.mark_build(id)?;
1022        let mut widget = widget;
1023        self.prepare_widget(&mut widget);
1024        if self.pending.insert(id, widget).is_none() {
1025            self.order.push_back(id);
1026        }
1027        self.frame_requested = true;
1028        Ok(())
1029    }
1030    #[must_use]
1031    pub fn frame_requested(&self) -> bool {
1032        self.frame_requested || self.reactive.borrow().has_work()
1033    }
1034    #[must_use]
1035    pub fn handle_input(&mut self, event: InputEvent) -> Option<EventTarget> {
1036        let trigger = diagnostic_input_trigger(&event);
1037        self.handle_input_with_trigger(event, trigger)
1038    }
1039
1040    pub(crate) fn handle_input_with_trigger(
1041        &mut self,
1042        event: InputEvent,
1043        trigger: impl Into<String>,
1044    ) -> Option<EventTarget> {
1045        self.tree.set_diagnostic_trigger(trigger);
1046        let started = std::time::Instant::now();
1047        let target = self.handle_input_inner(event);
1048        self.pending_event_processing_us = self
1049            .pending_event_processing_us
1050            .saturating_add(us_since_instant(started));
1051        target
1052    }
1053
1054    /// Associates subsequent frame work with a higher-level command such as a
1055    /// semantic simulation click. The trigger survives the input callback so
1056    /// deferred build/layout failures retain their original cause.
1057    pub fn set_diagnostic_trigger(&self, trigger: impl Into<String>) {
1058        self.tree.set_diagnostic_trigger(trigger);
1059    }
1060    fn handle_input_inner(&mut self, event: InputEvent) -> Option<EventTarget> {
1061        let gesture_window = self.window_id.map_or(0, |window| {
1062            (u64::from(window.index()) << 32) | u64::from(window.generation())
1063        });
1064        let (pointer, device, kind, buttons, button, sample, phase, position, metadata_rich) =
1065            match event {
1066                InputEvent::Pointer { phase, position } => (
1067                    0,
1068                    0,
1069                    PointerDeviceKind::Mouse,
1070                    0,
1071                    None,
1072                    PointerSampleMetadata::default(),
1073                    phase,
1074                    position,
1075                    false,
1076                ),
1077                InputEvent::PointerWithId {
1078                    pointer,
1079                    phase,
1080                    position,
1081                } => (
1082                    pointer,
1083                    0,
1084                    PointerDeviceKind::Mouse,
1085                    0,
1086                    None,
1087                    PointerSampleMetadata::default(),
1088                    phase,
1089                    position,
1090                    false,
1091                ),
1092                InputEvent::PointerWithMetadata {
1093                    pointer,
1094                    device,
1095                    kind,
1096                    buttons,
1097                    button,
1098                    sample,
1099                    phase,
1100                    position,
1101                } => (
1102                    pointer, device, kind, buttons, button, sample, phase, position, true,
1103                ),
1104                event => {
1105                    match event {
1106                        InputEvent::Scroll { delta } => {
1107                            // The platform normalizes wheel values to logical pixels. The
1108                            // latest pointer position selects the nearest viewport.
1109                            if self.tree.scroll_at(self.last_pointer, delta) {
1110                                self.frame_requested = true;
1111                            }
1112                        }
1113                        InputEvent::Key(key) => {
1114                            self.handle_key(key);
1115                        }
1116                        InputEvent::Text(text) => {
1117                            self.insert_text(&text);
1118                        }
1119                        InputEvent::Ime(ime) => {
1120                            self.handle_ime(ime);
1121                        }
1122                        InputEvent::TrackpadGesture(gesture) => {
1123                            // Aggregate gestures do not carry a focal position in
1124                            // Winit. Use only a genuine cursor position already
1125                            // observed for this window; never invent a touch point.
1126                            if self.pointer_position_known
1127                                && let Some(element) =
1128                                    self.tree.dispatch_trackpad_gesture_in_window(
1129                                        gesture_window,
1130                                        self.last_pointer,
1131                                        gesture,
1132                                    )
1133                            {
1134                                self.frame_requested = true;
1135                                return Some(EventTarget {
1136                                    element,
1137                                    action: None,
1138                                });
1139                            }
1140                        }
1141                        InputEvent::WindowResized { .. } => {}
1142                        InputEvent::Pointer { .. } => unreachable!(),
1143                        InputEvent::PointerWithId { .. } => unreachable!(),
1144                        InputEvent::PointerWithMetadata { .. } => unreachable!(),
1145                    }
1146                    return None;
1147                }
1148            };
1149        let hover_capable = matches!(
1150            kind,
1151            PointerDeviceKind::Mouse
1152                | PointerDeviceKind::Trackpad
1153                | PointerDeviceKind::Stylus
1154                | PointerDeviceKind::InvertedStylus
1155        );
1156        if hover_capable {
1157            match phase {
1158                PointerPhase::Enter => {
1159                    self.pointer_inside = true;
1160                    self.pointer_position_known = false;
1161                }
1162                PointerPhase::Exit => {
1163                    self.pointer_inside = false;
1164                    self.pointer_position_known = false;
1165                }
1166                _ => {
1167                    self.pointer_inside = true;
1168                    self.pointer_position_known = true;
1169                }
1170            }
1171        }
1172        if phase != PointerPhase::Enter {
1173            self.last_pointer = position;
1174        }
1175        if phase == PointerPhase::Down && self.tree.dismiss_transients_for_pointer(position) > 0 {
1176            self.frame_requested = true;
1177        }
1178        let raw_target = self.tree.dispatch_raw_pointer_in_window(
1179            gesture_window,
1180            RawPointerEvent {
1181                pointer,
1182                device,
1183                kind,
1184                buttons,
1185                button,
1186                sample,
1187                position,
1188                phase,
1189                time: Instant::now(),
1190            },
1191        );
1192        if matches!(phase, PointerPhase::Enter | PointerPhase::Exit) {
1193            if phase == PointerPhase::Exit {
1194                self.set_hover(None);
1195                if self.tree.scrollbar_pointer(PointerPhase::Exit, position) {
1196                    self.frame_requested = true;
1197                }
1198            }
1199            return None;
1200        }
1201
1202        let primary_changed = !metadata_rich || button == Some(PRIMARY_POINTER_BUTTON);
1203        let stream = (device, pointer);
1204        let active_primary = self.legacy_pointer == Some(stream);
1205        let legacy_pointer = match phase {
1206            PointerPhase::Down if primary_changed && self.legacy_pointer.is_none() => {
1207                self.legacy_pointer = Some(stream);
1208                true
1209            }
1210            PointerPhase::Down => primary_changed && active_primary,
1211            PointerPhase::Move => {
1212                active_primary || (hover_capable && self.legacy_pointer.is_none())
1213            }
1214            PointerPhase::Up => primary_changed && active_primary,
1215            PointerPhase::Cancel => active_primary,
1216            PointerPhase::Enter | PointerPhase::Exit => false,
1217        };
1218
1219        if let Some(element) = raw_target {
1220            self.frame_requested = true;
1221            self.release_legacy_pointer(device, pointer, phase, primary_changed);
1222            return Some(EventTarget {
1223                element,
1224                action: None,
1225            });
1226        }
1227        let ordinary_gesture_allowed = legacy_pointer || !metadata_rich || !hover_capable;
1228        if ordinary_gesture_allowed
1229            && let Some(element) = self.tree.dispatch_device_gesture_in_window(
1230                gesture_window,
1231                device,
1232                PointerEvent {
1233                    pointer,
1234                    position,
1235                    phase,
1236                    time: Instant::now(),
1237                },
1238            )
1239        {
1240            self.frame_requested = true;
1241            self.release_legacy_pointer(device, pointer, phase, primary_changed);
1242            return Some(EventTarget {
1243                element,
1244                action: None,
1245            });
1246        }
1247        // Legacy controls and scrollbars admit just the primary contact.
1248        // Additional contacts remain available to retained gesture regions.
1249        if !legacy_pointer {
1250            return None;
1251        }
1252        if self.tree.scrollbar_pointer(phase, position) {
1253            self.frame_requested = true;
1254            self.release_legacy_pointer(device, pointer, phase, primary_changed);
1255            return None;
1256        }
1257        let text_target = self.tree.text_field_at(position);
1258        let selectable_target = self.tree.selectable_text_at(position);
1259        let target = self
1260            .tree
1261            .hit_test(position)
1262            .and_then(|render| self.tree.element_for_render(render))
1263            .and_then(|element| self.tree.button_ancestor(element));
1264        let result = match phase {
1265            PointerPhase::Enter | PointerPhase::Exit => {
1266                unreachable!("pointer boundary handled above")
1267            }
1268            PointerPhase::Move => {
1269                if let Some(field) = self.captured_text_field {
1270                    self.tree
1271                        .text_field_set_caret(field, position, true, Instant::now());
1272                    self.frame_requested = true;
1273                }
1274                if let Some(label) = self.captured_selectable_text {
1275                    if let Some(target) = selectable_target {
1276                        if self
1277                            .tree
1278                            .selectable_text_set_selection(target, position, true)
1279                        {
1280                            self.captured_selectable_text = Some(target);
1281                            self.set_focus(Some(target));
1282                            self.frame_requested = true;
1283                        }
1284                    } else if self.tree.is_selectable_text(label) {
1285                        self.frame_requested = true;
1286                    }
1287                }
1288                self.set_hover(target.map(|(element, _)| element));
1289                target.map(|(element, action)| EventTarget { element, action })
1290            }
1291            PointerPhase::Down => {
1292                if let Some(label) = selectable_target {
1293                    self.set_focus(Some(label));
1294                    self.captured_selectable_text = Some(label);
1295                    if self
1296                        .tree
1297                        .selectable_text_set_selection(label, position, false)
1298                    {
1299                        self.frame_requested = true;
1300                        return Some(EventTarget {
1301                            element: label,
1302                            action: None,
1303                        });
1304                    }
1305                }
1306                if selectable_target.is_none()
1307                    && text_target.is_none()
1308                    && target.is_none()
1309                    && let Some((element, interaction)) = self.tree.window_interaction_at(position)
1310                    && let (Some(window_id), Some(manager)) =
1311                        (self.window_id, self.window_manager.as_ref())
1312                {
1313                    let operation = match interaction {
1314                        WindowInteraction::Move => WindowOperation::BeginMoveDrag,
1315                        WindowInteraction::Resize(direction) => {
1316                            WindowOperation::BeginResizeDrag(direction)
1317                        }
1318                    };
1319                    if manager.send_window_operation(window_id, operation) {
1320                        self.release_legacy_pointer(device, pointer, phase, primary_changed);
1321                        return Some(EventTarget {
1322                            element,
1323                            action: None,
1324                        });
1325                    }
1326                }
1327                let keep_text_field_focus = self
1328                    .focused
1329                    .is_some_and(|focused| self.tree.is_text_field(focused))
1330                    && self.tree.preserves_text_field_focus(position);
1331                self.set_focus(if keep_text_field_focus {
1332                    self.focused
1333                } else {
1334                    text_target
1335                });
1336                self.captured_text_field = text_target;
1337                if let Some(field) = text_target {
1338                    self.tree
1339                        .text_field_set_caret(field, position, false, Instant::now());
1340                    self.frame_requested = true;
1341                    return Some(EventTarget {
1342                        element: field,
1343                        action: None,
1344                    });
1345                }
1346                self.set_hover(target.map(|(element, _)| element));
1347                self.pressed_button = target.map(|(element, _)| element);
1348                if let Some(element) = self.pressed_button {
1349                    let _ = self
1350                        .tree
1351                        .set_button_interaction(element, None, Some(true), None);
1352                    self.frame_requested = true;
1353                }
1354                target.map(|(element, action)| EventTarget { element, action })
1355            }
1356            PointerPhase::Up => {
1357                self.captured_text_field = None;
1358                self.captured_selectable_text = None;
1359                let pressed = self.pressed_button.take();
1360                let valid = pressed
1361                    .zip(target)
1362                    .filter(|(pressed, (target, _))| pressed == target)
1363                    .map(|(_, target)| target);
1364                if let Some(element) = pressed {
1365                    let _ = self
1366                        .tree
1367                        .set_button_interaction(element, None, Some(false), None);
1368                }
1369                if let Some((element, action)) = valid {
1370                    if let Some(action) = action {
1371                        self.activate_action(action);
1372                    }
1373                    Some(EventTarget { element, action })
1374                } else {
1375                    None
1376                }
1377            }
1378            PointerPhase::Cancel => {
1379                self.captured_text_field = None;
1380                self.captured_selectable_text = None;
1381                if let Some(element) = self.pressed_button.take() {
1382                    let _ = self
1383                        .tree
1384                        .set_button_interaction(element, None, Some(false), None);
1385                    self.frame_requested = true;
1386                }
1387                None
1388            }
1389        };
1390        self.release_legacy_pointer(device, pointer, phase, primary_changed);
1391        result
1392    }
1393    fn release_legacy_pointer(
1394        &mut self,
1395        device: u64,
1396        pointer: u64,
1397        phase: PointerPhase,
1398        primary_changed: bool,
1399    ) {
1400        if (phase == PointerPhase::Cancel || (phase == PointerPhase::Up && primary_changed))
1401            && self.legacy_pointer == Some((device, pointer))
1402        {
1403            self.legacy_pointer = None;
1404        }
1405    }
1406
1407    /// Cursor selected by the current retained hover route. `Defer` is resolved
1408    /// to the ordinary platform cursor before crossing the native boundary.
1409    #[must_use]
1410    pub fn effective_mouse_cursor(&self) -> MouseCursor {
1411        if !self.pointer_inside || !self.pointer_position_known {
1412            return MouseCursor::Basic;
1413        }
1414        match self.tree.mouse_cursor_at(self.last_pointer) {
1415            MouseCursor::Defer => MouseCursor::Basic,
1416            cursor => cursor,
1417        }
1418    }
1419    pub(crate) fn set_focus(&mut self, next: Option<ElementId>) {
1420        // Explicit input (including clearing focus) wins over a pending initial
1421        // autofocus request, even when it leaves the focused element unchanged.
1422        self.initial_focus = InitialFocus::Settled;
1423        if self.focused == next {
1424            return;
1425        }
1426        // A focus change retires the native client behind any open
1427        // composition: without a session id later IME events are
1428        // unattributable, so the old composition ends here and later
1429        // events start fresh on the newly focused field.
1430        self.cancel_ime_composition();
1431        if let Some(next) = next
1432            && self.tree.dismiss_transients_for_focus(next) > 0
1433        {
1434            self.frame_requested = true;
1435        }
1436        if let Some(previous) = self.focused {
1437            self.tree.set_keyboard_focus(previous, false);
1438            let _ = self.tree.set_focused(previous, false, Instant::now());
1439        }
1440        self.focused = next;
1441        if let Some(current) = next {
1442            let _ = self.tree.set_focused(current, true, Instant::now());
1443            self.tree.set_keyboard_focus(current, true);
1444            if self.tree.is_text_field(current) {
1445                let _ = self.ensure_text_history(current);
1446            }
1447        }
1448        self.frame_requested = true;
1449        self.sync_text_input_client();
1450    }
1451
1452    fn reconcile_focus_and_selection_captures(&mut self) {
1453        if self.captured_text_field.is_some_and(|field| {
1454            !self.tree.is_text_field(field) || !self.tree.can_request_focus(field)
1455        }) {
1456            self.captured_text_field = None;
1457        }
1458        if self.captured_selectable_text.is_some_and(|label| {
1459            !self.tree.is_selectable_text(label) || !self.tree.can_request_focus(label)
1460        }) {
1461            self.captured_selectable_text = None;
1462        }
1463        if self
1464            .focused
1465            .is_some_and(|focused| !self.tree.can_request_focus(focused))
1466        {
1467            self.set_focus(None);
1468        }
1469        self.reconcile_button_tenure();
1470    }
1471
1472    fn reconcile_button_tenure(&mut self) {
1473        // Pressed tenure belongs to a still-pressable button element.
1474        // `button_ancestor` already filters disabled and missing elements,
1475        // so reuse it instead of restating button liveness here. A stale
1476        // press clears its visual when still mounted and never activates.
1477        if self.pressed_button.is_some_and(|pressed| {
1478            self.tree
1479                .button_ancestor(pressed)
1480                .map(|(ancestor, _)| ancestor)
1481                != Some(pressed)
1482        }) && let Some(pressed) = self.pressed_button.take()
1483        {
1484            let _ = self
1485                .tree
1486                .set_button_interaction(pressed, None, Some(false), None);
1487            self.frame_requested = true;
1488        }
1489        // Hover tenure reuses the single `set_hover` owner so exit callbacks
1490        // fire for still-mounted buttons; pruned handlers make unmounted exit
1491        // a silent visual release.
1492        if self.hovered_button.is_some_and(|hovered| {
1493            self.tree
1494                .button_ancestor(hovered)
1495                .map(|(ancestor, _)| ancestor)
1496                != Some(hovered)
1497        }) {
1498            self.set_hover(None);
1499        }
1500    }
1501
1502    fn ensure_text_history(&mut self, field: ElementId) -> Option<UndoHistoryController> {
1503        let editor = self.tree.text_controller(field)?;
1504        let replace = self
1505            .text_histories
1506            .get(&field)
1507            .is_none_or(|history| history.editor() != editor);
1508        if replace {
1509            self.text_histories
1510                .insert(field, UndoHistoryController::new(editor));
1511        }
1512        let history = self.text_histories.get(&field).cloned()?;
1513        if let Some(max_entries) = self.tree.text_field_history_max_entries(field)
1514            && history.max_entries() != max_entries
1515        {
1516            history.set_max_entries(max_entries);
1517        }
1518        Some(history)
1519    }
1520
1521    fn sync_text_input_client(&mut self) {
1522        let snapshot = self
1523            .focused
1524            .and_then(|id| self.tree.text_field_input_snapshot(id));
1525        let Some(snapshot) = snapshot else {
1526            if let Some(client) = self.text_input_client.take() {
1527                self.text_input_commands
1528                    .push_back(TextInputCommand::Hide { client });
1529                self.text_input_commands
1530                    .push_back(TextInputCommand::Clear { client });
1531            }
1532            self.text_input_configuration = None;
1533            self.text_input_state = None;
1534            self.text_input_caret_rect = None;
1535            return;
1536        };
1537
1538        let client = TextInputClientId::new(snapshot.client_id);
1539        let input_type = match snapshot.input_type {
1540            TextInputTypeHint::Text if snapshot.multiline => TextInputType::Multiline,
1541            TextInputTypeHint::Text => TextInputType::Text,
1542            TextInputTypeHint::Multiline => TextInputType::Multiline,
1543            TextInputTypeHint::Number => TextInputType::Number,
1544            TextInputTypeHint::Phone => TextInputType::Phone,
1545            TextInputTypeHint::Email => TextInputType::Email,
1546            TextInputTypeHint::Url => TextInputType::Url,
1547            TextInputTypeHint::Password => TextInputType::Password,
1548        };
1549        let action = match snapshot.input_action {
1550            TextInputActionHint::Unspecified => {
1551                if snapshot.multiline {
1552                    TextInputAction::Newline
1553                } else {
1554                    TextInputAction::Done
1555                }
1556            }
1557            TextInputActionHint::None => TextInputAction::None,
1558            TextInputActionHint::Done => TextInputAction::Done,
1559            TextInputActionHint::Go => TextInputAction::Go,
1560            TextInputActionHint::Search => TextInputAction::Search,
1561            TextInputActionHint::Send => TextInputAction::Send,
1562            TextInputActionHint::Next => TextInputAction::Next,
1563            TextInputActionHint::Previous => TextInputAction::Previous,
1564            TextInputActionHint::Newline => TextInputAction::Newline,
1565        };
1566        let configuration = TextInputConfiguration {
1567            client,
1568            input_type: if snapshot.obscure_text {
1569                TextInputType::Password
1570            } else {
1571                input_type
1572            },
1573            action,
1574            multiline: snapshot.multiline,
1575            enabled: snapshot.enabled,
1576            read_only: snapshot.read_only,
1577            obscure_text: snapshot.obscure_text,
1578        };
1579        let state = TextInputState {
1580            text: snapshot.text,
1581            selection_start: snapshot.selection.base,
1582            selection_end: snapshot.selection.extent,
1583            composing: snapshot.composing.map(|range| (range.start, range.end)),
1584        };
1585        let client_changed = self.text_input_client != Some(client);
1586        let configuration_changed = self.text_input_configuration.as_ref() != Some(&configuration);
1587        if client_changed || configuration_changed {
1588            self.text_input_commands
1589                .push_back(TextInputCommand::SetClient {
1590                    configuration: configuration.clone(),
1591                    state: state.clone(),
1592                    caret_rect: snapshot.caret_rect,
1593                });
1594        } else if self.text_input_state.as_ref() != Some(&state) {
1595            self.text_input_commands
1596                .push_back(TextInputCommand::Update {
1597                    client,
1598                    state: state.clone(),
1599                });
1600        }
1601        if !client_changed
1602            && self.text_input_caret_rect != Some(snapshot.caret_rect)
1603            && !configuration_changed
1604        {
1605            self.text_input_commands
1606                .push_back(TextInputCommand::SetCaretRect {
1607                    client,
1608                    rect: snapshot.caret_rect,
1609                });
1610        }
1611        self.text_input_client = Some(client);
1612        self.text_input_configuration = Some(configuration);
1613        self.text_input_state = Some(state);
1614        self.text_input_caret_rect = Some(snapshot.caret_rect);
1615    }
1616    fn focus_next(&mut self, reverse: bool) {
1617        let fields = self.tree.focusable_elements();
1618        if fields.is_empty() {
1619            return;
1620        }
1621        let index = self
1622            .focused
1623            .and_then(|focused| fields.iter().position(|id| *id == focused));
1624        let next = match index {
1625            Some(index) if reverse => fields[(index + fields.len() - 1) % fields.len()],
1626            Some(index) => fields[(index + 1) % fields.len()],
1627            None if reverse => *fields.last().expect("not empty"),
1628            None => fields[0],
1629        };
1630        self.set_focus(Some(next));
1631    }
1632    fn handle_key(&mut self, event: KeyboardEvent) {
1633        if event.state.is_down() {
1634            self.editing_diagnostics.key_down_received += 1;
1635        } else {
1636            self.editing_diagnostics.key_up_received += 1;
1637        }
1638
1639        // A FocusNode may be requested by application code or a FocusScopeNode
1640        // without going through runtime pointer/Tab handling. Adopt that
1641        // external selection before routing the event through retained parents.
1642        if let Some(external) = self.tree.focused_keyboard_element()
1643            && self.focused != Some(external)
1644        {
1645            self.set_focus(Some(external));
1646        }
1647
1648        // KeyboardListener/Shortcuts are the nearest retained command scopes.
1649        // Give them first refusal for every transition, including key-up and
1650        // auto-repeat; text editing and Tab traversal remain fallthrough paths.
1651        if self.tree.dispatch_keyboard(self.focused, event.clone()) {
1652            self.frame_requested = true;
1653            return;
1654        }
1655        if !event.state.is_down() {
1656            return;
1657        }
1658        if event.code == Code::Escape
1659            && self
1660                .tree
1661                .dismiss_transients(incular_widgets::TransientDismissReason::Escape)
1662                > 0
1663        {
1664            self.frame_requested = true;
1665            return;
1666        }
1667        if matches!(
1668            event.code,
1669            Code::ArrowUp | Code::ArrowDown | Code::ArrowLeft | Code::ArrowRight
1670        ) && let Some(focused) = self.focused
1671            && let Some(next) = self.tree.menu_directional_focus_target(focused, event.code)
1672        {
1673            self.set_focus(Some(next));
1674            return;
1675        }
1676        // Activate the focused surface itself. Only menus delegate activation
1677        // from a focused descendant; nested editors retain their own keys.
1678        if matches!(event.code, Code::Enter | Code::NumpadEnter | Code::Space)
1679            && let Some(focused) = self.focused
1680            && let Some((owner, Some(action))) = self.tree.button_ancestor(focused)
1681            && (owner == focused || self.tree.is_menu_item_focus(focused))
1682            && self.activate_action(action)
1683        {
1684            return;
1685        }
1686        if event.code == Code::Tab {
1687            self.focus_next(event.modifiers.shift());
1688            return;
1689        }
1690        if let Some(label) = self.focused.filter(|id| self.tree.is_selectable_text(*id)) {
1691            let extend = event.modifiers.shift();
1692            let handled = if command_modifier(event.modifiers) {
1693                match event.code {
1694                    Code::KeyA => self.tree.selectable_text_select_all(label),
1695                    Code::KeyC => {
1696                        if let Some(text) = self.tree.selectable_text_selected_text(label) {
1697                            self.clipboard.set_text(text);
1698                            true
1699                        } else {
1700                            false
1701                        }
1702                    }
1703                    _ => false,
1704                }
1705            } else {
1706                match event.code {
1707                    Code::ArrowLeft => self.tree.selectable_text_move(label, false, extend),
1708                    Code::ArrowRight => self.tree.selectable_text_move(label, true, extend),
1709                    Code::Home => self.tree.selectable_text_move_to_edge(label, false, extend),
1710                    Code::End => self.tree.selectable_text_move_to_edge(label, true, extend),
1711                    _ => false,
1712                }
1713            };
1714            if handled {
1715                self.frame_requested = true;
1716            }
1717            return;
1718        }
1719        let Some(field) = self.focused.filter(|id| self.tree.is_text_field(*id)) else {
1720            return;
1721        };
1722        let Some(controller) = self.tree.text_controller(field) else {
1723            return;
1724        };
1725        let editable = self.tree.text_field_is_editable(field);
1726        let extend = event.modifiers.shift();
1727        if command_modifier(event.modifiers) {
1728            match event.code {
1729                Code::KeyA => controller.select_all(),
1730                Code::KeyC => self.clipboard.set_text(controller.selected_text()),
1731                Code::KeyZ => {
1732                    let Some(history) = self.ensure_text_history(field) else {
1733                        return;
1734                    };
1735                    let changed = if event.modifiers.shift() {
1736                        history.redo()
1737                    } else {
1738                        history.undo()
1739                    };
1740                    if !changed {
1741                        return;
1742                    }
1743                }
1744                Code::KeyY => {
1745                    let Some(history) = self.ensure_text_history(field) else {
1746                        return;
1747                    };
1748                    if !history.redo() {
1749                        return;
1750                    }
1751                }
1752                Code::KeyX => {
1753                    if !editable {
1754                        return;
1755                    }
1756                    self.clipboard.set_text(controller.selected_text());
1757                    controller.replace_selection("");
1758                }
1759                Code::KeyV => {
1760                    if !editable {
1761                        return;
1762                    }
1763                    if let Some(text) = self.clipboard.get_text() {
1764                        controller.insert(&text);
1765                    }
1766                }
1767                _ => return,
1768            }
1769        } else {
1770            match event.code {
1771                Code::Backspace => {
1772                    if !editable {
1773                        return;
1774                    }
1775                    controller.backspace();
1776                    self.editing_diagnostics.backspace_commands += 1;
1777                }
1778                Code::Delete => {
1779                    if !editable {
1780                        return;
1781                    }
1782                    controller.delete();
1783                    self.editing_diagnostics.delete_commands += 1;
1784                }
1785                Code::ArrowLeft => {
1786                    if !self.tree.text_field_move_horizontal(field, false, extend) {
1787                        controller.move_left(extend);
1788                    }
1789                }
1790                Code::ArrowRight => {
1791                    if !self.tree.text_field_move_horizontal(field, true, extend) {
1792                        controller.move_right(extend);
1793                    }
1794                }
1795                Code::ArrowUp => {
1796                    let _ = self.tree.text_field_move_vertical(field, false, extend);
1797                }
1798                Code::ArrowDown => {
1799                    let _ = self.tree.text_field_move_vertical(field, true, extend);
1800                }
1801                Code::Home => {
1802                    if !self.tree.text_field_move_line_edge(field, false, extend) {
1803                        controller.move_home(extend);
1804                    }
1805                }
1806                Code::End => {
1807                    if !self.tree.text_field_move_line_edge(field, true, extend) {
1808                        controller.move_end(extend);
1809                    }
1810                }
1811                Code::Enter => {
1812                    if !editable {
1813                        return;
1814                    }
1815                    if self.tree.is_multiline_text_field(field) {
1816                        controller.insert("\n");
1817                    } else {
1818                        self.tree.submit_text_field(field);
1819                    }
1820                }
1821                _ => return,
1822            }
1823        }
1824        controller.reset_caret(Instant::now());
1825        self.frame_requested = true;
1826    }
1827    fn insert_text(&mut self, text: &str) {
1828        let Some(field) = self.focused.filter(|id| self.tree.is_text_field(*id)) else {
1829            return;
1830        };
1831        if !self.tree.text_field_is_editable(field) {
1832            return;
1833        }
1834        if text.chars().any(char::is_control) {
1835            return;
1836        }
1837        if let Some(controller) = self.tree.text_controller(field) {
1838            controller.insert(text);
1839            self.editing_diagnostics.text_commits += 1;
1840            controller.reset_caret(Instant::now());
1841            self.frame_requested = true;
1842        }
1843    }
1844
1845    /// Handles the action chosen by a native software keyboard. Desktop
1846    /// Winit backends reach the same code path through Enter, while mobile
1847    /// hosts can preserve the IME's explicit Done/Next/Search intent.
1848    pub fn handle_text_input_action(&mut self, action: TextInputAction) -> bool {
1849        let Some(field) = self.focused.filter(|id| self.tree.is_text_field(*id)) else {
1850            return false;
1851        };
1852        let editable = self.tree.text_field_is_editable(field);
1853        if !editable {
1854            return false;
1855        }
1856        let handled = match action {
1857            TextInputAction::Newline => {
1858                if self.tree.is_multiline_text_field(field) {
1859                    self.tree.text_controller(field).is_some_and(|controller| {
1860                        controller.insert("\n");
1861                        controller.reset_caret(Instant::now());
1862                        true
1863                    })
1864                } else {
1865                    self.tree.submit_text_field(field)
1866                }
1867            }
1868            TextInputAction::Next => {
1869                let submitted = self.tree.submit_text_field(field);
1870                self.focus_next(false);
1871                submitted
1872            }
1873            TextInputAction::Previous => {
1874                let submitted = self.tree.submit_text_field(field);
1875                self.focus_next(true);
1876                submitted
1877            }
1878            TextInputAction::Done
1879            | TextInputAction::Go
1880            | TextInputAction::Search
1881            | TextInputAction::Send => self.tree.submit_text_field(field),
1882            TextInputAction::Unspecified | TextInputAction::None => false,
1883        };
1884        if handled {
1885            self.frame_requested = true;
1886        }
1887        handled
1888    }
1889    /// Drops the open composition, clearing the owner's stale preedit.
1890    /// Single cleanup path: the owner's own controller is the only
1891    /// writer, so cleanup never lands in another editor. Clearing an
1892    /// unset preedit notifies nothing.
1893    fn cancel_ime_composition(&mut self) {
1894        let owner = self.ime_composition.take();
1895        if let Some(owner) = owner {
1896            owner.controller.clear_preedit();
1897        }
1898    }
1899
1900    /// Whether the retained owner still names the live editing target:
1901    /// the focused field, with the same controller identity, still
1902    /// editable. Anything else means the composition's client is gone.
1903    fn ime_owner_current(&self) -> bool {
1904        self.ime_composition.as_ref().is_some_and(|owner| {
1905            self.focused == Some(owner.field)
1906                && self.tree.is_text_field(owner.field)
1907                && self.tree.text_field_is_editable(owner.field)
1908                && self
1909                    .tree
1910                    .text_controller(owner.field)
1911                    .is_some_and(|current| current == owner.controller)
1912        })
1913    }
1914
1915    fn ime_focused_target(&self) -> Option<ImeCompositionOwner> {
1916        let field = self
1917            .focused
1918            .filter(|id| self.tree.text_field_is_editable(*id))?;
1919        Some(ImeCompositionOwner {
1920            field,
1921            controller: self.tree.text_controller(field)?,
1922        })
1923    }
1924
1925    fn validate_ime_composition(&mut self) {
1926        if !self.ime_owner_current() {
1927            self.cancel_ime_composition();
1928        }
1929    }
1930
1931    /// Routes one native IME event. An owner that no longer validates
1932    /// (focus moved off-path, controller replaced, field unmounted or
1933    /// no longer editable) is cancelled before routing, so its preedit
1934    /// clears on its own controller and is never committed elsewhere.
1935    /// Owned events address the owner; only ownerless Commits fall back
1936    /// to the focused field, and only ownerless Preedits adopt a target.
1937    fn handle_ime(&mut self, event: ImeEvent) {
1938        self.editing_diagnostics.ime_events += 1;
1939        self.validate_ime_composition();
1940        let target = match &event {
1941            ImeEvent::Preedit { .. } => {
1942                if self.ime_composition.is_none() {
1943                    self.ime_composition = self.ime_focused_target();
1944                }
1945                self.ime_composition
1946                    .as_ref()
1947                    .map(|owner| owner.controller.clone())
1948            }
1949            ImeEvent::Commit(_) => self
1950                .ime_composition
1951                .take()
1952                .or_else(|| self.ime_focused_target())
1953                .map(|owner| owner.controller),
1954            ImeEvent::End => self.ime_composition.take().map(|owner| owner.controller),
1955        };
1956        let Some(controller) = target else {
1957            return;
1958        };
1959        match event {
1960            ImeEvent::Preedit { text, selection } => {
1961                controller.set_preedit(
1962                    text,
1963                    selection.map(|(start, end)| TextRange::new(start, end)),
1964                );
1965            }
1966            ImeEvent::Commit(text) => {
1967                // Ownerless commit policy: without a session or client
1968                // identity, a late Commit after focus changes cannot be
1969                // distinguished from a legitimate direct commit, so it
1970                // applies to the focused editable field. This is a routing
1971                // policy, not stale-commit protection.
1972                controller.commit_preedit(&text);
1973            }
1974            ImeEvent::End => {
1975                controller.clear_preedit();
1976            }
1977        }
1978        controller.reset_caret(Instant::now());
1979        self.frame_requested = true;
1980    }
1981    pub fn run_frame(
1982        &mut self,
1983        constraints: Constraints,
1984    ) -> Result<(DisplayList, FrameStats), TreeError> {
1985        self.run_frame_at(constraints, Instant::now())
1986    }
1987    /// Deterministic frame entry point used by animation tests and embedders
1988    /// with an existing monotonic clock.
1989    pub fn run_frame_at(
1990        &mut self,
1991        constraints: Constraints,
1992        now: Instant,
1993    ) -> Result<(DisplayList, FrameStats), TreeError> {
1994        let message_guard = tracing::info_span!("incular.messages").entered();
1995        let message_span = profiling::PhaseSpan::start();
1996        self.process_runtime_work_at(now);
1997        let runtime_messages = message_span.elapsed_us();
1998        drop(message_guard);
1999        let before = self.tree.diagnostics();
2000        let build_guard =
2001            tracing::info_span!("incular.build", elements_updated = tracing::field::Empty,)
2002                .entered();
2003        let build_span = profiling::PhaseSpan::start();
2004        let mut updated = 0;
2005        while let Some(id) = self.order.pop_front() {
2006            if let Some(widget) = self.pending.remove(&id)
2007                && self.tree.element_exists(id)
2008            {
2009                self.tree.update(id, widget)?;
2010                updated += 1;
2011            }
2012        }
2013        loop {
2014            while let Some((root, node)) = { self.reactive.borrow_mut().take_node() } {
2015                if let Some(node) = node.upgrade() {
2016                    node.run(root);
2017                }
2018            }
2019            while let Some(id) = { self.reactive.borrow_mut().take() } {
2020                if self.tree.element_exists(id) && self.builders.contains_key(&id) {
2021                    #[cfg(feature = "devtools")]
2022                    for cause in self.reactive.borrow_mut().take_causes(id) {
2023                        self.tree.note_invalidation(id, cause);
2024                    }
2025                    self.rebuild_from_builder(id)?;
2026                    updated += 1;
2027                }
2028            }
2029            if !self.reactive.borrow().has_work() {
2030                break;
2031            }
2032        }
2033        for id in self.tree.take_unmounted() {
2034            self.handlers_stale = true;
2035            self.builders.remove(&id);
2036            self.reactive.borrow_mut().forget(id);
2037            if let Some(scope) = self.owner_scopes.remove(&id) {
2038                scope.cancel();
2039            }
2040            self.text_histories.remove(&id);
2041        }
2042        self.reconcile_focus_and_selection_captures();
2043        self.validate_ime_composition();
2044        self.prune_handlers();
2045        let build = build_span.elapsed_us();
2046        drop(build_guard);
2047        // Ballistic tenures pump on the frame clock before layout, so
2048        // pumped offsets lay out and paint in the same frame. The pump
2049        // reports live drivers; that liveness is captured here and
2050        // ORed into the end-of-frame demand below, so the reset point
2051        // cannot discard it — the scheduler stays awake while a driver
2052        // lives and idles otherwise. No timer loop, just frame demand.
2053        let reduced_motion = self.environment.borrow().reduced_motion;
2054        let fling_live = self.tree.pump_scroll_flings(now, reduced_motion);
2055        let _layout_guard = tracing::info_span!("incular.layout").entered();
2056        let layout_span = profiling::PhaseSpan::start();
2057        self.tree.layout(constraints)?;
2058        let layout = layout_span.elapsed_us();
2059        drop(_layout_guard);
2060        for (action, handler) in self.tree.take_pending_handlers() {
2061            self.handlers_stale = true;
2062            self.handlers.insert(action, handler);
2063        }
2064        // Lazy viewport expiry occurs during layout, after the ordinary dirty
2065        // queue drain above. Release those builder subscriptions and callbacks
2066        // in the same frame rather than retaining one stale cache generation.
2067        for id in self.tree.take_unmounted() {
2068            self.handlers_stale = true;
2069            self.builders.remove(&id);
2070            self.reactive.borrow_mut().forget(id);
2071            if let Some(scope) = self.owner_scopes.remove(&id) {
2072                scope.cancel();
2073            }
2074            self.text_histories.remove(&id);
2075        }
2076        self.reconcile_focus_and_selection_captures();
2077        self.validate_ime_composition();
2078        self.prune_handlers();
2079        // Layout builders create their retained controls during the first
2080        // layout. Resolve initial autofocus once those controls exist, without
2081        // reclaiming focus on later frames or after an explicit input decision.
2082        if self.initial_focus == InitialFocus::AwaitingLayout {
2083            self.set_focus(self.tree.autofocus_element());
2084        }
2085        let _composite_guard = tracing::info_span!("incular.composite").entered();
2086        let composite_span = profiling::PhaseSpan::start();
2087        let (composited, animations_active) = self.tree.update_compositor(now)?;
2088        let composite = composite_span.elapsed_us();
2089        drop(_composite_guard);
2090        let _semantics_guard = tracing::info_span!("incular.semantics").entered();
2091        let semantics_span = profiling::PhaseSpan::start();
2092        self.tree.update_semantics();
2093        let semantics = semantics_span.elapsed_us();
2094        drop(_semantics_guard);
2095        let _paint_guard = tracing::info_span!("incular.paint").entered();
2096        let paint_span = profiling::PhaseSpan::start();
2097        let display_list = self.tree.paint();
2098        let paint = paint_span.elapsed_us();
2099        drop(_paint_guard);
2100        self.sync_text_input_client();
2101        // End-of-frame reset: recomputed from every demand source, not
2102        // blindly cleared. `fling_live` above is one source — a write
2103        // before layout alone would not survive this assignment, so it
2104        // is ORed back in. Nothing else in this frame clears the flag:
2105        // intermediate paths only ever set it.
2106        self.frame_requested = fling_live
2107            || !self.pending.is_empty()
2108            || self.reactive.borrow().has_work()
2109            || animations_active;
2110        let after = self.tree.diagnostics();
2111        let timings = FrameTimings {
2112            event_processing: std::mem::take(&mut self.pending_event_processing_us),
2113            runtime_messages,
2114            build,
2115            layout,
2116            composite,
2117            semantics,
2118            paint,
2119            cpu_total: runtime_messages + build + layout + composite + semantics + paint,
2120        };
2121        tracing::debug!(
2122            target: "incular::frame",
2123            cpu_total_us = timings.cpu_total,
2124            build_us = timings.build,
2125            layout_us = timings.layout,
2126            paint_us = timings.paint,
2127        );
2128        Ok((
2129            display_list.clone(),
2130            FrameStats {
2131                updated_elements: updated,
2132                rebuilt_elements: after.rebuilds - before.rebuilds,
2133                laid_out_render_objects: after.layouts - before.layouts,
2134                repainted_render_objects: after.paints - before.paints,
2135                composited: u64::from(composited),
2136                active_animations: u64::from(animations_active),
2137                display_list_commands: display_list.len(),
2138                requested_another_frame: self.frame_requested,
2139                timings,
2140            },
2141        ))
2142    }
2143    #[must_use]
2144    pub fn diagnostics(&self) -> Diagnostics {
2145        self.tree.diagnostics()
2146    }
2147    /// Queues every registered builder for the next frame's build drain,
2148    /// application root first so descendants it unmounts are skipped.
2149    #[cfg(feature = "hot-reload")]
2150    pub(crate) fn reassemble(&mut self) {
2151        let root = self.application_root;
2152        let ids = root
2153            .into_iter()
2154            .chain(self.builders.keys().copied().filter(|id| Some(*id) != root))
2155            .collect::<Vec<_>>();
2156        let mut reactive = self.reactive.borrow_mut();
2157        for id in ids {
2158            #[cfg(feature = "devtools")]
2159            reactive.note_cause(id, InvalidationCause::Manual);
2160            reactive.enqueue(id);
2161        }
2162        drop(reactive);
2163        self.frame_requested = true;
2164    }
2165    /// Rebuilds one registered builder outside the frame drain. Route
2166    /// outlets use this to refresh mounted content deterministically
2167    /// before framing, rather than only when reactive dependencies fire.
2168    pub(crate) fn rebuild_from_builder(&mut self, id: ElementId) -> Result<(), TreeError> {
2169        self.reactive.borrow_mut().refresh(id);
2170        let widget = {
2171            let _build_scope = BuildScopeGuard::enter(BuildScope {
2172                root: self.reactive.borrow().root,
2173                element: Some(id),
2174                queue: Rc::downgrade(&self.reactive),
2175                initial_dependencies: None,
2176                spawner: self.spawner(),
2177                owner_scope: self
2178                    .owner_scopes
2179                    .get(&id)
2180                    .cloned()
2181                    .unwrap_or_else(|| self.window_scope.clone()),
2182            });
2183            self.builders.get_mut(&id).expect("registered builder")()
2184        };
2185        let mut widget = widget;
2186        self.prepare_widget(&mut widget);
2187        self.tree.update(id, widget)?;
2188        Ok(())
2189    }
2190    fn prepare_widget(&mut self, widget: &mut Widget) {
2191        self.handlers_stale = true;
2192        let handlers = &mut self.handlers;
2193        let tree = &mut self.tree;
2194        widget.bind_callbacks(&mut |callback| {
2195            let id = tree.allocate_action();
2196            handlers.insert(id, callback);
2197            id
2198        });
2199    }
2200    fn activate_action(&mut self, action: ActionId) -> bool {
2201        let mut handled = false;
2202        if let Some(callback) = self.handlers.get(&action).cloned() {
2203            callback();
2204            handled = true;
2205        }
2206        if let Some(callback) = self.legacy_action_handler.as_mut() {
2207            callback(action);
2208            handled = true;
2209        }
2210        self.frame_requested |= handled;
2211        handled
2212    }
2213
2214    fn prune_handlers(&mut self) {
2215        if !std::mem::take(&mut self.handlers_stale) {
2216            return;
2217        }
2218        let active = self.tree.action_ids();
2219        self.handlers.retain(|id, _| active.contains(id));
2220    }
2221    fn set_hover(&mut self, next: Option<ElementId>) {
2222        if self.hovered_button == next {
2223            return;
2224        }
2225        if let Some(previous) = self.hovered_button {
2226            let _ = self
2227                .tree
2228                .set_button_interaction(previous, Some(false), None, None);
2229            if let Some(action) = self.tree.hover_actions_for_element(previous).1
2230                && let Some(callback) = self.handlers.get(&action).cloned()
2231            {
2232                callback();
2233            }
2234        }
2235        self.hovered_button = next;
2236        if let Some(current) = next {
2237            if self.pressed_button != Some(current) {
2238                let _ = self
2239                    .tree
2240                    .set_button_interaction(current, Some(true), None, None);
2241            }
2242            if let Some(action) = self.tree.hover_actions_for_element(current).0
2243                && let Some(callback) = self.handlers.get(&action).cloned()
2244            {
2245                callback();
2246            }
2247        }
2248        self.frame_requested = true;
2249    }
2250}
2251
2252/// Conventional desktop shortcut modifier: Control on Linux/Windows and Meta
2253/// (Command) on macOS.  `keyboard-types` retains the actual modifier bits;
2254/// this policy belongs to the runtime command dispatcher rather than input
2255/// normalization.
2256fn command_modifier(modifiers: Modifiers) -> bool {
2257    modifiers.ctrl() || modifiers.meta()
2258}
2259
2260/// Framework-controlled build scope for declarative roots. It intentionally
2261/// exposes no element IDs or scheduler handles. Signals use the runtime's
2262/// scoped collector while a builder executes; reads outside a build simply
2263/// do not create dependencies.
2264fn us_since_instant(started: std::time::Instant) -> u32 {
2265    started.elapsed().as_micros().try_into().unwrap_or(u32::MAX)
2266}