1use crate::application_types::{
2 ApplicationLifecycle, LifecycleTransition, RuntimeErrorReport, WindowError,
3};
4#[cfg(feature = "devtools")]
5use crate::environment::DEV_TASK_COMPLETION;
6#[cfg(feature = "devtools")]
7use crate::environment::ReactiveRootId;
8use crate::environment::{
9 BuildScope, BuildScopeGuard, InitialBuildDependencies, ReactiveQueue, environment_change_mask,
10 install_focus_scope_watch,
11};
12use crate::profiling;
13use crate::profiling::FrameTimings;
14use crate::tasks::{
15 self, RuntimeDiagnostics, RuntimeSpawner, RuntimeWake, Task, TaskFailure, TaskHandle,
16 TaskScope, TokioHandle, UiDispatcher,
17};
18use crate::undo::UndoHistoryController;
19use crate::window_commands::{WindowHandle, WindowOpener};
20use crate::window_state::WindowManager;
21use incular_config::{Constraints, RuntimeEnvironment};
22use incular_core::{
23 Code, ImeEvent, InputEvent, KeyboardEvent, Modifiers, Offset, PRIMARY_POINTER_BUTTON,
24 PointerPhase, PointerSampleMetadata, Rect,
25};
26use incular_platform::{
27 Clipboard, ClipboardCapabilities, ClipboardError, ClipboardWriteReport, DataTransfer,
28 ExternalDragEvent, ExternalDragResponse, MemoryClipboard, PlatformEvent, PlatformLifecycle,
29 TextInputAction, TextInputClientId, TextInputCommand, TextInputConfiguration, TextInputState,
30 TextInputType, TransferReadRequest, WindowId, WindowMetrics, WindowOperation, WindowOptions,
31};
32use incular_rendering::DisplayList;
33use incular_semantics::{SemanticAction, SemanticNodeId};
34#[cfg(feature = "devtools")]
35use incular_widgets::internal::InvalidationCause;
36use incular_widgets::internal::{
37 ActionId, Diagnostics, ElementId, MouseCursor, PlatformMenuBinding, PointerDeviceKind,
38 PointerEvent, RawPointerEvent, TextEditingController, TextRange, TextSelection, TreeError,
39 WidgetTree, WindowInteraction,
40};
41use incular_widgets::{TextInputActionHint, TextInputTypeHint, Widget};
42use std::{
43 cell::{Cell, RefCell},
44 collections::{HashMap, VecDeque},
45 rc::{Rc, Weak},
46 sync::atomic::{AtomicU64, Ordering},
47 time::Instant,
48};
49
50static RUNTIME_SEQUENCE: AtomicU64 = AtomicU64::new(1);
53
54#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
55pub struct FrameStats {
56 pub updated_elements: usize,
57 pub rebuilt_elements: u64,
58 pub laid_out_render_objects: u64,
59 pub repainted_render_objects: u64,
60 pub composited: u64,
61 pub active_animations: u64,
62 pub display_list_commands: usize,
63 pub requested_another_frame: bool,
64 pub timings: FrameTimings,
66}
67#[derive(Clone, Copy, Debug, PartialEq, Eq)]
68pub struct EventTarget {
69 pub element: ElementId,
70 pub action: Option<ActionId>,
71}
72#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
74pub struct FocusDiagnostics {
75 pub focused_element: Option<ElementId>,
76 pub text_pointer_capture: Option<ElementId>,
77}
78#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
79pub struct EditingDiagnostics {
80 pub key_down_received: u64,
81 pub key_up_received: u64,
82 pub backspace_commands: u64,
83 pub delete_commands: u64,
84 pub text_commits: u64,
85 pub ime_events: u64,
86}
87
88struct ImeCompositionOwner {
101 field: ElementId,
102 controller: TextEditingController,
103}
104#[derive(Clone, Copy, PartialEq, Eq)]
105enum InitialFocus {
106 AwaitingLayout,
107 Settled,
108}
109
110pub struct Runtime {
112 pub(crate) tree: WidgetTree,
113 pub(crate) pending_event_processing_us: u32,
115 pub(crate) pending: HashMap<ElementId, Widget>,
116 pub(crate) order: VecDeque<ElementId>,
117 pub(crate) reactive: Rc<RefCell<ReactiveQueue>>,
118 pub(crate) builders: HashMap<ElementId, Box<dyn FnMut() -> Widget>>,
119 pub(crate) handlers: HashMap<ActionId, Rc<dyn Fn()>>,
120 handlers_stale: bool,
123 pub(crate) legacy_action_handler: Option<Box<dyn FnMut(ActionId)>>,
124 pub(crate) hovered_button: Option<ElementId>,
125 pub(crate) pressed_button: Option<ElementId>,
126 pub(crate) legacy_pointer: Option<(u64, u64)>,
129 pub(crate) last_pointer: Offset,
130 pub(crate) pointer_inside: bool,
131 pub(crate) pointer_position_known: bool,
132 pub(crate) focused: Option<ElementId>,
133 initial_focus: InitialFocus,
134 pub(crate) captured_text_field: Option<ElementId>,
135 pub(crate) captured_selectable_text: Option<ElementId>,
136 ime_composition: Option<ImeCompositionOwner>,
137 pub(crate) text_histories: HashMap<ElementId, UndoHistoryController>,
138 pub(crate) text_input_commands: VecDeque<TextInputCommand>,
139 pub(crate) text_input_client: Option<TextInputClientId>,
140 pub(crate) text_input_configuration: Option<TextInputConfiguration>,
141 pub(crate) text_input_state: Option<TextInputState>,
142 pub(crate) text_input_caret_rect: Option<Rect>,
143 pub(crate) clipboard: Box<dyn Clipboard>,
144 pub(crate) editing_diagnostics: EditingDiagnostics,
145 pub(crate) frame_requested: bool,
146 pub(crate) scheduler: Rc<RefCell<tasks::TaskScheduler>>,
147 pub(crate) environment: Rc<RefCell<RuntimeEnvironment>>,
148 pub(crate) lifecycle: ApplicationLifecycle,
149 pub(crate) lifecycle_observers: Vec<Rc<dyn Fn(LifecycleTransition)>>,
150 pub(crate) error_observers: Vec<Rc<dyn Fn(RuntimeErrorReport)>>,
151 pub(crate) environment_generation: u64,
152 pub(crate) environment_dependencies: Rc<Cell<u16>>,
153 pub(crate) application_root: Option<ElementId>,
154 pub(crate) owner_scopes: HashMap<ElementId, TaskScope>,
155 pub(crate) window_id: Option<WindowId>,
156 pub(crate) window_scope: TaskScope,
157 pub(crate) window_manager: Option<WindowManager>,
158 driver_domain: Rc<u64>,
163}
164
165pub(crate) fn diagnostic_input_trigger(event: &InputEvent) -> String {
166 match event {
167 InputEvent::Pointer { phase, position } => {
168 format!("pointer({phase:?}, {:.1}, {:.1})", position.x, position.y)
169 }
170 InputEvent::PointerWithId {
171 pointer,
172 phase,
173 position,
174 } => format!(
175 "pointer({pointer}, {phase:?}, {:.1}, {:.1})",
176 position.x, position.y
177 ),
178 InputEvent::PointerWithMetadata {
179 pointer,
180 device,
181 kind,
182 buttons,
183 button,
184 sample: _,
185 phase,
186 position,
187 } => format!(
188 "pointer({pointer}, device={device}, {kind:?}, buttons={buttons:#x}, changed={button:?}, {phase:?}, {:.1}, {:.1})",
189 position.x, position.y
190 ),
191 InputEvent::TrackpadGesture(event) => format!(
192 "trackpad(device={}, kind={:?}, phase={:?})",
193 event.device(),
194 event.kind(),
195 event.phase()
196 ),
197 InputEvent::Scroll { delta } => format!("scroll({:.1}, {:.1})", delta.x, delta.y),
198 InputEvent::Key(event) => format!("key({:?}, {:?})", event.state, event.code),
199 InputEvent::Text(text) => format!("text_input(chars={})", text.chars().count()),
200 InputEvent::Ime(ImeEvent::Preedit { text, .. }) => {
201 format!("ime_preedit(chars={})", text.chars().count())
202 }
203 InputEvent::Ime(ImeEvent::Commit(text)) => {
204 format!("ime_commit(chars={})", text.chars().count())
205 }
206 InputEvent::Ime(ImeEvent::End) => "ime_end".to_owned(),
207 InputEvent::WindowResized { size, scale_factor } => format!(
208 "window_resize({:.1}x{:.1}, scale={scale_factor:.2})",
209 size.width, size.height
210 ),
211 }
212}
213
214impl Runtime {
215 pub fn new(root: Widget) -> Result<Self, TreeError> {
216 Self::with_scheduler(root, tasks::TaskScheduler::new())
217 }
218 fn with_scheduler(
219 root: Widget,
220 scheduler: Rc<RefCell<tasks::TaskScheduler>>,
221 ) -> Result<Self, TreeError> {
222 let window_scope = tasks::TaskScheduler::spawner(&scheduler).scope();
223 Self::with_window(root, scheduler, None, window_scope, None)
224 }
225
226 pub(crate) fn with_window(
227 root: Widget,
228 scheduler: Rc<RefCell<tasks::TaskScheduler>>,
229 window_id: Option<WindowId>,
230 window_scope: TaskScope,
231 window_manager: Option<WindowManager>,
232 ) -> Result<Self, TreeError> {
233 Self::with_window_and_reactive(
234 root,
235 scheduler,
236 window_id,
237 window_scope,
238 window_manager,
239 ReactiveQueue::new(),
240 )
241 }
242 pub(crate) fn with_window_and_reactive(
243 mut root: Widget,
244 scheduler: Rc<RefCell<tasks::TaskScheduler>>,
245 window_id: Option<WindowId>,
246 window_scope: TaskScope,
247 window_manager: Option<WindowManager>,
248 reactive: Rc<RefCell<ReactiveQueue>>,
249 ) -> Result<Self, TreeError> {
250 let mut tree = WidgetTree::new();
251 let mut handlers = HashMap::new();
252 root.bind_callbacks(&mut |callback| {
253 let action = tree.allocate_action();
254 handlers.insert(action, callback);
255 action
256 });
257 tree.mount(root)?;
258 for (action, handler) in tree.take_pending_handlers() {
259 handlers.insert(action, handler);
260 }
261 let mut runtime = Self {
262 pending_event_processing_us: 0,
263 tree,
264 pending: HashMap::new(),
265 order: VecDeque::new(),
266 reactive,
267 builders: HashMap::new(),
268 handlers,
269 handlers_stale: false,
270 legacy_action_handler: None,
271 hovered_button: None,
272 pressed_button: None,
273 legacy_pointer: None,
274 last_pointer: Offset::ZERO,
275 pointer_inside: false,
276 pointer_position_known: false,
277 focused: None,
278 initial_focus: InitialFocus::AwaitingLayout,
279 captured_text_field: None,
280 captured_selectable_text: None,
281 ime_composition: None,
282 text_histories: HashMap::new(),
283 text_input_commands: VecDeque::new(),
284 text_input_client: None,
285 text_input_configuration: None,
286 text_input_state: None,
287 text_input_caret_rect: None,
288 clipboard: Box::new(MemoryClipboard::default()),
289 editing_diagnostics: EditingDiagnostics::default(),
290 frame_requested: true,
291 scheduler,
292 environment: Rc::new(RefCell::new(RuntimeEnvironment::default())),
293 lifecycle: ApplicationLifecycle::Starting,
294 lifecycle_observers: Vec::new(),
295 error_observers: Vec::new(),
296 environment_generation: 0,
297 environment_dependencies: Rc::new(Cell::new(0)),
298 application_root: None,
299 owner_scopes: HashMap::new(),
300 window_id,
301 window_scope,
302 window_manager,
303 driver_domain: Rc::new(
304 RUNTIME_SEQUENCE
305 .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |id| id.checked_add(1))
306 .expect("runtime driver identity space exhausted"),
307 ),
308 };
309 if let Some(autofocus) = runtime.tree.autofocus_element() {
314 runtime.set_focus(Some(autofocus));
315 }
316 Ok(runtime)
317 }
318 #[must_use]
322 pub fn id(&self) -> u64 {
323 *self.driver_domain
324 }
325
326 pub(crate) fn driver_token(&self) -> Weak<u64> {
329 Rc::downgrade(&self.driver_domain)
330 }
331
332 #[must_use]
335 pub fn spawner(&self) -> RuntimeSpawner {
336 self.window_id.map_or_else(
337 || tasks::TaskScheduler::spawner(&self.scheduler),
338 |window_id| tasks::TaskScheduler::spawner_for(&self.scheduler, window_id),
339 )
340 }
341 #[must_use]
344 pub fn dispatcher(&self) -> UiDispatcher {
345 self.scheduler.borrow().dispatcher_for(self.window_id)
346 }
347 #[must_use]
350 pub fn tokio_handle(&self) -> TokioHandle {
351 self.scheduler.borrow().tokio_handle()
352 }
353 pub fn set_wake_handler(&mut self, wake: std::sync::Arc<dyn RuntimeWake>) {
354 self.scheduler.borrow_mut().set_wake(wake);
355 }
356
357 #[must_use]
360 pub const fn window_id(&self) -> Option<WindowId> {
361 self.window_id
362 }
363
364 #[doc(hidden)]
368 #[must_use]
369 pub fn platform_menu_bindings(&self) -> Vec<PlatformMenuBinding> {
370 self.tree.platform_menu_bindings()
371 }
372
373 pub fn request_frame(&mut self) {
375 self.frame_requested = true;
376 }
377
378 pub fn take_text_input_commands(&mut self) -> Vec<TextInputCommand> {
382 self.text_input_commands.drain(..).collect()
383 }
384
385 pub fn open_window(
389 &mut self,
390 options: WindowOptions,
391 root: Widget,
392 ) -> Result<WindowHandle, WindowError> {
393 self.window_manager
394 .as_ref()
395 .ok_or(WindowError::ApplicationStopped)?
396 .open_window(options, root)
397 }
398
399 #[must_use]
402 pub fn window_opener(&self) -> Option<WindowOpener> {
403 self.window_manager
404 .as_ref()
405 .cloned()
406 .map(|manager| WindowOpener { manager })
407 }
408
409 #[must_use]
411 pub fn window_handle(&self) -> Option<WindowHandle> {
412 let id = self.window_id?;
413 self.window_manager.as_ref()?.handle(id)
414 }
415
416 #[must_use]
419 pub fn window_task_scope(&self) -> TaskScope {
420 self.window_scope.clone()
421 }
422
423 pub fn spawn_blocking<T>(
426 &self,
427 work: impl FnOnce() -> T + Send + 'static,
428 complete: impl FnOnce(Result<T, TaskFailure>, &mut Runtime) + 'static,
429 ) -> TaskHandle
430 where
431 T: Send + 'static,
432 {
433 self.spawner()
434 .spawn_blocking_in(&self.window_scope, work, complete)
435 }
436 pub fn spawn<F, T>(&self, future: F) -> Task<T>
439 where
440 F: std::future::Future<Output = T> + Send + 'static,
441 T: Send + 'static,
442 {
443 self.spawner().spawn_in(&self.window_scope, future)
444 }
445 pub fn spawn_into<F, T>(
447 &self,
448 future: F,
449 complete: impl FnOnce(Result<T, TaskFailure>, &mut Runtime) + 'static,
450 ) -> TaskHandle
451 where
452 F: std::future::Future<Output = T> + Send + 'static,
453 T: Send + 'static,
454 {
455 self.spawner()
456 .spawn_into_in(&self.window_scope, future, complete)
457 }
458 pub fn process_runtime_work_at(&mut self, _now: Instant) {
462 let scheduler = self.scheduler.clone();
463 let work = scheduler.borrow_mut().take_turn();
467 for work in work {
468 self.process_ui_work(&scheduler, work);
469 }
470 if self.reactive.borrow().has_work() || !self.pending.is_empty() {
471 self.frame_requested = true;
472 }
473 }
474
475 pub(crate) fn process_ui_work(
476 &mut self,
477 scheduler: &Rc<RefCell<tasks::TaskScheduler>>,
478 work: tasks::UiWork,
479 ) {
480 let mut failures = Vec::new();
481 match work {
482 tasks::UiWork::Dispatch { callback, .. } => {
483 if scheduler.borrow().accepting()
484 && std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| callback(self)))
485 .is_err()
486 {
487 failures.push(TaskFailure::Panicked);
488 }
489 }
490 tasks::UiWork::Task(pending) => {
491 scheduler.borrow().detach_scope(&pending.control);
492 let stale_owner = pending.control.owner_is_stale(self);
493 if !scheduler.borrow().accepting() || pending.control.is_cancelled() || stale_owner
494 {
495 (pending.discard)();
496 scheduler
497 .borrow_mut()
498 .record_discard(pending.blocking, stale_owner);
499 } else {
500 #[cfg(feature = "devtools")]
501 let previous_task_context = DEV_TASK_COMPLETION.with(|context| {
502 let previous = context.get();
503 context.set(true);
504 previous
505 });
506 let failure = (pending.finish)(self);
507 #[cfg(feature = "devtools")]
508 DEV_TASK_COMPLETION.with(|context| context.set(previous_task_context));
509 if let Some(failure) = scheduler
510 .borrow_mut()
511 .record_completion(pending.blocking, failure)
512 {
513 failures.push(failure);
514 }
515 }
516 }
517 }
518 for failure in failures {
519 for observer in &self.error_observers {
520 observer(RuntimeErrorReport {
521 failure: failure.clone(),
522 });
523 }
524 }
525 if self.reactive.borrow().has_work() || !self.pending.is_empty() {
526 self.frame_requested = true;
527 }
528 }
529 pub fn process_runtime_work(&mut self) {
530 self.process_runtime_work_at(Instant::now());
531 }
532 #[must_use]
533 pub fn runtime_diagnostics(&self) -> RuntimeDiagnostics {
534 self.scheduler.borrow().diagnostics()
535 }
536 #[must_use]
539 pub fn debug_dump(&self) -> String {
540 let diagnostics = self.runtime_diagnostics();
541 let (application_tasks, owner_tasks) = self.scheduler.borrow().active_ownership();
542 format!(
543 "Lifecycle: {:?}\nTokio tasks tracked: {}\n application: {}\n owner-scoped: {}\nPending UI messages: {}\nEnvironment generation: {}",
544 self.lifecycle,
545 diagnostics.active_tracked_tasks,
546 application_tasks,
547 owner_tasks,
548 diagnostics
549 .ui_messages_enqueued
550 .saturating_sub(diagnostics.ui_messages_processed),
551 self.environment_generation,
552 )
553 }
554 #[must_use]
555 pub fn environment(&self) -> RuntimeEnvironment {
556 self.environment.borrow().clone()
557 }
558 #[must_use]
559 pub const fn lifecycle(&self) -> ApplicationLifecycle {
560 self.lifecycle
561 }
562 #[must_use]
563 pub const fn environment_generation(&self) -> u64 {
564 self.environment_generation
565 }
566 pub fn set_environment(&mut self, environment: RuntimeEnvironment) -> bool {
569 let environment = environment.normalized();
570 let previous = self.environment.borrow().clone();
571 if previous == environment {
572 return false;
573 }
574 let changed = environment_change_mask(&previous, &environment);
575 *self.environment.borrow_mut() = environment.clone();
576 let retained_environment_dirty = self.tree.set_environment(environment);
581 self.environment_generation = self
582 .environment_generation
583 .checked_add(1)
584 .expect("runtime environment generation exhausted");
585 if retained_environment_dirty {
586 self.frame_requested = true;
587 }
588 if self.environment_dependencies.get() & changed != 0
589 && let Some(root) = self.application_root
590 {
591 #[cfg(feature = "devtools")]
592 self.tree
593 .note_invalidation(root, InvalidationCause::EnvironmentChanged);
594 let _ = self.rebuild_from_builder(root);
595 self.frame_requested = true;
596 }
597 true
598 }
599 pub fn transition_lifecycle(&mut self, lifecycle: ApplicationLifecycle) -> bool {
600 if self.lifecycle == lifecycle {
601 return false;
602 }
603 let transition = LifecycleTransition {
604 previous: self.lifecycle,
605 current: lifecycle,
606 };
607 self.lifecycle = lifecycle;
608 for observer in &self.lifecycle_observers {
609 observer(transition);
610 }
611 if matches!(
612 lifecycle,
613 ApplicationLifecycle::Stopping | ApplicationLifecycle::Terminated
614 ) {
615 self.reactive.borrow_mut().clear();
616 self.scheduler.borrow_mut().shutdown();
617 }
618 true
619 }
620 pub fn observe_lifecycle(&mut self, observer: impl Fn(LifecycleTransition) + 'static) {
621 self.lifecycle_observers.push(Rc::new(observer));
622 }
623 pub fn observe_runtime_error(&mut self, observer: impl Fn(RuntimeErrorReport) + 'static) {
624 self.error_observers.push(Rc::new(observer));
625 }
626 pub fn shutdown(&mut self) {
630 if self.focused.is_some() {
631 self.set_focus(None);
632 } else {
633 self.cancel_ime_composition();
634 self.sync_text_input_client();
635 }
636 self.tree.cancel_all_gesture_streams();
637 self.captured_text_field = None;
638 self.captured_selectable_text = None;
639 self.hovered_button = None;
640 self.pressed_button = None;
641 self.legacy_pointer = None;
642 self.text_histories.clear();
643 self.tree.release_edit_subscriptions();
644 let _ = self.transition_lifecycle(ApplicationLifecycle::Stopping);
645 for (_, scope) in self.owner_scopes.drain() {
646 scope.cancel();
647 }
648 self.scheduler.borrow_mut().shutdown();
649 self.lifecycle = ApplicationLifecycle::Terminated;
650 }
651
652 pub(crate) fn dispose_window(&mut self) {
656 if self.focused.is_some() {
657 self.set_focus(None);
658 } else {
659 self.cancel_ime_composition();
660 self.sync_text_input_client();
661 }
662 self.tree.cancel_all_gesture_streams();
663 self.tree.release_edit_subscriptions();
664 self.legacy_pointer = None;
665 self.window_scope.cancel();
666 for (_, scope) in self.owner_scopes.drain() {
667 scope.cancel();
668 }
669 self.reactive.borrow_mut().clear();
670 self.builders.clear();
671 self.pending.clear();
672 self.order.clear();
673 self.handlers.clear();
674 self.legacy_action_handler = None;
675 self.text_histories.clear();
676 self.text_input_commands.clear();
677 self.text_input_client = None;
678 self.text_input_configuration = None;
679 self.text_input_state = None;
680 self.text_input_caret_rect = None;
681 self.captured_text_field = None;
682 self.captured_selectable_text = None;
683 self.hovered_button = None;
684 self.pressed_button = None;
685 }
686 #[must_use]
687 pub fn tree(&self) -> &WidgetTree {
688 &self.tree
689 }
690
691 #[cfg(feature = "devtools")]
692 pub(crate) fn devtools_reactive_root(&self) -> ReactiveRootId {
693 self.reactive.borrow().root
694 }
695 #[must_use]
696 pub fn tree_mut(&mut self) -> &mut WidgetTree {
697 &mut self.tree
698 }
699
700 #[must_use]
704 pub fn content_sensitivity(&self) -> incular_config::ContentSensitivity {
705 self.tree.content_sensitivity()
706 }
707
708 #[must_use]
712 pub fn transient_surfaces(&self) -> Vec<incular_widgets::TransientSurfaceSnapshot> {
713 self.tree.transient_surfaces()
714 }
715
716 pub fn set_native_transient_bounds(&mut self, bounds: Option<incular_core::Rect>) -> bool {
719 let changed = self.tree.set_native_transient_bounds(bounds);
720 self.frame_requested |= changed;
721 changed
722 }
723
724 pub fn set_native_transient_presentations(
728 &mut self,
729 ids: impl IntoIterator<Item = incular_widgets::TransientSurfaceId>,
730 ) -> bool {
731 let changed = self.tree.set_native_transient_presentations(ids);
732 self.frame_requested |= changed;
733 changed
734 }
735
736 pub fn dismiss_transients(&mut self, reason: incular_widgets::TransientDismissReason) -> bool {
740 let dismissed = self.tree.dismiss_transients(reason) > 0;
741 self.frame_requested |= dismissed;
742 dismissed
743 }
744 #[cfg(feature = "devtools")]
745 pub(crate) fn devtools_edit_property(
746 &mut self,
747 id: incular_devtools_protocol::DevWidgetId,
748 name: &str,
749 value: &incular_devtools_protocol::DebugValue,
750 ) -> bool {
751 let edited = self.tree.devtools_edit_property(id, name, value);
752 self.frame_requested |= edited;
753 edited
754 }
755 pub fn set_animation_time_scale(&mut self, scale: f32) {
759 self.tree.set_animation_time_scale(scale);
760 }
761 #[must_use]
762 pub fn focused_element(&self) -> Option<ElementId> {
763 self.focused
764 }
765 pub fn dispatch_semantic_action(
781 &mut self,
782 node: SemanticNodeId,
783 action: SemanticAction,
784 ) -> bool {
785 let Some(element) = self.tree.element_for_semantic_node(node) else {
786 return false;
787 };
788 self.tree.note_semantic_action();
789 let handled = match action {
790 SemanticAction::Focus => {
791 if self.tree.can_request_focus(element) {
792 self.set_focus(Some(element));
793 true
794 } else {
795 false
796 }
797 }
798 SemanticAction::Activate => {
799 if let Some(callback) = self.tree.semantic_action_callback(
800 element,
801 incular_semantics::SemanticActionKind::Activate,
802 ) {
803 callback();
804 true
805 } else if self.tree.semantic_activate_executable(element) {
806 self.tree
807 .action_for_element(element)
808 .map(|action| self.activate_action(action))
809 .unwrap_or(false)
810 } else {
811 false
812 }
813 }
814 SemanticAction::SetText(text) => {
815 if !self.tree.text_field_is_editable(element) {
816 false
817 } else {
818 self.tree
819 .text_controller(element)
820 .map(|controller| {
821 controller.set_text(text);
822 true
823 })
824 .unwrap_or(false)
825 }
826 }
827 SemanticAction::SetSelection { base, extent } => self
828 .tree
829 .text_controller(element)
830 .map(|controller| {
831 let length = controller.text().len();
832 controller.set_selection(TextSelection {
833 base: base.min(length),
834 extent: extent.min(length),
835 });
836 true
837 })
838 .unwrap_or(false),
839 SemanticAction::ScrollForward => self
840 .tree
841 .semantic_action_callback(
842 element,
843 incular_semantics::SemanticActionKind::ScrollForward,
844 )
845 .map(|callback| {
846 callback();
847 true
848 })
849 .unwrap_or_else(|| self.tree.semantic_scroll(element, true)),
850 SemanticAction::ScrollBackward => self
851 .tree
852 .semantic_action_callback(
853 element,
854 incular_semantics::SemanticActionKind::ScrollBackward,
855 )
856 .map(|callback| {
857 callback();
858 true
859 })
860 .unwrap_or_else(|| self.tree.semantic_scroll(element, false)),
861 SemanticAction::Increment => self
862 .tree
863 .semantic_action_callback(element, incular_semantics::SemanticActionKind::Increment)
864 .map(|callback| {
865 callback();
866 true
867 })
868 .unwrap_or_else(|| self.tree.dispatch_semantic_increment(element, true)),
869 SemanticAction::Decrement => self
870 .tree
871 .semantic_action_callback(element, incular_semantics::SemanticActionKind::Decrement)
872 .map(|callback| {
873 callback();
874 true
875 })
876 .unwrap_or_else(|| self.tree.dispatch_semantic_increment(element, false)),
877 };
878 if handled {
879 self.frame_requested = true;
880 }
881 handled
882 }
883 #[must_use]
884 pub fn focus_diagnostics(&self) -> FocusDiagnostics {
885 FocusDiagnostics {
886 focused_element: self.focused,
887 text_pointer_capture: self.captured_text_field,
888 }
889 }
890 #[must_use]
891 pub const fn editing_diagnostics(&self) -> EditingDiagnostics {
892 self.editing_diagnostics
893 }
894 pub fn set_clipboard(&mut self, clipboard: Box<dyn Clipboard>) {
895 self.clipboard = clipboard;
896 }
897 #[must_use]
900 pub fn clipboard_capabilities(&self) -> ClipboardCapabilities {
901 self.clipboard.capabilities()
902 }
903 pub fn read_clipboard(
905 &mut self,
906 request: TransferReadRequest,
907 ) -> Result<DataTransfer, ClipboardError> {
908 self.clipboard.read(request)
909 }
910 pub fn write_clipboard(
913 &mut self,
914 transfer: DataTransfer,
915 ) -> Result<ClipboardWriteReport, ClipboardError> {
916 self.clipboard.write(transfer)
917 }
918 pub fn set_clipboard_text(&mut self, text: impl Into<String>) {
919 self.clipboard.set_text(text.into());
920 }
921 #[must_use]
924 pub fn handle_external_drag(&mut self, event: ExternalDragEvent) -> ExternalDragResponse {
925 let response = self.tree.dispatch_external_drag(event);
926 self.frame_requested = true;
927 response
928 }
929 pub fn handle_platform_event(&mut self, event: PlatformEvent) -> Option<EventTarget> {
932 match event {
933 PlatformEvent::Input(input) => self.handle_input(input),
934 PlatformEvent::TextInputAction(action) => {
935 self.handle_text_input_action(action);
936 None
937 }
938 PlatformEvent::Metrics(metrics) => {
939 self.update_window_metrics(metrics);
940 None
941 }
942 PlatformEvent::Environment(environment) => {
943 self.set_environment(environment);
944 None
945 }
946 PlatformEvent::Lifecycle(lifecycle) => {
947 self.transition_lifecycle(match lifecycle {
948 PlatformLifecycle::Active => ApplicationLifecycle::Active,
949 PlatformLifecycle::Inactive => ApplicationLifecycle::Inactive,
950 PlatformLifecycle::Suspended => ApplicationLifecycle::Suspended,
951 PlatformLifecycle::Resumed => ApplicationLifecycle::Active,
952 PlatformLifecycle::Stopping => ApplicationLifecycle::Stopping,
953 });
954 None
955 }
956 PlatformEvent::ExternalDrag(event) => {
957 let _ = self.handle_external_drag(event);
958 None
959 }
960 PlatformEvent::CloseRequested => {
961 self.shutdown();
962 None
963 }
964 }
965 }
966 pub fn update_window_metrics(&mut self, metrics: WindowMetrics) -> bool {
970 let mut environment = self.environment();
971 environment.viewport = metrics.logical_size();
972 environment.physical_width = metrics.physical_size.width;
973 environment.physical_height = metrics.physical_size.height;
974 environment.scale_factor = metrics.scale_factor;
975 self.set_environment(environment)
976 }
977 pub fn register_builder(
978 &mut self,
979 id: ElementId,
980 builder: impl FnMut() -> Widget + 'static,
981 ) -> Result<(), TreeError> {
982 self.register_builder_without_rebuild(id, builder)?;
983 self.rebuild_from_builder(id)
984 }
985 pub(crate) fn register_builder_without_rebuild(
986 &mut self,
987 id: ElementId,
988 builder: impl FnMut() -> Widget + 'static,
989 ) -> Result<(), TreeError> {
990 if !self.tree.element_exists(id) {
991 return Err(TreeError::MissingElement(id));
992 }
993 self.builders.insert(id, Box::new(builder));
994 Ok(())
995 }
996 pub(crate) fn install_initial_dependencies(
997 &mut self,
998 id: ElementId,
999 initial_dependencies: Rc<RefCell<InitialBuildDependencies>>,
1000 ) {
1001 let initial_dependencies = std::mem::take(&mut *initial_dependencies.borrow_mut());
1002 let queue = self.reactive.clone();
1003 let root = queue.borrow().root;
1004 let queue_weak = Rc::downgrade(&queue);
1005 for dependency in initial_dependencies.signals {
1006 let tracked =
1007 crate::reactive::subscribe_element(dependency, root, id, queue_weak.clone());
1008 queue.borrow_mut().record(id, tracked);
1009 }
1010 for scope in initial_dependencies.focus_scopes {
1011 install_focus_scope_watch(&queue, id, &scope);
1012 }
1013 }
1014 pub fn schedule_update(&mut self, id: ElementId, widget: Widget) -> Result<(), TreeError> {
1015 if !self.tree.element_exists(id) {
1016 return Err(TreeError::MissingElement(id));
1017 }
1018 #[cfg(feature = "devtools")]
1019 self.tree
1020 .note_invalidation(id, InvalidationCause::WidgetConfigurationChanged);
1021 self.tree.mark_build(id)?;
1022 let mut widget = widget;
1023 self.prepare_widget(&mut widget);
1024 if self.pending.insert(id, widget).is_none() {
1025 self.order.push_back(id);
1026 }
1027 self.frame_requested = true;
1028 Ok(())
1029 }
1030 #[must_use]
1031 pub fn frame_requested(&self) -> bool {
1032 self.frame_requested || self.reactive.borrow().has_work()
1033 }
1034 #[must_use]
1035 pub fn handle_input(&mut self, event: InputEvent) -> Option<EventTarget> {
1036 let trigger = diagnostic_input_trigger(&event);
1037 self.handle_input_with_trigger(event, trigger)
1038 }
1039
1040 pub(crate) fn handle_input_with_trigger(
1041 &mut self,
1042 event: InputEvent,
1043 trigger: impl Into<String>,
1044 ) -> Option<EventTarget> {
1045 self.tree.set_diagnostic_trigger(trigger);
1046 let started = std::time::Instant::now();
1047 let target = self.handle_input_inner(event);
1048 self.pending_event_processing_us = self
1049 .pending_event_processing_us
1050 .saturating_add(us_since_instant(started));
1051 target
1052 }
1053
1054 pub fn set_diagnostic_trigger(&self, trigger: impl Into<String>) {
1058 self.tree.set_diagnostic_trigger(trigger);
1059 }
1060 fn handle_input_inner(&mut self, event: InputEvent) -> Option<EventTarget> {
1061 let gesture_window = self.window_id.map_or(0, |window| {
1062 (u64::from(window.index()) << 32) | u64::from(window.generation())
1063 });
1064 let (pointer, device, kind, buttons, button, sample, phase, position, metadata_rich) =
1065 match event {
1066 InputEvent::Pointer { phase, position } => (
1067 0,
1068 0,
1069 PointerDeviceKind::Mouse,
1070 0,
1071 None,
1072 PointerSampleMetadata::default(),
1073 phase,
1074 position,
1075 false,
1076 ),
1077 InputEvent::PointerWithId {
1078 pointer,
1079 phase,
1080 position,
1081 } => (
1082 pointer,
1083 0,
1084 PointerDeviceKind::Mouse,
1085 0,
1086 None,
1087 PointerSampleMetadata::default(),
1088 phase,
1089 position,
1090 false,
1091 ),
1092 InputEvent::PointerWithMetadata {
1093 pointer,
1094 device,
1095 kind,
1096 buttons,
1097 button,
1098 sample,
1099 phase,
1100 position,
1101 } => (
1102 pointer, device, kind, buttons, button, sample, phase, position, true,
1103 ),
1104 event => {
1105 match event {
1106 InputEvent::Scroll { delta } => {
1107 if self.tree.scroll_at(self.last_pointer, delta) {
1110 self.frame_requested = true;
1111 }
1112 }
1113 InputEvent::Key(key) => {
1114 self.handle_key(key);
1115 }
1116 InputEvent::Text(text) => {
1117 self.insert_text(&text);
1118 }
1119 InputEvent::Ime(ime) => {
1120 self.handle_ime(ime);
1121 }
1122 InputEvent::TrackpadGesture(gesture) => {
1123 if self.pointer_position_known
1127 && let Some(element) =
1128 self.tree.dispatch_trackpad_gesture_in_window(
1129 gesture_window,
1130 self.last_pointer,
1131 gesture,
1132 )
1133 {
1134 self.frame_requested = true;
1135 return Some(EventTarget {
1136 element,
1137 action: None,
1138 });
1139 }
1140 }
1141 InputEvent::WindowResized { .. } => {}
1142 InputEvent::Pointer { .. } => unreachable!(),
1143 InputEvent::PointerWithId { .. } => unreachable!(),
1144 InputEvent::PointerWithMetadata { .. } => unreachable!(),
1145 }
1146 return None;
1147 }
1148 };
1149 let hover_capable = matches!(
1150 kind,
1151 PointerDeviceKind::Mouse
1152 | PointerDeviceKind::Trackpad
1153 | PointerDeviceKind::Stylus
1154 | PointerDeviceKind::InvertedStylus
1155 );
1156 if hover_capable {
1157 match phase {
1158 PointerPhase::Enter => {
1159 self.pointer_inside = true;
1160 self.pointer_position_known = false;
1161 }
1162 PointerPhase::Exit => {
1163 self.pointer_inside = false;
1164 self.pointer_position_known = false;
1165 }
1166 _ => {
1167 self.pointer_inside = true;
1168 self.pointer_position_known = true;
1169 }
1170 }
1171 }
1172 if phase != PointerPhase::Enter {
1173 self.last_pointer = position;
1174 }
1175 if phase == PointerPhase::Down && self.tree.dismiss_transients_for_pointer(position) > 0 {
1176 self.frame_requested = true;
1177 }
1178 let raw_target = self.tree.dispatch_raw_pointer_in_window(
1179 gesture_window,
1180 RawPointerEvent {
1181 pointer,
1182 device,
1183 kind,
1184 buttons,
1185 button,
1186 sample,
1187 position,
1188 phase,
1189 time: Instant::now(),
1190 },
1191 );
1192 if matches!(phase, PointerPhase::Enter | PointerPhase::Exit) {
1193 if phase == PointerPhase::Exit {
1194 self.set_hover(None);
1195 if self.tree.scrollbar_pointer(PointerPhase::Exit, position) {
1196 self.frame_requested = true;
1197 }
1198 }
1199 return None;
1200 }
1201
1202 let primary_changed = !metadata_rich || button == Some(PRIMARY_POINTER_BUTTON);
1203 let stream = (device, pointer);
1204 let active_primary = self.legacy_pointer == Some(stream);
1205 let legacy_pointer = match phase {
1206 PointerPhase::Down if primary_changed && self.legacy_pointer.is_none() => {
1207 self.legacy_pointer = Some(stream);
1208 true
1209 }
1210 PointerPhase::Down => primary_changed && active_primary,
1211 PointerPhase::Move => {
1212 active_primary || (hover_capable && self.legacy_pointer.is_none())
1213 }
1214 PointerPhase::Up => primary_changed && active_primary,
1215 PointerPhase::Cancel => active_primary,
1216 PointerPhase::Enter | PointerPhase::Exit => false,
1217 };
1218
1219 if let Some(element) = raw_target {
1220 self.frame_requested = true;
1221 self.release_legacy_pointer(device, pointer, phase, primary_changed);
1222 return Some(EventTarget {
1223 element,
1224 action: None,
1225 });
1226 }
1227 let ordinary_gesture_allowed = legacy_pointer || !metadata_rich || !hover_capable;
1228 if ordinary_gesture_allowed
1229 && let Some(element) = self.tree.dispatch_device_gesture_in_window(
1230 gesture_window,
1231 device,
1232 PointerEvent {
1233 pointer,
1234 position,
1235 phase,
1236 time: Instant::now(),
1237 },
1238 )
1239 {
1240 self.frame_requested = true;
1241 self.release_legacy_pointer(device, pointer, phase, primary_changed);
1242 return Some(EventTarget {
1243 element,
1244 action: None,
1245 });
1246 }
1247 if !legacy_pointer {
1250 return None;
1251 }
1252 if self.tree.scrollbar_pointer(phase, position) {
1253 self.frame_requested = true;
1254 self.release_legacy_pointer(device, pointer, phase, primary_changed);
1255 return None;
1256 }
1257 let text_target = self.tree.text_field_at(position);
1258 let selectable_target = self.tree.selectable_text_at(position);
1259 let target = self
1260 .tree
1261 .hit_test(position)
1262 .and_then(|render| self.tree.element_for_render(render))
1263 .and_then(|element| self.tree.button_ancestor(element));
1264 let result = match phase {
1265 PointerPhase::Enter | PointerPhase::Exit => {
1266 unreachable!("pointer boundary handled above")
1267 }
1268 PointerPhase::Move => {
1269 if let Some(field) = self.captured_text_field {
1270 self.tree
1271 .text_field_set_caret(field, position, true, Instant::now());
1272 self.frame_requested = true;
1273 }
1274 if let Some(label) = self.captured_selectable_text {
1275 if let Some(target) = selectable_target {
1276 if self
1277 .tree
1278 .selectable_text_set_selection(target, position, true)
1279 {
1280 self.captured_selectable_text = Some(target);
1281 self.set_focus(Some(target));
1282 self.frame_requested = true;
1283 }
1284 } else if self.tree.is_selectable_text(label) {
1285 self.frame_requested = true;
1286 }
1287 }
1288 self.set_hover(target.map(|(element, _)| element));
1289 target.map(|(element, action)| EventTarget { element, action })
1290 }
1291 PointerPhase::Down => {
1292 if let Some(label) = selectable_target {
1293 self.set_focus(Some(label));
1294 self.captured_selectable_text = Some(label);
1295 if self
1296 .tree
1297 .selectable_text_set_selection(label, position, false)
1298 {
1299 self.frame_requested = true;
1300 return Some(EventTarget {
1301 element: label,
1302 action: None,
1303 });
1304 }
1305 }
1306 if selectable_target.is_none()
1307 && text_target.is_none()
1308 && target.is_none()
1309 && let Some((element, interaction)) = self.tree.window_interaction_at(position)
1310 && let (Some(window_id), Some(manager)) =
1311 (self.window_id, self.window_manager.as_ref())
1312 {
1313 let operation = match interaction {
1314 WindowInteraction::Move => WindowOperation::BeginMoveDrag,
1315 WindowInteraction::Resize(direction) => {
1316 WindowOperation::BeginResizeDrag(direction)
1317 }
1318 };
1319 if manager.send_window_operation(window_id, operation) {
1320 self.release_legacy_pointer(device, pointer, phase, primary_changed);
1321 return Some(EventTarget {
1322 element,
1323 action: None,
1324 });
1325 }
1326 }
1327 let keep_text_field_focus = self
1328 .focused
1329 .is_some_and(|focused| self.tree.is_text_field(focused))
1330 && self.tree.preserves_text_field_focus(position);
1331 self.set_focus(if keep_text_field_focus {
1332 self.focused
1333 } else {
1334 text_target
1335 });
1336 self.captured_text_field = text_target;
1337 if let Some(field) = text_target {
1338 self.tree
1339 .text_field_set_caret(field, position, false, Instant::now());
1340 self.frame_requested = true;
1341 return Some(EventTarget {
1342 element: field,
1343 action: None,
1344 });
1345 }
1346 self.set_hover(target.map(|(element, _)| element));
1347 self.pressed_button = target.map(|(element, _)| element);
1348 if let Some(element) = self.pressed_button {
1349 let _ = self
1350 .tree
1351 .set_button_interaction(element, None, Some(true), None);
1352 self.frame_requested = true;
1353 }
1354 target.map(|(element, action)| EventTarget { element, action })
1355 }
1356 PointerPhase::Up => {
1357 self.captured_text_field = None;
1358 self.captured_selectable_text = None;
1359 let pressed = self.pressed_button.take();
1360 let valid = pressed
1361 .zip(target)
1362 .filter(|(pressed, (target, _))| pressed == target)
1363 .map(|(_, target)| target);
1364 if let Some(element) = pressed {
1365 let _ = self
1366 .tree
1367 .set_button_interaction(element, None, Some(false), None);
1368 }
1369 if let Some((element, action)) = valid {
1370 if let Some(action) = action {
1371 self.activate_action(action);
1372 }
1373 Some(EventTarget { element, action })
1374 } else {
1375 None
1376 }
1377 }
1378 PointerPhase::Cancel => {
1379 self.captured_text_field = None;
1380 self.captured_selectable_text = None;
1381 if let Some(element) = self.pressed_button.take() {
1382 let _ = self
1383 .tree
1384 .set_button_interaction(element, None, Some(false), None);
1385 self.frame_requested = true;
1386 }
1387 None
1388 }
1389 };
1390 self.release_legacy_pointer(device, pointer, phase, primary_changed);
1391 result
1392 }
1393 fn release_legacy_pointer(
1394 &mut self,
1395 device: u64,
1396 pointer: u64,
1397 phase: PointerPhase,
1398 primary_changed: bool,
1399 ) {
1400 if (phase == PointerPhase::Cancel || (phase == PointerPhase::Up && primary_changed))
1401 && self.legacy_pointer == Some((device, pointer))
1402 {
1403 self.legacy_pointer = None;
1404 }
1405 }
1406
1407 #[must_use]
1410 pub fn effective_mouse_cursor(&self) -> MouseCursor {
1411 if !self.pointer_inside || !self.pointer_position_known {
1412 return MouseCursor::Basic;
1413 }
1414 match self.tree.mouse_cursor_at(self.last_pointer) {
1415 MouseCursor::Defer => MouseCursor::Basic,
1416 cursor => cursor,
1417 }
1418 }
1419 pub(crate) fn set_focus(&mut self, next: Option<ElementId>) {
1420 self.initial_focus = InitialFocus::Settled;
1423 if self.focused == next {
1424 return;
1425 }
1426 self.cancel_ime_composition();
1431 if let Some(next) = next
1432 && self.tree.dismiss_transients_for_focus(next) > 0
1433 {
1434 self.frame_requested = true;
1435 }
1436 if let Some(previous) = self.focused {
1437 self.tree.set_keyboard_focus(previous, false);
1438 let _ = self.tree.set_focused(previous, false, Instant::now());
1439 }
1440 self.focused = next;
1441 if let Some(current) = next {
1442 let _ = self.tree.set_focused(current, true, Instant::now());
1443 self.tree.set_keyboard_focus(current, true);
1444 if self.tree.is_text_field(current) {
1445 let _ = self.ensure_text_history(current);
1446 }
1447 }
1448 self.frame_requested = true;
1449 self.sync_text_input_client();
1450 }
1451
1452 fn reconcile_focus_and_selection_captures(&mut self) {
1453 if self.captured_text_field.is_some_and(|field| {
1454 !self.tree.is_text_field(field) || !self.tree.can_request_focus(field)
1455 }) {
1456 self.captured_text_field = None;
1457 }
1458 if self.captured_selectable_text.is_some_and(|label| {
1459 !self.tree.is_selectable_text(label) || !self.tree.can_request_focus(label)
1460 }) {
1461 self.captured_selectable_text = None;
1462 }
1463 if self
1464 .focused
1465 .is_some_and(|focused| !self.tree.can_request_focus(focused))
1466 {
1467 self.set_focus(None);
1468 }
1469 self.reconcile_button_tenure();
1470 }
1471
1472 fn reconcile_button_tenure(&mut self) {
1473 if self.pressed_button.is_some_and(|pressed| {
1478 self.tree
1479 .button_ancestor(pressed)
1480 .map(|(ancestor, _)| ancestor)
1481 != Some(pressed)
1482 }) && let Some(pressed) = self.pressed_button.take()
1483 {
1484 let _ = self
1485 .tree
1486 .set_button_interaction(pressed, None, Some(false), None);
1487 self.frame_requested = true;
1488 }
1489 if self.hovered_button.is_some_and(|hovered| {
1493 self.tree
1494 .button_ancestor(hovered)
1495 .map(|(ancestor, _)| ancestor)
1496 != Some(hovered)
1497 }) {
1498 self.set_hover(None);
1499 }
1500 }
1501
1502 fn ensure_text_history(&mut self, field: ElementId) -> Option<UndoHistoryController> {
1503 let editor = self.tree.text_controller(field)?;
1504 let replace = self
1505 .text_histories
1506 .get(&field)
1507 .is_none_or(|history| history.editor() != editor);
1508 if replace {
1509 self.text_histories
1510 .insert(field, UndoHistoryController::new(editor));
1511 }
1512 let history = self.text_histories.get(&field).cloned()?;
1513 if let Some(max_entries) = self.tree.text_field_history_max_entries(field)
1514 && history.max_entries() != max_entries
1515 {
1516 history.set_max_entries(max_entries);
1517 }
1518 Some(history)
1519 }
1520
1521 fn sync_text_input_client(&mut self) {
1522 let snapshot = self
1523 .focused
1524 .and_then(|id| self.tree.text_field_input_snapshot(id));
1525 let Some(snapshot) = snapshot else {
1526 if let Some(client) = self.text_input_client.take() {
1527 self.text_input_commands
1528 .push_back(TextInputCommand::Hide { client });
1529 self.text_input_commands
1530 .push_back(TextInputCommand::Clear { client });
1531 }
1532 self.text_input_configuration = None;
1533 self.text_input_state = None;
1534 self.text_input_caret_rect = None;
1535 return;
1536 };
1537
1538 let client = TextInputClientId::new(snapshot.client_id);
1539 let input_type = match snapshot.input_type {
1540 TextInputTypeHint::Text if snapshot.multiline => TextInputType::Multiline,
1541 TextInputTypeHint::Text => TextInputType::Text,
1542 TextInputTypeHint::Multiline => TextInputType::Multiline,
1543 TextInputTypeHint::Number => TextInputType::Number,
1544 TextInputTypeHint::Phone => TextInputType::Phone,
1545 TextInputTypeHint::Email => TextInputType::Email,
1546 TextInputTypeHint::Url => TextInputType::Url,
1547 TextInputTypeHint::Password => TextInputType::Password,
1548 };
1549 let action = match snapshot.input_action {
1550 TextInputActionHint::Unspecified => {
1551 if snapshot.multiline {
1552 TextInputAction::Newline
1553 } else {
1554 TextInputAction::Done
1555 }
1556 }
1557 TextInputActionHint::None => TextInputAction::None,
1558 TextInputActionHint::Done => TextInputAction::Done,
1559 TextInputActionHint::Go => TextInputAction::Go,
1560 TextInputActionHint::Search => TextInputAction::Search,
1561 TextInputActionHint::Send => TextInputAction::Send,
1562 TextInputActionHint::Next => TextInputAction::Next,
1563 TextInputActionHint::Previous => TextInputAction::Previous,
1564 TextInputActionHint::Newline => TextInputAction::Newline,
1565 };
1566 let configuration = TextInputConfiguration {
1567 client,
1568 input_type: if snapshot.obscure_text {
1569 TextInputType::Password
1570 } else {
1571 input_type
1572 },
1573 action,
1574 multiline: snapshot.multiline,
1575 enabled: snapshot.enabled,
1576 read_only: snapshot.read_only,
1577 obscure_text: snapshot.obscure_text,
1578 };
1579 let state = TextInputState {
1580 text: snapshot.text,
1581 selection_start: snapshot.selection.base,
1582 selection_end: snapshot.selection.extent,
1583 composing: snapshot.composing.map(|range| (range.start, range.end)),
1584 };
1585 let client_changed = self.text_input_client != Some(client);
1586 let configuration_changed = self.text_input_configuration.as_ref() != Some(&configuration);
1587 if client_changed || configuration_changed {
1588 self.text_input_commands
1589 .push_back(TextInputCommand::SetClient {
1590 configuration: configuration.clone(),
1591 state: state.clone(),
1592 caret_rect: snapshot.caret_rect,
1593 });
1594 } else if self.text_input_state.as_ref() != Some(&state) {
1595 self.text_input_commands
1596 .push_back(TextInputCommand::Update {
1597 client,
1598 state: state.clone(),
1599 });
1600 }
1601 if !client_changed
1602 && self.text_input_caret_rect != Some(snapshot.caret_rect)
1603 && !configuration_changed
1604 {
1605 self.text_input_commands
1606 .push_back(TextInputCommand::SetCaretRect {
1607 client,
1608 rect: snapshot.caret_rect,
1609 });
1610 }
1611 self.text_input_client = Some(client);
1612 self.text_input_configuration = Some(configuration);
1613 self.text_input_state = Some(state);
1614 self.text_input_caret_rect = Some(snapshot.caret_rect);
1615 }
1616 fn focus_next(&mut self, reverse: bool) {
1617 let fields = self.tree.focusable_elements();
1618 if fields.is_empty() {
1619 return;
1620 }
1621 let index = self
1622 .focused
1623 .and_then(|focused| fields.iter().position(|id| *id == focused));
1624 let next = match index {
1625 Some(index) if reverse => fields[(index + fields.len() - 1) % fields.len()],
1626 Some(index) => fields[(index + 1) % fields.len()],
1627 None if reverse => *fields.last().expect("not empty"),
1628 None => fields[0],
1629 };
1630 self.set_focus(Some(next));
1631 }
1632 fn handle_key(&mut self, event: KeyboardEvent) {
1633 if event.state.is_down() {
1634 self.editing_diagnostics.key_down_received += 1;
1635 } else {
1636 self.editing_diagnostics.key_up_received += 1;
1637 }
1638
1639 if let Some(external) = self.tree.focused_keyboard_element()
1643 && self.focused != Some(external)
1644 {
1645 self.set_focus(Some(external));
1646 }
1647
1648 if self.tree.dispatch_keyboard(self.focused, event.clone()) {
1652 self.frame_requested = true;
1653 return;
1654 }
1655 if !event.state.is_down() {
1656 return;
1657 }
1658 if event.code == Code::Escape
1659 && self
1660 .tree
1661 .dismiss_transients(incular_widgets::TransientDismissReason::Escape)
1662 > 0
1663 {
1664 self.frame_requested = true;
1665 return;
1666 }
1667 if matches!(
1668 event.code,
1669 Code::ArrowUp | Code::ArrowDown | Code::ArrowLeft | Code::ArrowRight
1670 ) && let Some(focused) = self.focused
1671 && let Some(next) = self.tree.menu_directional_focus_target(focused, event.code)
1672 {
1673 self.set_focus(Some(next));
1674 return;
1675 }
1676 if matches!(event.code, Code::Enter | Code::NumpadEnter | Code::Space)
1679 && let Some(focused) = self.focused
1680 && let Some((owner, Some(action))) = self.tree.button_ancestor(focused)
1681 && (owner == focused || self.tree.is_menu_item_focus(focused))
1682 && self.activate_action(action)
1683 {
1684 return;
1685 }
1686 if event.code == Code::Tab {
1687 self.focus_next(event.modifiers.shift());
1688 return;
1689 }
1690 if let Some(label) = self.focused.filter(|id| self.tree.is_selectable_text(*id)) {
1691 let extend = event.modifiers.shift();
1692 let handled = if command_modifier(event.modifiers) {
1693 match event.code {
1694 Code::KeyA => self.tree.selectable_text_select_all(label),
1695 Code::KeyC => {
1696 if let Some(text) = self.tree.selectable_text_selected_text(label) {
1697 self.clipboard.set_text(text);
1698 true
1699 } else {
1700 false
1701 }
1702 }
1703 _ => false,
1704 }
1705 } else {
1706 match event.code {
1707 Code::ArrowLeft => self.tree.selectable_text_move(label, false, extend),
1708 Code::ArrowRight => self.tree.selectable_text_move(label, true, extend),
1709 Code::Home => self.tree.selectable_text_move_to_edge(label, false, extend),
1710 Code::End => self.tree.selectable_text_move_to_edge(label, true, extend),
1711 _ => false,
1712 }
1713 };
1714 if handled {
1715 self.frame_requested = true;
1716 }
1717 return;
1718 }
1719 let Some(field) = self.focused.filter(|id| self.tree.is_text_field(*id)) else {
1720 return;
1721 };
1722 let Some(controller) = self.tree.text_controller(field) else {
1723 return;
1724 };
1725 let editable = self.tree.text_field_is_editable(field);
1726 let extend = event.modifiers.shift();
1727 if command_modifier(event.modifiers) {
1728 match event.code {
1729 Code::KeyA => controller.select_all(),
1730 Code::KeyC => self.clipboard.set_text(controller.selected_text()),
1731 Code::KeyZ => {
1732 let Some(history) = self.ensure_text_history(field) else {
1733 return;
1734 };
1735 let changed = if event.modifiers.shift() {
1736 history.redo()
1737 } else {
1738 history.undo()
1739 };
1740 if !changed {
1741 return;
1742 }
1743 }
1744 Code::KeyY => {
1745 let Some(history) = self.ensure_text_history(field) else {
1746 return;
1747 };
1748 if !history.redo() {
1749 return;
1750 }
1751 }
1752 Code::KeyX => {
1753 if !editable {
1754 return;
1755 }
1756 self.clipboard.set_text(controller.selected_text());
1757 controller.replace_selection("");
1758 }
1759 Code::KeyV => {
1760 if !editable {
1761 return;
1762 }
1763 if let Some(text) = self.clipboard.get_text() {
1764 controller.insert(&text);
1765 }
1766 }
1767 _ => return,
1768 }
1769 } else {
1770 match event.code {
1771 Code::Backspace => {
1772 if !editable {
1773 return;
1774 }
1775 controller.backspace();
1776 self.editing_diagnostics.backspace_commands += 1;
1777 }
1778 Code::Delete => {
1779 if !editable {
1780 return;
1781 }
1782 controller.delete();
1783 self.editing_diagnostics.delete_commands += 1;
1784 }
1785 Code::ArrowLeft => {
1786 if !self.tree.text_field_move_horizontal(field, false, extend) {
1787 controller.move_left(extend);
1788 }
1789 }
1790 Code::ArrowRight => {
1791 if !self.tree.text_field_move_horizontal(field, true, extend) {
1792 controller.move_right(extend);
1793 }
1794 }
1795 Code::ArrowUp => {
1796 let _ = self.tree.text_field_move_vertical(field, false, extend);
1797 }
1798 Code::ArrowDown => {
1799 let _ = self.tree.text_field_move_vertical(field, true, extend);
1800 }
1801 Code::Home => {
1802 if !self.tree.text_field_move_line_edge(field, false, extend) {
1803 controller.move_home(extend);
1804 }
1805 }
1806 Code::End => {
1807 if !self.tree.text_field_move_line_edge(field, true, extend) {
1808 controller.move_end(extend);
1809 }
1810 }
1811 Code::Enter => {
1812 if !editable {
1813 return;
1814 }
1815 if self.tree.is_multiline_text_field(field) {
1816 controller.insert("\n");
1817 } else {
1818 self.tree.submit_text_field(field);
1819 }
1820 }
1821 _ => return,
1822 }
1823 }
1824 controller.reset_caret(Instant::now());
1825 self.frame_requested = true;
1826 }
1827 fn insert_text(&mut self, text: &str) {
1828 let Some(field) = self.focused.filter(|id| self.tree.is_text_field(*id)) else {
1829 return;
1830 };
1831 if !self.tree.text_field_is_editable(field) {
1832 return;
1833 }
1834 if text.chars().any(char::is_control) {
1835 return;
1836 }
1837 if let Some(controller) = self.tree.text_controller(field) {
1838 controller.insert(text);
1839 self.editing_diagnostics.text_commits += 1;
1840 controller.reset_caret(Instant::now());
1841 self.frame_requested = true;
1842 }
1843 }
1844
1845 pub fn handle_text_input_action(&mut self, action: TextInputAction) -> bool {
1849 let Some(field) = self.focused.filter(|id| self.tree.is_text_field(*id)) else {
1850 return false;
1851 };
1852 let editable = self.tree.text_field_is_editable(field);
1853 if !editable {
1854 return false;
1855 }
1856 let handled = match action {
1857 TextInputAction::Newline => {
1858 if self.tree.is_multiline_text_field(field) {
1859 self.tree.text_controller(field).is_some_and(|controller| {
1860 controller.insert("\n");
1861 controller.reset_caret(Instant::now());
1862 true
1863 })
1864 } else {
1865 self.tree.submit_text_field(field)
1866 }
1867 }
1868 TextInputAction::Next => {
1869 let submitted = self.tree.submit_text_field(field);
1870 self.focus_next(false);
1871 submitted
1872 }
1873 TextInputAction::Previous => {
1874 let submitted = self.tree.submit_text_field(field);
1875 self.focus_next(true);
1876 submitted
1877 }
1878 TextInputAction::Done
1879 | TextInputAction::Go
1880 | TextInputAction::Search
1881 | TextInputAction::Send => self.tree.submit_text_field(field),
1882 TextInputAction::Unspecified | TextInputAction::None => false,
1883 };
1884 if handled {
1885 self.frame_requested = true;
1886 }
1887 handled
1888 }
1889 fn cancel_ime_composition(&mut self) {
1894 let owner = self.ime_composition.take();
1895 if let Some(owner) = owner {
1896 owner.controller.clear_preedit();
1897 }
1898 }
1899
1900 fn ime_owner_current(&self) -> bool {
1904 self.ime_composition.as_ref().is_some_and(|owner| {
1905 self.focused == Some(owner.field)
1906 && self.tree.is_text_field(owner.field)
1907 && self.tree.text_field_is_editable(owner.field)
1908 && self
1909 .tree
1910 .text_controller(owner.field)
1911 .is_some_and(|current| current == owner.controller)
1912 })
1913 }
1914
1915 fn ime_focused_target(&self) -> Option<ImeCompositionOwner> {
1916 let field = self
1917 .focused
1918 .filter(|id| self.tree.text_field_is_editable(*id))?;
1919 Some(ImeCompositionOwner {
1920 field,
1921 controller: self.tree.text_controller(field)?,
1922 })
1923 }
1924
1925 fn validate_ime_composition(&mut self) {
1926 if !self.ime_owner_current() {
1927 self.cancel_ime_composition();
1928 }
1929 }
1930
1931 fn handle_ime(&mut self, event: ImeEvent) {
1938 self.editing_diagnostics.ime_events += 1;
1939 self.validate_ime_composition();
1940 let target = match &event {
1941 ImeEvent::Preedit { .. } => {
1942 if self.ime_composition.is_none() {
1943 self.ime_composition = self.ime_focused_target();
1944 }
1945 self.ime_composition
1946 .as_ref()
1947 .map(|owner| owner.controller.clone())
1948 }
1949 ImeEvent::Commit(_) => self
1950 .ime_composition
1951 .take()
1952 .or_else(|| self.ime_focused_target())
1953 .map(|owner| owner.controller),
1954 ImeEvent::End => self.ime_composition.take().map(|owner| owner.controller),
1955 };
1956 let Some(controller) = target else {
1957 return;
1958 };
1959 match event {
1960 ImeEvent::Preedit { text, selection } => {
1961 controller.set_preedit(
1962 text,
1963 selection.map(|(start, end)| TextRange::new(start, end)),
1964 );
1965 }
1966 ImeEvent::Commit(text) => {
1967 controller.commit_preedit(&text);
1973 }
1974 ImeEvent::End => {
1975 controller.clear_preedit();
1976 }
1977 }
1978 controller.reset_caret(Instant::now());
1979 self.frame_requested = true;
1980 }
1981 pub fn run_frame(
1982 &mut self,
1983 constraints: Constraints,
1984 ) -> Result<(DisplayList, FrameStats), TreeError> {
1985 self.run_frame_at(constraints, Instant::now())
1986 }
1987 pub fn run_frame_at(
1990 &mut self,
1991 constraints: Constraints,
1992 now: Instant,
1993 ) -> Result<(DisplayList, FrameStats), TreeError> {
1994 let message_guard = tracing::info_span!("incular.messages").entered();
1995 let message_span = profiling::PhaseSpan::start();
1996 self.process_runtime_work_at(now);
1997 let runtime_messages = message_span.elapsed_us();
1998 drop(message_guard);
1999 let before = self.tree.diagnostics();
2000 let build_guard =
2001 tracing::info_span!("incular.build", elements_updated = tracing::field::Empty,)
2002 .entered();
2003 let build_span = profiling::PhaseSpan::start();
2004 let mut updated = 0;
2005 while let Some(id) = self.order.pop_front() {
2006 if let Some(widget) = self.pending.remove(&id)
2007 && self.tree.element_exists(id)
2008 {
2009 self.tree.update(id, widget)?;
2010 updated += 1;
2011 }
2012 }
2013 loop {
2014 while let Some((root, node)) = { self.reactive.borrow_mut().take_node() } {
2015 if let Some(node) = node.upgrade() {
2016 node.run(root);
2017 }
2018 }
2019 while let Some(id) = { self.reactive.borrow_mut().take() } {
2020 if self.tree.element_exists(id) && self.builders.contains_key(&id) {
2021 #[cfg(feature = "devtools")]
2022 for cause in self.reactive.borrow_mut().take_causes(id) {
2023 self.tree.note_invalidation(id, cause);
2024 }
2025 self.rebuild_from_builder(id)?;
2026 updated += 1;
2027 }
2028 }
2029 if !self.reactive.borrow().has_work() {
2030 break;
2031 }
2032 }
2033 for id in self.tree.take_unmounted() {
2034 self.handlers_stale = true;
2035 self.builders.remove(&id);
2036 self.reactive.borrow_mut().forget(id);
2037 if let Some(scope) = self.owner_scopes.remove(&id) {
2038 scope.cancel();
2039 }
2040 self.text_histories.remove(&id);
2041 }
2042 self.reconcile_focus_and_selection_captures();
2043 self.validate_ime_composition();
2044 self.prune_handlers();
2045 let build = build_span.elapsed_us();
2046 drop(build_guard);
2047 let reduced_motion = self.environment.borrow().reduced_motion;
2054 let fling_live = self.tree.pump_scroll_flings(now, reduced_motion);
2055 let _layout_guard = tracing::info_span!("incular.layout").entered();
2056 let layout_span = profiling::PhaseSpan::start();
2057 self.tree.layout(constraints)?;
2058 let layout = layout_span.elapsed_us();
2059 drop(_layout_guard);
2060 for (action, handler) in self.tree.take_pending_handlers() {
2061 self.handlers_stale = true;
2062 self.handlers.insert(action, handler);
2063 }
2064 for id in self.tree.take_unmounted() {
2068 self.handlers_stale = true;
2069 self.builders.remove(&id);
2070 self.reactive.borrow_mut().forget(id);
2071 if let Some(scope) = self.owner_scopes.remove(&id) {
2072 scope.cancel();
2073 }
2074 self.text_histories.remove(&id);
2075 }
2076 self.reconcile_focus_and_selection_captures();
2077 self.validate_ime_composition();
2078 self.prune_handlers();
2079 if self.initial_focus == InitialFocus::AwaitingLayout {
2083 self.set_focus(self.tree.autofocus_element());
2084 }
2085 let _composite_guard = tracing::info_span!("incular.composite").entered();
2086 let composite_span = profiling::PhaseSpan::start();
2087 let (composited, animations_active) = self.tree.update_compositor(now)?;
2088 let composite = composite_span.elapsed_us();
2089 drop(_composite_guard);
2090 let _semantics_guard = tracing::info_span!("incular.semantics").entered();
2091 let semantics_span = profiling::PhaseSpan::start();
2092 self.tree.update_semantics();
2093 let semantics = semantics_span.elapsed_us();
2094 drop(_semantics_guard);
2095 let _paint_guard = tracing::info_span!("incular.paint").entered();
2096 let paint_span = profiling::PhaseSpan::start();
2097 let display_list = self.tree.paint();
2098 let paint = paint_span.elapsed_us();
2099 drop(_paint_guard);
2100 self.sync_text_input_client();
2101 self.frame_requested = fling_live
2107 || !self.pending.is_empty()
2108 || self.reactive.borrow().has_work()
2109 || animations_active;
2110 let after = self.tree.diagnostics();
2111 let timings = FrameTimings {
2112 event_processing: std::mem::take(&mut self.pending_event_processing_us),
2113 runtime_messages,
2114 build,
2115 layout,
2116 composite,
2117 semantics,
2118 paint,
2119 cpu_total: runtime_messages + build + layout + composite + semantics + paint,
2120 };
2121 tracing::debug!(
2122 target: "incular::frame",
2123 cpu_total_us = timings.cpu_total,
2124 build_us = timings.build,
2125 layout_us = timings.layout,
2126 paint_us = timings.paint,
2127 );
2128 Ok((
2129 display_list.clone(),
2130 FrameStats {
2131 updated_elements: updated,
2132 rebuilt_elements: after.rebuilds - before.rebuilds,
2133 laid_out_render_objects: after.layouts - before.layouts,
2134 repainted_render_objects: after.paints - before.paints,
2135 composited: u64::from(composited),
2136 active_animations: u64::from(animations_active),
2137 display_list_commands: display_list.len(),
2138 requested_another_frame: self.frame_requested,
2139 timings,
2140 },
2141 ))
2142 }
2143 #[must_use]
2144 pub fn diagnostics(&self) -> Diagnostics {
2145 self.tree.diagnostics()
2146 }
2147 #[cfg(feature = "hot-reload")]
2150 pub(crate) fn reassemble(&mut self) {
2151 let root = self.application_root;
2152 let ids = root
2153 .into_iter()
2154 .chain(self.builders.keys().copied().filter(|id| Some(*id) != root))
2155 .collect::<Vec<_>>();
2156 let mut reactive = self.reactive.borrow_mut();
2157 for id in ids {
2158 #[cfg(feature = "devtools")]
2159 reactive.note_cause(id, InvalidationCause::Manual);
2160 reactive.enqueue(id);
2161 }
2162 drop(reactive);
2163 self.frame_requested = true;
2164 }
2165 pub(crate) fn rebuild_from_builder(&mut self, id: ElementId) -> Result<(), TreeError> {
2169 self.reactive.borrow_mut().refresh(id);
2170 let widget = {
2171 let _build_scope = BuildScopeGuard::enter(BuildScope {
2172 root: self.reactive.borrow().root,
2173 element: Some(id),
2174 queue: Rc::downgrade(&self.reactive),
2175 initial_dependencies: None,
2176 spawner: self.spawner(),
2177 owner_scope: self
2178 .owner_scopes
2179 .get(&id)
2180 .cloned()
2181 .unwrap_or_else(|| self.window_scope.clone()),
2182 });
2183 self.builders.get_mut(&id).expect("registered builder")()
2184 };
2185 let mut widget = widget;
2186 self.prepare_widget(&mut widget);
2187 self.tree.update(id, widget)?;
2188 Ok(())
2189 }
2190 fn prepare_widget(&mut self, widget: &mut Widget) {
2191 self.handlers_stale = true;
2192 let handlers = &mut self.handlers;
2193 let tree = &mut self.tree;
2194 widget.bind_callbacks(&mut |callback| {
2195 let id = tree.allocate_action();
2196 handlers.insert(id, callback);
2197 id
2198 });
2199 }
2200 fn activate_action(&mut self, action: ActionId) -> bool {
2201 let mut handled = false;
2202 if let Some(callback) = self.handlers.get(&action).cloned() {
2203 callback();
2204 handled = true;
2205 }
2206 if let Some(callback) = self.legacy_action_handler.as_mut() {
2207 callback(action);
2208 handled = true;
2209 }
2210 self.frame_requested |= handled;
2211 handled
2212 }
2213
2214 fn prune_handlers(&mut self) {
2215 if !std::mem::take(&mut self.handlers_stale) {
2216 return;
2217 }
2218 let active = self.tree.action_ids();
2219 self.handlers.retain(|id, _| active.contains(id));
2220 }
2221 fn set_hover(&mut self, next: Option<ElementId>) {
2222 if self.hovered_button == next {
2223 return;
2224 }
2225 if let Some(previous) = self.hovered_button {
2226 let _ = self
2227 .tree
2228 .set_button_interaction(previous, Some(false), None, None);
2229 if let Some(action) = self.tree.hover_actions_for_element(previous).1
2230 && let Some(callback) = self.handlers.get(&action).cloned()
2231 {
2232 callback();
2233 }
2234 }
2235 self.hovered_button = next;
2236 if let Some(current) = next {
2237 if self.pressed_button != Some(current) {
2238 let _ = self
2239 .tree
2240 .set_button_interaction(current, Some(true), None, None);
2241 }
2242 if let Some(action) = self.tree.hover_actions_for_element(current).0
2243 && let Some(callback) = self.handlers.get(&action).cloned()
2244 {
2245 callback();
2246 }
2247 }
2248 self.frame_requested = true;
2249 }
2250}
2251
2252fn command_modifier(modifiers: Modifiers) -> bool {
2257 modifiers.ctrl() || modifiers.meta()
2258}
2259
2260fn us_since_instant(started: std::time::Instant) -> u32 {
2265 started.elapsed().as_micros().try_into().unwrap_or(u32::MAX)
2266}