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