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, EnvironmentFields, InitialBuildDependencies, ReactiveQueue,
10 environment_change_mask, install_focus_scope_watch,
11};
12use crate::profiling;
13use crate::profiling::FrameTimings;
14#[cfg(not(target_arch = "wasm32"))]
15use crate::tasks::TokioHandle;
16use crate::tasks::{
17 self, RuntimeDiagnostics, RuntimeSpawner, RuntimeWake, Task, TaskFailure, TaskHandle,
18 TaskScope, UiDispatcher,
19};
20use crate::text_shortcuts::TextShortcutPlatform;
21use crate::undo::UndoHistoryController;
22use crate::window_commands::{WindowHandle, WindowOpener};
23use crate::window_state::WindowManager;
24use incular_config::{Constraints, RuntimeEnvironment};
25use incular_core::time::Instant;
26use incular_core::{
27 Code, ImeEvent, InputEvent, KeyboardEvent, Offset, PRIMARY_POINTER_BUTTON, PointerPhase,
28 PointerSampleMetadata, Rect,
29};
30use incular_platform::{
31 Clipboard, ClipboardCapabilities, ClipboardError, ClipboardWriteReport, DataTransfer,
32 ExternalDragEvent, ExternalDragResponse, MemoryClipboard, PlatformEvent, PlatformLifecycle,
33 TextInputAction, TextInputClientId, TextInputCommand, TextInputConfiguration, TextInputState,
34 TextInputType, TransferReadRequest, WindowId, WindowMetrics, WindowOperation, WindowOptions,
35};
36use incular_rendering::DisplayList;
37use incular_semantics::{SemanticAction, SemanticNodeId};
38use incular_text::TextEditingValue;
39#[cfg(feature = "devtools")]
40use incular_widgets::internal::InvalidationCause;
41use incular_widgets::internal::{
42 ActionId, ButtonInk, Diagnostics, ElementId, MouseCursor, PlatformMenuBinding,
43 PointerDeviceKind, PointerEvent, PointerHitTest, RawPointerEvent, TextEditingController,
44 TextRange, TextSelection, TreeError, WidgetTree, WindowInteraction,
45};
46use incular_widgets::{TextInputActionHint, TextInputTypeHint, Widget};
47use std::{
48 cell::{Cell, RefCell},
49 collections::{HashMap, VecDeque},
50 rc::{Rc, Weak},
51 sync::atomic::{AtomicU64, Ordering},
52};
53
54mod text_actions;
55mod text_gestures;
56
57static RUNTIME_SEQUENCE: AtomicU64 = AtomicU64::new(1);
60
61#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
62pub struct FrameStats {
63 pub updated_elements: usize,
64 pub rebuilt_elements: u64,
65 pub laid_out_render_objects: u64,
66 pub repainted_render_objects: u64,
67 pub composited: u64,
68 pub active_animations: u64,
69 pub display_list_commands: usize,
70 pub requested_another_frame: bool,
71 pub timings: FrameTimings,
73}
74#[derive(Clone, Copy, Debug, PartialEq, Eq)]
75pub struct EventTarget {
76 pub element: ElementId,
77 pub action: Option<ActionId>,
78}
79#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
81pub struct FocusDiagnostics {
82 pub focused_element: Option<ElementId>,
83 pub text_pointer_capture: Option<ElementId>,
84}
85#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
86pub struct EditingDiagnostics {
87 pub key_down_received: u64,
88 pub key_up_received: u64,
89 pub backspace_commands: u64,
90 pub delete_commands: u64,
91 pub text_commits: u64,
92 pub ime_events: u64,
93}
94
95struct ImeCompositionOwner {
108 field: ElementId,
109 controller: TextEditingController,
110}
111#[derive(Clone, Copy, PartialEq, Eq)]
112enum InitialFocus {
113 AwaitingLayout,
114 Settled,
115}
116
117pub struct Runtime {
119 pub(crate) tree: WidgetTree,
120 pub(crate) pending_event_processing_us: u32,
122 input_clock: Option<Instant>,
125 pub(crate) pending: HashMap<ElementId, Widget>,
126 pub(crate) order: VecDeque<ElementId>,
127 pub(crate) reactive: Rc<RefCell<ReactiveQueue>>,
128 pub(crate) builders: HashMap<ElementId, Box<dyn FnMut() -> Widget>>,
129 pub(crate) handlers: HashMap<ActionId, Rc<dyn Fn()>>,
130 handlers_stale: bool,
133 pub(crate) legacy_action_handler: Option<Box<dyn FnMut(ActionId)>>,
134 pub(crate) hovered_button: Option<ElementId>,
135 pub(crate) pressed_button: Option<ElementId>,
136 pub(crate) legacy_pointer: Option<(u64, u64)>,
139 pub(crate) last_pointer: Offset,
140 pub(crate) pointer_inside: bool,
141 pub(crate) pointer_position_known: bool,
142 last_hover_sample: Option<RawPointerEvent>,
145 pub(crate) focused: Option<ElementId>,
146 adopted_focus_epoch: Option<u64>,
147 initial_focus: InitialFocus,
148 pub(crate) captured_text_field: Option<ElementId>,
149 pub(crate) captured_selectable_text: Option<ElementId>,
150 pub(crate) secondary_text_press: Option<text_gestures::SecondaryTextPress>,
151 pub(crate) last_selectable_secondary: Option<Offset>,
153 pub(crate) text_taps: Option<text_gestures::TextTapRun>,
155 pub(crate) keyboard_modifiers: incular_core::Modifiers,
157 pub(crate) caret_reveal_key: Option<(ElementId, u64, TextSelection)>,
160 ime_composition: Option<ImeCompositionOwner>,
161 pub(crate) text_histories: HashMap<ElementId, UndoHistoryController>,
162 pub(crate) text_input_commands: VecDeque<TextInputCommand>,
163 pub(crate) text_input_client: Option<TextInputClientId>,
164 pub(crate) text_input_configuration: Option<TextInputConfiguration>,
165 pub(crate) text_input_state: Option<TextInputState>,
166 pub(crate) text_input_caret_rect: Option<Rect>,
167 pub(crate) clipboard: Box<dyn Clipboard>,
168 pub(crate) editing_diagnostics: EditingDiagnostics,
169 pub(crate) text_shortcut_platform: TextShortcutPlatform,
171 pub(crate) frame_requested: bool,
172 pub(crate) scheduler: Rc<RefCell<tasks::TaskScheduler>>,
173 pub(crate) environment: Rc<RefCell<RuntimeEnvironment>>,
174 pub(crate) lifecycle: ApplicationLifecycle,
175 pub(crate) lifecycle_observers: Vec<Rc<dyn Fn(LifecycleTransition)>>,
176 pub(crate) error_observers: Vec<Rc<dyn Fn(RuntimeErrorReport)>>,
177 pub(crate) environment_generation: u64,
178 pub(crate) environment_dependencies: Rc<Cell<EnvironmentFields>>,
179 pub(crate) application_root: Option<ElementId>,
180 pub(crate) owner_scopes: HashMap<ElementId, TaskScope>,
181 pub(crate) window_id: Option<WindowId>,
182 pub(crate) window_scope: TaskScope,
183 pub(crate) window_manager: Option<WindowManager>,
184 driver_domain: Rc<u64>,
189}
190
191pub(crate) fn diagnostic_input_trigger(event: &InputEvent) -> String {
192 match event {
193 InputEvent::Pointer { phase, position } => {
194 format!("pointer({phase:?}, {:.1}, {:.1})", position.x, position.y)
195 }
196 InputEvent::PointerWithId {
197 pointer,
198 phase,
199 position,
200 } => format!(
201 "pointer({pointer}, {phase:?}, {:.1}, {:.1})",
202 position.x, position.y
203 ),
204 InputEvent::PointerWithMetadata {
205 pointer,
206 device,
207 kind,
208 buttons,
209 button,
210 sample: _,
211 phase,
212 position,
213 } => format!(
214 "pointer({pointer}, device={device}, {kind:?}, buttons={buttons:#x}, changed={button:?}, {phase:?}, {:.1}, {:.1})",
215 position.x, position.y
216 ),
217 InputEvent::TrackpadGesture(event) => format!(
218 "trackpad(device={}, kind={:?}, phase={:?})",
219 event.device(),
220 event.kind(),
221 event.phase()
222 ),
223 InputEvent::Scroll { delta } => format!("scroll({:.1}, {:.1})", delta.x, delta.y),
224 InputEvent::Key(event) => format!("key({:?}, {:?})", event.state, event.code),
225 InputEvent::Text(text) => format!("text_input(chars={})", text.chars().count()),
226 InputEvent::Ime(ImeEvent::Preedit { text, .. }) => {
227 format!("ime_preedit(chars={})", text.chars().count())
228 }
229 InputEvent::Ime(ImeEvent::Commit(text)) => {
230 format!("ime_commit(chars={})", text.chars().count())
231 }
232 InputEvent::Ime(ImeEvent::End) => "ime_end".to_owned(),
233 InputEvent::WindowResized { size, scale_factor } => format!(
234 "window_resize({:.1}x{:.1}, scale={scale_factor:.2})",
235 size.width, size.height
236 ),
237 }
238}
239
240impl Runtime {
241 pub fn new(root: Widget) -> Result<Self, TreeError> {
242 Self::with_scheduler(root, tasks::TaskScheduler::new())
243 }
244 fn with_scheduler(
245 root: Widget,
246 scheduler: Rc<RefCell<tasks::TaskScheduler>>,
247 ) -> Result<Self, TreeError> {
248 let window_scope = tasks::TaskScheduler::spawner(&scheduler).scope();
249 Self::with_window(root, scheduler, None, window_scope, None)
250 }
251
252 pub(crate) fn with_window(
253 root: Widget,
254 scheduler: Rc<RefCell<tasks::TaskScheduler>>,
255 window_id: Option<WindowId>,
256 window_scope: TaskScope,
257 window_manager: Option<WindowManager>,
258 ) -> Result<Self, TreeError> {
259 Self::with_window_and_reactive(
260 root,
261 scheduler,
262 window_id,
263 window_scope,
264 window_manager,
265 ReactiveQueue::new(),
266 )
267 }
268 pub(crate) fn with_window_and_reactive(
269 mut root: Widget,
270 scheduler: Rc<RefCell<tasks::TaskScheduler>>,
271 window_id: Option<WindowId>,
272 window_scope: TaskScope,
273 window_manager: Option<WindowManager>,
274 reactive: Rc<RefCell<ReactiveQueue>>,
275 ) -> Result<Self, TreeError> {
276 let mut tree = WidgetTree::new();
277 let mut handlers = HashMap::new();
278 root.bind_callbacks(&mut |callback| {
279 let action = tree.allocate_action();
280 handlers.insert(action, callback);
281 action
282 });
283 tree.mount(root)?;
284 for (action, handler) in tree.take_pending_handlers() {
285 handlers.insert(action, handler);
286 }
287 let mut runtime = Self {
288 pending_event_processing_us: 0,
289 input_clock: None,
290 tree,
291 pending: HashMap::new(),
292 order: VecDeque::new(),
293 reactive,
294 builders: HashMap::new(),
295 handlers,
296 handlers_stale: false,
297 legacy_action_handler: None,
298 hovered_button: None,
299 pressed_button: None,
300 legacy_pointer: None,
301 last_pointer: Offset::ZERO,
302 pointer_inside: false,
303 pointer_position_known: false,
304 last_hover_sample: None,
305 focused: None,
306 adopted_focus_epoch: None,
307 initial_focus: InitialFocus::AwaitingLayout,
308 captured_text_field: None,
309 captured_selectable_text: None,
310 secondary_text_press: None,
311 last_selectable_secondary: None,
312 text_taps: None,
313 keyboard_modifiers: incular_core::Modifiers::empty(),
314 caret_reveal_key: None,
315 ime_composition: None,
316 text_histories: HashMap::new(),
317 text_input_commands: VecDeque::new(),
318 text_input_client: None,
319 text_input_configuration: None,
320 text_input_state: None,
321 text_input_caret_rect: None,
322 clipboard: Box::new(MemoryClipboard::default()),
323 editing_diagnostics: EditingDiagnostics::default(),
324 text_shortcut_platform: TextShortcutPlatform::current(),
325 frame_requested: true,
326 scheduler,
327 environment: Rc::new(RefCell::new(RuntimeEnvironment::default())),
328 lifecycle: ApplicationLifecycle::Starting,
329 lifecycle_observers: Vec::new(),
330 error_observers: Vec::new(),
331 environment_generation: 0,
332 environment_dependencies: Rc::new(Cell::new(EnvironmentFields::empty())),
333 application_root: None,
334 owner_scopes: HashMap::new(),
335 window_id,
336 window_scope,
337 window_manager,
338 driver_domain: Rc::new(
339 RUNTIME_SEQUENCE
340 .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |id| id.checked_add(1))
341 .expect("runtime driver identity space exhausted"),
342 ),
343 };
344 if let Some(autofocus) = runtime.tree.autofocus_element() {
349 runtime.set_focus(Some(autofocus));
350 }
351 Ok(runtime)
352 }
353 #[must_use]
357 pub fn id(&self) -> u64 {
358 *self.driver_domain
359 }
360
361 pub(crate) fn driver_token(&self) -> Weak<u64> {
364 Rc::downgrade(&self.driver_domain)
365 }
366
367 #[must_use]
370 pub fn spawner(&self) -> RuntimeSpawner {
371 self.window_id.map_or_else(
372 || tasks::TaskScheduler::spawner(&self.scheduler),
373 |window_id| tasks::TaskScheduler::spawner_for(&self.scheduler, window_id),
374 )
375 }
376 #[must_use]
379 pub fn dispatcher(&self) -> UiDispatcher {
380 self.scheduler.borrow().dispatcher_for(self.window_id)
381 }
382 #[must_use]
385 #[cfg(not(target_arch = "wasm32"))]
386 pub fn tokio_handle(&self) -> TokioHandle {
387 self.scheduler.borrow().tokio_handle()
388 }
389 pub fn set_wake_handler(&mut self, wake: std::sync::Arc<dyn RuntimeWake>) {
390 self.scheduler.borrow_mut().set_wake(wake);
391 }
392
393 #[must_use]
396 pub const fn window_id(&self) -> Option<WindowId> {
397 self.window_id
398 }
399
400 #[doc(hidden)]
404 #[must_use]
405 pub fn platform_menu_bindings(&self) -> Vec<PlatformMenuBinding> {
406 self.tree.platform_menu_bindings()
407 }
408
409 pub fn request_frame(&mut self) {
411 self.frame_requested = true;
412 }
413
414 pub fn take_text_input_commands(&mut self) -> Vec<TextInputCommand> {
418 self.text_input_commands.drain(..).collect()
419 }
420
421 pub fn open_window(
425 &mut self,
426 options: WindowOptions,
427 root: Widget,
428 ) -> Result<WindowHandle, WindowError> {
429 self.window_manager
430 .as_ref()
431 .ok_or(WindowError::ApplicationStopped)?
432 .open_window(options, root)
433 }
434
435 #[must_use]
438 pub fn window_opener(&self) -> Option<WindowOpener> {
439 self.window_manager
440 .as_ref()
441 .cloned()
442 .map(|manager| WindowOpener { manager })
443 }
444
445 #[must_use]
447 pub fn window_handle(&self) -> Option<WindowHandle> {
448 let id = self.window_id?;
449 self.window_manager.as_ref()?.handle(id)
450 }
451
452 #[must_use]
455 pub fn window_task_scope(&self) -> TaskScope {
456 self.window_scope.clone()
457 }
458
459 pub fn spawn_blocking<T>(
462 &self,
463 work: impl FnOnce() -> T + Send + 'static,
464 complete: impl FnOnce(Result<T, TaskFailure>, &mut Runtime) + 'static,
465 ) -> TaskHandle
466 where
467 T: Send + 'static,
468 {
469 self.spawner()
470 .spawn_blocking_in(&self.window_scope, work, complete)
471 }
472 pub fn spawn<F, T>(&self, future: F) -> Task<T>
475 where
476 F: std::future::Future<Output = T> + Send + 'static,
477 T: Send + 'static,
478 {
479 self.spawner().spawn_in(&self.window_scope, future)
480 }
481 pub fn spawn_into<F, T>(
483 &self,
484 future: F,
485 complete: impl FnOnce(Result<T, TaskFailure>, &mut Runtime) + 'static,
486 ) -> TaskHandle
487 where
488 F: std::future::Future<Output = T> + Send + 'static,
489 T: Send + 'static,
490 {
491 self.spawner()
492 .spawn_into_in(&self.window_scope, future, complete)
493 }
494 pub fn process_runtime_work_at(&mut self, _now: Instant) {
498 let scheduler = self.scheduler.clone();
499 let work = scheduler.borrow_mut().take_turn();
503 for work in work {
504 self.process_ui_work(&scheduler, work);
505 }
506 if self.reactive.borrow().has_work()
509 || !self.pending.is_empty()
510 || self.tree.has_dirty_dependencies()
511 {
512 self.frame_requested = true;
513 }
514 }
515
516 pub(crate) fn process_ui_work(
517 &mut self,
518 scheduler: &Rc<RefCell<tasks::TaskScheduler>>,
519 work: tasks::UiWork,
520 ) {
521 let mut failures = Vec::new();
522 match work {
523 tasks::UiWork::Dispatch { callback, .. } => {
524 if scheduler.borrow().accepting()
525 && std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| callback(self)))
526 .is_err()
527 {
528 failures.push(TaskFailure::Panicked);
529 }
530 }
531 tasks::UiWork::Task(pending) => {
532 scheduler.borrow().detach_scope(&pending.control);
533 let stale_owner = pending.control.owner_is_stale(self);
534 if !scheduler.borrow().accepting() || pending.control.is_cancelled() || stale_owner
535 {
536 (pending.discard)();
537 scheduler
538 .borrow_mut()
539 .record_discard(pending.blocking, stale_owner);
540 } else {
541 #[cfg(feature = "devtools")]
542 let previous_task_context = DEV_TASK_COMPLETION.with(|context| {
543 let previous = context.get();
544 context.set(true);
545 previous
546 });
547 let failure = (pending.finish)(self);
548 #[cfg(feature = "devtools")]
549 DEV_TASK_COMPLETION.with(|context| context.set(previous_task_context));
550 if let Some(failure) = scheduler
551 .borrow_mut()
552 .record_completion(pending.blocking, failure)
553 {
554 failures.push(failure);
555 }
556 }
557 }
558 }
559 for failure in failures {
560 if self.error_observers.is_empty() {
561 eprintln!("Incular runtime error: {failure:?}");
565 }
566 for observer in &self.error_observers {
567 observer(RuntimeErrorReport {
568 failure: failure.clone(),
569 });
570 }
571 }
572 if self.reactive.borrow().has_work()
575 || !self.pending.is_empty()
576 || self.tree.has_dirty_dependencies()
577 {
578 self.frame_requested = true;
579 }
580 }
581 pub fn process_runtime_work(&mut self) {
582 self.process_runtime_work_at(Instant::now());
583 }
584 #[must_use]
585 pub fn runtime_diagnostics(&self) -> RuntimeDiagnostics {
586 self.scheduler.borrow().diagnostics()
587 }
588 #[must_use]
591 pub fn debug_dump(&self) -> String {
592 let diagnostics = self.runtime_diagnostics();
593 let (application_tasks, owner_tasks) = self.scheduler.borrow().active_ownership();
594 format!(
595 "Lifecycle: {:?}\nTokio tasks tracked: {}\n application: {}\n owner-scoped: {}\nPending UI messages: {}\nEnvironment generation: {}",
596 self.lifecycle,
597 diagnostics.active_tracked_tasks,
598 application_tasks,
599 owner_tasks,
600 diagnostics
601 .ui_messages_enqueued
602 .saturating_sub(diagnostics.ui_messages_processed),
603 self.environment_generation,
604 )
605 }
606 #[must_use]
607 pub fn environment(&self) -> RuntimeEnvironment {
608 self.environment.borrow().clone()
609 }
610 #[must_use]
611 pub const fn lifecycle(&self) -> ApplicationLifecycle {
612 self.lifecycle
613 }
614 #[must_use]
615 pub const fn environment_generation(&self) -> u64 {
616 self.environment_generation
617 }
618 pub fn set_environment(&mut self, environment: RuntimeEnvironment) -> bool {
621 let environment = environment.normalized();
622 let previous = self.environment.borrow().clone();
623 if previous == environment {
624 return false;
625 }
626 let changed = environment_change_mask(&previous, &environment);
627 *self.environment.borrow_mut() = environment.clone();
628 let retained_environment_dirty = self.tree.set_environment(environment);
633 self.environment_generation = self
634 .environment_generation
635 .checked_add(1)
636 .expect("runtime environment generation exhausted");
637 if retained_environment_dirty {
638 self.frame_requested = true;
639 }
640 if self.environment_dependencies.get().intersects(changed)
641 && let Some(root) = self.application_root
642 {
643 #[cfg(feature = "devtools")]
644 self.tree
645 .note_invalidation(root, InvalidationCause::EnvironmentChanged);
646 let _ = self.rebuild_from_builder(root);
647 self.frame_requested = true;
648 }
649 true
650 }
651 pub fn transition_lifecycle(&mut self, lifecycle: ApplicationLifecycle) -> bool {
652 if self.lifecycle == lifecycle {
653 return false;
654 }
655 let transition = LifecycleTransition {
656 previous: self.lifecycle,
657 current: lifecycle,
658 };
659 self.lifecycle = lifecycle;
660 for observer in &self.lifecycle_observers {
661 observer(transition);
662 }
663 if matches!(
664 lifecycle,
665 ApplicationLifecycle::Stopping | ApplicationLifecycle::Terminated
666 ) {
667 self.reactive.borrow_mut().clear();
668 self.scheduler.borrow_mut().shutdown();
669 }
670 true
671 }
672 pub fn observe_lifecycle(&mut self, observer: impl Fn(LifecycleTransition) + 'static) {
673 self.lifecycle_observers.push(Rc::new(observer));
674 }
675 pub fn observe_runtime_error(&mut self, observer: impl Fn(RuntimeErrorReport) + 'static) {
676 self.error_observers.push(Rc::new(observer));
677 }
678 pub fn shutdown(&mut self) {
682 if self.focused.is_some() {
683 self.set_focus(None);
684 } else {
685 self.cancel_ime_composition();
686 self.sync_text_input_client();
687 }
688 self.tree.cancel_all_gesture_streams();
689 self.captured_text_field = None;
690 self.captured_selectable_text = None;
691 self.hovered_button = None;
692 self.pressed_button = None;
693 self.legacy_pointer = None;
694 self.text_histories.clear();
695 self.tree.release_edit_subscriptions();
696 let _ = self.transition_lifecycle(ApplicationLifecycle::Stopping);
697 for (_, scope) in self.owner_scopes.drain() {
698 scope.cancel();
699 }
700 self.scheduler.borrow_mut().shutdown();
701 self.lifecycle = ApplicationLifecycle::Terminated;
702 }
703
704 pub(crate) fn dispose_window(&mut self) {
708 if self.focused.is_some() {
709 self.set_focus(None);
710 } else {
711 self.cancel_ime_composition();
712 self.sync_text_input_client();
713 }
714 self.tree.cancel_all_gesture_streams();
715 self.tree.release_edit_subscriptions();
716 self.legacy_pointer = None;
717 self.window_scope.cancel();
718 for (_, scope) in self.owner_scopes.drain() {
719 scope.cancel();
720 }
721 self.reactive.borrow_mut().clear();
722 self.builders.clear();
723 self.pending.clear();
724 self.order.clear();
725 self.handlers.clear();
726 self.legacy_action_handler = None;
727 self.text_histories.clear();
728 self.text_input_commands.clear();
729 self.text_input_client = None;
730 self.text_input_configuration = None;
731 self.text_input_state = None;
732 self.text_input_caret_rect = None;
733 self.captured_text_field = None;
734 self.captured_selectable_text = None;
735 self.hovered_button = None;
736 self.pressed_button = None;
737 }
738 #[must_use]
739 pub fn tree(&self) -> &WidgetTree {
740 &self.tree
741 }
742
743 #[cfg(feature = "devtools")]
744 pub(crate) fn devtools_reactive_root(&self) -> ReactiveRootId {
745 self.reactive.borrow().root
746 }
747 #[must_use]
748 pub fn tree_mut(&mut self) -> &mut WidgetTree {
749 &mut self.tree
750 }
751
752 #[must_use]
756 pub fn content_sensitivity(&self) -> incular_config::ContentSensitivity {
757 self.tree.content_sensitivity()
758 }
759
760 #[must_use]
764 pub fn transient_surfaces(&self) -> Vec<incular_widgets::TransientSurfaceSnapshot> {
765 self.tree.transient_surfaces()
766 }
767
768 pub fn set_native_transient_bounds(&mut self, bounds: Option<incular_core::Rect>) -> bool {
771 let changed = self.tree.set_native_transient_bounds(bounds);
772 self.frame_requested |= changed;
773 changed
774 }
775
776 pub fn set_native_transient_presentations(
780 &mut self,
781 ids: impl IntoIterator<Item = incular_widgets::TransientSurfaceId>,
782 ) -> bool {
783 let changed = self.tree.set_native_transient_presentations(ids);
784 self.frame_requested |= changed;
785 changed
786 }
787
788 pub fn dismiss_transients(&mut self, reason: incular_widgets::TransientDismissReason) -> bool {
792 let dismissed = self.tree.dismiss_transients(reason) > 0;
793 self.frame_requested |= dismissed;
794 dismissed
795 }
796 #[cfg(feature = "devtools")]
797 pub(crate) fn devtools_edit_property(
798 &mut self,
799 id: incular_devtools_protocol::DevWidgetId,
800 name: &str,
801 value: &incular_devtools_protocol::DebugValue,
802 ) -> bool {
803 let edit = self.tree.devtools_edit_property(id, name, value);
804 self.frame_requested |= edit == incular_widgets::devtools::DevEdit::Applied;
805 edit != incular_widgets::devtools::DevEdit::Rejected
806 }
807 #[cfg(feature = "devtools")]
808 pub(crate) fn devtools_revert_property(
809 &mut self,
810 id: incular_devtools_protocol::DevWidgetId,
811 name: &str,
812 original: &incular_devtools_protocol::DebugValue,
813 ) -> bool {
814 let edit = self.tree.devtools_revert_property(id, name, original);
815 self.frame_requested |= edit == incular_widgets::devtools::DevEdit::Applied;
816 edit != incular_widgets::devtools::DevEdit::Rejected
817 }
818 pub fn set_animation_time_scale(&mut self, scale: f32) {
822 self.tree.set_animation_time_scale(scale);
823 }
824 #[must_use]
825 pub fn focused_element(&self) -> Option<ElementId> {
826 self.focused
827 }
828 pub fn dispatch_semantic_action(
844 &mut self,
845 node: SemanticNodeId,
846 action: SemanticAction,
847 ) -> bool {
848 let Some(element) = self.tree.element_for_semantic_node(node) else {
849 return false;
850 };
851 self.tree.note_semantic_action();
852 let handled = match action {
853 SemanticAction::Focus => {
854 if self.tree.can_request_focus(element) {
855 self.set_focus(Some(element));
856 true
857 } else {
858 false
859 }
860 }
861 SemanticAction::Activate => {
862 if let Some(callback) = self.tree.semantic_action_callback(
863 element,
864 incular_semantics::SemanticActionKind::Activate,
865 ) {
866 callback();
867 true
868 } else if self.tree.semantic_activate_executable(element) {
869 self.tree
870 .action_for_element(element)
871 .map(|action| self.activate_action(action))
872 .unwrap_or(false)
873 } else {
874 false
875 }
876 }
877 SemanticAction::SetText(text) => {
878 if !self.tree.text_field_is_editable(element) {
879 false
880 } else {
881 self.tree
882 .text_controller(element)
883 .map(|controller| {
884 controller.set_text(text);
885 true
886 })
887 .unwrap_or(false)
888 }
889 }
890 SemanticAction::SetSelection { base, extent } => self
891 .tree
892 .text_controller(element)
893 .map(|controller| {
894 let length = controller.text().len();
895 controller.set_selection(TextSelection {
896 base: base.min(length),
897 extent: extent.min(length),
898 });
899 true
900 })
901 .unwrap_or(false),
902 SemanticAction::ScrollForward => self
903 .tree
904 .semantic_action_callback(
905 element,
906 incular_semantics::SemanticActionKind::ScrollForward,
907 )
908 .map(|callback| {
909 callback();
910 true
911 })
912 .unwrap_or_else(|| self.tree.semantic_scroll(element, true)),
913 SemanticAction::ScrollBackward => self
914 .tree
915 .semantic_action_callback(
916 element,
917 incular_semantics::SemanticActionKind::ScrollBackward,
918 )
919 .map(|callback| {
920 callback();
921 true
922 })
923 .unwrap_or_else(|| self.tree.semantic_scroll(element, false)),
924 SemanticAction::Increment => self
925 .tree
926 .semantic_action_callback(element, incular_semantics::SemanticActionKind::Increment)
927 .map(|callback| {
928 callback();
929 true
930 })
931 .unwrap_or_else(|| self.tree.dispatch_semantic_increment(element, true)),
932 SemanticAction::Decrement => self
933 .tree
934 .semantic_action_callback(element, incular_semantics::SemanticActionKind::Decrement)
935 .map(|callback| {
936 callback();
937 true
938 })
939 .unwrap_or_else(|| self.tree.dispatch_semantic_increment(element, false)),
940 };
941 if handled {
942 self.frame_requested = true;
943 }
944 handled
945 }
946 #[must_use]
947 pub fn focus_diagnostics(&self) -> FocusDiagnostics {
948 FocusDiagnostics {
949 focused_element: self.focused,
950 text_pointer_capture: self.captured_text_field,
951 }
952 }
953 #[must_use]
954 pub const fn editing_diagnostics(&self) -> EditingDiagnostics {
955 self.editing_diagnostics
956 }
957 pub fn set_text_shortcut_platform(&mut self, platform: TextShortcutPlatform) {
962 self.text_shortcut_platform = platform;
963 }
964
965 #[must_use]
967 pub fn text_shortcut_platform(&self) -> TextShortcutPlatform {
968 self.text_shortcut_platform
969 }
970
971 pub fn set_clipboard(&mut self, clipboard: Box<dyn Clipboard>) {
972 self.clipboard = clipboard;
973 }
974 #[must_use]
977 pub fn clipboard_capabilities(&self) -> ClipboardCapabilities {
978 self.clipboard.capabilities()
979 }
980 pub fn read_clipboard(
982 &mut self,
983 request: TransferReadRequest,
984 ) -> Result<DataTransfer, ClipboardError> {
985 self.clipboard.read(request)
986 }
987 pub fn write_clipboard(
990 &mut self,
991 transfer: DataTransfer,
992 ) -> Result<ClipboardWriteReport, ClipboardError> {
993 self.clipboard.write(transfer)
994 }
995 pub fn set_clipboard_text(&mut self, text: impl Into<String>) {
996 self.clipboard.set_text(text.into());
997 }
998 #[must_use]
1001 pub fn handle_external_drag(&mut self, event: ExternalDragEvent) -> ExternalDragResponse {
1002 let response = self.tree.dispatch_external_drag(event);
1003 self.frame_requested = true;
1004 response
1005 }
1006 pub fn handle_platform_event(&mut self, event: PlatformEvent) -> Option<EventTarget> {
1009 match event {
1010 PlatformEvent::Input(input) => self.handle_input(input),
1011 PlatformEvent::TextInputAction(action) => {
1012 self.handle_text_input_action(action);
1013 None
1014 }
1015 PlatformEvent::Metrics(metrics) => {
1016 self.update_window_metrics(metrics);
1017 None
1018 }
1019 PlatformEvent::Environment(environment) => {
1020 self.set_environment(environment);
1021 None
1022 }
1023 PlatformEvent::Lifecycle(lifecycle) => {
1024 self.transition_lifecycle(match lifecycle {
1025 PlatformLifecycle::Active => ApplicationLifecycle::Active,
1026 PlatformLifecycle::Inactive => ApplicationLifecycle::Inactive,
1027 PlatformLifecycle::Suspended => ApplicationLifecycle::Suspended,
1028 PlatformLifecycle::Resumed => ApplicationLifecycle::Active,
1029 PlatformLifecycle::Stopping => ApplicationLifecycle::Stopping,
1030 });
1031 None
1032 }
1033 PlatformEvent::ExternalDrag(event) => {
1034 let _ = self.handle_external_drag(event);
1035 None
1036 }
1037 PlatformEvent::CloseRequested => {
1038 self.shutdown();
1039 None
1040 }
1041 }
1042 }
1043 pub fn update_window_metrics(&mut self, metrics: WindowMetrics) -> bool {
1047 let mut environment = self.environment();
1048 environment.viewport = metrics.logical_size();
1049 environment.physical_width = metrics.physical_size.width;
1050 environment.physical_height = metrics.physical_size.height;
1051 environment.scale_factor = metrics.scale_factor;
1052 self.set_environment(environment)
1053 }
1054 pub fn register_builder(
1055 &mut self,
1056 id: ElementId,
1057 builder: impl FnMut() -> Widget + 'static,
1058 ) -> Result<(), TreeError> {
1059 self.register_builder_without_rebuild(id, builder)?;
1060 self.rebuild_from_builder(id)
1061 }
1062 pub(crate) fn register_builder_without_rebuild(
1063 &mut self,
1064 id: ElementId,
1065 builder: impl FnMut() -> Widget + 'static,
1066 ) -> Result<(), TreeError> {
1067 if !self.tree.element_exists(id) {
1068 return Err(TreeError::MissingElement(id));
1069 }
1070 self.builders.insert(id, Box::new(builder));
1071 Ok(())
1072 }
1073 pub(crate) fn install_initial_dependencies(
1074 &mut self,
1075 id: ElementId,
1076 initial_dependencies: Rc<RefCell<InitialBuildDependencies>>,
1077 ) {
1078 let initial_dependencies = std::mem::take(&mut *initial_dependencies.borrow_mut());
1079 let queue = self.reactive.clone();
1080 let root = queue.borrow().root;
1081 let queue_weak = Rc::downgrade(&queue);
1082 for dependency in initial_dependencies.signals {
1083 let tracked =
1084 crate::reactive::subscribe_element(dependency, root, id, queue_weak.clone());
1085 queue.borrow_mut().record(id, tracked);
1086 }
1087 for scope in initial_dependencies.focus_scopes {
1088 install_focus_scope_watch(&queue, id, &scope);
1089 }
1090 }
1091 pub fn schedule_update(&mut self, id: ElementId, widget: Widget) -> Result<(), TreeError> {
1092 if !self.tree.element_exists(id) {
1093 return Err(TreeError::MissingElement(id));
1094 }
1095 #[cfg(feature = "devtools")]
1096 self.tree
1097 .note_invalidation(id, InvalidationCause::WidgetConfigurationChanged);
1098 self.tree.mark_build(id)?;
1099 let mut widget = widget;
1100 self.prepare_widget(&mut widget);
1101 if self.pending.insert(id, widget).is_none() {
1102 self.order.push_back(id);
1103 }
1104 self.frame_requested = true;
1105 Ok(())
1106 }
1107 #[must_use]
1108 pub fn frame_requested(&self) -> bool {
1109 self.frame_requested
1110 || self.reactive.borrow().has_work()
1111 || self.tree.has_pending_application_fonts()
1112 || self.tree.has_pending_scroll_animations()
1113 || incular_animation::scheduler::has_scheduled_frame()
1114 }
1115 #[must_use]
1116 pub fn handle_input(&mut self, event: InputEvent) -> Option<EventTarget> {
1117 self.tree
1118 .set_diagnostic_input_trigger(None, &event, diagnostic_input_trigger);
1119 self.handle_input_timed(event)
1120 }
1121
1122 #[doc(hidden)]
1126 pub fn handle_input_at(&mut self, event: InputEvent, at: Instant) -> Option<EventTarget> {
1127 self.input_clock = Some(at);
1128 let target = self.handle_input(event);
1129 self.input_clock = None;
1130 target
1131 }
1132
1133 fn input_now(&self) -> Instant {
1134 self.input_clock.unwrap_or_else(Instant::now)
1135 }
1136
1137 pub(crate) fn handle_input_with_trigger(
1138 &mut self,
1139 event: InputEvent,
1140 trigger: impl Into<String>,
1141 ) -> Option<EventTarget> {
1142 self.tree.set_diagnostic_trigger(trigger);
1143 self.handle_input_timed(event)
1144 }
1145
1146 pub(crate) fn handle_input_timed(&mut self, event: InputEvent) -> Option<EventTarget> {
1147 let started = incular_core::time::Instant::now();
1148 let target = self.handle_input_inner(event);
1149 self.pending_event_processing_us = self
1150 .pending_event_processing_us
1151 .saturating_add(us_since_instant(started));
1152 target
1153 }
1154
1155 pub fn set_diagnostic_trigger(&self, trigger: impl Into<String>) {
1159 self.tree.set_diagnostic_trigger(trigger);
1160 }
1161 fn gesture_window(&self) -> u64 {
1163 self.window_id.map_or(0, |window| {
1164 (u64::from(window.index()) << 32) | u64::from(window.generation())
1165 })
1166 }
1167 fn handle_input_inner(&mut self, event: InputEvent) -> Option<EventTarget> {
1168 let gesture_window = self.gesture_window();
1169 let (pointer, device, kind, buttons, button, sample, phase, position, metadata_rich) =
1170 match event {
1171 InputEvent::Pointer { phase, position } => (
1172 0,
1173 0,
1174 PointerDeviceKind::Mouse,
1175 0,
1176 None,
1177 PointerSampleMetadata::default(),
1178 phase,
1179 position,
1180 false,
1181 ),
1182 InputEvent::PointerWithId {
1183 pointer,
1184 phase,
1185 position,
1186 } => (
1187 pointer,
1188 0,
1189 PointerDeviceKind::Mouse,
1190 0,
1191 None,
1192 PointerSampleMetadata::default(),
1193 phase,
1194 position,
1195 false,
1196 ),
1197 InputEvent::PointerWithMetadata {
1198 pointer,
1199 device,
1200 kind,
1201 buttons,
1202 button,
1203 sample,
1204 phase,
1205 position,
1206 } => (
1207 pointer, device, kind, buttons, button, sample, phase, position, true,
1208 ),
1209 event => {
1210 match event {
1211 InputEvent::Scroll { delta } => {
1212 if self.tree.scroll_at_with_axis_flip(
1217 self.last_pointer,
1218 delta,
1219 self.keyboard_modifiers.shift(),
1220 ) {
1221 self.frame_requested = true;
1222 }
1223 }
1224 InputEvent::Key(key) => {
1225 self.handle_key(key);
1226 }
1227 InputEvent::Text(text) => {
1228 self.insert_text(&text);
1229 }
1230 InputEvent::Ime(ime) => {
1231 self.handle_ime(ime);
1232 }
1233 InputEvent::TrackpadGesture(gesture) => {
1234 if self.pointer_position_known
1238 && let Some(element) =
1239 self.tree.dispatch_trackpad_gesture_in_window(
1240 gesture_window,
1241 self.last_pointer,
1242 gesture,
1243 )
1244 {
1245 self.frame_requested = true;
1246 return Some(EventTarget {
1247 element,
1248 action: None,
1249 });
1250 }
1251 }
1252 InputEvent::WindowResized { .. } => {}
1253 InputEvent::Pointer { .. } => unreachable!(),
1254 InputEvent::PointerWithId { .. } => unreachable!(),
1255 InputEvent::PointerWithMetadata { .. } => unreachable!(),
1256 }
1257 return None;
1258 }
1259 };
1260 let hover_capable = matches!(
1261 kind,
1262 PointerDeviceKind::Mouse
1263 | PointerDeviceKind::Trackpad
1264 | PointerDeviceKind::Stylus
1265 | PointerDeviceKind::InvertedStylus
1266 );
1267 if hover_capable {
1268 match phase {
1269 PointerPhase::Enter => {
1270 self.pointer_inside = true;
1271 self.pointer_position_known = false;
1272 }
1273 PointerPhase::Exit => {
1274 self.pointer_inside = false;
1275 self.pointer_position_known = false;
1276 }
1277 _ => {
1278 self.pointer_inside = true;
1279 self.pointer_position_known = true;
1280 }
1281 }
1282 }
1283 if phase != PointerPhase::Enter {
1284 self.last_pointer = position;
1285 }
1286 if phase == PointerPhase::Down {
1287 self.frame_requested = true;
1290 }
1291 let text_menu_was_showing = phase == PointerPhase::Down
1292 && button == Some(incular_core::SECONDARY_POINTER_BUTTON)
1293 && self.text_menu_open_at(position);
1294 if phase == PointerPhase::Down && self.tree.dismiss_transients_for_pointer(position) > 0 {
1295 self.frame_requested = true;
1296 }
1297 let raw_event = RawPointerEvent::new(
1298 pointer,
1299 device,
1300 kind,
1301 buttons,
1302 position,
1303 phase,
1304 self.input_now(),
1305 )
1306 .with_button(button)
1307 .with_sample(sample);
1308 if hover_capable {
1309 self.last_hover_sample =
1310 (!matches!(phase, PointerPhase::Enter | PointerPhase::Exit)).then_some(raw_event);
1311 }
1312 let hit = if matches!(phase, PointerPhase::Enter | PointerPhase::Exit) {
1315 PointerHitTest::default()
1316 } else {
1317 self.tree.pointer_hit_test(position)
1318 };
1319 let raw_target = self
1320 .tree
1321 .dispatch_raw_pointer_with_hit(gesture_window, raw_event, &hit);
1322 if metadata_rich
1323 && self.text_secondary_pointer(
1324 phase,
1325 button,
1326 device,
1327 pointer,
1328 position,
1329 &hit,
1330 text_menu_was_showing,
1331 )
1332 {
1333 self.frame_requested = true;
1334 }
1335 if matches!(phase, PointerPhase::Enter | PointerPhase::Exit) {
1336 if phase == PointerPhase::Exit {
1337 self.set_hover(None);
1338 if self.tree.scrollbar_pointer(PointerPhase::Exit, position) {
1339 self.frame_requested = true;
1340 }
1341 }
1342 return None;
1343 }
1344
1345 let primary_changed = !metadata_rich || button == Some(PRIMARY_POINTER_BUTTON);
1346 let stream = (device, pointer);
1347 let active_primary = self.legacy_pointer == Some(stream);
1348 let legacy_pointer = match phase {
1349 PointerPhase::Down if primary_changed && self.legacy_pointer.is_none() => {
1350 self.legacy_pointer = Some(stream);
1351 true
1352 }
1353 PointerPhase::Down => primary_changed && active_primary,
1354 PointerPhase::Move => {
1355 active_primary || (hover_capable && self.legacy_pointer.is_none())
1356 }
1357 PointerPhase::Up => primary_changed && active_primary,
1358 PointerPhase::Cancel => active_primary,
1359 PointerPhase::Enter | PointerPhase::Exit => false,
1360 };
1361
1362 if let Some(element) = raw_target {
1363 self.frame_requested = true;
1364 self.release_legacy_pointer(device, pointer, phase, primary_changed);
1365 return Some(EventTarget {
1366 element,
1367 action: None,
1368 });
1369 }
1370 let ordinary_gesture_allowed = legacy_pointer || !metadata_rich || !hover_capable;
1371 if ordinary_gesture_allowed
1372 && let Some(element) = self.tree.dispatch_pointer_gesture_with_hit(
1373 gesture_window,
1374 device,
1375 kind,
1376 PointerEvent {
1377 pointer,
1378 position,
1379 phase,
1380 time: self.input_now(),
1381 },
1382 &hit,
1383 )
1384 {
1385 self.frame_requested = true;
1386 if phase != PointerPhase::Down
1390 && active_primary
1391 && let Some(pressed) = self.pressed_button.take()
1392 {
1393 let _ = self
1394 .tree
1395 .set_button_interaction(pressed, None, Some(false), None);
1396 self.tree.button_ink(pressed, ButtonInk::Cancel);
1397 }
1398 self.release_legacy_pointer(device, pointer, phase, primary_changed);
1399 return Some(EventTarget {
1400 element,
1401 action: None,
1402 });
1403 }
1404 if !legacy_pointer {
1407 return None;
1408 }
1409 if self.tree.scrollbar_pointer_with_kind(phase, position, kind) {
1410 self.frame_requested = true;
1411 self.release_legacy_pointer(device, pointer, phase, primary_changed);
1412 return None;
1413 }
1414 let text_target = self.tree.text_field_for_hit(&hit);
1415 let selectable_target = self.tree.selectable_text_for_hit(&hit);
1416 let target = self.button_target(&hit);
1417 let result = match phase {
1418 PointerPhase::Enter | PointerPhase::Exit => {
1419 unreachable!("pointer boundary handled above")
1420 }
1421 PointerPhase::Move => {
1422 if let Some(field) = self.captured_text_field {
1423 self.text_field_primary_drag(field, position);
1424 self.frame_requested = true;
1425 }
1426 if let Some(label) = self.captured_selectable_text {
1427 if let Some(target) = selectable_target {
1428 if self
1429 .tree
1430 .selectable_text_set_selection(target, position, true)
1431 {
1432 self.captured_selectable_text = Some(target);
1433 self.set_focus(Some(target));
1434 self.frame_requested = true;
1435 }
1436 } else if self.tree.is_selectable_text(label) {
1437 self.frame_requested = true;
1438 }
1439 }
1440 self.set_hover(target.map(|(element, _)| element));
1441 target.map(|(element, action)| EventTarget { element, action })
1442 }
1443 PointerPhase::Down => {
1444 if let Some(label) = selectable_target {
1445 self.set_focus(Some(label));
1446 self.captured_selectable_text = Some(label);
1447 if self
1448 .tree
1449 .selectable_text_set_selection(label, position, false)
1450 {
1451 self.frame_requested = true;
1452 return Some(EventTarget {
1453 element: label,
1454 action: None,
1455 });
1456 }
1457 }
1458 if selectable_target.is_none()
1459 && text_target.is_none()
1460 && target.is_none()
1461 && let Some((element, interaction)) = self.tree.window_interaction_at(position)
1462 && let (Some(window_id), Some(manager)) =
1463 (self.window_id, self.window_manager.as_ref())
1464 {
1465 let operation = match interaction {
1466 WindowInteraction::Move => WindowOperation::BeginMoveDrag,
1467 WindowInteraction::Resize(direction) => {
1468 WindowOperation::BeginResizeDrag(direction)
1469 }
1470 };
1471 if manager.send_window_operation(window_id, operation) {
1472 self.release_legacy_pointer(device, pointer, phase, primary_changed);
1473 return Some(EventTarget {
1474 element,
1475 action: None,
1476 });
1477 }
1478 }
1479 let text_target_was_focused =
1480 text_target.is_some_and(|field| self.focused == Some(field));
1481 let keep_text_field_focus = self
1482 .focused
1483 .is_some_and(|focused| self.tree.is_text_field(focused))
1484 && self.tree.preserves_text_field_focus(position);
1485 let focused_text_field = self
1492 .focused
1493 .is_some_and(|focused| self.tree.is_text_field(focused));
1494 self.set_focus(if keep_text_field_focus {
1495 self.focused
1496 } else if text_target.is_some() || focused_text_field {
1497 text_target
1498 } else {
1499 self.focused
1500 });
1501 self.captured_text_field =
1505 text_target.filter(|field| self.text_field_pointer_selection_enabled(*field));
1506 if let Some(field) = text_target {
1507 if self.captured_text_field.is_some() {
1508 self.text_field_primary_down(field, position, text_target_was_focused);
1509 }
1510 self.frame_requested = true;
1511 return Some(EventTarget {
1512 element: field,
1513 action: None,
1514 });
1515 }
1516 self.set_hover(target.map(|(element, _)| element));
1517 self.pressed_button = target.map(|(element, _)| element);
1518 if let Some(element) = self.pressed_button {
1519 let _ = self
1520 .tree
1521 .set_button_interaction(element, None, Some(true), None);
1522 self.tree.button_ink(element, ButtonInk::Down(position));
1523 self.frame_requested = true;
1524 }
1525 target.map(|(element, action)| EventTarget { element, action })
1526 }
1527 PointerPhase::Up => {
1528 if self.captured_text_field.take().is_some() {
1529 self.text_field_primary_up();
1530 }
1531 self.captured_selectable_text = None;
1532 let pressed = self.pressed_button.take();
1533 let valid = pressed
1534 .zip(target)
1535 .filter(|(pressed, (target, _))| pressed == target)
1536 .map(|(_, target)| target);
1537 if let Some(element) = pressed {
1538 let _ = self
1539 .tree
1540 .set_button_interaction(element, None, Some(false), None);
1541 let ink = if valid.is_some() {
1542 ButtonInk::Confirm
1543 } else {
1544 ButtonInk::Cancel
1545 };
1546 self.tree.button_ink(element, ink);
1547 }
1548 if let Some((element, action)) = valid {
1549 if let Some(action) = action {
1550 self.activate_action(action);
1551 }
1552 Some(EventTarget { element, action })
1553 } else {
1554 None
1555 }
1556 }
1557 PointerPhase::Cancel => {
1558 self.captured_text_field = None;
1559 self.captured_selectable_text = None;
1560 if let Some(element) = self.pressed_button.take() {
1561 let _ = self
1562 .tree
1563 .set_button_interaction(element, None, Some(false), None);
1564 self.tree.button_ink(element, ButtonInk::Cancel);
1565 self.frame_requested = true;
1566 }
1567 None
1568 }
1569 };
1570 self.release_legacy_pointer(device, pointer, phase, primary_changed);
1571 result
1572 }
1573 fn button_target(&self, hit: &PointerHitTest) -> Option<(ElementId, Option<ActionId>)> {
1574 hit.target()
1575 .and_then(|render| self.tree.element_for_render(render))
1576 .and_then(|element| self.tree.button_ancestor(element))
1577 }
1578 fn refresh_hover_after_layout(&mut self) {
1584 if !self.pointer_inside || !self.pointer_position_known {
1585 return;
1586 }
1587 let Some(sample) = self.last_hover_sample else {
1588 return;
1589 };
1590 let event = RawPointerEvent {
1591 position: self.last_pointer,
1592 local_position: self.last_pointer,
1593 delta: incular_core::Offset::ZERO,
1594 local_delta: incular_core::Offset::ZERO,
1595 time: self.input_now(),
1596 ..sample
1597 };
1598 let hit = self.tree.pointer_hit_test(self.last_pointer);
1599 let window = self.gesture_window();
1600 self.tree.refresh_mouse_hover_in_window(window, event, &hit);
1601 if self.legacy_pointer.is_none() {
1602 let target = self.button_target(&hit);
1603 self.set_hover(target.map(|(element, _)| element));
1604 }
1605 }
1606 fn release_legacy_pointer(
1607 &mut self,
1608 device: u64,
1609 pointer: u64,
1610 phase: PointerPhase,
1611 primary_changed: bool,
1612 ) {
1613 if (phase == PointerPhase::Cancel || (phase == PointerPhase::Up && primary_changed))
1614 && self.legacy_pointer == Some((device, pointer))
1615 {
1616 self.legacy_pointer = None;
1617 }
1618 }
1619
1620 #[must_use]
1623 pub fn effective_mouse_cursor(&self) -> MouseCursor {
1624 if !self.pointer_inside || !self.pointer_position_known {
1625 return MouseCursor::Basic;
1626 }
1627 match self.tree.mouse_cursor_at(self.last_pointer) {
1628 MouseCursor::Defer => MouseCursor::Basic,
1629 cursor => cursor,
1630 }
1631 }
1632 pub(crate) fn set_focus(&mut self, next: Option<ElementId>) {
1633 self.initial_focus = InitialFocus::Settled;
1636 if self.focused == next {
1637 return;
1638 }
1639 self.cancel_ime_composition();
1644 if let Some(next) = next
1645 && self.tree.dismiss_transients_for_focus(next) > 0
1646 {
1647 self.frame_requested = true;
1648 }
1649 if let Some(previous) = self.focused {
1650 self.tree.set_keyboard_focus(previous, false);
1651 let _ = self.tree.set_focused(previous, false, Instant::now());
1652 }
1653 self.focused = next;
1654 if let Some(current) = next {
1655 let _ = self.tree.set_focused(current, true, Instant::now());
1656 self.tree.set_keyboard_focus(current, true);
1657 if self.tree.is_text_field(current) {
1658 let _ = self.ensure_text_history(current);
1659 }
1660 }
1661 self.frame_requested = true;
1662 self.sync_text_input_client();
1663 }
1664
1665 fn adopt_external_focus_change(&mut self) {
1669 let epoch = self.tree.focus_epoch();
1672 if self.adopted_focus_epoch == Some(epoch) {
1673 return;
1674 }
1675 if let Some(next) = self.tree.external_focus_change(self.focused) {
1676 self.set_focus(next);
1677 }
1678 self.adopted_focus_epoch = Some(self.tree.focus_epoch());
1679 }
1680
1681 fn reconcile_focus_and_selection_captures(&mut self) {
1682 if self.captured_text_field.is_some_and(|field| {
1683 !self.tree.is_text_field(field) || !self.tree.can_request_focus(field)
1684 }) {
1685 self.captured_text_field = None;
1686 }
1687 if self.captured_selectable_text.is_some_and(|label| {
1688 !self.tree.is_selectable_text(label) || !self.tree.can_request_focus(label)
1689 }) {
1690 self.captured_selectable_text = None;
1691 }
1692 if self
1693 .focused
1694 .is_some_and(|focused| !self.tree.can_request_focus(focused))
1695 {
1696 self.set_focus(None);
1697 }
1698 self.reconcile_button_tenure();
1699 }
1700
1701 fn reconcile_button_tenure(&mut self) {
1702 if self.pressed_button.is_some_and(|pressed| {
1707 self.tree
1708 .button_ancestor(pressed)
1709 .map(|(ancestor, _)| ancestor)
1710 != Some(pressed)
1711 }) && let Some(pressed) = self.pressed_button.take()
1712 {
1713 let _ = self
1714 .tree
1715 .set_button_interaction(pressed, None, Some(false), None);
1716 self.frame_requested = true;
1717 }
1718 if self.hovered_button.is_some_and(|hovered| {
1722 self.tree
1723 .button_ancestor(hovered)
1724 .map(|(ancestor, _)| ancestor)
1725 != Some(hovered)
1726 }) {
1727 self.set_hover(None);
1728 }
1729 }
1730
1731 fn ensure_text_history(&mut self, field: ElementId) -> Option<UndoHistoryController> {
1732 let editor = self.tree.text_controller(field)?;
1733 let replace = self
1734 .text_histories
1735 .get(&field)
1736 .is_none_or(|history| history.editor() != editor);
1737 if replace {
1738 self.text_histories
1739 .insert(field, UndoHistoryController::new(editor));
1740 }
1741 let history = self.text_histories.get(&field).cloned()?;
1742 if let Some(max_entries) = self.tree.text_field_history_max_entries(field)
1743 && history.max_entries() != max_entries
1744 {
1745 history.set_max_entries(max_entries);
1746 }
1747 Some(history)
1748 }
1749
1750 fn show_focused_caret_on_screen(&mut self) -> bool {
1754 let Some(field) = self.focused.filter(|id| self.tree.is_text_field(*id)) else {
1755 self.caret_reveal_key = None;
1756 return false;
1757 };
1758 let Some(controller) = self.tree.text_controller(field) else {
1759 return false;
1760 };
1761 let key = (field, controller.revisions().0, controller.selection());
1762 if self.caret_reveal_key == Some(key) {
1763 return false;
1764 }
1765 self.caret_reveal_key = Some(key);
1766 self.tree.text_field_show_caret_on_screen(field)
1767 }
1768
1769 fn sync_text_input_client(&mut self) {
1770 let snapshot = self
1771 .focused
1772 .and_then(|id| self.tree.text_field_input_snapshot(id));
1773 let Some(snapshot) = snapshot else {
1774 if let Some(client) = self.text_input_client.take() {
1775 self.text_input_commands
1776 .push_back(TextInputCommand::Hide { client });
1777 self.text_input_commands
1778 .push_back(TextInputCommand::Clear { client });
1779 }
1780 self.text_input_configuration = None;
1781 self.text_input_state = None;
1782 self.text_input_caret_rect = None;
1783 return;
1784 };
1785
1786 let client = TextInputClientId::new(snapshot.client_id);
1787 let input_type = match snapshot.input_type {
1788 TextInputTypeHint::Text if snapshot.multiline => TextInputType::Multiline,
1789 TextInputTypeHint::Text => TextInputType::Text,
1790 TextInputTypeHint::Multiline => TextInputType::Multiline,
1791 TextInputTypeHint::Number => TextInputType::Number,
1792 TextInputTypeHint::Phone => TextInputType::Phone,
1793 TextInputTypeHint::Email => TextInputType::Email,
1794 TextInputTypeHint::Url => TextInputType::Url,
1795 TextInputTypeHint::Password => TextInputType::Password,
1796 };
1797 let action = match snapshot.input_action {
1798 TextInputActionHint::Unspecified => {
1799 if snapshot.multiline {
1800 TextInputAction::Newline
1801 } else {
1802 TextInputAction::Done
1803 }
1804 }
1805 TextInputActionHint::None => TextInputAction::None,
1806 TextInputActionHint::Done => TextInputAction::Done,
1807 TextInputActionHint::Go => TextInputAction::Go,
1808 TextInputActionHint::Search => TextInputAction::Search,
1809 TextInputActionHint::Send => TextInputAction::Send,
1810 TextInputActionHint::Next => TextInputAction::Next,
1811 TextInputActionHint::Previous => TextInputAction::Previous,
1812 TextInputActionHint::Newline => TextInputAction::Newline,
1813 };
1814 let configuration = TextInputConfiguration {
1815 client,
1816 input_type: if snapshot.obscure_text {
1817 TextInputType::Password
1818 } else {
1819 input_type
1820 },
1821 action,
1822 multiline: snapshot.multiline,
1823 enabled: snapshot.enabled,
1824 read_only: snapshot.read_only,
1825 obscure_text: snapshot.obscure_text,
1826 traits: snapshot.traits,
1827 };
1828 let state = TextInputState {
1829 text: snapshot.text,
1830 selection_start: snapshot.selection.base,
1831 selection_end: snapshot.selection.extent,
1832 composing: snapshot.composing.map(|range| (range.start, range.end)),
1833 };
1834 let client_changed = self.text_input_client != Some(client);
1835 let configuration_changed = self.text_input_configuration.as_ref() != Some(&configuration);
1836 if client_changed || configuration_changed {
1837 self.text_input_commands
1838 .push_back(TextInputCommand::SetClient {
1839 configuration: configuration.clone(),
1840 state: state.clone(),
1841 caret_rect: snapshot.caret_rect,
1842 });
1843 } else if self.text_input_state.as_ref() != Some(&state) {
1844 self.text_input_commands
1845 .push_back(TextInputCommand::Update {
1846 client,
1847 state: state.clone(),
1848 });
1849 }
1850 if !client_changed
1851 && self.text_input_caret_rect != Some(snapshot.caret_rect)
1852 && !configuration_changed
1853 {
1854 self.text_input_commands
1855 .push_back(TextInputCommand::SetCaretRect {
1856 client,
1857 rect: snapshot.caret_rect,
1858 });
1859 }
1860 self.text_input_client = Some(client);
1861 self.text_input_configuration = Some(configuration);
1862 self.text_input_state = Some(state);
1863 self.text_input_caret_rect = Some(snapshot.caret_rect);
1864 }
1865 fn focus_next(&mut self, reverse: bool) {
1866 let fields = self.tree.focusable_elements();
1867 if fields.is_empty() {
1868 return;
1869 }
1870 let index = self
1871 .focused
1872 .and_then(|focused| fields.iter().position(|id| *id == focused));
1873 let next = match index {
1874 Some(index) if reverse => fields[(index + fields.len() - 1) % fields.len()],
1875 Some(index) => fields[(index + 1) % fields.len()],
1876 None if reverse => *fields.last().expect("not empty"),
1877 None => fields[0],
1878 };
1879 self.set_focus(Some(next));
1880 }
1881 fn handle_key(&mut self, event: KeyboardEvent) {
1882 self.track_keyboard_modifiers(&event);
1883 if matches!(
1886 event.code,
1887 Code::AltLeft
1888 | Code::AltRight
1889 | Code::ControlLeft
1890 | Code::ControlRight
1891 | Code::ShiftLeft
1892 | Code::ShiftRight
1893 | Code::MetaLeft
1894 | Code::MetaRight
1895 ) {
1896 self.frame_requested = true;
1897 }
1898 if event.state.is_down() {
1899 self.editing_diagnostics.key_down_received += 1;
1900 } else {
1901 self.editing_diagnostics.key_up_received += 1;
1902 }
1903
1904 self.adopt_external_focus_change();
1908
1909 if self.tree.dispatch_keyboard(self.focused, event.clone()) {
1913 self.frame_requested = true;
1914 return;
1915 }
1916 if !event.state.is_down() {
1917 return;
1918 }
1919 if event.code == Code::Escape
1920 && self
1921 .tree
1922 .dismiss_transients(incular_widgets::TransientDismissReason::Escape)
1923 > 0
1924 {
1925 self.frame_requested = true;
1926 return;
1927 }
1928 if matches!(
1929 event.code,
1930 Code::ArrowUp | Code::ArrowDown | Code::ArrowLeft | Code::ArrowRight
1931 ) && let Some(focused) = self.focused
1932 && let Some(next) = self.tree.menu_directional_focus_target(focused, event.code)
1933 {
1934 self.set_focus(Some(next));
1935 return;
1936 }
1937 if matches!(event.code, Code::Enter | Code::NumpadEnter | Code::Space)
1940 && let Some(focused) = self.focused
1941 && let Some((owner, Some(action))) = self.tree.button_ancestor(focused)
1942 && (owner == focused || self.tree.is_menu_item_focus(focused))
1943 && {
1944 self.tree.button_ink(owner, ButtonInk::Activate);
1947 true
1948 }
1949 && self.activate_action(action)
1950 {
1951 return;
1952 }
1953 if event.code == Code::Tab
1956 && !event.modifiers.ctrl()
1957 && !event.modifiers.alt()
1958 && !event.modifiers.meta()
1959 {
1960 self.focus_next(event.modifiers.shift());
1961 return;
1962 }
1963 if let Some(label) = self.focused.filter(|id| self.tree.is_selectable_text(*id)) {
1964 if self.run_selectable_text_key(label, &event) {
1965 self.frame_requested = true;
1966 }
1967 return;
1968 }
1969 let Some(field) = self.focused.filter(|id| self.tree.is_text_field(*id)) else {
1970 return;
1971 };
1972 let Some(controller) = self.tree.text_controller(field) else {
1973 return;
1974 };
1975 if !self.run_text_field_key(field, &controller, &event) {
1978 return;
1979 }
1980 self.hide_text_field_menu(field);
1981 controller.reset_caret(Instant::now());
1982 self.frame_requested = true;
1983 }
1984 fn text_field_obscured(&self, field: ElementId) -> bool {
1986 self.tree
1987 .text_field_input_snapshot(field)
1988 .is_some_and(|snapshot| snapshot.obscure_text)
1989 }
1990
1991 fn text_field_pointer_selection_enabled(&self, field: ElementId) -> bool {
1994 self.tree
1995 .text_field_input_snapshot(field)
1996 .is_none_or(|snapshot| !(snapshot.read_only && snapshot.obscure_text))
1997 }
1998
1999 fn text_field_copy_allowed(
2002 &self,
2003 field: ElementId,
2004 controller: &TextEditingController,
2005 ) -> bool {
2006 !controller.value().selection.is_collapsed() && !self.text_field_obscured(field)
2007 }
2008
2009 fn cut_text_field_selection(
2012 &mut self,
2013 field: ElementId,
2014 controller: &TextEditingController,
2015 ) -> bool {
2016 if !self.tree.text_field_is_editable(field)
2017 || !self.text_field_copy_allowed(field, controller)
2018 {
2019 return false;
2020 }
2021 self.clipboard.set_text(controller.selected_text());
2022 controller.replace_selection("");
2023 true
2024 }
2025
2026 fn insert_text(&mut self, text: &str) {
2027 let Some(field) = self.focused.filter(|id| self.tree.is_text_field(*id)) else {
2028 return;
2029 };
2030 if !self.tree.text_field_is_editable(field) {
2031 return;
2032 }
2033 if text.chars().any(char::is_control) {
2034 return;
2035 }
2036 if let Some(controller) = self.tree.text_controller(field) {
2037 controller.insert(text);
2038 self.editing_diagnostics.text_commits += 1;
2039 controller.reset_caret(Instant::now());
2040 self.frame_requested = true;
2041 }
2042 }
2043
2044 pub fn handle_text_input_action(&mut self, action: TextInputAction) -> bool {
2048 let Some(field) = self.focused.filter(|id| self.tree.is_text_field(*id)) else {
2049 return false;
2050 };
2051 let editable = self.tree.text_field_is_editable(field);
2052 if !editable {
2053 return false;
2054 }
2055 let handled = match action {
2056 TextInputAction::Newline => {
2057 if self.tree.is_multiline_text_field(field) {
2058 self.tree.text_controller(field).is_some_and(|controller| {
2059 controller.insert("\n");
2060 controller.reset_caret(Instant::now());
2061 true
2062 })
2063 } else {
2064 self.tree.submit_text_field(field)
2065 }
2066 }
2067 TextInputAction::Next => {
2068 let submitted = self.tree.submit_text_field(field);
2069 self.focus_next(false);
2070 submitted
2071 }
2072 TextInputAction::Previous => {
2073 let submitted = self.tree.submit_text_field(field);
2074 self.focus_next(true);
2075 submitted
2076 }
2077 TextInputAction::Done
2078 | TextInputAction::Go
2079 | TextInputAction::Search
2080 | TextInputAction::Send => self.tree.submit_text_field(field),
2081 TextInputAction::Unspecified | TextInputAction::None => false,
2082 };
2083 if handled {
2084 self.frame_requested = true;
2085 }
2086 handled
2087 }
2088 fn cancel_ime_composition(&mut self) {
2093 let owner = self.ime_composition.take();
2094 if let Some(owner) = owner {
2095 owner.controller.clear_preedit();
2096 }
2097 }
2098
2099 fn ime_owner_current(&self) -> bool {
2103 self.ime_composition.as_ref().is_some_and(|owner| {
2104 self.focused == Some(owner.field)
2105 && self.tree.is_text_field(owner.field)
2106 && self.tree.text_field_is_editable(owner.field)
2107 && self
2108 .tree
2109 .text_controller(owner.field)
2110 .is_some_and(|current| current == owner.controller)
2111 })
2112 }
2113
2114 fn ime_focused_target(&self) -> Option<ImeCompositionOwner> {
2115 let field = self
2116 .focused
2117 .filter(|id| self.tree.text_field_is_editable(*id))?;
2118 Some(ImeCompositionOwner {
2119 field,
2120 controller: self.tree.text_controller(field)?,
2121 })
2122 }
2123
2124 fn validate_ime_composition(&mut self) {
2125 if !self.ime_owner_current() {
2126 self.cancel_ime_composition();
2127 }
2128 }
2129
2130 fn handle_ime(&mut self, event: ImeEvent) {
2137 self.editing_diagnostics.ime_events += 1;
2138 self.validate_ime_composition();
2139 let target = match &event {
2140 ImeEvent::Preedit { .. } => {
2141 if self.ime_composition.is_none() {
2142 self.ime_composition = self.ime_focused_target();
2143 }
2144 self.ime_composition
2145 .as_ref()
2146 .map(|owner| owner.controller.clone())
2147 }
2148 ImeEvent::Commit(_) => self
2149 .ime_composition
2150 .take()
2151 .or_else(|| self.ime_focused_target())
2152 .map(|owner| owner.controller),
2153 ImeEvent::End => self.ime_composition.take().map(|owner| owner.controller),
2154 };
2155 let Some(controller) = target else {
2156 return;
2157 };
2158 match event {
2159 ImeEvent::Preedit { text, selection } => {
2160 controller.set_preedit(
2161 text,
2162 selection.map(|(start, end)| TextRange::new(start, end)),
2163 );
2164 }
2165 ImeEvent::Commit(text) => {
2166 controller.commit_preedit(&text);
2172 }
2173 ImeEvent::End => {
2174 controller.clear_preedit();
2175 }
2176 }
2177 controller.reset_caret(Instant::now());
2178 self.frame_requested = true;
2179 }
2180 pub fn run_frame(
2181 &mut self,
2182 constraints: Constraints,
2183 ) -> Result<(DisplayList, FrameStats), TreeError> {
2184 self.run_frame_at(constraints, Instant::now())
2185 }
2186 pub fn run_frame_at(
2189 &mut self,
2190 constraints: Constraints,
2191 now: Instant,
2192 ) -> Result<(DisplayList, FrameStats), TreeError> {
2193 let message_guard = tracing::info_span!("incular.messages").entered();
2194 let message_span = profiling::PhaseSpan::start();
2195 crate::environment::flush_deferred_invalidations();
2198 self.process_runtime_work_at(now);
2199 incular_animation::scheduler::begin_frame(now);
2202 self.tree.tick_animations(now);
2205 let runtime_messages = message_span.elapsed_us();
2206 drop(message_guard);
2207 let before = self.tree.diagnostics();
2208 let build_guard =
2209 tracing::info_span!("incular.build", elements_updated = tracing::field::Empty,)
2210 .entered();
2211 let build_span = profiling::PhaseSpan::start();
2212 let mut updated = 0;
2213 while let Some(id) = self.order.pop_front() {
2214 if let Some(widget) = self.pending.remove(&id)
2215 && self.tree.element_exists(id)
2216 {
2217 self.tree.update(id, widget)?;
2218 updated += 1;
2219 }
2220 }
2221 loop {
2222 while let Some((root, node)) = { self.reactive.borrow_mut().take_node() } {
2223 if let Some(node) = node.upgrade() {
2224 node.run(root);
2225 }
2226 }
2227 while let Some(id) = { self.reactive.borrow_mut().take() } {
2228 if self.tree.element_exists(id) && self.builders.contains_key(&id) {
2229 #[cfg(feature = "devtools")]
2230 for cause in self.reactive.borrow_mut().take_causes(id) {
2231 self.tree.note_invalidation(id, cause);
2232 }
2233 self.rebuild_from_builder(id)?;
2234 updated += 1;
2235 }
2236 }
2237 if !self.reactive.borrow().has_work() {
2238 break;
2239 }
2240 }
2241 self.adopt_external_focus_change();
2244 for id in self.tree.take_unmounted() {
2245 self.handlers_stale = true;
2246 self.builders.remove(&id);
2247 self.reactive.borrow_mut().forget(id);
2248 if let Some(scope) = self.owner_scopes.remove(&id) {
2249 scope.cancel();
2250 }
2251 self.text_histories.remove(&id);
2252 }
2253 self.reconcile_focus_and_selection_captures();
2254 self.validate_ime_composition();
2255 self.prune_handlers();
2256 let build = build_span.elapsed_us();
2257 drop(build_guard);
2258 let reduced_motion = self.environment.borrow().reduced_motion;
2265 let fling_live = self.tree.pump_scroll_flings(now, reduced_motion);
2266 let scrollbar_live = self.tree.pump_scrollbar_fades(now);
2269 let gesture_timers_live = self.tree.pump_gesture_timers(now);
2272 let _layout_guard = tracing::info_span!("incular.layout").entered();
2273 let layout_span = profiling::PhaseSpan::start();
2274 self.tree.layout(constraints)?;
2275 let layout = layout_span.elapsed_us();
2276 drop(_layout_guard);
2277 for (action, handler) in self.tree.take_pending_handlers() {
2278 self.handlers_stale = true;
2279 self.handlers.insert(action, handler);
2280 }
2281 for id in self.tree.take_unmounted() {
2285 self.handlers_stale = true;
2286 self.builders.remove(&id);
2287 self.reactive.borrow_mut().forget(id);
2288 if let Some(scope) = self.owner_scopes.remove(&id) {
2289 scope.cancel();
2290 }
2291 self.text_histories.remove(&id);
2292 }
2293 self.reconcile_focus_and_selection_captures();
2294 self.validate_ime_composition();
2295 self.prune_handlers();
2296 if self.initial_focus == InitialFocus::AwaitingLayout {
2300 self.set_focus(self.tree.autofocus_element());
2301 }
2302 let _composite_guard = tracing::info_span!("incular.composite").entered();
2303 let composite_span = profiling::PhaseSpan::start();
2304 let (composited, animations_active) = self.tree.update_compositor(now)?;
2305 let composite = composite_span.elapsed_us();
2306 drop(_composite_guard);
2307 let geometry = self.tree.diagnostics();
2311 if geometry.layouts != before.layouts
2312 || geometry.scroll_offset_updates != before.scroll_offset_updates
2313 {
2314 self.refresh_hover_after_layout();
2315 }
2316 let _semantics_guard = tracing::info_span!("incular.semantics").entered();
2317 let semantics_span = profiling::PhaseSpan::start();
2318 self.tree.update_semantics();
2319 let semantics = semantics_span.elapsed_us();
2320 drop(_semantics_guard);
2321 let _paint_guard = tracing::info_span!("incular.paint").entered();
2322 let paint_span = profiling::PhaseSpan::start();
2323 let display_list = self.tree.paint();
2324 let paint = paint_span.elapsed_us();
2325 drop(_paint_guard);
2326 self.sync_text_input_client();
2327 let caret_revealed = self.show_focused_caret_on_screen();
2330 self.frame_requested = fling_live
2336 || caret_revealed
2337 || gesture_timers_live
2338 || scrollbar_live
2339 || !self.pending.is_empty()
2340 || self.reactive.borrow().has_work()
2341 || crate::environment::has_deferred_invalidations()
2342 || animations_active;
2343 incular_animation::scheduler::end_frame(now);
2345 let after = self.tree.diagnostics();
2346 let timings = FrameTimings {
2347 event_processing: std::mem::take(&mut self.pending_event_processing_us),
2348 runtime_messages,
2349 build,
2350 layout,
2351 composite,
2352 semantics,
2353 paint,
2354 cpu_total: runtime_messages + build + layout + composite + semantics + paint,
2355 };
2356 tracing::debug!(
2357 target: "incular::frame",
2358 cpu_total_us = timings.cpu_total,
2359 build_us = timings.build,
2360 layout_us = timings.layout,
2361 paint_us = timings.paint,
2362 );
2363 Ok((
2364 display_list.clone(),
2365 FrameStats {
2366 updated_elements: updated,
2367 rebuilt_elements: after.rebuilds - before.rebuilds,
2368 laid_out_render_objects: after.layouts - before.layouts,
2369 repainted_render_objects: after.paints - before.paints,
2370 composited: u64::from(composited),
2371 active_animations: u64::from(animations_active),
2372 display_list_commands: display_list.len(),
2373 requested_another_frame: self.frame_requested,
2374 timings,
2375 },
2376 ))
2377 }
2378 #[must_use]
2379 pub fn diagnostics(&self) -> Diagnostics {
2380 self.tree.diagnostics()
2381 }
2382 #[cfg(feature = "hot-reload")]
2385 pub(crate) fn reassemble(&mut self) {
2386 let root = self.application_root;
2387 let ids = root
2388 .into_iter()
2389 .chain(self.builders.keys().copied().filter(|id| Some(*id) != root))
2390 .collect::<Vec<_>>();
2391 let mut reactive = self.reactive.borrow_mut();
2392 for id in ids {
2393 #[cfg(feature = "devtools")]
2394 reactive.note_cause(id, InvalidationCause::Manual);
2395 reactive.enqueue(id);
2396 }
2397 drop(reactive);
2398 self.frame_requested = true;
2399 }
2400 pub(crate) fn rebuild_from_builder(&mut self, id: ElementId) -> Result<(), TreeError> {
2404 self.reactive.borrow_mut().refresh(id);
2405 let widget = {
2406 let _build_scope = BuildScopeGuard::enter(BuildScope {
2407 root: self.reactive.borrow().root,
2408 element: Some(id),
2409 queue: Rc::downgrade(&self.reactive),
2410 initial_dependencies: None,
2411 spawner: self.spawner(),
2412 owner_scope: self
2413 .owner_scopes
2414 .get(&id)
2415 .cloned()
2416 .unwrap_or_else(|| self.window_scope.clone()),
2417 });
2418 self.builders.get_mut(&id).expect("registered builder")()
2419 };
2420 let mut widget = widget;
2421 self.prepare_widget(&mut widget);
2422 self.tree.update(id, widget)?;
2423 Ok(())
2424 }
2425 fn prepare_widget(&mut self, widget: &mut Widget) {
2426 self.handlers_stale = true;
2427 let handlers = &mut self.handlers;
2428 let tree = &mut self.tree;
2429 widget.bind_callbacks(&mut |callback| {
2430 let id = tree.allocate_action();
2431 handlers.insert(id, callback);
2432 id
2433 });
2434 }
2435 fn activate_action(&mut self, action: ActionId) -> bool {
2436 let mut handled = false;
2437 if let Some(callback) = self.handlers.get(&action).cloned() {
2438 callback();
2439 handled = true;
2440 }
2441 if let Some(callback) = self.legacy_action_handler.as_mut() {
2442 callback(action);
2443 handled = true;
2444 }
2445 self.frame_requested |= handled;
2446 handled
2447 }
2448
2449 fn prune_handlers(&mut self) {
2450 if !std::mem::take(&mut self.handlers_stale) {
2451 return;
2452 }
2453 let active = self.tree.action_ids();
2454 self.handlers.retain(|id, _| active.contains(id));
2455 }
2456 fn set_hover(&mut self, next: Option<ElementId>) {
2457 if self.hovered_button == next {
2458 return;
2459 }
2460 if let Some(previous) = self.hovered_button {
2461 let _ = self
2462 .tree
2463 .set_button_interaction(previous, Some(false), None, None);
2464 if let Some(action) = self.tree.hover_actions_for_element(previous).1
2465 && let Some(callback) = self.handlers.get(&action).cloned()
2466 {
2467 callback();
2468 }
2469 }
2470 self.hovered_button = next;
2471 if let Some(current) = next {
2472 if self.pressed_button != Some(current) {
2473 let _ = self
2474 .tree
2475 .set_button_interaction(current, Some(true), None, None);
2476 }
2477 if let Some(action) = self.tree.hover_actions_for_element(current).0
2478 && let Some(callback) = self.handlers.get(&action).cloned()
2479 {
2480 callback();
2481 }
2482 }
2483 self.frame_requested = true;
2484 }
2485}
2486
2487fn us_since_instant(started: incular_core::time::Instant) -> u32 {
2492 started.elapsed().as_micros().try_into().unwrap_or(u32::MAX)
2493}