1#[cfg(feature = "profiler")]
2use crate::DebugFrameOverlayMode;
3#[cfg(any(feature = "inspector", debug_assertions))]
4use crate::Inspector;
5#[cfg(feature = "profiler")]
6use crate::profiler;
7use crate::subscription::SingleSubscriberSet;
8use crate::taffy::TaffyLayoutEngine;
9use crate::{
10 Action, AnyDrag, AnyElement, AnyImageCache, AnyTooltip, AnyView, App, AppContext, Arena, Asset,
11 AsyncWindowContext, AtlasTile, AvailableSpace, Background, BorderStyle, Bounds, BoxShadow,
12 Capslock, Context, Corners, CursorHideMode, CursorStyle, Decorations, DevicePixels,
13 DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity,
14 EntityId, EventEmitter, FileDropEvent, FontId, Global, GlobalElementId, GlyphId, GpuSpecs,
15 Hsla, InputHandler, InputPreference, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeyEvent,
16 Keystroke, KeystrokeEvent, LayoutId, LineLayoutIndex, Modifiers, ModifiersChangedEvent,
17 MonochromeSprite, MouseButton, MouseEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels,
18 PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point,
19 PolychromeSprite, Priority, PromptButton, PromptLevel, Quad, Render, RenderGlyphParams,
20 RenderImage, RenderImageParams, RenderSvgParams, Replay, ResizeEdge, SMOOTH_SVG_SCALE_FACTOR,
21 SUBPIXEL_VARIANTS_X, SUBPIXEL_VARIANTS_Y, ScaledPixels, Scene, Shadow, SharedString, Size,
22 StrikethroughStyle, Style, SubpixelSprite, Subscription, SystemWindowTab,
23 SystemWindowTabController, TabStopMap, Task, TextInputConfiguration, TextInputStateChange,
24 TextRenderingMode, TextStyle, TextStyleRefinement, ThermalState, TransformationMatrix,
25 Underline, UnderlineStyle, WindowAppearance, WindowBackgroundAppearance, WindowBounds,
26 WindowControls, WindowDecorations, WindowInsets, WindowOptions, WindowParams, WindowTextSystem,
27 WindowVisibility, point, prelude::*, px, rems, size, transparent_black,
28};
29
30use crate::gestures::{GestureTuning, RecognizedTouchGesture, TouchGestureRecognizer};
31use crate::interactive::TouchEvent;
32use anyhow::{Context as _, Result, anyhow};
33use collections::{FxHashMap, FxHashSet};
34use derive_more::{Deref, DerefMut};
35use futures::channel::oneshot;
36use gpui_util::post_inc;
37use gpui_util::{ResultExt, measure};
38use itertools::FoldWhile::{Continue, Done};
39use itertools::Itertools;
40#[cfg(any(target_os = "macos", target_os = "ios"))]
41use objc2_core_foundation::CFRetained;
42#[cfg(any(target_os = "macos", target_os = "ios"))]
43use objc2_core_video::CVPixelBuffer;
44use parking_lot::{RwLock, RwLockUpgradableReadGuard};
45use raw_window_handle::{HandleError, HasDisplayHandle, HasWindowHandle};
46use refineable::Refineable;
47use scheduler::Instant;
48use slotmap::SlotMap;
49use smallvec::SmallVec;
50use std::{
51 any::{Any, TypeId},
52 borrow::Cow,
53 cell::{Cell, RefCell},
54 cmp,
55 fmt::{Debug, Display},
56 hash::{Hash, Hasher},
57 marker::PhantomData,
58 mem,
59 ops::Range,
60 rc::Rc,
61 sync::{
62 Arc, Weak,
63 atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst},
64 },
65 thread::AccessError,
66 time::Duration,
67};
68use uuid::Uuid;
69
70pub(crate) mod a11y;
71mod prompts;
72
73pub use a11y::A11ySubtreeBuilder;
74
75use self::a11y::A11y;
76#[cfg(not(target_family = "wasm"))]
77use self::a11y::ROOT_NODE_ID;
78use crate::util::{
79 atomic_incr_if_not_zero, ceil_to_device_pixel, floor_to_device_pixel, round_half_toward_zero,
80 round_half_toward_zero_f64, round_stroke_to_device_pixel, round_to_device_pixel,
81};
82pub use prompts::*;
83
84pub const DEFAULT_WINDOW_SIZE: Size<Pixels> = size(px(1536.), px(1095.));
86
87pub const DEFAULT_ADDITIONAL_WINDOW_SIZE: Size<Pixels> = Size {
90 width: Pixels(900.),
91 height: Pixels(750.),
92};
93
94#[derive(Default, Copy, Clone, Debug, Eq, PartialEq)]
96pub enum DispatchPhase {
97 #[default]
102 Bubble,
103 Capture,
109}
110
111impl DispatchPhase {
112 #[inline]
114 pub fn bubble(self) -> bool {
115 self == DispatchPhase::Bubble
116 }
117
118 #[inline]
120 pub fn capture(self) -> bool {
121 self == DispatchPhase::Capture
122 }
123}
124
125struct WindowInvalidatorInner {
126 #[cfg(feature = "profiler")]
127 pub window_id: WindowId,
128 pub dirty: bool,
129 pub draw_phase: DrawPhase,
130 pub dirty_views: FxHashSet<EntityId>,
131 pub update_count: usize,
132 #[cfg(feature = "profiler")]
133 pub frame_dirty: FrameDirtyAccumulator,
134 pub platform_waker: Option<Rc<dyn Fn()>>,
135}
136
137#[cfg(feature = "profiler")]
143#[derive(Default)]
144struct FrameDirtyAccumulator {
145 dirty_at: Option<Instant>,
146 invalidations: u64,
147}
148
149#[derive(Clone)]
150pub(crate) struct WindowInvalidator {
151 inner: Rc<RefCell<WindowInvalidatorInner>>,
152}
153
154impl WindowInvalidator {
155 pub fn new(#[allow(unused_variables)] window_id: WindowId) -> Self {
156 WindowInvalidator {
157 inner: Rc::new(RefCell::new(WindowInvalidatorInner {
158 #[cfg(feature = "profiler")]
159 window_id,
160 dirty: true,
161 draw_phase: DrawPhase::None,
162 dirty_views: FxHashSet::default(),
163 update_count: 0,
164 #[cfg(feature = "profiler")]
165 frame_dirty: FrameDirtyAccumulator::default(),
166 platform_waker: None,
167 })),
168 }
169 }
170
171 pub fn invalidate_view(&self, entity: EntityId, cx: &mut App) -> bool {
172 let mut inner = self.inner.borrow_mut();
173 inner.update_count += 1;
174 inner.dirty_views.insert(entity);
175 if inner.draw_phase == DrawPhase::None {
176 #[cfg(feature = "profiler")]
177 let dirty_at = Self::record_frame_dirty(&mut inner);
178 let became_dirty = !inner.dirty;
179 inner.dirty = true;
180 let waker = became_dirty.then(|| inner.platform_waker.clone()).flatten();
181 #[cfg(feature = "profiler")]
182 let window_id = inner.window_id;
183 drop(inner);
184 #[cfg(feature = "profiler")]
185 if became_dirty {
186 profiler::journal::record_frame_pending(window_id, dirty_at);
187 }
188 cx.push_effect(Effect::Notify { emitter: entity });
189 if let Some(waker) = waker {
190 waker();
191 }
192 true
193 } else {
194 false
195 }
196 }
197
198 pub fn is_dirty(&self) -> bool {
199 self.inner.borrow().dirty
200 }
201
202 pub fn set_dirty(&self, dirty: bool) {
203 let mut inner = self.inner.borrow_mut();
204 let became_dirty = dirty && !inner.dirty;
205 inner.dirty = dirty;
206 if dirty {
207 inner.update_count += 1;
208 }
209 #[cfg(feature = "profiler")]
210 let dirty_at = dirty.then(|| Self::record_frame_dirty(&mut inner));
211 let waker = became_dirty.then(|| inner.platform_waker.clone()).flatten();
212 #[cfg(feature = "profiler")]
213 let window_id = inner.window_id;
214 drop(inner);
215 #[cfg(feature = "profiler")]
216 if became_dirty && let Some(dirty_at) = dirty_at {
217 profiler::journal::record_frame_pending(window_id, dirty_at);
218 }
219 if let Some(waker) = waker {
220 waker();
221 }
222 }
223
224 pub fn set_platform_waker(&self, waker: Option<Rc<dyn Fn()>>) {
225 let mut inner = self.inner.borrow_mut();
226 inner.platform_waker = waker;
227 let waker = inner.dirty.then(|| inner.platform_waker.clone()).flatten();
228 drop(inner);
229 if let Some(waker) = waker {
230 waker();
231 }
232 }
233
234 pub fn wake_platform(&self) {
238 let waker = self.inner.borrow().platform_waker.clone();
239 if let Some(waker) = waker {
240 waker();
241 }
242 }
243
244 pub fn set_phase(&self, phase: DrawPhase) {
245 self.inner.borrow_mut().draw_phase = phase
246 }
247
248 pub fn update_count(&self) -> usize {
249 self.inner.borrow().update_count
250 }
251
252 #[cfg(feature = "profiler")]
253 fn record_frame_dirty(inner: &mut WindowInvalidatorInner) -> Instant {
254 let dirty_at = *inner.frame_dirty.dirty_at.get_or_insert_with(Instant::now);
255 inner.frame_dirty.invalidations += 1;
256 dirty_at
257 }
258
259 #[cfg(feature = "profiler")]
260 fn take_frame_dirty(&self) -> FrameDirtyAccumulator {
261 mem::take(&mut self.inner.borrow_mut().frame_dirty)
262 }
263
264 pub fn take_views(&self) -> FxHashSet<EntityId> {
265 mem::take(&mut self.inner.borrow_mut().dirty_views)
266 }
267
268 pub fn replace_views(&self, views: FxHashSet<EntityId>) {
269 self.inner.borrow_mut().dirty_views = views;
270 }
271
272 pub fn not_drawing(&self) -> bool {
273 self.inner.borrow().draw_phase == DrawPhase::None
274 }
275
276 #[track_caller]
277 pub fn debug_assert_paint(&self) {
278 debug_assert!(
279 matches!(self.inner.borrow().draw_phase, DrawPhase::Paint),
280 "this method can only be called during paint"
281 );
282 }
283
284 #[track_caller]
285 pub fn debug_assert_prepaint(&self) {
286 debug_assert!(
287 matches!(self.inner.borrow().draw_phase, DrawPhase::Prepaint),
288 "this method can only be called during request_layout, or prepaint"
289 );
290 }
291
292 #[track_caller]
293 pub fn debug_assert_paint_or_prepaint(&self) {
294 debug_assert!(
295 matches!(
296 self.inner.borrow().draw_phase,
297 DrawPhase::Paint | DrawPhase::Prepaint
298 ),
299 "this method can only be called during request_layout, prepaint, or paint"
300 );
301 }
302}
303
304type AnyObserver = Box<dyn FnMut(&mut Window, &mut App) -> bool + 'static>;
305
306pub(crate) type AnyWindowFocusListener =
307 Box<dyn FnMut(&WindowFocusEvent, &mut Window, &mut App) -> bool + 'static>;
308
309pub(crate) struct WindowFocusEvent {
310 pub(crate) previous_focus_path: SmallVec<[FocusId; 8]>,
311 pub(crate) current_focus_path: SmallVec<[FocusId; 8]>,
312}
313
314impl WindowFocusEvent {
315 pub fn is_focus_in(&self, focus_id: FocusId) -> bool {
316 !self.previous_focus_path.contains(&focus_id) && self.current_focus_path.contains(&focus_id)
317 }
318
319 pub fn is_focus_out(&self, focus_id: FocusId) -> bool {
320 self.previous_focus_path.contains(&focus_id) && !self.current_focus_path.contains(&focus_id)
321 }
322}
323
324pub struct FocusOutEvent {
326 pub blurred: WeakFocusHandle,
328}
329
330slotmap::new_key_type! {
331 pub struct FocusId;
333}
334
335thread_local! {
336 pub(crate) static ELEMENT_ARENA: RefCell<Arena> = RefCell::new(Arena::new(1024 * 1024));
339
340 static CURRENT_ELEMENT_ARENA: Cell<Option<*const RefCell<Arena>>> = const { Cell::new(None) };
344}
345
346fn draw_in_progress() -> bool {
357 CURRENT_ELEMENT_ARENA.with(|current| current.get().is_some())
358}
359
360#[inline(always)]
363pub(crate) fn with_element_arena<R>(callback: impl FnOnce(&mut Arena) -> R) -> R {
364 let mut callback = Some(callback);
365 let mut result = None;
366 let access = with_element_arena_erased(&mut |arena| {
367 result = Some(callback.take().expect("arena callback runs once")(arena));
368 });
369 drop(callback);
370 access.expect("cannot access a Thread Local Storage value during or after destruction");
371 result.expect("arena callback produces a result")
372}
373
374pub(crate) struct ElementArenaScope {
389 entered: *const RefCell<Arena>,
392 previous: Option<*const RefCell<Arena>>,
393 exited: bool,
394}
395
396impl ElementArenaScope {
397 pub(crate) fn enter(arena: &RefCell<Arena>) -> Self {
399 arena.borrow_mut().begin_scope();
400 let previous = CURRENT_ELEMENT_ARENA.with(|current| {
401 let prev = current.get();
402 current.set(Some(arena as *const RefCell<Arena>));
403 prev
404 });
405 Self {
406 entered: arena as *const RefCell<Arena>,
407 previous,
408 exited: false,
409 }
410 }
411
412 pub(crate) fn exit(mut self, arena: &RefCell<Arena>) -> ArenaClearNeeded {
421 assert!(
422 std::ptr::eq(self.entered, arena),
423 "ElementArenaScope::exit called with a different arena than was entered"
424 );
425 self.exited = true;
426 ArenaClearNeeded::new(arena)
431 }
432}
433
434impl Drop for ElementArenaScope {
435 fn drop(&mut self) {
436 CURRENT_ELEMENT_ARENA.with(|current| {
443 current.set(self.previous);
444 });
445 unsafe { &*self.entered }.borrow_mut().end_scope();
449 if !self.exited && !std::thread::panicking() {
450 debug_assert!(false, "ElementArenaScope dropped without calling exit()");
451 log::error!(
452 "ElementArenaScope dropped without calling exit(); \
453 the arena clear for this draw was never requested"
454 );
455 }
456 }
457}
458
459#[must_use]
461pub struct ArenaClearNeeded {
462 arena: *const RefCell<Arena>,
465}
466
467impl ArenaClearNeeded {
468 fn new(arena: &RefCell<Arena>) -> Self {
471 Self {
472 arena: arena as *const RefCell<Arena>,
473 }
474 }
475
476 pub fn clear(self, cx: &mut App) {
485 assert!(
486 std::ptr::eq(self.arena, &*cx.element_arena),
487 "ArenaClearNeeded::clear called with a different App than the draw ran against"
488 );
489 RefCell::borrow_mut(&cx.element_arena).clear();
490 }
491}
492
493#[derive(Default)]
494pub(crate) struct FocusMap {
495 pub(crate) handles: SlotMap<FocusId, FocusRef>,
496 pub(crate) dropped_handles: Vec<FocusId>,
497}
498
499pub(crate) struct FocusRef {
500 pub(crate) ref_count: AtomicUsize,
501 pub(crate) tab_index: isize,
502 pub(crate) tab_stop: bool,
503}
504
505impl FocusId {
506 pub fn is_focused(&self, window: &Window) -> bool {
508 window.focus == Some(*self)
509 }
510
511 pub fn contains_focused(&self, window: &Window, cx: &App) -> bool {
514 window
515 .focused(cx)
516 .is_some_and(|focused| self.contains(focused.id, window))
517 }
518
519 pub fn within_focused(&self, window: &Window, cx: &App) -> bool {
522 let focused = window.focused(cx);
523 focused.is_some_and(|focused| focused.id.contains(*self, window))
524 }
525
526 pub(crate) fn contains(&self, other: Self, window: &Window) -> bool {
528 window
529 .rendered_frame
530 .dispatch_tree
531 .focus_contains(*self, other)
532 }
533}
534
535pub struct FocusHandle {
537 pub(crate) id: FocusId,
538 handles: Arc<RwLock<FocusMap>>,
539 pub tab_index: isize,
541 pub tab_stop: bool,
543}
544
545impl std::fmt::Debug for FocusHandle {
546 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
547 f.write_fmt(format_args!("FocusHandle({:?})", self.id))
548 }
549}
550
551impl FocusHandle {
552 pub(crate) fn new(handles: &Arc<RwLock<FocusMap>>) -> Self {
553 let id = handles.write().handles.insert(FocusRef {
554 ref_count: AtomicUsize::new(1),
555 tab_index: 0,
556 tab_stop: false,
557 });
558
559 Self {
560 id,
561 tab_index: 0,
562 tab_stop: false,
563 handles: handles.clone(),
564 }
565 }
566
567 pub(crate) fn for_id(id: FocusId, handles: &Arc<RwLock<FocusMap>>) -> Option<Self> {
568 let lock = handles.read();
569 let focus = lock.handles.get(id)?;
570 if atomic_incr_if_not_zero(&focus.ref_count) == 0 {
571 return None;
572 }
573 Some(Self {
574 id,
575 tab_index: focus.tab_index,
576 tab_stop: focus.tab_stop,
577 handles: handles.clone(),
578 })
579 }
580
581 pub fn tab_index(mut self, index: isize) -> Self {
583 self.tab_index = index;
584 if let Some(focus) = self.handles.write().handles.get_mut(self.id) {
585 focus.tab_index = index;
586 }
587 self
588 }
589
590 pub fn tab_stop(mut self, tab_stop: bool) -> Self {
594 self.tab_stop = tab_stop;
595 if let Some(focus) = self.handles.write().handles.get_mut(self.id) {
596 focus.tab_stop = tab_stop;
597 }
598 self
599 }
600
601 pub fn downgrade(&self) -> WeakFocusHandle {
603 WeakFocusHandle {
604 id: self.id,
605 handles: Arc::downgrade(&self.handles),
606 }
607 }
608
609 pub fn focus(&self, window: &mut Window, cx: &mut App) {
611 window.focus(self, cx)
612 }
613
614 pub fn is_focused(&self, window: &Window) -> bool {
616 self.id.is_focused(window)
617 }
618
619 pub fn contains_focused(&self, window: &Window, cx: &App) -> bool {
622 self.id.contains_focused(window, cx)
623 }
624
625 pub fn within_focused(&self, window: &Window, cx: &mut App) -> bool {
628 self.id.within_focused(window, cx)
629 }
630
631 pub fn contains(&self, other: &Self, window: &Window) -> bool {
633 self.id.contains(other.id, window)
634 }
635
636 pub fn dispatch_action(&self, action: &dyn Action, window: &mut Window, cx: &mut App) {
638 if let Some(node_id) = window
639 .rendered_frame
640 .dispatch_tree
641 .focusable_node_id(self.id)
642 {
643 window.dispatch_action_on_node(node_id, action, cx)
644 }
645 }
646}
647
648impl Clone for FocusHandle {
649 fn clone(&self) -> Self {
650 Self::for_id(self.id, &self.handles).unwrap()
651 }
652}
653
654impl PartialEq for FocusHandle {
655 fn eq(&self, other: &Self) -> bool {
656 self.id == other.id
657 }
658}
659
660impl Eq for FocusHandle {}
661
662impl Drop for FocusHandle {
663 fn drop(&mut self) {
664 let handles = self.handles.upgradable_read();
665 let focus = handles.handles.get(self.id).expect("focus handle missing");
666 let previous_count = focus.ref_count.fetch_sub(1, SeqCst);
667 assert_ne!(previous_count, 0, "over-released focus handle");
668 if previous_count == 1 {
669 RwLockUpgradableReadGuard::upgrade(handles)
670 .dropped_handles
671 .push(self.id);
672 }
673 }
674}
675
676#[derive(Clone, Debug)]
678pub struct WeakFocusHandle {
679 pub(crate) id: FocusId,
680 pub(crate) handles: Weak<RwLock<FocusMap>>,
681}
682
683impl WeakFocusHandle {
684 pub fn upgrade(&self) -> Option<FocusHandle> {
686 let handles = self.handles.upgrade()?;
687 FocusHandle::for_id(self.id, &handles)
688 }
689}
690
691impl PartialEq for WeakFocusHandle {
692 fn eq(&self, other: &WeakFocusHandle) -> bool {
693 self.id == other.id
694 }
695}
696
697impl Eq for WeakFocusHandle {}
698
699impl PartialEq<FocusHandle> for WeakFocusHandle {
700 fn eq(&self, other: &FocusHandle) -> bool {
701 self.id == other.id
702 }
703}
704
705impl PartialEq<WeakFocusHandle> for FocusHandle {
706 fn eq(&self, other: &WeakFocusHandle) -> bool {
707 self.id == other.id
708 }
709}
710
711pub trait Focusable: 'static {
714 fn focus_handle(&self, cx: &App) -> FocusHandle;
716}
717
718impl<V: Focusable> Focusable for Entity<V> {
719 fn focus_handle(&self, cx: &App) -> FocusHandle {
720 self.read(cx).focus_handle(cx)
721 }
722}
723
724pub trait ManagedView: Focusable + EventEmitter<DismissEvent> + Render {}
727
728impl<M: Focusable + EventEmitter<DismissEvent> + Render> ManagedView for M {}
729
730pub struct DismissEvent;
732
733type FrameCallback = Box<dyn FnOnce(&mut Window, &mut App)>;
734
735pub(crate) type AnyMouseListener =
736 Box<dyn FnMut(&dyn Any, DispatchPhase, &mut Window, &mut App) + 'static>;
737
738#[derive(Clone)]
739pub(crate) struct CursorStyleRequest {
740 pub(crate) hitbox_id: Option<HitboxId>,
741 pub(crate) style: CursorStyle,
742}
743
744#[derive(Default, Eq, PartialEq)]
745pub(crate) struct HitTest {
746 pub(crate) ids: SmallVec<[HitboxId; 8]>,
747 pub(crate) hover_hitbox_count: usize,
748}
749
750#[derive(Clone, Copy, Debug, Eq, PartialEq)]
752pub enum WindowControlArea {
753 Drag,
755 Close,
757 Max,
759 Min,
761}
762
763#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
765pub struct HitboxId(u64);
766
767#[cfg(feature = "test-support")]
768impl HitboxId {
769 pub const fn placeholder() -> Self {
774 Self(0)
775 }
776}
777
778impl HitboxId {
779 pub fn is_hovered(self, window: &Window) -> bool {
786 if window.captured_hitbox == Some(self) {
788 return true;
789 }
790 if window.last_input_was_keyboard() {
791 return false;
792 }
793 self.hit_test(window)
794 }
795
796 pub(crate) fn is_hovered_ignoring_last_input(self, window: &Window) -> bool {
801 if window.captured_hitbox == Some(self) {
803 return true;
804 }
805 self.hit_test(window)
806 }
807
808 fn hit_test(self, window: &Window) -> bool {
809 let hit_test = &window.mouse_hit_test;
810 for id in hit_test.ids.iter().take(hit_test.hover_hitbox_count) {
811 if self == *id {
812 return true;
813 }
814 }
815 false
816 }
817
818 pub fn should_handle_scroll(self, window: &Window) -> bool {
823 window.mouse_hit_test.ids.contains(&self)
824 }
825
826 fn next(self) -> HitboxId {
827 HitboxId(self.0.wrapping_add(1))
828 }
829}
830
831#[derive(Clone, Debug, Deref)]
834pub struct Hitbox {
835 pub id: HitboxId,
837 #[deref]
839 pub bounds: Bounds<Pixels>,
840 pub content_mask: ContentMask<Pixels>,
842 pub behavior: HitboxBehavior,
844}
845
846impl Hitbox {
847 pub fn is_hovered(&self, window: &Window) -> bool {
864 self.id.is_hovered(window)
865 }
866
867 pub fn is_hovered_at(&self, position: Point<Pixels>, window: &Window) -> bool {
870 let hit_test = window.rendered_frame.hit_test(position);
871 hit_test
872 .ids
873 .iter()
874 .take(hit_test.hover_hitbox_count)
875 .any(|id| self.id == *id)
876 }
877
878 pub fn should_handle_scroll(&self, window: &Window) -> bool {
885 self.id.should_handle_scroll(window)
886 }
887}
888
889#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
891pub enum HitboxBehavior {
892 #[default]
894 Normal,
895
896 BlockMouse,
918
919 BlockMouseExceptScroll,
946}
947
948#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
950pub struct TooltipId(usize);
951
952impl TooltipId {
953 pub fn is_hovered(&self, window: &Window) -> bool {
955 window
956 .tooltip_bounds
957 .as_ref()
958 .is_some_and(|tooltip_bounds| {
959 tooltip_bounds.id == *self
960 && tooltip_bounds.bounds.contains(&window.mouse_position())
961 })
962 }
963}
964
965pub(crate) struct TooltipBounds {
966 id: TooltipId,
967 bounds: Bounds<Pixels>,
968}
969
970#[derive(Clone)]
971pub(crate) struct TooltipRequest {
972 id: TooltipId,
973 tooltip: AnyTooltip,
974}
975
976#[derive(Clone, Default)]
982pub(crate) struct ElementIdStack {
983 ids: SmallVec<[ElementId; 32]>,
984 hashes: SmallVec<[u64; 32]>,
985}
986
987impl ElementIdStack {
988 pub(crate) fn push(&mut self, id: ElementId) {
989 self.hashes.push(crate::extend_path_hash(self.hash(), &id));
990 self.ids.push(id);
991 }
992
993 pub(crate) fn pop(&mut self) -> Option<ElementId> {
994 self.hashes.pop();
995 self.ids.pop()
996 }
997
998 pub(crate) fn clear(&mut self) {
999 self.ids.clear();
1000 self.hashes.clear();
1001 }
1002
1003 fn hash(&self) -> u64 {
1005 self.hashes
1006 .last()
1007 .copied()
1008 .unwrap_or(crate::EMPTY_PATH_HASH)
1009 }
1010
1011 pub(crate) fn global_id(&self) -> GlobalElementId {
1012 GlobalElementId::with_hash(&self.ids, self.hash())
1013 }
1014}
1015
1016impl std::ops::Deref for ElementIdStack {
1017 type Target = [ElementId];
1018
1019 fn deref(&self) -> &Self::Target {
1020 &self.ids
1021 }
1022}
1023
1024pub(crate) struct DeferredDraw {
1025 current_view: EntityId,
1026 priority: usize,
1027 parent_node: DispatchNodeId,
1028 element_id_stack: ElementIdStack,
1029 text_style_stack: Vec<TextStyleRefinement>,
1030 content_mask: Option<ContentMask<Pixels>>,
1031 rem_size: Pixels,
1032 element: Option<AnyElement>,
1033 absolute_offset: Point<Pixels>,
1034 prepaint_range: Range<PrepaintStateIndex>,
1035 paint_range: Range<PaintIndex>,
1036 accessed_entities: FxHashSet<EntityId>,
1039}
1040
1041pub(crate) struct Frame {
1042 pub(crate) focus: Option<FocusId>,
1043 pub(crate) window_active: bool,
1044 pub(crate) element_states: FxHashMap<(GlobalElementId, TypeId), ElementStateBox>,
1045 accessed_element_states: Vec<(GlobalElementId, TypeId)>,
1046 pub(crate) mouse_listeners: Vec<Option<AnyMouseListener>>,
1047 pub(crate) dispatch_tree: DispatchTree,
1048 pub(crate) scene: Scene,
1049 pub(crate) hitboxes: Vec<Hitbox>,
1050 pub(crate) window_control_hitboxes: Vec<(WindowControlArea, Hitbox)>,
1051 pub(crate) deferred_draws: Vec<DeferredDraw>,
1052 pub(crate) input_handlers: Vec<Option<PlatformInputHandler>>,
1053 pub(crate) tooltip_requests: Vec<Option<TooltipRequest>>,
1054 pub(crate) cursor_styles: Vec<CursorStyleRequest>,
1055 #[cfg(any(test, feature = "test-support"))]
1056 pub(crate) debug_bounds: FxHashMap<String, Bounds<Pixels>>,
1057 #[cfg(any(test, feature = "test-support"))]
1058 debug_bounds_records: Vec<(String, Bounds<Pixels>)>,
1059 #[cfg(any(feature = "inspector", debug_assertions))]
1060 pub(crate) next_inspector_instance_ids: FxHashMap<Rc<crate::InspectorElementPath>, usize>,
1061 #[cfg(any(feature = "inspector", debug_assertions))]
1062 pub(crate) inspector_hitboxes: FxHashMap<HitboxId, crate::InspectorElementId>,
1063 pub(crate) tab_stops: TabStopMap,
1064}
1065
1066#[derive(Clone, Default)]
1067pub(crate) struct PrepaintStateIndex {
1068 hitboxes_index: usize,
1069 tooltips_index: usize,
1070 deferred_draws_index: usize,
1071 dispatch_tree_index: usize,
1072 accessed_element_states_index: usize,
1073 line_layout_index: LineLayoutIndex,
1074}
1075
1076#[derive(Clone, Default)]
1077pub(crate) struct PaintIndex {
1078 scene_index: usize,
1079 #[cfg(any(test, feature = "test-support"))]
1080 debug_bounds_index: usize,
1081 mouse_listeners_index: usize,
1082 input_handlers_index: usize,
1083 cursor_styles_index: usize,
1084 accessed_element_states_index: usize,
1085 tab_handle_index: usize,
1086 line_layout_index: LineLayoutIndex,
1087}
1088
1089impl Frame {
1090 #[cfg(any(test, feature = "test-support"))]
1091 pub(crate) fn record_debug_bounds(&mut self, selector: String, bounds: Bounds<Pixels>) {
1092 self.debug_bounds.insert(selector.clone(), bounds);
1093 self.debug_bounds_records.push((selector, bounds));
1094 }
1095
1096 pub(crate) fn new(dispatch_tree: DispatchTree) -> Self {
1097 Frame {
1098 focus: None,
1099 window_active: false,
1100 element_states: FxHashMap::default(),
1101 accessed_element_states: Vec::new(),
1102 mouse_listeners: Vec::new(),
1103 dispatch_tree,
1104 scene: Scene::default(),
1105 hitboxes: Vec::new(),
1106 window_control_hitboxes: Vec::new(),
1107 deferred_draws: Vec::new(),
1108 input_handlers: Vec::new(),
1109 tooltip_requests: Vec::new(),
1110 cursor_styles: Vec::new(),
1111
1112 #[cfg(any(test, feature = "test-support"))]
1113 debug_bounds: FxHashMap::default(),
1114 #[cfg(any(test, feature = "test-support"))]
1115 debug_bounds_records: Vec::new(),
1116
1117 #[cfg(any(feature = "inspector", debug_assertions))]
1118 next_inspector_instance_ids: FxHashMap::default(),
1119
1120 #[cfg(any(feature = "inspector", debug_assertions))]
1121 inspector_hitboxes: FxHashMap::default(),
1122 tab_stops: TabStopMap::default(),
1123 }
1124 }
1125
1126 pub(crate) fn clear(&mut self) {
1127 self.element_states.clear();
1128 self.accessed_element_states.clear();
1129 self.mouse_listeners.clear();
1130 self.dispatch_tree.clear();
1131 self.scene.clear();
1132 self.input_handlers.clear();
1133 self.tooltip_requests.clear();
1134 self.cursor_styles.clear();
1135 self.hitboxes.clear();
1136 self.window_control_hitboxes.clear();
1137 self.deferred_draws.clear();
1138 self.tab_stops.clear();
1139 self.focus = None;
1140
1141 #[cfg(any(test, feature = "test-support"))]
1142 {
1143 self.debug_bounds.clear();
1144 self.debug_bounds_records.clear();
1145 }
1146
1147 #[cfg(any(feature = "inspector", debug_assertions))]
1148 {
1149 self.next_inspector_instance_ids.clear();
1150 self.inspector_hitboxes.clear();
1151 }
1152 }
1153
1154 pub(crate) fn cursor_style(&self, window: &Window) -> Option<CursorStyle> {
1155 self.cursor_styles
1156 .iter()
1157 .rev()
1158 .fold_while(None, |style, request| match request.hitbox_id {
1159 None => Done(Some(request.style)),
1160 Some(hitbox_id) => Continue(style.or_else(|| {
1161 hitbox_id
1162 .is_hovered_ignoring_last_input(window)
1163 .then_some(request.style)
1164 })),
1165 })
1166 .into_inner()
1167 }
1168
1169 pub(crate) fn hit_test(&self, position: Point<Pixels>) -> HitTest {
1170 let mut set_hover_hitbox_count = false;
1171 let mut hit_test = HitTest::default();
1172 for hitbox in self.hitboxes.iter().rev() {
1173 let bounds = hitbox.bounds.intersect(&hitbox.content_mask.bounds);
1174 if bounds.contains(&position) {
1175 hit_test.ids.push(hitbox.id);
1176 if !set_hover_hitbox_count
1177 && hitbox.behavior == HitboxBehavior::BlockMouseExceptScroll
1178 {
1179 hit_test.hover_hitbox_count = hit_test.ids.len();
1180 set_hover_hitbox_count = true;
1181 }
1182 if hitbox.behavior == HitboxBehavior::BlockMouse {
1183 break;
1184 }
1185 }
1186 }
1187 if !set_hover_hitbox_count {
1188 hit_test.hover_hitbox_count = hit_test.ids.len();
1189 }
1190 hit_test
1191 }
1192
1193 pub(crate) fn focus_path(&self) -> SmallVec<[FocusId; 8]> {
1194 self.focus
1195 .map(|focus_id| self.dispatch_tree.focus_path(focus_id))
1196 .unwrap_or_default()
1197 }
1198
1199 pub(crate) fn finish(&mut self, prev_frame: &mut Self) {
1200 for element_state_key in &self.accessed_element_states {
1201 if let Some((element_state_key, element_state)) =
1202 prev_frame.element_states.remove_entry(element_state_key)
1203 {
1204 self.element_states.insert(element_state_key, element_state);
1205 }
1206 }
1207
1208 self.scene.finish();
1209 }
1210}
1211
1212#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
1213enum InputModality {
1214 Mouse,
1215 Keyboard,
1216 Touch,
1217}
1218
1219pub struct Window {
1221 pub(crate) handle: AnyWindowHandle,
1222 pub(crate) invalidator: WindowInvalidator,
1223 pub(crate) removed: bool,
1224 pub(crate) platform_window: Box<dyn PlatformWindow>,
1225 pub(crate) display_id: Option<DisplayId>,
1226 pub(crate) display_observers: SingleSubscriberSet<AnyObserver>,
1227 is_resizable: bool,
1228 is_minimizable: bool,
1229 sprite_atlas: Arc<dyn PlatformAtlas>,
1230 text_system: Rc<WindowTextSystem>,
1231 text_rendering_mode: Rc<Cell<TextRenderingMode>>,
1232 rem_size: Pixels,
1233 rem_size_override_stack: SmallVec<[Pixels; 8]>,
1238 pub(crate) viewport_size: Size<Pixels>,
1239 layout_engine: Option<TaffyLayoutEngine>,
1240 pub(crate) root: Option<AnyView>,
1241 pub(crate) element_id_stack: ElementIdStack,
1242 pub(crate) text_style_stack: Vec<TextStyleRefinement>,
1243 pub(crate) rendered_entity_stack: Vec<EntityId>,
1244 pub(crate) element_offset_stack: Vec<Point<Pixels>>,
1245 pub(crate) element_opacity: f32,
1246 pub(crate) content_mask_stack: Vec<ContentMask<Pixels>>,
1247 pub(crate) requested_autoscroll: Option<Bounds<Pixels>>,
1248 last_text_input_configuration: Option<TextInputConfiguration>,
1252 focused_text_input: Option<FocusId>,
1253 pub(crate) image_cache_stack: Vec<AnyImageCache>,
1254 pub(crate) rendered_frame: Frame,
1255 pub(crate) next_frame: Frame,
1256 next_hitbox_id: HitboxId,
1257 pub(crate) next_tooltip_id: TooltipId,
1258 pub(crate) tooltip_bounds: Option<TooltipBounds>,
1259 pub(crate) next_frame_callbacks: Rc<RefCell<Vec<FrameCallback>>>,
1260 pub(crate) dirty_views: FxHashSet<EntityId>,
1261 focus_listeners: SingleSubscriberSet<AnyWindowFocusListener>,
1262 pub(crate) focus_lost_listeners: SingleSubscriberSet<AnyObserver>,
1263 focus_lost_path: SmallVec<[FocusId; 8]>,
1264 default_prevented: bool,
1265 mouse_position: Point<Pixels>,
1266 mouse_hit_test: HitTest,
1267 modifiers: Modifiers,
1268 capslock: Capslock,
1269 scale_factor: f32,
1270 pub(crate) bounds_observers: SingleSubscriberSet<AnyObserver>,
1271 appearance: WindowAppearance,
1272 pub(crate) appearance_observers: SingleSubscriberSet<AnyObserver>,
1273 pub(crate) button_layout_observers: SingleSubscriberSet<AnyObserver>,
1274 active: Rc<Cell<bool>>,
1275 visibility: WindowVisibility,
1276 pub(crate) visibility_observers:
1277 SingleSubscriberSet<Box<dyn FnMut(WindowVisibility, &mut Window, &mut App) -> bool>>,
1278 hovered: Rc<Cell<bool>>,
1279 pub(crate) needs_present: Rc<Cell<bool>>,
1280 pub(crate) input_rate_tracker: Rc<RefCell<InputRateTracker>>,
1283 #[cfg(feature = "profiler")]
1284 pub(crate) window_profiler: profiler::WindowProfiler,
1285 last_input_modality: InputModality,
1286 touch_gestures: TouchGestureRecognizer,
1287 touch_prediction_enabled: bool,
1288 long_press_timer: Option<Task<()>>,
1289 long_press_capture: Option<EntityId>,
1290 pub(crate) refreshing: bool,
1291 pub(crate) activation_observers: SingleSubscriberSet<AnyObserver>,
1292 pub(crate) focus: Option<FocusId>,
1293 focus_enabled: bool,
1294 pub(crate) focus_generation: u64,
1297 pending_input: Option<PendingInput>,
1298 pending_modifier: ModifierState,
1299 pub(crate) pending_input_observers: SingleSubscriberSet<AnyObserver>,
1300 prompt: Option<RenderablePromptHandle>,
1301 pub(crate) client_inset: Option<Pixels>,
1302 captured_hitbox: Option<HitboxId>,
1305 #[cfg(any(feature = "inspector", debug_assertions))]
1306 inspector: Option<Entity<Inspector>>,
1307 #[cfg(feature = "profiler")]
1308 debug_frame_overlay: crate::debug_overlay::DebugFrameOverlay,
1309 pub(crate) a11y: A11y,
1310}
1311
1312#[derive(Clone, Debug, Default)]
1313struct ModifierState {
1314 modifiers: Modifiers,
1315 saw_other_input: bool,
1316}
1317
1318#[derive(Clone, Debug)]
1321pub(crate) struct InputRateTracker {
1322 timestamps: Vec<Instant>,
1323 window: Duration,
1324 inputs_per_second: u32,
1325 sustain_until: Instant,
1326 sustain_duration: Duration,
1327}
1328
1329const FRAME_INTERVAL_TOLERANCE: Duration = Duration::from_millis(1);
1330
1331#[derive(Default)]
1333struct FramePacer {
1334 last_frame: Option<Instant>,
1335}
1336
1337impl FramePacer {
1338 fn should_defer(&mut self, now: Instant, minimum_interval: Option<Duration>) -> bool {
1339 if let Some(interval) = minimum_interval
1340 && let Some(last_frame) = self.last_frame
1341 {
1342 let deadline = last_frame + interval;
1343 if now + FRAME_INTERVAL_TOLERANCE < deadline {
1344 return true;
1345 }
1346 self.last_frame = Some(if now.saturating_duration_since(deadline) <= interval / 4 {
1350 deadline
1351 } else {
1352 now
1353 });
1354 } else {
1355 self.last_frame = Some(now);
1356 }
1357 false
1358 }
1359}
1360
1361fn minimum_frame_interval(
1362 should_throttle: bool,
1363 inactive_without_high_rate_input: bool,
1364 inactive_frame_interval: Option<Duration>,
1365 low_power_frame_interval: Option<Duration>,
1366 thermal_frame_interval: Option<Duration>,
1367 thermal_state: ThermalState,
1368 power_mode: crate::PowerMode,
1369) -> Option<Duration> {
1370 if !should_throttle {
1371 return None;
1372 }
1373
1374 [
1375 inactive_frame_interval.filter(|_| inactive_without_high_rate_input),
1376 low_power_frame_interval.filter(|_| power_mode == crate::PowerMode::LowPower),
1377 thermal_frame_interval.filter(|_| {
1378 matches!(
1379 thermal_state,
1380 ThermalState::Critical | ThermalState::Serious
1381 )
1382 }),
1383 ]
1384 .into_iter()
1385 .flatten()
1386 .max()
1387}
1388
1389impl Default for InputRateTracker {
1390 fn default() -> Self {
1391 Self {
1392 timestamps: Vec::new(),
1393 window: Duration::from_millis(100),
1394 inputs_per_second: 60,
1395 sustain_until: Instant::now(),
1396 sustain_duration: Duration::from_secs(1),
1397 }
1398 }
1399}
1400
1401impl InputRateTracker {
1402 pub fn record_input(&mut self) {
1403 let now = Instant::now();
1404 self.timestamps.push(now);
1405 self.prune_old_timestamps(now);
1406
1407 let min_events = self.inputs_per_second as u128 * self.window.as_millis() / 1000;
1408 if self.timestamps.len() as u128 >= min_events {
1409 self.sustain_until = now + self.sustain_duration;
1410 }
1411 }
1412
1413 pub fn is_high_rate(&self) -> bool {
1414 Instant::now() < self.sustain_until
1415 }
1416
1417 fn prune_old_timestamps(&mut self, now: Instant) {
1418 self.timestamps
1419 .retain(|&t| now.duration_since(t) <= self.window);
1420 }
1421}
1422
1423#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1424pub(crate) enum DrawPhase {
1425 None,
1426 Prepaint,
1427 Paint,
1428 Focus,
1429}
1430
1431pub(crate) const PENDING_INPUT_TIMEOUT: Duration = Duration::from_secs(1);
1432
1433pub struct PendingInputStatus<'a> {
1435 keystrokes: &'a [Keystroke],
1436 timeout: Option<PendingInputTimeoutStatus>,
1437}
1438
1439impl<'a> PendingInputStatus<'a> {
1440 pub fn keystrokes(&self) -> &'a [Keystroke] {
1442 self.keystrokes
1443 }
1444
1445 pub fn timeout(&self) -> Option<PendingInputTimeoutStatus> {
1447 self.timeout
1448 }
1449}
1450
1451#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1453pub struct PendingInputTimeoutStatus {
1454 duration: Duration,
1455 remaining: Duration,
1456 started_at: Option<Instant>,
1457 paused: bool,
1458}
1459
1460impl PendingInputTimeoutStatus {
1461 pub fn duration(&self) -> Duration {
1463 self.duration
1464 }
1465
1466 pub fn remaining(&self, cx: &App) -> Duration {
1468 self.started_at
1469 .map(|started_at| {
1470 self.remaining
1471 .saturating_sub(cx.background_executor().now() - started_at)
1472 })
1473 .unwrap_or(self.remaining)
1474 }
1475
1476 pub fn is_paused(&self) -> bool {
1478 self.paused
1479 }
1480}
1481
1482#[derive(Debug)]
1483struct PendingInputTimeout {
1484 duration: Duration,
1485 remaining: Duration,
1486 state: PendingInputTimeoutState,
1487}
1488
1489#[derive(Debug)]
1490enum PendingInputTimeoutState {
1491 Running { started_at: Instant, task: Task<()> },
1492 Paused { pause: PendingInputTimeoutPause },
1493}
1494
1495#[derive(Debug)]
1496struct PendingInputTimeoutPause {
1497 owner_id: EntityId,
1498 _release_subscription: Subscription,
1499}
1500
1501impl PendingInputTimeout {
1502 fn is_paused(&self) -> bool {
1503 matches!(&self.state, PendingInputTimeoutState::Paused { .. })
1504 }
1505
1506 fn pause(&mut self, pause: PendingInputTimeoutPause, now: Instant) -> bool {
1507 match std::mem::replace(&mut self.state, PendingInputTimeoutState::Paused { pause }) {
1508 PendingInputTimeoutState::Running { started_at, task } => {
1509 self.remaining = self.remaining.saturating_sub(now - started_at);
1510 drop(task);
1511 true
1512 }
1513 previous_state @ PendingInputTimeoutState::Paused { .. } => {
1514 self.state = previous_state;
1515 false
1516 }
1517 }
1518 }
1519
1520 fn pause_owner_id(&self) -> Option<EntityId> {
1521 match &self.state {
1522 PendingInputTimeoutState::Running { .. } => None,
1523 PendingInputTimeoutState::Paused { pause } => Some(pause.owner_id),
1524 }
1525 }
1526
1527 fn resume(&mut self, owner_id: EntityId, started_at: Instant, task: Task<()>) -> bool {
1528 match std::mem::replace(
1529 &mut self.state,
1530 PendingInputTimeoutState::Running { started_at, task },
1531 ) {
1532 PendingInputTimeoutState::Paused { pause } if pause.owner_id == owner_id => true,
1533 previous_state => {
1534 self.state = previous_state;
1535 false
1536 }
1537 }
1538 }
1539
1540 fn reset_duration(&mut self, duration: Duration) {
1541 self.duration = duration;
1542 self.remaining = duration;
1543 }
1544
1545 fn status(&self) -> PendingInputTimeoutStatus {
1546 let (started_at, paused) = match &self.state {
1547 PendingInputTimeoutState::Running { started_at, .. } => (Some(*started_at), false),
1548 PendingInputTimeoutState::Paused { .. } => (None, true),
1549 };
1550 PendingInputTimeoutStatus {
1551 duration: self.duration,
1552 remaining: self.remaining,
1553 started_at,
1554 paused,
1555 }
1556 }
1557}
1558
1559#[derive(Default, Debug)]
1560struct PendingInput {
1561 keystrokes: SmallVec<[Keystroke; 1]>,
1562 focus: Option<FocusId>,
1563 timeout: Option<PendingInputTimeout>,
1564}
1565
1566pub(crate) struct ElementStateBox {
1567 pub(crate) inner: Box<dyn Any>,
1568 #[cfg(debug_assertions)]
1569 pub(crate) type_name: &'static str,
1570}
1571
1572fn default_bounds(display_id: Option<DisplayId>, cx: &mut App) -> WindowBounds {
1573 let active_window_bounds = cx
1578 .active_window()
1579 .and_then(|w| w.update(cx, |_, window, _| window.window_bounds()).ok());
1580
1581 const CASCADE_OFFSET: f32 = 25.0;
1582
1583 let display = display_id
1584 .map(|id| cx.find_display(id))
1585 .unwrap_or_else(|| cx.primary_display());
1586
1587 let default_placement = || Bounds::new(point(px(0.), px(0.)), DEFAULT_WINDOW_SIZE);
1588
1589 let display_bounds = display
1591 .as_ref()
1592 .map(|d| d.visible_bounds())
1593 .unwrap_or_else(default_placement);
1594
1595 let (
1596 Bounds {
1597 origin: base_origin,
1598 size: base_size,
1599 },
1600 window_bounds_ctor,
1601 ): (_, fn(Bounds<Pixels>) -> WindowBounds) = match active_window_bounds {
1602 Some(bounds) => match bounds {
1603 WindowBounds::Windowed(bounds) => (bounds, WindowBounds::Windowed),
1604 WindowBounds::Maximized(bounds) => (bounds, WindowBounds::Maximized),
1605 WindowBounds::Fullscreen(bounds) => (bounds, WindowBounds::Fullscreen),
1606 },
1607 None => (
1608 display
1609 .as_ref()
1610 .map(|d| d.default_bounds())
1611 .unwrap_or_else(default_placement),
1612 WindowBounds::Windowed,
1613 ),
1614 };
1615
1616 let cascade_offset = point(px(CASCADE_OFFSET), px(CASCADE_OFFSET));
1617 let proposed_origin = base_origin + cascade_offset;
1618 let proposed_bounds = Bounds::new(proposed_origin, base_size);
1619
1620 let display_right = display_bounds.origin.x + display_bounds.size.width;
1621 let display_bottom = display_bounds.origin.y + display_bounds.size.height;
1622 let window_right = proposed_bounds.origin.x + proposed_bounds.size.width;
1623 let window_bottom = proposed_bounds.origin.y + proposed_bounds.size.height;
1624
1625 let fits_horizontally = window_right <= display_right;
1626 let fits_vertically = window_bottom <= display_bottom;
1627
1628 let final_origin = match (fits_horizontally, fits_vertically) {
1629 (true, true) => proposed_origin,
1630 (false, true) => point(display_bounds.origin.x, base_origin.y),
1631 (true, false) => point(base_origin.x, display_bounds.origin.y),
1632 (false, false) => display_bounds.origin,
1633 };
1634 window_bounds_ctor(Bounds::new(final_origin, base_size))
1635}
1636
1637impl Window {
1638 pub(crate) fn new(
1639 handle: AnyWindowHandle,
1640 options: WindowOptions,
1641 cx: &mut App,
1642 ) -> Result<Self> {
1643 let WindowOptions {
1644 window_bounds,
1645 titlebar,
1646 focus,
1647 show,
1648 kind,
1649 is_movable,
1650 app_owns_titlebar_drag,
1651 inactive_frame_interval,
1652 low_power_frame_interval,
1653 thermal_frame_interval,
1654 is_resizable,
1655 is_minimizable,
1656 display_id,
1657 window_background,
1658 app_id,
1659 window_min_size,
1660 window_decorations,
1661 #[cfg_attr(
1662 not(any(target_os = "linux", target_os = "freebsd")),
1663 allow(unused_variables)
1664 )]
1665 icon,
1666 #[cfg_attr(not(target_os = "macos"), allow(unused_variables))]
1667 tabbing_identifier,
1668 #[cfg_attr(not(target_os = "macos"), allow(unused_variables))]
1669 use_display_p3,
1670 } = options;
1671
1672 let initial_window_title = titlebar
1673 .as_ref()
1674 .and_then(|titlebar| titlebar.title.clone());
1675
1676 let window_bounds = window_bounds.unwrap_or_else(|| default_bounds(display_id, cx));
1677 let mut platform_window = cx.platform.open_window(
1678 handle,
1679 WindowParams {
1680 bounds: window_bounds.get_bounds(),
1681 titlebar,
1682 kind,
1683 is_movable,
1684 app_owns_titlebar_drag,
1685 is_resizable,
1686 is_minimizable,
1687 focus,
1688 show,
1689 display_id,
1690 window_min_size,
1691 app_id: app_id.clone(),
1692 icon,
1693 #[cfg(target_os = "macos")]
1694 tabbing_identifier,
1695 #[cfg(target_os = "macos")]
1696 use_display_p3,
1697 },
1698 )?;
1699
1700 let tab_bar_visible = platform_window.tab_bar_visible();
1701 SystemWindowTabController::init_visible(cx, tab_bar_visible);
1702 if let Some(tabs) = platform_window.tabbed_windows() {
1703 SystemWindowTabController::add_tab(cx, handle.window_id(), tabs);
1704 }
1705
1706 let display_id = platform_window.display().map(|display| display.id());
1707 let sprite_atlas = platform_window.sprite_atlas();
1708 let mouse_position = platform_window.mouse_position();
1709 let modifiers = platform_window.modifiers();
1710 let capslock = platform_window.capslock();
1711 let content_size = platform_window.content_size();
1712 let scale_factor = platform_window.scale_factor();
1713 let appearance = platform_window.appearance();
1714 let text_system = Rc::new(WindowTextSystem::new(cx.text_system().clone()));
1715 let invalidator = WindowInvalidator::new(handle.window_id());
1716 let active = Rc::new(Cell::new(platform_window.is_active()));
1717 let visibility = platform_window.visibility();
1718 #[cfg(feature = "profiler")]
1719 profiler::journal::record_window_visibility(handle.window_id(), visibility);
1720 let hovered = Rc::new(Cell::new(platform_window.is_hovered()));
1721 let needs_present = Rc::new(Cell::new(false));
1722 let next_frame_callbacks: Rc<RefCell<Vec<FrameCallback>>> = Default::default();
1723 let input_rate_tracker = Rc::new(RefCell::new(InputRateTracker::default()));
1724
1725 platform_window
1726 .request_decorations(window_decorations.unwrap_or(WindowDecorations::Server));
1727 platform_window.set_background_appearance(window_background);
1728
1729 match window_bounds {
1730 WindowBounds::Fullscreen(_) => platform_window.toggle_fullscreen(),
1731 WindowBounds::Maximized(_) => platform_window.zoom(),
1732 WindowBounds::Windowed(_) => {}
1733 }
1734
1735 let accessibility_force_disabled = cx.accessibility_force_disabled;
1736 let a11y_active_flag = Arc::new(AtomicBool::new(false));
1737
1738 #[cfg(not(target_family = "wasm"))]
1739 if !accessibility_force_disabled {
1740 let mut initial_root_node = accesskit::Node::new(accesskit::Role::Window);
1741 if let Some(title) = &initial_window_title {
1742 initial_root_node.set_label(title.to_string());
1743 }
1744 let initial_tree = accesskit::TreeUpdate {
1745 nodes: vec![(ROOT_NODE_ID, initial_root_node)],
1746 tree: Some(accesskit::Tree::new(ROOT_NODE_ID)),
1747 tree_id: accesskit::TreeId::ROOT,
1748 focus: ROOT_NODE_ID,
1749 };
1750 let (activation_sender, activation_receiver) = async_channel::unbounded::<()>();
1751 let (deactivation_sender, deactivation_receiver) = async_channel::unbounded::<()>();
1752 let (action_sender, action_receiver) =
1753 async_channel::unbounded::<accesskit::ActionRequest>();
1754
1755 platform_window.a11y_init(crate::A11yCallbacks {
1756 activation: {
1757 let active_flag = a11y_active_flag.clone();
1758 Box::new(move || {
1759 log::info!("Accessibility activated");
1760 active_flag.store(true, SeqCst);
1761 activation_sender
1762 .send_blocking(())
1763 .with_context(|| {
1764 format!(
1765 "queuing accessibility activation for window {:?}",
1766 handle.window_id()
1767 )
1768 })
1769 .log_err();
1770 Some(initial_tree.clone())
1771 })
1772 },
1773 action: Box::new(move |request| {
1774 action_sender
1775 .send_blocking(request)
1776 .with_context(|| {
1777 format!(
1778 "queuing accessibility action for window {:?}",
1779 handle.window_id()
1780 )
1781 })
1782 .log_err();
1783 }),
1784 deactivation: {
1785 let active_flag = a11y_active_flag.clone();
1786 Box::new(move || {
1787 log::info!("Accessibility deactivated");
1788 active_flag.store(false, SeqCst);
1789 deactivation_sender
1790 .send_blocking(())
1791 .with_context(|| {
1792 format!(
1793 "queuing accessibility deactivation for window {:?}",
1794 handle.window_id()
1795 )
1796 })
1797 .log_err();
1798 })
1799 },
1800 });
1801
1802 let mut async_cx = cx.to_async();
1808 cx.foreground_executor()
1809 .spawn(async move {
1810 while activation_receiver.recv().await.is_ok() {
1811 handle
1812 .update(&mut async_cx, |_, window, _| window.refresh())
1813 .with_context(|| {
1814 format!(
1815 "handling accessibility activation for window {:?}",
1816 handle.window_id()
1817 )
1818 })
1819 .log_err();
1820 }
1821 })
1822 .detach();
1823
1824 let mut async_cx = cx.to_async();
1825 cx.foreground_executor()
1826 .spawn(async move {
1827 while deactivation_receiver.recv().await.is_ok() {
1828 handle
1829 .update(&mut async_cx, |_, window, _| window.refresh())
1830 .with_context(|| {
1831 format!(
1832 "handling accessibility deactivation for window {:?}",
1833 handle.window_id()
1834 )
1835 })
1836 .log_err();
1837 }
1838 })
1839 .detach();
1840
1841 let mut async_cx = cx.to_async();
1842 cx.foreground_executor()
1843 .spawn(async move {
1844 while let Ok(request) = action_receiver.recv().await {
1845 handle
1846 .update(&mut async_cx, |_, window, cx| {
1847 window.handle_a11y_action(request, cx);
1848 })
1849 .with_context(|| {
1850 format!(
1851 "handling accessibility action for window {:?}",
1852 handle.window_id()
1853 )
1854 })
1855 .log_err();
1856 }
1857 })
1858 .detach();
1859 }
1860
1861 platform_window.on_close(Box::new({
1862 let window_id = handle.window_id();
1863 let cx = cx.to_async();
1864 move || {
1865 cx.spawn(async move |cx| {
1868 cx.update(|cx| {
1869 if cx.windows.contains_key(window_id) {
1871 handle
1872 .update(cx, |_, window, _| window.remove_window())
1873 .with_context(|| format!("handling close for window {window_id:?}"))
1874 .log_err();
1875 }
1876 SystemWindowTabController::remove_tab(cx, window_id);
1877 });
1878 })
1879 .detach();
1880 }
1881 }));
1882 platform_window.on_request_frame(Box::new({
1883 let mut cx = cx.to_async();
1884 let invalidator = invalidator.clone();
1885 let active = active.clone();
1886 let needs_present = needs_present.clone();
1887 let next_frame_callbacks = next_frame_callbacks.clone();
1888 let input_rate_tracker = input_rate_tracker.clone();
1889 let mut deferred_force_render = false;
1890 let mut frame_pacer = FramePacer::default();
1891 move |request_frame_options| {
1892 #[cfg(feature = "profiler")]
1893 let (_foreground_turn, window_id) = {
1894 let foreground_turn = profiler::journal::foreground_turn();
1895 let window_id = handle.window_id();
1896 profiler::journal::record_platform_signal(profiler::journal::PlatformSignal {
1897 window_id,
1898 signal_at: request_frame_options.signal_at,
1899 handled_at: Instant::now(),
1900 source: request_frame_options.signal_source,
1901 });
1902 (foreground_turn, window_id)
1903 };
1904 if draw_in_progress() {
1920 log::debug!("deferring re-entrant window draw request");
1921 deferred_force_render |= request_frame_options.force_render;
1922 #[cfg(feature = "profiler")]
1923 profiler::journal::record_reentrant_frame_skipped(window_id, Instant::now());
1924 return;
1925 }
1926 let force_render =
1930 mem::take(&mut deferred_force_render) || request_frame_options.force_render;
1931
1932 let power_state = handle
1933 .update(&mut cx, |_, _, cx| (cx.thermal_state(), cx.power_mode()))
1934 .with_context(|| {
1935 format!(
1936 "reading power state for frame request for window {:?}",
1937 handle.window_id()
1938 )
1939 })
1940 .log_err();
1941
1942 let high_rate_input = input_rate_tracker.borrow_mut().is_high_rate();
1945 let has_next_frame_callbacks = !next_frame_callbacks.borrow().is_empty();
1946 let has_frame_demand = invalidator.is_dirty()
1947 || force_render
1948 || has_next_frame_callbacks
1949 || request_frame_options.require_presentation
1950 || needs_present.get()
1951 || high_rate_input;
1952
1953 let should_throttle = !request_frame_options.require_presentation;
1954 let inactive_without_high_rate_input =
1955 should_throttle && !active.get() && !high_rate_input;
1956 let (thermal_state, power_mode) =
1957 power_state.unwrap_or((ThermalState::Nominal, crate::PowerMode::Unknown));
1958 let min_frame_interval = minimum_frame_interval(
1959 should_throttle,
1960 inactive_without_high_rate_input,
1961 inactive_frame_interval,
1962 low_power_frame_interval,
1963 thermal_frame_interval,
1964 thermal_state,
1965 power_mode,
1966 );
1967
1968 let now = Instant::now();
1969 if has_frame_demand && frame_pacer.should_defer(now, min_frame_interval) {
1972 deferred_force_render |= force_render;
1974 handle
1976 .update(&mut cx, |_, window, _| {
1977 window.platform_window.schedule_frame();
1978 })
1979 .with_context(|| {
1980 format!(
1981 "rescheduling throttled frame for window {:?}",
1982 handle.window_id()
1983 )
1984 })
1985 .log_err();
1986 invalidator.wake_platform();
1989 #[cfg(feature = "profiler")]
1990 profiler::journal::record_frame_skipped(
1991 window_id,
1992 Instant::now(),
1993 if inactive_without_high_rate_input
1994 && inactive_frame_interval == min_frame_interval
1995 {
1996 profiler::journal::FrameSkipReason::InactiveFrameRateLimit
1997 } else if power_mode == crate::PowerMode::LowPower
1998 && low_power_frame_interval == min_frame_interval
1999 {
2000 profiler::journal::FrameSkipReason::LowPowerFrameRateLimit
2001 } else {
2002 profiler::journal::FrameSkipReason::ThermalFrameRateLimit
2003 },
2004 );
2005 return;
2006 }
2007
2008 let pending_next_frame_callbacks = next_frame_callbacks.take();
2009 if !pending_next_frame_callbacks.is_empty() {
2010 handle
2011 .update(&mut cx, |_, window, cx| {
2012 for callback in pending_next_frame_callbacks {
2013 callback(window, cx);
2014 }
2015 })
2016 .with_context(|| {
2017 format!(
2018 "running next-frame callbacks for window {:?}",
2019 handle.window_id()
2020 )
2021 })
2022 .log_err();
2023 }
2024
2025 let should_draw = invalidator.is_dirty() || force_render;
2026 let needs_present = request_frame_options.require_presentation
2027 || needs_present.get()
2028 || high_rate_input;
2029 if should_draw {
2030 measure("frame duration", || {
2031 handle
2032 .update(&mut cx, |_, window, cx| {
2033 #[cfg(feature = "profiler")]
2034 window
2035 .window_profiler
2036 .record_frame_signal(request_frame_options.signal_at);
2037 if force_render {
2038 window.refresh();
2041 }
2042 let arena_clear_needed = window.draw(cx);
2043 window.present();
2044 arena_clear_needed.clear(cx);
2045 })
2046 .with_context(|| {
2047 format!(
2048 "drawing and presenting frame for window {:?}",
2049 handle.window_id()
2050 )
2051 })
2052 .log_err();
2053 })
2054 } else if needs_present {
2055 handle
2056 .update(&mut cx, |_, window, _| window.present())
2057 .with_context(|| {
2058 format!("presenting frame for window {:?}", handle.window_id())
2059 })
2060 .log_err();
2061 }
2062
2063 handle
2064 .update(&mut cx, |_, window, _| {
2065 if window.invalidator.is_dirty()
2066 || !window.next_frame_callbacks.borrow().is_empty()
2067 {
2068 window.platform_window.schedule_frame();
2069 }
2070 })
2071 .with_context(|| {
2072 format!(
2073 "scheduling follow-up frame for window {:?}",
2074 handle.window_id()
2075 )
2076 })
2077 .log_err();
2078
2079 if invalidator.is_dirty() || !next_frame_callbacks.borrow().is_empty() {
2085 invalidator.wake_platform();
2086 }
2087 #[cfg(feature = "profiler")]
2088 if !should_draw {
2089 profiler::journal::record_frame_skipped(
2090 window_id,
2091 Instant::now(),
2092 profiler::journal::FrameSkipReason::NoRenderNeeded,
2093 );
2094 }
2095 }
2096 }));
2097 invalidator.set_platform_waker(platform_window.frame_waker());
2098 platform_window.on_visual_viewport_changed(Box::new({
2099 let mut cx = cx.to_async();
2100 move || {
2101 handle
2102 .update(&mut cx, |_, window, _| window.refresh())
2103 .with_context(|| {
2104 format!(
2105 "handling visual viewport change for window {:?}",
2106 handle.window_id()
2107 )
2108 })
2109 .log_err();
2110 }
2111 }));
2112 platform_window.on_insets_changed(Box::new({
2113 let mut cx = cx.to_async();
2114 move |_| {
2115 handle
2116 .update(&mut cx, |_, window, _| window.refresh())
2117 .with_context(|| {
2118 format!("handling insets change for window {:?}", handle.window_id())
2119 })
2120 .log_err();
2121 }
2122 }));
2123 platform_window.on_resize(Box::new({
2124 let mut cx = cx.to_async();
2125 move |_, _| {
2126 handle
2127 .update(&mut cx, |_, window, cx| window.bounds_changed(cx))
2128 .with_context(|| format!("handling resize for window {:?}", handle.window_id()))
2129 .log_err();
2130 }
2131 }));
2132 platform_window.on_moved(Box::new({
2133 let mut cx = cx.to_async();
2134 move || {
2135 handle
2136 .update(&mut cx, |_, window, cx| window.bounds_changed(cx))
2137 .with_context(|| format!("handling move for window {:?}", handle.window_id()))
2138 .log_err();
2139 }
2140 }));
2141 platform_window.on_display_changed(Box::new({
2142 let cx = cx.to_async();
2143 move || cx.update(|cx| Window::display_changed(handle, cx))
2144 }));
2145 platform_window.on_appearance_changed(Box::new({
2146 let cx = cx.to_async();
2147 let foreground_executor = cx.foreground_executor().clone();
2148 move || {
2149 let mut cx = cx.clone();
2150 foreground_executor
2153 .spawn(async move {
2154 handle
2155 .update(&mut cx, |_, window, cx| window.appearance_changed(cx))
2156 .with_context(|| {
2157 format!(
2158 "handling appearance change for window {:?}",
2159 handle.window_id()
2160 )
2161 })
2162 .log_err();
2163 })
2164 .detach();
2165 }
2166 }));
2167 platform_window.on_button_layout_changed(Box::new({
2168 let mut cx = cx.to_async();
2169 move || {
2170 handle
2171 .update(&mut cx, |_, window, cx| window.button_layout_changed(cx))
2172 .with_context(|| {
2173 format!(
2174 "handling button layout change for window {:?}",
2175 handle.window_id()
2176 )
2177 })
2178 .log_err();
2179 }
2180 }));
2181 platform_window.on_active_status_change(Box::new({
2182 let mut cx = cx.to_async();
2183 move |active| {
2184 handle
2185 .update(&mut cx, |_, window, cx| {
2186 window.active.set(active);
2187 window.modifiers = window.platform_window.modifiers();
2188 window.capslock = window.platform_window.capslock();
2189 window
2190 .activation_observers
2191 .clone()
2192 .retain(|callback| callback(window, cx));
2193
2194 window.bounds_changed(cx);
2195 window.refresh();
2196
2197 SystemWindowTabController::update_last_active(cx, window.handle.id);
2198 })
2199 .with_context(|| {
2200 format!(
2201 "handling active status change for window {:?}",
2202 handle.window_id()
2203 )
2204 })
2205 .log_err();
2206 }
2207 }));
2208 platform_window.on_visibility_change(Box::new({
2209 let mut cx = cx.to_async();
2210 move |_| {
2211 handle
2212 .update(&mut cx, |_, window, cx| {
2213 window.refresh_visibility(cx);
2214 })
2215 .with_context(|| {
2216 format!(
2217 "handling visibility change for window {:?}",
2218 handle.window_id()
2219 )
2220 })
2221 .log_err();
2222 }
2223 }));
2224 platform_window.on_hover_status_change(Box::new({
2225 let mut cx = cx.to_async();
2226 move |active| {
2227 handle
2228 .update(&mut cx, |_, window, _| {
2229 window.hovered.set(active);
2230 window.refresh();
2231 })
2232 .with_context(|| {
2233 format!(
2234 "handling hover status change for window {:?}",
2235 handle.window_id()
2236 )
2237 })
2238 .log_err();
2239 }
2240 }));
2241 platform_window.on_input({
2242 let mut cx = cx.to_async();
2243 Box::new(move |event| {
2244 handle
2245 .update(&mut cx, |_, window, cx| window.dispatch_event(event, cx))
2246 .with_context(|| {
2247 format!("dispatching input for window {:?}", handle.window_id())
2248 })
2249 .log_err()
2250 .unwrap_or(DispatchEventResult::default())
2251 })
2252 });
2253 platform_window.on_hit_test_window_control({
2254 let mut cx = cx.to_async();
2255 Box::new(move || {
2256 handle
2257 .update(&mut cx, |_, window, _cx| {
2258 for (area, hitbox) in &window.rendered_frame.window_control_hitboxes {
2259 if window.mouse_hit_test.ids.contains(&hitbox.id) {
2260 return Some(*area);
2261 }
2262 }
2263 None
2264 })
2265 .with_context(|| {
2266 format!(
2267 "hit-testing window controls for window {:?}",
2268 handle.window_id()
2269 )
2270 })
2271 .log_err()
2272 .unwrap_or(None)
2273 })
2274 });
2275 platform_window.on_move_tab_to_new_window({
2276 let mut cx = cx.to_async();
2277 Box::new(move || {
2278 handle
2279 .update(&mut cx, |_, _window, cx| {
2280 SystemWindowTabController::move_tab_to_new_window(cx, handle.window_id());
2281 })
2282 .with_context(|| {
2283 format!(
2284 "moving tab to new window from window {:?}",
2285 handle.window_id()
2286 )
2287 })
2288 .log_err();
2289 })
2290 });
2291 platform_window.on_merge_all_windows({
2292 let mut cx = cx.to_async();
2293 Box::new(move || {
2294 handle
2295 .update(&mut cx, |_, _window, cx| {
2296 SystemWindowTabController::merge_all_windows(cx, handle.window_id());
2297 })
2298 .with_context(|| {
2299 format!("merging all windows into window {:?}", handle.window_id())
2300 })
2301 .log_err();
2302 })
2303 });
2304 platform_window.on_select_next_tab({
2305 let mut cx = cx.to_async();
2306 Box::new(move || {
2307 handle
2308 .update(&mut cx, |_, _window, cx| {
2309 SystemWindowTabController::select_next_tab(cx, handle.window_id());
2310 })
2311 .with_context(|| {
2312 format!("selecting next tab for window {:?}", handle.window_id())
2313 })
2314 .log_err();
2315 })
2316 });
2317 platform_window.on_select_previous_tab({
2318 let mut cx = cx.to_async();
2319 Box::new(move || {
2320 handle
2321 .update(&mut cx, |_, _window, cx| {
2322 SystemWindowTabController::select_previous_tab(cx, handle.window_id())
2323 })
2324 .with_context(|| {
2325 format!("selecting previous tab for window {:?}", handle.window_id())
2326 })
2327 .log_err();
2328 })
2329 });
2330 platform_window.on_toggle_tab_bar({
2331 let mut cx = cx.to_async();
2332 Box::new(move || {
2333 handle
2334 .update(&mut cx, |_, window, cx| {
2335 let tab_bar_visible = window.platform_window.tab_bar_visible();
2336 SystemWindowTabController::set_visible(cx, tab_bar_visible);
2337 })
2338 .with_context(|| {
2339 format!("toggling tab bar for window {:?}", handle.window_id())
2340 })
2341 .log_err();
2342 })
2343 });
2344
2345 if let Some(app_id) = app_id {
2346 platform_window.set_app_id(&app_id);
2347 }
2348
2349 platform_window.map_window().unwrap();
2350
2351 Ok(Window {
2352 handle,
2353 invalidator,
2354 removed: false,
2355 platform_window,
2356 display_id,
2357 display_observers: SingleSubscriberSet::new(),
2358 is_resizable,
2359 is_minimizable,
2360 sprite_atlas,
2361 text_system,
2362 text_rendering_mode: cx.text_rendering_mode.clone(),
2363 rem_size: px(16.),
2364 rem_size_override_stack: SmallVec::new(),
2365 viewport_size: content_size,
2366 layout_engine: Some(TaffyLayoutEngine::new()),
2367 root: None,
2368 element_id_stack: ElementIdStack::default(),
2369 text_style_stack: Vec::new(),
2370 rendered_entity_stack: Vec::new(),
2371 element_offset_stack: Vec::new(),
2372 content_mask_stack: Vec::new(),
2373 element_opacity: 1.0,
2374 requested_autoscroll: None,
2375 last_text_input_configuration: None,
2376 focused_text_input: None,
2377 rendered_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())),
2378 next_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())),
2379 next_frame_callbacks,
2380 next_hitbox_id: HitboxId(0),
2381 next_tooltip_id: TooltipId::default(),
2382 tooltip_bounds: None,
2383 dirty_views: FxHashSet::default(),
2384 focus_listeners: SingleSubscriberSet::new(),
2385 focus_lost_listeners: SingleSubscriberSet::new(),
2386 focus_lost_path: SmallVec::new(),
2387 default_prevented: true,
2388 mouse_position,
2389 mouse_hit_test: HitTest::default(),
2390 modifiers,
2391 capslock,
2392 scale_factor,
2393 bounds_observers: SingleSubscriberSet::new(),
2394 appearance,
2395 appearance_observers: SingleSubscriberSet::new(),
2396 button_layout_observers: SingleSubscriberSet::new(),
2397 active,
2398 visibility,
2399 visibility_observers: SingleSubscriberSet::new(),
2400 hovered,
2401 needs_present,
2402 input_rate_tracker,
2403 #[cfg(feature = "profiler")]
2404 window_profiler: profiler::WindowProfiler::new(handle.window_id())?,
2405 last_input_modality: InputModality::Mouse,
2406 touch_gestures: TouchGestureRecognizer::new(
2407 cx.platform
2408 .gestures()
2409 .map_or_else(GestureTuning::default, |gestures| gestures.tuning()),
2410 ),
2411 touch_prediction_enabled: true,
2412 long_press_timer: None,
2413 long_press_capture: None,
2414 refreshing: false,
2415 activation_observers: SingleSubscriberSet::new(),
2416 focus: None,
2417 focus_enabled: true,
2418 focus_generation: 0,
2419 pending_input: None,
2420 pending_modifier: ModifierState::default(),
2421 pending_input_observers: SingleSubscriberSet::new(),
2422 prompt: None,
2423 client_inset: None,
2424 image_cache_stack: Vec::new(),
2425 captured_hitbox: None,
2426 #[cfg(any(feature = "inspector", debug_assertions))]
2427 inspector: None,
2428 #[cfg(feature = "profiler")]
2429 debug_frame_overlay: crate::debug_overlay::DebugFrameOverlay::new(),
2430 a11y: A11y::new(
2431 a11y_active_flag,
2432 accessibility_force_disabled,
2433 initial_window_title,
2434 ),
2435 })
2436 }
2437
2438 pub(crate) fn new_focus_listener(
2439 &self,
2440 value: AnyWindowFocusListener,
2441 ) -> (Subscription, impl FnOnce() + use<>) {
2442 self.focus_listeners.insert_pending(value)
2443 }
2444}
2445
2446#[derive(Clone, Debug, Default, PartialEq, Eq)]
2447#[expect(missing_docs)]
2448pub struct DispatchEventResult {
2449 pub propagate: bool,
2450 pub default_prevented: bool,
2451}
2452
2453#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
2457#[repr(C)]
2458pub struct ContentMask<P: Clone + Debug + Default + PartialEq> {
2459 pub bounds: Bounds<P>,
2461}
2462
2463impl ContentMask<Pixels> {
2464 pub fn scale(&self, factor: f32) -> ContentMask<ScaledPixels> {
2466 ContentMask {
2467 bounds: self.bounds.scale(factor),
2468 }
2469 }
2470
2471 pub fn intersect(&self, other: &Self) -> Self {
2473 let bounds = self.bounds.intersect(&other.bounds);
2474 ContentMask { bounds }
2475 }
2476}
2477
2478impl Window {
2479 fn mark_view_dirty(&mut self, view_id: EntityId) {
2480 for view_id in self
2483 .rendered_frame
2484 .dispatch_tree
2485 .view_path_reversed(view_id)
2486 {
2487 if !self.dirty_views.insert(view_id) {
2488 break;
2489 }
2490 }
2491 }
2492
2493 pub(crate) fn refresh_visibility(&mut self, cx: &mut App) {
2494 let visibility = self.platform_window.visibility();
2495 if self.visibility != visibility {
2496 self.visibility = visibility;
2497 #[cfg(feature = "profiler")]
2498 profiler::journal::record_window_visibility(self.handle.window_id(), visibility);
2499 self.visibility_observers
2500 .clone()
2501 .retain(|callback| callback(visibility, self, cx));
2502 }
2503 #[cfg(feature = "profiler")]
2504 if self.invalidator.is_dirty() || self.needs_present.get() {
2505 profiler::journal::record_frame_pending(self.handle.window_id(), Instant::now());
2506 }
2507 }
2508
2509 pub fn visibility(&self) -> WindowVisibility {
2512 self.visibility
2513 }
2514
2515 pub fn is_visible(&self) -> bool {
2521 self.visibility.is_visible()
2522 }
2523
2524 pub fn observe_window_visibility(
2526 &self,
2527 mut callback: impl FnMut(WindowVisibility, &mut Window, &mut App) + 'static,
2528 ) -> Subscription {
2529 self.visibility_observers
2530 .insert(Box::new(move |visibility, window, cx| {
2531 callback(visibility, window, cx);
2532 true
2533 }))
2534 }
2535
2536 pub fn observe_window_appearance(
2538 &self,
2539 mut callback: impl FnMut(&mut Window, &mut App) + 'static,
2540 ) -> Subscription {
2541 self.appearance_observers
2542 .insert(Box::new(move |window, cx| {
2543 callback(window, cx);
2544 true
2545 }))
2546 }
2547
2548 pub fn observe_button_layout_changed(
2550 &self,
2551 mut callback: impl FnMut(&mut Window, &mut App) + 'static,
2552 ) -> Subscription {
2553 self.button_layout_observers
2554 .insert(Box::new(move |window, cx| {
2555 callback(window, cx);
2556 true
2557 }))
2558 }
2559
2560 pub fn replace_root<E>(
2562 &mut self,
2563 cx: &mut App,
2564 build_view: impl FnOnce(&mut Window, &mut Context<E>) -> E,
2565 ) -> Entity<E>
2566 where
2567 E: 'static + Render,
2568 {
2569 let view = cx.new(|cx| build_view(self, cx));
2570 self.root = Some(view.clone().into());
2571 self.refresh();
2572 view
2573 }
2574
2575 pub fn root<E>(&self) -> Option<Option<Entity<E>>>
2577 where
2578 E: 'static + Render,
2579 {
2580 self.root
2581 .as_ref()
2582 .map(|view| view.clone().downcast::<E>().ok())
2583 }
2584
2585 pub fn window_handle(&self) -> AnyWindowHandle {
2587 self.handle
2588 }
2589
2590 pub fn refresh(&mut self) {
2592 if self.invalidator.not_drawing() {
2593 self.refreshing = true;
2594 self.invalidator.set_dirty(true);
2595 }
2596 }
2597
2598 pub fn remove_window(&mut self) {
2600 self.removed = true;
2601 }
2602
2603 pub fn focused(&self, cx: &App) -> Option<FocusHandle> {
2605 self.focus
2606 .and_then(|id| FocusHandle::for_id(id, &cx.focus_handles))
2607 }
2608
2609 pub fn focus_lost_restore_target(&self, cx: &App) -> Option<FocusHandle> {
2613 let (_leaf, ancestors) = self.focus_lost_path.split_last()?;
2614 ancestors.iter().rev().find_map(|id| {
2615 self.rendered_frame.dispatch_tree.focusable_node_id(*id)?;
2616 FocusHandle::for_id(*id, &cx.focus_handles)
2617 })
2618 }
2619
2620 pub fn focus(&mut self, handle: &FocusHandle, cx: &mut App) {
2622 if !self.focus_enabled || self.focus == Some(handle.id) {
2623 return;
2624 }
2625
2626 self.focus = Some(handle.id);
2627 self.focus_generation = self.focus_generation.wrapping_add(1);
2628 self.clear_pending_keystrokes(cx);
2629
2630 self.refresh();
2631 }
2632
2633 pub fn blur(&mut self, cx: &mut App) {
2635 self.clear_pending_keystrokes(cx);
2636
2637 if !self.focus_enabled {
2638 return;
2639 }
2640
2641 if self.focus.is_some() {
2642 self.focus_generation = self.focus_generation.wrapping_add(1);
2643 }
2644 self.focus = None;
2645 self.refresh();
2646 }
2647
2648 pub fn disable_focus(&mut self, cx: &mut App) {
2650 self.blur(cx);
2651 self.focus_enabled = false;
2652 }
2653
2654 pub fn focus_next(&mut self, cx: &mut App) {
2656 if !self.focus_enabled {
2657 return;
2658 }
2659
2660 if let Some(handle) = self.rendered_frame.tab_stops.next(self.focus.as_ref()) {
2661 self.focus(&handle, cx)
2662 }
2663 }
2664
2665 pub fn focus_prev(&mut self, cx: &mut App) {
2667 if !self.focus_enabled {
2668 return;
2669 }
2670
2671 if let Some(handle) = self.rendered_frame.tab_stops.prev(self.focus.as_ref()) {
2672 self.focus(&handle, cx)
2673 }
2674 }
2675
2676 pub fn text_system(&self) -> &Rc<WindowTextSystem> {
2678 &self.text_system
2679 }
2680
2681 pub fn text_style(&self) -> TextStyle {
2683 let mut style = TextStyle::default();
2684 for refinement in &self.text_style_stack {
2685 style.refine(refinement);
2686 }
2687 style
2688 }
2689
2690 pub fn is_maximized(&self) -> bool {
2694 self.platform_window.is_maximized()
2695 }
2696
2697 pub fn request_decorations(&self, decorations: WindowDecorations) {
2699 self.platform_window.request_decorations(decorations);
2700 }
2701
2702 pub fn set_exclusive_zone(&self, zone: Pixels) {
2708 self.platform_window.set_exclusive_zone(zone);
2709 }
2710
2711 #[cfg(all(target_os = "linux", feature = "wayland"))]
2716 pub fn set_exclusive_edge(&self, edge: crate::layer_shell::Anchor) {
2717 self.platform_window.set_exclusive_edge(edge);
2718 }
2719
2720 pub fn start_window_resize(&self, edge: ResizeEdge) {
2722 if self.is_resizable {
2723 self.platform_window.start_window_resize(edge);
2724 }
2725 }
2726
2727 pub fn set_input_region(&self, region: Option<&[Bounds<Pixels>]>) {
2734 self.platform_window.set_input_region(region);
2735 }
2736
2737 pub fn window_bounds(&self) -> WindowBounds {
2740 self.platform_window.window_bounds()
2741 }
2742
2743 pub fn inner_window_bounds(&self) -> WindowBounds {
2745 self.platform_window.inner_window_bounds()
2746 }
2747
2748 pub fn native_window_state(&self) -> Option<Vec<u8>> {
2751 self.platform_window.native_window_state()
2752 }
2753
2754 pub fn restore_native_window_state(&self, state: &[u8]) {
2757 self.platform_window.restore_native_window_state(state);
2758 }
2759
2760 pub fn dispatch_action(&mut self, action: Box<dyn Action>, cx: &mut App) {
2762 let focus_id = self.focused(cx).map(|handle| handle.id);
2763
2764 let window = self.handle;
2765 cx.defer(move |cx| {
2766 window
2767 .update(cx, |_, window, cx| {
2768 let node_id = window.focus_node_id_in_rendered_frame(focus_id);
2769 window.dispatch_action_on_node(node_id, action.as_ref(), cx);
2770 })
2771 .with_context(|| format!("dispatching action for window {:?}", window.window_id()))
2772 .log_err();
2773 })
2774 }
2775
2776 pub(crate) fn dispatch_keystroke_observers(
2777 &mut self,
2778 keystroke: &Keystroke,
2779 input_preference: InputPreference,
2780 action: Option<&dyn Action>,
2781 context_stack: Vec<KeyContext>,
2782 cx: &mut App,
2783 ) {
2784 cx.keystroke_observers.clone().retain(move |callback| {
2785 (callback)(
2786 &KeystrokeEvent {
2787 keystroke: keystroke.clone(),
2788 input_preference,
2789 action: action.map(|action| action.boxed_clone()),
2790 context_stack: context_stack.clone(),
2791 },
2792 self,
2793 cx,
2794 )
2795 });
2796 }
2797
2798 pub(crate) fn dispatch_keystroke_interceptors(
2799 &mut self,
2800 keystroke: &Keystroke,
2801 input_preference: InputPreference,
2802 context_stack: Vec<KeyContext>,
2803 cx: &mut App,
2804 ) {
2805 cx.keystroke_interceptors.clone().retain(move |callback| {
2806 (callback)(
2807 &KeystrokeEvent {
2808 keystroke: keystroke.clone(),
2809 input_preference,
2810 action: None,
2811 context_stack: context_stack.clone(),
2812 },
2813 self,
2814 cx,
2815 )
2816 });
2817 }
2818
2819 pub fn defer(&self, cx: &mut App, f: impl FnOnce(&mut Window, &mut App) + 'static) {
2822 let handle = self.handle;
2823 cx.defer(move |cx| {
2824 handle.update(cx, |_, window, cx| f(window, cx)).ok();
2825 });
2826 }
2827
2828 pub fn observe<T: 'static>(
2832 &mut self,
2833 observed: &Entity<T>,
2834 cx: &mut App,
2835 mut on_notify: impl FnMut(Entity<T>, &mut Window, &mut App) + 'static,
2836 ) -> Subscription {
2837 let entity_id = observed.entity_id();
2838 let observed = observed.downgrade();
2839 let window_handle = self.handle;
2840 cx.new_observer(
2841 entity_id,
2842 Box::new(move |cx| {
2843 window_handle
2844 .update(cx, |_, window, cx| {
2845 if let Some(handle) = observed.upgrade() {
2846 on_notify(handle, window, cx);
2847 true
2848 } else {
2849 false
2850 }
2851 })
2852 .unwrap_or(false)
2853 }),
2854 )
2855 }
2856
2857 pub fn subscribe<Emitter, Evt>(
2861 &mut self,
2862 entity: &Entity<Emitter>,
2863 cx: &mut App,
2864 mut on_event: impl FnMut(Entity<Emitter>, &Evt, &mut Window, &mut App) + 'static,
2865 ) -> Subscription
2866 where
2867 Emitter: EventEmitter<Evt>,
2868 Evt: 'static,
2869 {
2870 let entity_id = entity.entity_id();
2871 let handle = entity.downgrade();
2872 let window_handle = self.handle;
2873 cx.new_subscription(
2874 entity_id,
2875 (
2876 TypeId::of::<Evt>(),
2877 Box::new(move |event, cx| {
2878 window_handle
2879 .update(cx, |_, window, cx| {
2880 if let Some(entity) = handle.upgrade() {
2881 let event = event.downcast_ref().expect("invalid event type");
2882 on_event(entity, event, window, cx);
2883 true
2884 } else {
2885 false
2886 }
2887 })
2888 .unwrap_or(false)
2889 }),
2890 ),
2891 )
2892 }
2893
2894 pub fn observe_release<T>(
2896 &self,
2897 entity: &Entity<T>,
2898 cx: &mut App,
2899 on_release: impl FnOnce(&mut T, &mut Window, &mut App) + 'static,
2900 ) -> Subscription
2901 where
2902 T: 'static,
2903 {
2904 let entity_id = entity.entity_id();
2905 let window_handle = self.handle;
2906 cx.release_listeners.insert(
2907 entity_id,
2908 Box::new(move |entity, cx| {
2909 let entity = entity.downcast_mut().expect("invalid entity type");
2910 let _ = window_handle.update(cx, |_, window, cx| on_release(entity, window, cx));
2911 }),
2912 )
2913 }
2914
2915 pub fn to_async(&self, cx: &App) -> AsyncWindowContext {
2918 AsyncWindowContext::new_context(cx.to_async(), self.handle)
2919 }
2920
2921 pub fn on_next_frame(&self, callback: impl FnOnce(&mut Window, &mut App) + 'static) {
2923 RefCell::borrow_mut(&self.next_frame_callbacks).push(Box::new(callback));
2924 self.platform_window.schedule_frame();
2925 self.invalidator.wake_platform();
2928 }
2929
2930 pub fn request_animation_frame(&self) {
2943 let entity = self.current_view();
2944 self.on_next_frame(move |_, cx| cx.notify(entity));
2945 }
2946
2947 #[cfg(any(test, feature = "test-support"))]
2952 pub fn simulate_next_frame(&mut self, cx: &mut App) -> usize {
2953 let callbacks = self.next_frame_callbacks.take();
2954 let count = callbacks.len();
2955 for callback in callbacks {
2956 callback(self, cx);
2957 }
2958 count
2959 }
2960
2961 #[track_caller]
2965 pub fn spawn<AsyncFn, R>(&self, cx: &App, f: AsyncFn) -> Task<R>
2966 where
2967 R: 'static,
2968 AsyncFn: AsyncFnOnce(&mut AsyncWindowContext) -> R + 'static,
2969 {
2970 let handle = self.handle;
2971 cx.spawn(async move |app| {
2972 let mut async_window_cx = AsyncWindowContext::new_context(app.clone(), handle);
2973 f(&mut async_window_cx).await
2974 })
2975 }
2976
2977 #[track_caller]
2981 pub fn spawn_with_priority<AsyncFn, R>(
2982 &self,
2983 priority: Priority,
2984 cx: &App,
2985 f: AsyncFn,
2986 ) -> Task<R>
2987 where
2988 R: 'static,
2989 AsyncFn: AsyncFnOnce(&mut AsyncWindowContext) -> R + 'static,
2990 {
2991 let handle = self.handle;
2992 cx.spawn_with_priority(priority, async move |app| {
2993 let mut async_window_cx = AsyncWindowContext::new_context(app.clone(), handle);
2994 f(&mut async_window_cx).await
2995 })
2996 }
2997
2998 pub fn bounds_changed(&mut self, cx: &mut App) {
3004 self.scale_factor = self.platform_window.scale_factor();
3005 self.viewport_size = self.platform_window.content_size();
3006 self.mouse_position = self.platform_window.mouse_position();
3007
3008 self.refresh();
3009
3010 self.bounds_observers
3011 .clone()
3012 .retain(|callback| callback(self, cx));
3013 }
3014
3015 fn display_changed(handle: AnyWindowHandle, cx: &mut App) {
3016 let moved_to = handle
3017 .update(cx, |_, window, _| {
3018 let display_id = window.platform_window.display().map(|display| display.id());
3019 let moved = display_id != window.display_id;
3020 window.display_id = display_id;
3021 moved.then_some(display_id)
3022 })
3023 .with_context(|| {
3024 format!(
3025 "handling display change for window {:?}",
3026 handle.window_id()
3027 )
3028 })
3029 .log_err()
3030 .flatten();
3031 let Some(display_id) = moved_to else {
3032 return;
3033 };
3034 if let Some(display_id) = display_id
3038 && !cx.knows_display(display_id)
3039 {
3040 cx.displays_changed();
3041 }
3042 handle
3043 .update(cx, |_, window, cx| window.notify_display_observers(cx))
3044 .with_context(|| {
3045 format!(
3046 "notifying display observers for window {:?}",
3047 handle.window_id()
3048 )
3049 })
3050 .log_err();
3051 }
3052
3053 pub(crate) fn notify_display_observers(&mut self, cx: &mut App) {
3054 self.display_observers
3055 .clone()
3056 .retain(|callback| callback(self, cx));
3057 }
3058
3059 pub fn refresh_interval(&self, cx: &App) -> Option<Duration> {
3064 cx.display_refresh_interval(self.display_id?)
3065 }
3066
3067 pub fn observe_window_display(
3072 &self,
3073 mut callback: impl FnMut(&mut Window, &mut App) + 'static,
3074 ) -> Subscription {
3075 self.display_observers.insert(Box::new(move |window, cx| {
3076 callback(window, cx);
3077 true
3078 }))
3079 }
3080
3081 pub fn bounds(&self) -> Bounds<Pixels> {
3083 self.platform_window.bounds()
3084 }
3085
3086 pub fn insets(&self) -> WindowInsets {
3088 self.platform_window.insets()
3089 }
3090
3091 #[cfg(any(test, feature = "test-support"))]
3095 pub fn render_to_image(&self) -> anyhow::Result<image::RgbaImage> {
3096 self.platform_window
3097 .render_to_image(&self.rendered_frame.scene)
3098 }
3099
3100 #[cfg(feature = "bench-support")]
3103 pub fn rendered_scene(&self) -> &Scene {
3104 &self.rendered_frame.scene
3105 }
3106
3107 #[cfg(any(test, feature = "test-support"))]
3112 pub fn painted_quads(&self) -> Vec<Quad> {
3113 self.rendered_frame.scene.quads.clone()
3114 }
3115
3116 #[cfg(any(test, feature = "test-support"))]
3118 pub fn painted_underlines(&self) -> Vec<Underline> {
3119 self.rendered_frame.scene.underlines.clone()
3120 }
3121
3122 pub fn resize(&mut self, size: Size<Pixels>) {
3124 self.platform_window.resize(size);
3125 }
3126
3127 pub fn is_fullscreen(&self) -> bool {
3129 self.platform_window.is_fullscreen()
3130 }
3131
3132 pub fn is_simple_fullscreen(&self) -> bool {
3136 self.platform_window.is_simple_fullscreen()
3137 }
3138
3139 pub(crate) fn appearance_changed(&mut self, cx: &mut App) {
3140 self.appearance = self.platform_window.appearance();
3141
3142 self.appearance_observers
3143 .clone()
3144 .retain(|callback| callback(self, cx));
3145 }
3146
3147 pub(crate) fn button_layout_changed(&mut self, cx: &mut App) {
3148 self.button_layout_observers
3149 .clone()
3150 .retain(|callback| callback(self, cx));
3151 }
3152
3153 pub fn appearance(&self) -> WindowAppearance {
3155 self.appearance
3156 }
3157
3158 pub fn viewport_size(&self) -> Size<Pixels> {
3160 self.viewport_size
3161 }
3162
3163 pub fn visual_viewport_bounds(&self) -> Bounds<Pixels> {
3169 self.platform_window.visual_viewport_bounds()
3170 }
3171
3172 pub fn fully_visible_bounds(&self) -> Bounds<Pixels> {
3177 let insets = self.platform_window.insets().effective();
3178 let viewport = self.viewport_size();
3179 let left = insets.left.max(Pixels::ZERO).min(viewport.width);
3180 let top = insets.top.max(Pixels::ZERO).min(viewport.height);
3181 let right = (viewport.width - insets.right.max(Pixels::ZERO)).max(left);
3182 let bottom = (viewport.height - insets.bottom.max(Pixels::ZERO)).max(top);
3183 let safe_bounds = Bounds::from_corners(point(left, top), point(right, bottom));
3184 let mut visible = safe_bounds.intersect(&self.visual_viewport_bounds());
3185 visible.size.width = visible.size.width.max(Pixels::ZERO);
3186 visible.size.height = visible.size.height.max(Pixels::ZERO);
3187 visible
3188 }
3189
3190 pub fn request_virtual_keyboard(&self) {
3195 self.platform_window.show_soft_keyboard();
3196 }
3197
3198 pub fn dismiss_virtual_keyboard(&self) {
3200 self.platform_window.hide_soft_keyboard();
3201 }
3202
3203 pub fn is_window_active(&self) -> bool {
3205 self.active.get()
3206 }
3207
3208 pub fn is_window_hovered(&self) -> bool {
3212 if cfg!(any(
3213 target_os = "windows",
3214 target_os = "linux",
3215 target_os = "freebsd"
3216 )) {
3217 self.hovered.get()
3218 } else {
3219 self.is_window_active()
3220 }
3221 }
3222
3223 pub fn zoom_window(&self) {
3225 self.platform_window.zoom();
3226 }
3227
3228 pub fn show_window_menu(&self, position: Point<Pixels>) {
3230 self.platform_window.show_window_menu(position)
3231 }
3232
3233 pub fn start_window_move(&self) {
3238 self.platform_window.start_window_move()
3239 }
3240
3241 pub fn set_client_inset(&mut self, inset: Pixels) {
3243 self.client_inset = Some(inset);
3244 self.platform_window.set_client_inset(inset);
3245 }
3246
3247 pub fn set_client_corner_radii(&mut self, radii: Corners<Pixels>) {
3249 self.platform_window.set_client_corner_radii(radii);
3250 }
3251
3252 pub fn client_inset(&self) -> Option<Pixels> {
3254 self.client_inset
3255 }
3256
3257 pub fn window_decorations(&self) -> Decorations {
3259 self.platform_window.window_decorations()
3260 }
3261
3262 pub fn is_resizable(&self) -> bool {
3264 self.is_resizable
3265 }
3266
3267 pub fn is_minimizable(&self) -> bool {
3269 self.is_minimizable
3270 }
3271
3272 pub fn window_controls(&self) -> WindowControls {
3274 self.platform_window.window_controls()
3275 }
3276
3277 pub fn set_window_title(&mut self, title: &str) {
3279 self.platform_window.set_title(title);
3280 self.a11y.set_window_title(title.to_string());
3281 }
3282
3283 #[cfg(target_os = "macos")]
3285 pub fn set_traffic_light_position(&self, position: Point<Pixels>) {
3286 self.platform_window.set_traffic_light_position(position);
3287 }
3288
3289 pub fn set_app_id(&mut self, app_id: &str) {
3291 self.platform_window.set_app_id(app_id);
3292 }
3293
3294 pub fn set_background_appearance(&self, background_appearance: WindowBackgroundAppearance) {
3296 self.platform_window
3297 .set_background_appearance(background_appearance);
3298 }
3299
3300 pub fn set_window_edited(&mut self, edited: bool) {
3302 self.platform_window.set_edited(edited);
3303 }
3304
3305 pub fn set_document_path(&self, path: Option<&std::path::Path>) {
3308 self.platform_window.set_document_path(path);
3309 }
3310
3311 pub fn display(&self, cx: &App) -> Option<Rc<dyn PlatformDisplay>> {
3313 cx.platform
3314 .displays()
3315 .into_iter()
3316 .find(|display| Some(display.id()) == self.display_id)
3317 }
3318
3319 pub fn show_character_palette(&self) {
3321 self.platform_window.show_character_palette();
3322 }
3323
3324 pub fn scale_factor(&self) -> f32 {
3328 self.scale_factor
3329 }
3330
3331 #[cfg(any(test, feature = "test-support"))]
3333 pub fn set_scale_factor(&mut self, scale_factor: f32) {
3334 self.scale_factor = scale_factor;
3335 self.refresh();
3336 }
3337
3338 pub fn rem_size(&self) -> Pixels {
3341 self.rem_size_override_stack
3342 .last()
3343 .copied()
3344 .unwrap_or(self.rem_size)
3345 }
3346
3347 pub fn set_rem_size(&mut self, rem_size: impl Into<Pixels>) {
3350 self.rem_size = rem_size.into();
3351 }
3352
3353 pub fn with_global_id<R>(
3356 &mut self,
3357 element_id: ElementId,
3358 f: impl FnOnce(&GlobalElementId, &mut Self) -> R,
3359 ) -> R {
3360 self.with_id(element_id, |this| {
3361 let global_id = this.element_id_stack.global_id();
3362
3363 f(&global_id, this)
3364 })
3365 }
3366
3367 #[inline]
3369 pub fn with_id<R>(
3370 &mut self,
3371 element_id: impl Into<ElementId>,
3372 f: impl FnOnce(&mut Self) -> R,
3373 ) -> R {
3374 self.element_id_stack.push(element_id.into());
3375 let result = f(self);
3376 self.element_id_stack.pop();
3377 result
3378 }
3379
3380 #[inline]
3386 pub fn with_rem_size<F, R>(&mut self, rem_size: Option<impl Into<Pixels>>, f: F) -> R
3387 where
3388 F: FnOnce(&mut Self) -> R,
3389 {
3390 self.invalidator.debug_assert_paint_or_prepaint();
3391
3392 if let Some(rem_size) = rem_size {
3393 self.rem_size_override_stack.push(rem_size.into());
3394 let result = f(self);
3395 self.rem_size_override_stack.pop();
3396 result
3397 } else {
3398 f(self)
3399 }
3400 }
3401
3402 pub fn line_height(&self) -> Pixels {
3404 self.text_style().line_height_in_pixels(self.rem_size())
3405 }
3406
3407 #[inline]
3409 pub fn pixel_snap(&self, value: Pixels) -> Pixels {
3410 px(round_to_device_pixel(value.0, self.scale_factor()) / self.scale_factor())
3411 }
3412
3413 #[inline]
3415 pub fn pixel_snap_f64(&self, value: f64) -> f64 {
3416 let scale_factor = f64::from(self.scale_factor());
3417 round_half_toward_zero_f64(value * scale_factor) / scale_factor
3418 }
3419
3420 #[inline]
3422 pub fn pixel_snap_bounds(&self, bounds: Bounds<Pixels>) -> Bounds<Pixels> {
3423 bounds.map(|c| self.pixel_snap(c))
3424 }
3425
3426 #[inline]
3428 pub fn pixel_snap_point(&self, position: Point<Pixels>) -> Point<Pixels> {
3429 position.map(|c| self.pixel_snap(c))
3430 }
3431
3432 pub fn underline_bounds(
3434 &self,
3435 origin: Point<Pixels>,
3436 width: Pixels,
3437 style: &UnderlineStyle,
3438 ) -> Bounds<ScaledPixels> {
3439 let scale_factor = self.scale_factor();
3440 let thickness = self.snap_stroke(style.thickness);
3441 let height = if style.wavy {
3442 ScaledPixels(thickness.0 * 3.)
3443 } else {
3444 thickness
3445 };
3446 Bounds {
3447 origin: origin.map(|c| ScaledPixels(round_to_device_pixel(c.0, scale_factor))),
3448 size: size(self.snap_stroke(width), height),
3449 }
3450 }
3451
3452 pub fn paint_underline_with_exclusions(
3454 &mut self,
3455 origin: Point<Pixels>,
3456 width: Pixels,
3457 style: &UnderlineStyle,
3458 exclusions: &[Range<ScaledPixels>],
3459 ) {
3460 self.invalidator.debug_assert_paint();
3461 let underline = self.underline(origin, width, style);
3462 if exclusions.is_empty() {
3463 self.next_frame.scene.insert_primitive(underline);
3464 return;
3465 }
3466
3467 let bounds = underline.bounds.intersect(&underline.content_mask.bounds);
3468 if bounds.is_empty() {
3469 return;
3470 }
3471
3472 let mut exclusions = exclusions
3473 .iter()
3474 .filter_map(|span| {
3475 let start = span.start.max(bounds.left());
3476 let end = span.end.min(bounds.right());
3477 (start < end).then_some(start..end)
3478 })
3479 .collect::<SmallVec<[_; 4]>>();
3480 if exclusions.is_empty() {
3481 self.next_frame.scene.insert_primitive(underline);
3482 return;
3483 }
3484 exclusions.sort_unstable_by_key(|span| span.start);
3485
3486 let mut paint_span = |start, end| {
3487 self.next_frame.scene.insert_primitive(Underline {
3488 content_mask: ContentMask {
3489 bounds: Bounds::from_corners(
3490 point(start, bounds.top()),
3491 point(end, bounds.bottom()),
3492 ),
3493 },
3494 ..underline
3495 });
3496 };
3497 let mut start = bounds.left();
3498 for exclusion in exclusions {
3499 if start < exclusion.start {
3500 paint_span(start, exclusion.start);
3501 }
3502 start = start.max(exclusion.end);
3503 }
3504 if start < bounds.right() {
3505 paint_span(start, bounds.right());
3506 }
3507 }
3508
3509 fn underline(&self, origin: Point<Pixels>, width: Pixels, style: &UnderlineStyle) -> Underline {
3510 Underline {
3511 order: 0,
3512 pad: 0,
3513 bounds: self.underline_bounds(origin, width, style),
3514 content_mask: self.snapped_content_mask(),
3515 color: style
3516 .color
3517 .unwrap_or_default()
3518 .opacity(self.element_opacity()),
3519 thickness: self.snap_stroke(style.thickness),
3520 wavy: style.wavy.into(),
3521 }
3522 }
3523
3524 #[inline]
3525 fn snap_bounds(&self, bounds: Bounds<Pixels>) -> Bounds<ScaledPixels> {
3526 let scale_factor = self.scale_factor();
3527 let left = round_to_device_pixel(bounds.left().0, scale_factor);
3528 let top = round_to_device_pixel(bounds.top().0, scale_factor);
3529 let right = round_to_device_pixel(bounds.right().0, scale_factor).max(left);
3530 let bottom = round_to_device_pixel(bounds.bottom().0, scale_factor).max(top);
3531 Bounds::from_corners(
3532 point(ScaledPixels(left), ScaledPixels(top)),
3533 point(ScaledPixels(right), ScaledPixels(bottom)),
3534 )
3535 }
3536
3537 #[inline]
3539 fn snap_stroke(&self, value: Pixels) -> ScaledPixels {
3540 ScaledPixels(round_stroke_to_device_pixel(value.0, self.scale_factor()))
3541 }
3542
3543 #[inline]
3544 fn snap_border_widths(&self, edges: Edges<Pixels>) -> Edges<ScaledPixels> {
3545 edges.map(|e| self.snap_stroke(*e))
3546 }
3547
3548 #[inline]
3550 fn cover_bounds(&self, bounds: Bounds<Pixels>) -> Bounds<ScaledPixels> {
3551 let scale_factor = self.scale_factor();
3552 let left = floor_to_device_pixel(bounds.left().0, scale_factor);
3553 let top = floor_to_device_pixel(bounds.top().0, scale_factor);
3554 let right = ceil_to_device_pixel(bounds.right().0, scale_factor).max(left);
3555 let bottom = ceil_to_device_pixel(bounds.bottom().0, scale_factor).max(top);
3556 Bounds::from_corners(
3557 point(ScaledPixels(left), ScaledPixels(top)),
3558 point(ScaledPixels(right), ScaledPixels(bottom)),
3559 )
3560 }
3561
3562 #[inline]
3563 fn snapped_content_mask(&self) -> ContentMask<ScaledPixels> {
3564 ContentMask {
3565 bounds: self.cover_bounds(self.content_mask().bounds),
3566 }
3567 }
3568
3569 pub fn prevent_default(&mut self) {
3572 self.default_prevented = true;
3573 }
3574
3575 pub fn default_prevented(&self) -> bool {
3577 self.default_prevented
3578 }
3579
3580 pub fn is_action_available(&self, action: &dyn Action, cx: &App) -> bool {
3582 let node_id =
3583 self.focus_node_id_in_rendered_frame(self.focused(cx).map(|handle| handle.id));
3584 self.rendered_frame
3585 .dispatch_tree
3586 .is_action_available(action, node_id)
3587 }
3588
3589 pub fn is_action_available_in(&self, action: &dyn Action, focus_handle: &FocusHandle) -> bool {
3591 let node_id = self.focus_node_id_in_rendered_frame(Some(focus_handle.id));
3592 self.rendered_frame
3593 .dispatch_tree
3594 .is_action_available(action, node_id)
3595 }
3596
3597 pub fn mouse_position(&self) -> Point<Pixels> {
3599 self.mouse_position
3600 }
3601
3602 pub fn capture_pointer(&mut self, hitbox_id: HitboxId) {
3609 self.captured_hitbox = Some(hitbox_id);
3610 }
3611
3612 pub fn release_pointer(&mut self) {
3614 self.captured_hitbox = None;
3615 }
3616
3617 pub fn captured_hitbox(&self) -> Option<HitboxId> {
3619 self.captured_hitbox
3620 }
3621
3622 pub fn capture_long_press<T: 'static>(&mut self, entity: &Entity<T>) {
3628 self.long_press_capture = Some(entity.entity_id());
3629 }
3630
3631 pub fn has_long_press_capture<T: 'static>(&self, entity: &Entity<T>) -> bool {
3633 self.long_press_capture == Some(entity.entity_id())
3634 }
3635
3636 pub fn modifiers(&self) -> Modifiers {
3638 self.modifiers
3639 }
3640
3641 pub fn last_input_was_keyboard(&self) -> bool {
3644 self.last_input_modality == InputModality::Keyboard
3645 }
3646
3647 pub fn last_input_was_touch(&self) -> bool {
3649 self.last_input_modality == InputModality::Touch
3650 }
3651
3652 pub fn capslock(&self) -> Capslock {
3654 self.capslock
3655 }
3656
3657 #[profiling::function]
3660 pub fn draw(&mut self, cx: &mut App) -> ArenaClearNeeded {
3661 #[cfg(feature = "profiler")]
3664 let frame_dirty = self.invalidator.take_frame_dirty();
3665 #[cfg(feature = "profiler")]
3666 self.window_profiler.begin_draw();
3667
3668 let arena_scope = ElementArenaScope::enter(&cx.element_arena);
3671
3672 if self.platform_window.prepare_frame() {
3673 self.refresh();
3674 }
3675 self.invalidate_entities();
3676 cx.entities.clear_accessed();
3677 debug_assert!(self.rendered_entity_stack.is_empty());
3678 self.invalidator.set_dirty(false);
3679 self.requested_autoscroll = None;
3680
3681 if let Some(input_handler) = self.platform_window.take_input_handler() {
3686 if let Some(slot) = self
3687 .rendered_frame
3688 .input_handlers
3689 .iter_mut()
3690 .rev()
3691 .find(|h| h.is_none())
3692 {
3693 *slot = Some(input_handler);
3694 } else {
3695 self.rendered_frame.input_handlers.push(Some(input_handler));
3696 }
3697 }
3698 if !cx.mode.skip_drawing() {
3699 self.draw_roots(cx);
3700 #[cfg(feature = "profiler")]
3701 {
3702 let viewport_size = self.viewport_size;
3703 let scale_factor = self.scale_factor();
3704 self.debug_frame_overlay.paint(
3705 &mut self.next_frame.scene,
3706 viewport_size,
3707 scale_factor,
3708 );
3709 }
3710 }
3711 self.dirty_views.clear();
3712 self.next_frame.window_active = self.active.get();
3713
3714 let focused_text_input = if let Some(mut input_handler) = self
3720 .next_frame
3721 .input_handlers
3722 .iter_mut()
3723 .rev()
3724 .find_map(|h| h.take())
3725 {
3726 let accepts_text_input = input_handler.accepts_text_input(self, cx);
3727 self.platform_window.set_input_handler(input_handler);
3728 self.focus.filter(|_| accepts_text_input)
3729 } else {
3730 None
3731 };
3732 self.apply_text_input_configuration(cx);
3733 let previous = mem::replace(&mut self.focused_text_input, focused_text_input);
3734 if previous.is_some() != focused_text_input.is_some() {
3735 self.platform_window
3736 .text_input_state_changed(if focused_text_input.is_some() {
3737 TextInputStateChange::FocusGained
3738 } else {
3739 TextInputStateChange::FocusLost
3740 });
3741 }
3742
3743 self.layout_engine.as_mut().unwrap().clear();
3744 self.text_system().finish_frame();
3745 self.next_frame.finish(&mut self.rendered_frame);
3746
3747 self.invalidator.set_phase(DrawPhase::Focus);
3748 let previous_focus_path = self.rendered_frame.focus_path();
3749 let previous_window_active = self.rendered_frame.window_active;
3750 mem::swap(&mut self.rendered_frame, &mut self.next_frame);
3751 self.next_frame.clear();
3752 let current_focus_path = self.rendered_frame.focus_path();
3753 let current_window_active = self.rendered_frame.window_active;
3754 let mut focus_before_listeners = self.focus;
3755
3756 if previous_focus_path != current_focus_path
3757 || previous_window_active != current_window_active
3758 {
3759 if !previous_focus_path.is_empty() && current_focus_path.is_empty() {
3760 self.focus_lost_path = previous_focus_path.clone();
3761 self.focus_lost_listeners
3762 .clone()
3763 .retain(|listener| listener(self, cx));
3764 self.focus_lost_path = SmallVec::new();
3765 focus_before_listeners = self.focus;
3770 }
3771
3772 let event = WindowFocusEvent {
3773 previous_focus_path: if previous_window_active {
3774 previous_focus_path
3775 } else {
3776 Default::default()
3777 },
3778 current_focus_path: if current_window_active {
3779 current_focus_path
3780 } else {
3781 Default::default()
3782 },
3783 };
3784 self.focus_listeners
3785 .clone()
3786 .retain(|listener| listener(&event, self, cx));
3787 }
3788
3789 debug_assert!(self.rendered_entity_stack.is_empty());
3790 self.record_entities_accessed(cx);
3791 self.reset_cursor_style(cx);
3792 self.refreshing = false;
3793 self.invalidator.set_phase(DrawPhase::None);
3794 if self.focus != focus_before_listeners {
3799 self.refresh();
3800 }
3801 self.needs_present.set(true);
3802
3803 #[cfg(feature = "profiler")]
3804 {
3805 self.window_profiler
3806 .set_refresh_interval(self.refresh_interval(cx));
3807 let draw_duration = self
3808 .window_profiler
3809 .end_draw(frame_dirty.dirty_at, frame_dirty.invalidations);
3810 self.debug_frame_overlay.record_frame(draw_duration);
3811 }
3812
3813 arena_scope.exit(&cx.element_arena)
3816 }
3817
3818 fn record_entities_accessed(&mut self, cx: &mut App) {
3819 let entities = cx.entities.take_accessed();
3820 let handle = self.handle;
3821 cx.record_entities_accessed(
3822 handle,
3823 self.invalidator.clone(),
3825 &entities,
3826 );
3827 cx.entities.restore_accessed(entities);
3828 }
3829
3830 fn invalidate_entities(&mut self) {
3831 let mut views = self.invalidator.take_views();
3832 for entity in views.drain() {
3833 self.mark_view_dirty(entity);
3834 }
3835 self.invalidator.replace_views(views);
3836 }
3837
3838 #[profiling::function]
3839 fn present(&mut self) {
3840 #[cfg(feature = "profiler")]
3841 let _foreground_turn = profiler::journal::foreground_turn();
3842 #[cfg(feature = "profiler")]
3843 let present_start = Instant::now();
3844 self.platform_window.draw(&self.rendered_frame.scene);
3845 #[cfg(feature = "profiler")]
3846 self.window_profiler.record_present(
3847 present_start,
3848 Instant::now(),
3849 self.active.get(),
3850 !self.next_frame_callbacks.borrow().is_empty(),
3851 );
3852 self.needs_present.set(false);
3853 profiling::finish_frame!();
3854 }
3855
3856 #[cfg(all(test, feature = "profiler"))]
3858 pub fn present_if_needed(&mut self) {
3859 if self.needs_present.get() {
3860 self.present();
3861 }
3862 }
3863
3864 #[cfg(feature = "profiler")]
3866 pub fn input_latency_snapshot(&self) -> profiler::InputLatencySnapshot {
3867 self.window_profiler.input_latency_snapshot()
3868 }
3869
3870 #[cfg(feature = "profiler")]
3872 pub fn frame_duration_snapshot(&self) -> profiler::FrameDurationSnapshot {
3873 self.window_profiler.frame_duration_snapshot()
3874 }
3875
3876 #[cfg(feature = "profiler")]
3878 pub fn debug_frame_overlay_mode(&self) -> DebugFrameOverlayMode {
3879 self.debug_frame_overlay.mode()
3880 }
3881
3882 #[cfg(feature = "profiler")]
3884 pub fn set_debug_frame_overlay_mode(&mut self, mode: DebugFrameOverlayMode) {
3885 self.debug_frame_overlay.set_mode(mode);
3886 self.refresh();
3887 }
3888
3889 #[cfg(feature = "profiler")]
3892 pub fn cycle_debug_frame_overlay_mode(&mut self) {
3893 self.set_debug_frame_overlay_mode(self.debug_frame_overlay.mode().next());
3894 }
3895
3896 #[cfg(feature = "profiler")]
3899 pub fn reset_debug_frame_overlay_stats(&mut self) {
3900 self.debug_frame_overlay.reset_stats();
3901 self.refresh();
3902 }
3903
3904 fn draw_roots(&mut self, cx: &mut App) {
3905 self.invalidator.set_phase(DrawPhase::Prepaint);
3906 self.tooltip_bounds.take();
3907
3908 self.a11y.sync_active_flag();
3909 if self.a11y.is_active() {
3910 self.a11y.begin_frame();
3911 }
3912
3913 let _inspector_width: Pixels = rems(30.0).to_pixels(self.rem_size());
3914 let root_size = {
3915 #[cfg(any(feature = "inspector", debug_assertions))]
3916 {
3917 if self.inspector.is_some() {
3918 let mut size = self.viewport_size;
3919 size.width = (size.width - _inspector_width).max(px(0.0));
3920 size
3921 } else {
3922 self.viewport_size
3923 }
3924 }
3925 #[cfg(not(any(feature = "inspector", debug_assertions)))]
3926 {
3927 self.viewport_size
3928 }
3929 };
3930
3931 let scale_factor = self.scale_factor();
3935 #[cfg(feature = "profiler")]
3936 let request_layout_start = Instant::now();
3937 let mut root_element = self.root.as_ref().unwrap().clone().into_any_element();
3938 let root_layout_id = root_element.request_layout(self, cx);
3939 #[cfg(feature = "profiler")]
3940 let prepaint_start = Instant::now();
3941 self.layout_engine
3942 .as_mut()
3943 .unwrap()
3944 .stretch_auto_size_to_fill(root_layout_id, root_size, scale_factor);
3945 root_element.prepaint_as_root(Point::default(), root_size.into(), self, cx);
3946
3947 #[cfg(any(feature = "inspector", debug_assertions))]
3948 let inspector_element = self.prepaint_inspector(_inspector_width, cx);
3949
3950 self.prepaint_deferred_draws(cx);
3951
3952 let mut prompt_element = None;
3953 let mut active_drag_element = None;
3954 let mut tooltip_element = None;
3955 if let Some(prompt) = self.prompt.take() {
3956 let mut element = prompt.view.any_view().into_any_element();
3957 let prompt_layout_id = element.request_layout(self, cx);
3958 self.layout_engine
3959 .as_mut()
3960 .unwrap()
3961 .stretch_auto_size_to_fill(prompt_layout_id, root_size, scale_factor);
3962 element.prepaint_as_root(Point::default(), root_size.into(), self, cx);
3963 prompt_element = Some(element);
3964 self.prompt = Some(prompt);
3965 } else if let Some(active_drag) = cx.active_drag.take() {
3966 let mut element = active_drag.view.clone().into_any_element();
3967 let offset = self.mouse_position() - active_drag.cursor_offset;
3968 element.prepaint_as_root(offset, AvailableSpace::min_size(), self, cx);
3969 active_drag_element = Some(element);
3970 cx.active_drag = Some(active_drag);
3971 } else {
3972 tooltip_element = self.prepaint_tooltip(cx);
3973 }
3974
3975 self.mouse_hit_test = self.next_frame.hit_test(self.mouse_position);
3976
3977 #[cfg(feature = "profiler")]
3979 let paint_start = Instant::now();
3980 self.invalidator.set_phase(DrawPhase::Paint);
3981 root_element.paint(self, cx);
3982
3983 #[cfg(any(feature = "inspector", debug_assertions))]
3984 self.paint_inspector(inspector_element, cx);
3985
3986 self.paint_deferred_draws(cx);
3987
3988 if let Some(mut prompt_element) = prompt_element {
3989 prompt_element.paint(self, cx);
3990 } else if let Some(mut drag_element) = active_drag_element {
3991 drag_element.paint(self, cx);
3992 } else if let Some(mut tooltip_element) = tooltip_element {
3993 tooltip_element.paint(self, cx);
3994 }
3995
3996 #[cfg(any(feature = "inspector", debug_assertions))]
3997 self.paint_inspector_hitbox(cx);
3998
3999 #[cfg(feature = "profiler")]
4000 self.window_profiler
4001 .record_draw_phases(profiler::DrawPhases {
4002 request_layout: prepaint_start.duration_since(request_layout_start),
4003 prepaint: paint_start.duration_since(prepaint_start),
4004 paint: Instant::now().duration_since(paint_start),
4005 });
4006
4007 let a11y_active_start_of_frame = self.a11y.is_active();
4009 self.a11y.sync_active_flag();
4010 let a11y_active_end_of_frame = self.a11y.is_active();
4011
4012 let should_send_a11y_update = a11y_active_start_of_frame && a11y_active_end_of_frame;
4013
4014 if a11y_active_start_of_frame {
4015 let frame_info = crate::window::a11y::debug::FrameDebugInfo {
4018 viewport_size: self.viewport_size,
4019 scale_factor: self.scale_factor,
4020 tab_stop_count: self.next_frame.tab_stops.tab_stop_count(),
4021 };
4022 let tree_update = self.a11y.end_frame(frame_info);
4024
4025 if should_send_a11y_update {
4026 log::debug!(
4027 "Sending a11y tree update: {} nodes",
4028 tree_update.nodes.len()
4029 );
4030 self.platform_window.a11y_tree_update(tree_update);
4031 }
4032 }
4033 }
4034
4035 fn prepaint_tooltip(&mut self, cx: &mut App) -> Option<AnyElement> {
4036 for tooltip_request_index in (0..self.next_frame.tooltip_requests.len()).rev() {
4038 let Some(Some(tooltip_request)) = self
4039 .next_frame
4040 .tooltip_requests
4041 .get(tooltip_request_index)
4042 .cloned()
4043 else {
4044 log::error!("Unexpectedly absent TooltipRequest");
4045 continue;
4046 };
4047 let mut element = tooltip_request.tooltip.view.clone().into_any_element();
4048 let mouse_position = tooltip_request.tooltip.mouse_position;
4049 let tooltip_size = element.layout_as_root(AvailableSpace::min_size(), self, cx);
4050
4051 let mut tooltip_bounds =
4052 Bounds::new(mouse_position + point(px(1.), px(1.)), tooltip_size);
4053 let window_bounds = Bounds {
4054 origin: Point::default(),
4055 size: self.viewport_size(),
4056 };
4057
4058 if tooltip_bounds.right() > window_bounds.right() {
4059 let new_x = mouse_position.x - tooltip_bounds.size.width - px(1.);
4060 if new_x >= Pixels::ZERO {
4061 tooltip_bounds.origin.x = new_x;
4062 } else {
4063 tooltip_bounds.origin.x = cmp::max(
4064 Pixels::ZERO,
4065 tooltip_bounds.origin.x - tooltip_bounds.right() - window_bounds.right(),
4066 );
4067 }
4068 }
4069
4070 if tooltip_bounds.bottom() > window_bounds.bottom() {
4071 let new_y = mouse_position.y - tooltip_bounds.size.height - px(1.);
4072 if new_y >= Pixels::ZERO {
4073 tooltip_bounds.origin.y = new_y;
4074 } else {
4075 tooltip_bounds.origin.y = cmp::max(
4076 Pixels::ZERO,
4077 tooltip_bounds.origin.y - tooltip_bounds.bottom() - window_bounds.bottom(),
4078 );
4079 }
4080 }
4081
4082 let is_visible =
4086 (tooltip_request.tooltip.check_visible_and_update)(tooltip_bounds, self, cx);
4087 if !is_visible {
4088 continue;
4089 }
4090
4091 self.with_absolute_element_offset(tooltip_bounds.origin, |window| {
4092 element.prepaint(window, cx)
4093 });
4094
4095 self.tooltip_bounds = Some(TooltipBounds {
4096 id: tooltip_request.id,
4097 bounds: tooltip_bounds,
4098 });
4099 return Some(element);
4100 }
4101 None
4102 }
4103
4104 fn prepaint_deferred_draws(&mut self, cx: &mut App) {
4105 assert_eq!(self.element_id_stack.len(), 0);
4106
4107 let mut round_start = 0;
4118 let mut depth = 0;
4119 loop {
4120 let round_end = self.next_frame.deferred_draws.len();
4121 if round_start == round_end {
4122 break;
4123 }
4124 assert!(depth < 10, "Exceeded maximum (10) deferred depth");
4126 depth += 1;
4127
4128 let mut traversal_order = (round_start..round_end).collect::<SmallVec<[usize; 8]>>();
4130 traversal_order.sort_by_key(|ix| self.next_frame.deferred_draws[*ix].priority);
4131
4132 for deferred_draw_ix in traversal_order {
4133 let (element, parent_node, current_view, rem_size, absolute_offset, prepaint_range) = {
4134 let deferred_draw = &mut self.next_frame.deferred_draws[deferred_draw_ix];
4135 self.element_id_stack
4136 .clone_from(&deferred_draw.element_id_stack);
4137 self.text_style_stack
4138 .clone_from(&deferred_draw.text_style_stack);
4139 (
4140 deferred_draw.element.take(),
4141 deferred_draw.parent_node,
4142 deferred_draw.current_view,
4143 deferred_draw.rem_size,
4144 deferred_draw.absolute_offset,
4145 deferred_draw.prepaint_range.clone(),
4146 )
4147 };
4148 self.next_frame.dispatch_tree.set_active_node(parent_node);
4149
4150 let prepaint_start = self.prepaint_index();
4151 if let Some(mut element) = element {
4152 let ((), accessed_entities) = cx.detect_accessed_entities(|cx| {
4153 self.with_rendered_view(current_view, |window| {
4154 window.with_rem_size(Some(rem_size), |window| {
4155 window.with_absolute_element_offset(absolute_offset, |window| {
4156 element.prepaint(window, cx);
4157 });
4158 });
4159 });
4160 });
4161 self.next_frame.deferred_draws[deferred_draw_ix].accessed_entities =
4162 accessed_entities;
4163 self.next_frame.deferred_draws[deferred_draw_ix].element = Some(element);
4164 } else {
4165 cx.entities.extend_accessed(
4166 self.next_frame.deferred_draws[deferred_draw_ix]
4167 .accessed_entities
4168 .iter()
4169 .copied(),
4170 );
4171 self.reuse_prepaint(prepaint_range);
4172 }
4173 let prepaint_end = self.prepaint_index();
4174 self.next_frame.deferred_draws[deferred_draw_ix].prepaint_range =
4175 prepaint_start..prepaint_end;
4176 }
4177
4178 self.element_id_stack.clear();
4179 self.text_style_stack.clear();
4180 round_start = round_end;
4181 }
4182 }
4183
4184 fn paint_deferred_draws(&mut self, cx: &mut App) {
4185 assert_eq!(self.element_id_stack.len(), 0);
4186
4187 if self.next_frame.deferred_draws.len() == 0 {
4190 return;
4191 }
4192
4193 let traversal_order = self.deferred_draw_traversal_order();
4194 let mut deferred_draws = mem::take(&mut self.next_frame.deferred_draws);
4195 for deferred_draw_ix in traversal_order {
4196 let deferred_draw = &mut deferred_draws[deferred_draw_ix];
4197 self.element_id_stack
4198 .clone_from(&deferred_draw.element_id_stack);
4199 self.next_frame
4200 .dispatch_tree
4201 .set_active_node(deferred_draw.parent_node);
4202
4203 let paint_start = self.paint_index();
4204 let content_mask = deferred_draw.content_mask;
4205 if let Some(element) = deferred_draw.element.as_mut() {
4206 let ((), accessed_entities) = cx.detect_accessed_entities(|cx| {
4207 self.with_rendered_view(deferred_draw.current_view, |window| {
4208 window.with_content_mask(content_mask, |window| {
4209 window.with_rem_size(Some(deferred_draw.rem_size), |window| {
4210 element.paint(window, cx);
4211 });
4212 })
4213 })
4214 });
4215 deferred_draw.accessed_entities.extend(accessed_entities);
4216 } else {
4217 self.reuse_paint(deferred_draw.paint_range.clone());
4218 }
4219 let paint_end = self.paint_index();
4220 deferred_draw.paint_range = paint_start..paint_end;
4221 }
4222 self.next_frame.deferred_draws = deferred_draws;
4223 self.element_id_stack.clear();
4224 }
4225
4226 fn deferred_draw_traversal_order(&mut self) -> SmallVec<[usize; 8]> {
4227 let deferred_count = self.next_frame.deferred_draws.len();
4228 let mut sorted_indices = (0..deferred_count).collect::<SmallVec<[_; 8]>>();
4229 sorted_indices.sort_by_key(|ix| self.next_frame.deferred_draws[*ix].priority);
4230 sorted_indices
4231 }
4232
4233 pub(crate) fn prepaint_index(&self) -> PrepaintStateIndex {
4234 PrepaintStateIndex {
4235 hitboxes_index: self.next_frame.hitboxes.len(),
4236 tooltips_index: self.next_frame.tooltip_requests.len(),
4237 deferred_draws_index: self.next_frame.deferred_draws.len(),
4238 dispatch_tree_index: self.next_frame.dispatch_tree.len(),
4239 accessed_element_states_index: self.next_frame.accessed_element_states.len(),
4240 line_layout_index: self.text_system.layout_index(),
4241 }
4242 }
4243
4244 pub(crate) fn reuse_prepaint(&mut self, range: Range<PrepaintStateIndex>) {
4245 self.next_frame.hitboxes.extend(
4246 self.rendered_frame.hitboxes[range.start.hitboxes_index..range.end.hitboxes_index]
4247 .iter()
4248 .cloned(),
4249 );
4250 self.next_frame.tooltip_requests.extend(
4251 self.rendered_frame.tooltip_requests
4252 [range.start.tooltips_index..range.end.tooltips_index]
4253 .iter_mut()
4254 .map(|request| request.take()),
4255 );
4256 self.next_frame.accessed_element_states.extend(
4257 self.rendered_frame.accessed_element_states[range.start.accessed_element_states_index
4258 ..range.end.accessed_element_states_index]
4259 .iter()
4260 .map(|(id, type_id)| (id.clone(), *type_id)),
4261 );
4262 self.text_system
4263 .reuse_layouts(range.start.line_layout_index..range.end.line_layout_index);
4264
4265 let reused_subtree = self.next_frame.dispatch_tree.reuse_subtree(
4266 range.start.dispatch_tree_index..range.end.dispatch_tree_index,
4267 &mut self.rendered_frame.dispatch_tree,
4268 self.focus,
4269 );
4270
4271 if reused_subtree.contains_focus() {
4272 self.next_frame.focus = self.focus;
4273 }
4274
4275 self.next_frame.deferred_draws.extend(
4276 self.rendered_frame.deferred_draws
4277 [range.start.deferred_draws_index..range.end.deferred_draws_index]
4278 .iter()
4279 .map(|deferred_draw| DeferredDraw {
4280 current_view: deferred_draw.current_view,
4281 parent_node: reused_subtree.refresh_node_id(deferred_draw.parent_node),
4282 element_id_stack: deferred_draw.element_id_stack.clone(),
4283 text_style_stack: deferred_draw.text_style_stack.clone(),
4284 content_mask: deferred_draw.content_mask,
4285 rem_size: deferred_draw.rem_size,
4286 priority: deferred_draw.priority,
4287 element: None,
4288 absolute_offset: deferred_draw.absolute_offset,
4289 prepaint_range: deferred_draw.prepaint_range.clone(),
4290 paint_range: deferred_draw.paint_range.clone(),
4291 accessed_entities: deferred_draw.accessed_entities.clone(),
4292 }),
4293 );
4294 }
4295
4296 pub(crate) fn paint_index(&self) -> PaintIndex {
4297 PaintIndex {
4298 scene_index: self.next_frame.scene.len(),
4299 #[cfg(any(test, feature = "test-support"))]
4300 debug_bounds_index: self.next_frame.debug_bounds_records.len(),
4301 mouse_listeners_index: self.next_frame.mouse_listeners.len(),
4302 input_handlers_index: self.next_frame.input_handlers.len(),
4303 cursor_styles_index: self.next_frame.cursor_styles.len(),
4304 accessed_element_states_index: self.next_frame.accessed_element_states.len(),
4305 tab_handle_index: self.next_frame.tab_stops.paint_index(),
4306 line_layout_index: self.text_system.layout_index(),
4307 }
4308 }
4309
4310 pub(crate) fn reuse_paint(&mut self, range: Range<PaintIndex>) {
4311 #[cfg(any(test, feature = "test-support"))]
4313 for (selector, bounds) in &self.rendered_frame.debug_bounds_records
4314 [range.start.debug_bounds_index..range.end.debug_bounds_index]
4315 {
4316 self.next_frame
4317 .record_debug_bounds(selector.clone(), *bounds);
4318 }
4319 self.next_frame.cursor_styles.extend(
4320 self.rendered_frame.cursor_styles
4321 [range.start.cursor_styles_index..range.end.cursor_styles_index]
4322 .iter()
4323 .cloned(),
4324 );
4325 self.next_frame.input_handlers.extend(
4326 self.rendered_frame.input_handlers
4327 [range.start.input_handlers_index..range.end.input_handlers_index]
4328 .iter_mut()
4329 .map(|handler| handler.take()),
4330 );
4331 self.next_frame.mouse_listeners.extend(
4332 self.rendered_frame.mouse_listeners
4333 [range.start.mouse_listeners_index..range.end.mouse_listeners_index]
4334 .iter_mut()
4335 .map(|listener| listener.take()),
4336 );
4337 self.next_frame.accessed_element_states.extend(
4338 self.rendered_frame.accessed_element_states[range.start.accessed_element_states_index
4339 ..range.end.accessed_element_states_index]
4340 .iter()
4341 .map(|(id, type_id)| (id.clone(), *type_id)),
4342 );
4343 self.next_frame.tab_stops.replay(
4344 &self.rendered_frame.tab_stops.insertion_history
4345 [range.start.tab_handle_index..range.end.tab_handle_index],
4346 );
4347
4348 self.text_system
4349 .reuse_layouts(range.start.line_layout_index..range.end.line_layout_index);
4350 self.next_frame.scene.replay(
4351 range.start.scene_index..range.end.scene_index,
4352 &self.rendered_frame.scene,
4353 );
4354 }
4355
4356 pub fn with_text_style<F, R>(&mut self, style: Option<TextStyleRefinement>, f: F) -> R
4360 where
4361 F: FnOnce(&mut Self) -> R,
4362 {
4363 self.invalidator.debug_assert_paint_or_prepaint();
4364 if let Some(style) = style {
4365 self.text_style_stack.push(style);
4366 let result = f(self);
4367 self.text_style_stack.pop();
4368 result
4369 } else {
4370 f(self)
4371 }
4372 }
4373
4374 pub fn set_cursor_style(&mut self, style: CursorStyle, hitbox: &Hitbox) {
4377 self.invalidator.debug_assert_paint();
4378 self.next_frame.cursor_styles.push(CursorStyleRequest {
4379 hitbox_id: Some(hitbox.id),
4380 style,
4381 });
4382 }
4383
4384 pub fn set_window_cursor_style(&mut self, style: CursorStyle) {
4389 self.invalidator.debug_assert_paint();
4390 self.next_frame.cursor_styles.push(CursorStyleRequest {
4391 hitbox_id: None,
4392 style,
4393 })
4394 }
4395
4396 pub fn set_tooltip(&mut self, tooltip: AnyTooltip) -> TooltipId {
4399 self.invalidator.debug_assert_prepaint();
4400 let id = TooltipId(post_inc(&mut self.next_tooltip_id.0));
4401 self.next_frame
4402 .tooltip_requests
4403 .push(Some(TooltipRequest { id, tooltip }));
4404 id
4405 }
4406
4407 #[inline]
4412 pub fn with_content_mask<R>(
4413 &mut self,
4414 mask: Option<ContentMask<Pixels>>,
4415 f: impl FnOnce(&mut Self) -> R,
4416 ) -> R {
4417 self.invalidator.debug_assert_paint_or_prepaint();
4418 if let Some(mask) = mask {
4419 let mask = mask.intersect(&self.content_mask());
4420 self.content_mask_stack.push(mask);
4421 let result = f(self);
4422 self.content_mask_stack.pop();
4423 result
4424 } else {
4425 f(self)
4426 }
4427 }
4428
4429 pub fn with_element_offset<R>(
4432 &mut self,
4433 offset: Point<Pixels>,
4434 f: impl FnOnce(&mut Self) -> R,
4435 ) -> R {
4436 self.invalidator.debug_assert_prepaint();
4437
4438 if offset.is_zero() {
4439 return f(self);
4440 };
4441
4442 let abs_offset = self.element_offset() + offset;
4443 self.with_absolute_element_offset(abs_offset, f)
4444 }
4445
4446 pub fn with_absolute_element_offset<R>(
4450 &mut self,
4451 offset: Point<Pixels>,
4452 f: impl FnOnce(&mut Self) -> R,
4453 ) -> R {
4454 self.invalidator.debug_assert_prepaint();
4455 self.element_offset_stack.push(offset);
4456 let result = f(self);
4457 self.element_offset_stack.pop();
4458 result
4459 }
4460
4461 pub(crate) fn with_element_opacity<R>(
4462 &mut self,
4463 opacity: Option<f32>,
4464 f: impl FnOnce(&mut Self) -> R,
4465 ) -> R {
4466 self.invalidator.debug_assert_paint_or_prepaint();
4467
4468 let Some(opacity) = opacity else {
4469 return f(self);
4470 };
4471
4472 let previous_opacity = self.element_opacity;
4473 self.element_opacity = previous_opacity * opacity;
4474 let result = f(self);
4475 self.element_opacity = previous_opacity;
4476 result
4477 }
4478
4479 pub fn transact<T, U>(&mut self, f: impl FnOnce(&mut Self) -> Result<T, U>) -> Result<T, U> {
4485 self.invalidator.debug_assert_prepaint();
4486 let index = self.prepaint_index();
4487 let result = f(self);
4488 if result.is_err() {
4489 self.next_frame.hitboxes.truncate(index.hitboxes_index);
4490 self.next_frame
4491 .tooltip_requests
4492 .truncate(index.tooltips_index);
4493 self.next_frame
4494 .deferred_draws
4495 .truncate(index.deferred_draws_index);
4496 self.next_frame
4497 .dispatch_tree
4498 .truncate(index.dispatch_tree_index);
4499 self.next_frame
4500 .accessed_element_states
4501 .truncate(index.accessed_element_states_index);
4502 self.text_system.truncate_layouts(index.line_layout_index);
4503 }
4504 result
4505 }
4506
4507 pub fn request_autoscroll(&mut self, bounds: Bounds<Pixels>) {
4513 self.invalidator.debug_assert_prepaint();
4514 self.requested_autoscroll = Some(bounds);
4515 }
4516
4517 pub fn take_autoscroll(&mut self) -> Option<Bounds<Pixels>> {
4520 self.invalidator.debug_assert_prepaint();
4521 self.requested_autoscroll.take()
4522 }
4523
4524 pub fn use_asset<A: Asset>(&mut self, source: &A::Source, cx: &mut App) -> Option<A::Output> {
4530 cx.asset_entry::<A>(source).use_by(self.current_view())
4531 }
4532
4533 pub fn get_asset<A: Asset>(&mut self, source: &A::Source, cx: &mut App) -> Option<A::Output> {
4539 cx.fetch_asset::<A>(source)
4540 }
4541 pub fn element_offset(&self) -> Point<Pixels> {
4544 self.invalidator.debug_assert_prepaint();
4545 self.element_offset_stack
4546 .last()
4547 .copied()
4548 .unwrap_or_default()
4549 }
4550
4551 #[inline]
4554 pub(crate) fn element_opacity(&self) -> f32 {
4555 self.invalidator.debug_assert_paint_or_prepaint();
4556 self.element_opacity
4557 }
4558
4559 pub fn content_mask(&self) -> ContentMask<Pixels> {
4561 self.invalidator.debug_assert_paint_or_prepaint();
4562 self.content_mask_stack
4563 .last()
4564 .cloned()
4565 .unwrap_or_else(|| ContentMask {
4566 bounds: Bounds {
4567 origin: Point::default(),
4568 size: self.viewport_size,
4569 },
4570 })
4571 }
4572
4573 pub fn with_element_namespace<R>(
4576 &mut self,
4577 element_id: impl Into<ElementId>,
4578 f: impl FnOnce(&mut Self) -> R,
4579 ) -> R {
4580 self.element_id_stack.push(element_id.into());
4581 let result = f(self);
4582 self.element_id_stack.pop();
4583 result
4584 }
4585
4586 pub fn use_keyed_state<S: 'static>(
4588 &mut self,
4589 key: impl Into<ElementId>,
4590 cx: &mut App,
4591 init: impl FnOnce(&mut Self, &mut Context<S>) -> S,
4592 ) -> Entity<S> {
4593 let current_view = self.current_view();
4594 self.with_global_id(key.into(), |global_id, window| {
4595 window.with_element_state(global_id, |state: Option<Entity<S>>, window| {
4596 if let Some(state) = state {
4597 (state.clone(), state)
4598 } else {
4599 let new_state = cx.new(|cx| init(window, cx));
4600 Self::observe_keyed_state(&new_state, current_view, cx);
4601 (new_state.clone(), new_state)
4602 }
4603 })
4604 })
4605 }
4606
4607 #[track_caller]
4613 pub fn use_state<S: 'static>(
4614 &mut self,
4615 cx: &mut App,
4616 init: impl FnOnce(&mut Self, &mut Context<S>) -> S,
4617 ) -> Entity<S> {
4618 self.use_keyed_state(
4619 ElementId::CodeLocation(*core::panic::Location::caller()),
4620 cx,
4621 init,
4622 )
4623 }
4624
4625 #[inline(always)]
4630 pub fn with_element_state<S, R>(
4631 &mut self,
4632 global_id: &GlobalElementId,
4633 f: impl FnOnce(Option<S>, &mut Self) -> (R, S),
4634 ) -> R
4635 where
4636 S: 'static,
4637 {
4638 self.invalidator.debug_assert_paint_or_prepaint();
4639
4640 let (key, state) = self.take_element_state(global_id, TypeId::of::<S>());
4641
4642 if let Some(any) = state {
4643 let ElementStateBox {
4644 inner,
4645 #[cfg(debug_assertions)]
4646 type_name,
4647 } = any;
4648 let mut state_box = inner
4650 .downcast::<Option<S>>()
4651 .map_err(|_| {
4652 #[cfg(debug_assertions)]
4653 {
4654 anyhow::anyhow!(
4655 "invalid element state type for id, requested {:?}, actual: {:?}",
4656 std::any::type_name::<S>(),
4657 type_name
4658 )
4659 }
4660
4661 #[cfg(not(debug_assertions))]
4662 {
4663 anyhow::anyhow!(
4664 "invalid element state type for id, requested {:?}",
4665 std::any::type_name::<S>(),
4666 )
4667 }
4668 })
4669 .unwrap();
4670
4671 let state = state_box.take().expect(
4672 "reentrant call to with_element_state for the same state type and element id",
4673 );
4674 let (result, state) = f(Some(state), self);
4675 state_box.replace(state);
4676 self.insert_element_state(
4677 key,
4678 ElementStateBox {
4679 inner: state_box,
4680 #[cfg(debug_assertions)]
4681 type_name,
4682 },
4683 );
4684 result
4685 } else {
4686 let (result, state) = f(None, self);
4687 self.insert_element_state(
4688 key,
4689 ElementStateBox {
4690 inner: Box::new(Some(state)),
4691 #[cfg(debug_assertions)]
4692 type_name: std::any::type_name::<S>(),
4693 },
4694 );
4695 result
4696 }
4697 }
4698
4699 pub fn with_optional_element_state<S, R>(
4706 &mut self,
4707 global_id: Option<&GlobalElementId>,
4708 f: impl FnOnce(Option<Option<S>>, &mut Self) -> (R, Option<S>),
4709 ) -> R
4710 where
4711 S: 'static,
4712 {
4713 self.invalidator.debug_assert_paint_or_prepaint();
4714
4715 if let Some(global_id) = global_id {
4716 self.with_element_state(global_id, |state, cx| {
4717 let (result, state) = f(Some(state), cx);
4718 let state =
4719 state.expect("you must return some state when you pass some element id");
4720 (result, state)
4721 })
4722 } else {
4723 let (result, state) = f(None, self);
4724 debug_assert!(
4725 state.is_none(),
4726 "you must not return an element state when passing None for the global id"
4727 );
4728 result
4729 }
4730 }
4731
4732 #[inline]
4734 pub fn with_tab_group<R>(&mut self, index: Option<isize>, f: impl FnOnce(&mut Self) -> R) -> R {
4735 if let Some(index) = index {
4736 self.next_frame.tab_stops.begin_group(index);
4737 let result = f(self);
4738 self.next_frame.tab_stops.end_group();
4739 result
4740 } else {
4741 f(self)
4742 }
4743 }
4744
4745 pub fn defer_draw(
4754 &mut self,
4755 element: AnyElement,
4756 absolute_offset: Point<Pixels>,
4757 priority: usize,
4758 content_mask: Option<ContentMask<Pixels>>,
4759 ) {
4760 self.invalidator.debug_assert_prepaint();
4761 let parent_node = self.next_frame.dispatch_tree.active_node_id().unwrap();
4762 self.next_frame.deferred_draws.push(DeferredDraw {
4763 current_view: self.current_view(),
4764 parent_node,
4765 element_id_stack: self.element_id_stack.clone(),
4766 text_style_stack: self.text_style_stack.clone(),
4767 content_mask,
4768 rem_size: self.rem_size(),
4769 priority,
4770 element: Some(element),
4771 absolute_offset,
4772 prepaint_range: PrepaintStateIndex::default()..PrepaintStateIndex::default(),
4773 paint_range: PaintIndex::default()..PaintIndex::default(),
4774 accessed_entities: FxHashSet::default(),
4775 });
4776 }
4777
4778 pub fn paint_layer<R>(&mut self, bounds: Bounds<Pixels>, f: impl FnOnce(&mut Self) -> R) -> R {
4784 self.invalidator.debug_assert_paint();
4785
4786 let content_mask = self.content_mask();
4787 let clipped_bounds = bounds.intersect(&content_mask.bounds);
4788 if !clipped_bounds.is_empty() {
4789 self.next_frame
4790 .scene
4791 .push_layer(self.cover_bounds(clipped_bounds));
4792 }
4793
4794 let result = f(self);
4795
4796 if !clipped_bounds.is_empty() {
4797 self.next_frame.scene.pop_layer();
4798 }
4799
4800 result
4801 }
4802
4803 pub fn paint_drop_shadows(
4809 &mut self,
4810 bounds: Bounds<Pixels>,
4811 corner_radii: Corners<Pixels>,
4812 shadows: &[BoxShadow],
4813 ) {
4814 self.invalidator.debug_assert_paint();
4815
4816 let scale_factor = self.scale_factor();
4817 let content_mask = self.snapped_content_mask();
4818 let opacity = self.element_opacity();
4819 let element_bounds = self.cover_bounds(bounds);
4820 let element_corner_radii = corner_radii.scale(scale_factor);
4821 for shadow in shadows {
4822 if shadow.inset {
4823 continue;
4824 }
4825 let shadow_bounds = (bounds + shadow.offset).dilate(shadow.spread_radius);
4826 self.next_frame.scene.insert_primitive(Shadow {
4827 order: 0,
4828 blur_radius: shadow.blur_radius.scale(scale_factor),
4829 bounds: self.cover_bounds(shadow_bounds),
4830 content_mask,
4831 corner_radii: corner_radii.scale(scale_factor),
4832 color: shadow.color.opacity(opacity),
4833 element_bounds,
4834 element_corner_radii,
4835 inset: 0,
4836 pad: 0,
4837 });
4838 }
4839 }
4840
4841 pub fn paint_inset_shadows(
4845 &mut self,
4846 bounds: Bounds<Pixels>,
4847 corner_radii: Corners<Pixels>,
4848 shadows: &[BoxShadow],
4849 ) {
4850 self.invalidator.debug_assert_paint();
4851
4852 let scale_factor = self.scale_factor();
4853 let content_mask = self.snapped_content_mask();
4854 let opacity = self.element_opacity();
4855 let element_bounds = self.cover_bounds(bounds);
4856 let element_corner_radii = corner_radii.scale(scale_factor);
4857 for shadow in shadows {
4858 if !shadow.inset {
4859 continue;
4860 }
4861 let hole = (bounds + shadow.offset).dilate(-shadow.spread_radius);
4862 let zero = Pixels::ZERO;
4865 let hole_corner_radii = Corners {
4866 top_left: (corner_radii.top_left - shadow.spread_radius).max(zero),
4867 top_right: (corner_radii.top_right - shadow.spread_radius).max(zero),
4868 bottom_right: (corner_radii.bottom_right - shadow.spread_radius).max(zero),
4869 bottom_left: (corner_radii.bottom_left - shadow.spread_radius).max(zero),
4870 };
4871 self.next_frame.scene.insert_primitive(Shadow {
4872 order: 0,
4873 blur_radius: shadow.blur_radius.scale(scale_factor),
4874 bounds: self.cover_bounds(hole),
4875 content_mask,
4876 corner_radii: hole_corner_radii.scale(scale_factor),
4877 color: shadow.color.opacity(opacity),
4878 element_bounds,
4879 element_corner_radii,
4880 inset: 1,
4881 pad: 0,
4882 });
4883 }
4884 }
4885
4886 fn largest_border_interior(quad: &Quad) -> Bounds<ScaledPixels> {
4887 let radii = &quad.corner_radii;
4888 let widths = &quad.border_widths;
4889 let edge_radii = Edges {
4890 top: radii.top_left.max(radii.top_right),
4891 right: radii.top_right.max(radii.bottom_right),
4892 bottom: radii.bottom_left.max(radii.bottom_right),
4893 left: radii.top_left.max(radii.bottom_left),
4894 };
4895
4896 let antialias_inset = point(ScaledPixels(1.0), ScaledPixels(1.0));
4897 let inset_bounds = |top_left_inset, bottom_right_inset| {
4898 Bounds::from_corners(
4899 quad.bounds.origin + top_left_inset + antialias_inset,
4900 quad.bounds.bottom_right() - bottom_right_inset - antialias_inset,
4901 )
4902 };
4903
4904 let horizontal_band = inset_bounds(
4907 point(widths.left, widths.top.max(edge_radii.top)),
4908 point(widths.right, widths.bottom.max(edge_radii.bottom)),
4909 );
4910 let vertical_band = inset_bounds(
4911 point(widths.left.max(edge_radii.left), widths.top),
4912 point(widths.right.max(edge_radii.right), widths.bottom),
4913 );
4914
4915 let area = |bounds: &Bounds<ScaledPixels>| {
4916 bounds.size.width.0.max(0.) * bounds.size.height.0.max(0.)
4917 };
4918 if area(&horizontal_band) >= area(&vertical_band) {
4919 horizontal_band
4920 } else {
4921 vertical_band
4922 }
4923 }
4924
4925 pub fn paint_quad(&mut self, quad: PaintQuad) {
4935 self.invalidator.debug_assert_paint();
4936
4937 let opacity = self.element_opacity();
4938 let snapped_bounds = self.snap_bounds(quad.bounds);
4939 let snapped_border_widths = self.snap_border_widths(quad.border_widths);
4940 let quad = Quad {
4941 order: 0,
4942 bounds: snapped_bounds,
4943 content_mask: self.snapped_content_mask(),
4944 background: quad.background.opacity(opacity),
4945 border_color: quad.border_color.opacity(opacity),
4946 corner_radii: quad.corner_radii.scale(self.scale_factor()),
4947 border_widths: snapped_border_widths,
4948 border_style: quad.border_style,
4949 };
4950
4951 if !quad.background.is_transparent() {
4952 self.next_frame.scene.insert_primitive(quad);
4953 return;
4954 }
4955
4956 let outer_bounds = quad.bounds;
4959 let inner_bounds = Self::largest_border_interior(&quad);
4960
4961 if inner_bounds.is_empty() {
4962 self.next_frame.scene.insert_primitive(quad);
4963 return;
4964 }
4965
4966 let strips = [
4967 Bounds::from_corners(
4969 outer_bounds.origin,
4970 point(outer_bounds.right(), inner_bounds.top()),
4971 ),
4972 Bounds::from_corners(
4974 point(outer_bounds.left(), inner_bounds.bottom()),
4975 outer_bounds.bottom_right(),
4976 ),
4977 Bounds::from_corners(
4979 point(outer_bounds.left(), inner_bounds.top()),
4980 inner_bounds.bottom_left(),
4981 ),
4982 Bounds::from_corners(
4984 inner_bounds.top_right(),
4985 point(outer_bounds.right(), inner_bounds.bottom()),
4986 ),
4987 ];
4988
4989 for strip in strips {
4990 let content_mask_bounds = quad.content_mask.bounds.intersect(&strip);
4991 if !content_mask_bounds.is_empty() {
4992 self.next_frame.scene.insert_primitive(Quad {
4993 content_mask: ContentMask {
4994 bounds: content_mask_bounds,
4995 },
4996 ..quad
4997 });
4998 }
4999 }
5000 }
5001
5002 pub fn paint_path(&mut self, mut path: Path<Pixels>, color: impl Into<Background>) {
5006 self.invalidator.debug_assert_paint();
5007
5008 let scale_factor = self.scale_factor();
5009 let content_mask = self.content_mask();
5010 let opacity = self.element_opacity();
5011 path.content_mask = content_mask;
5012 let color: Background = color.into();
5013 path.color = color.opacity(opacity);
5014 self.next_frame
5015 .scene
5016 .insert_primitive(path.scale(scale_factor));
5017 }
5018
5019 pub fn paint_underline(
5023 &mut self,
5024 origin: Point<Pixels>,
5025 width: Pixels,
5026 style: &UnderlineStyle,
5027 ) {
5028 self.invalidator.debug_assert_paint();
5029 let underline = self.underline(origin, width, style);
5030 self.next_frame.scene.insert_primitive(underline);
5031 }
5032
5033 pub fn paint_strikethrough(
5037 &mut self,
5038 origin: Point<Pixels>,
5039 width: Pixels,
5040 style: &StrikethroughStyle,
5041 ) {
5042 self.invalidator.debug_assert_paint();
5043
5044 let scale_factor = self.scale_factor();
5045 let height = style.thickness;
5046 let bounds = Bounds {
5047 origin: origin.map(|c| ScaledPixels(round_to_device_pixel(c.0, scale_factor))),
5048 size: size(self.snap_stroke(width), self.snap_stroke(height)),
5049 };
5050 let opacity = self.element_opacity();
5051
5052 self.next_frame.scene.insert_primitive(Underline {
5053 order: 0,
5054 pad: 0,
5055 bounds,
5056 content_mask: self.snapped_content_mask(),
5057 thickness: self.snap_stroke(style.thickness),
5058 color: style.color.unwrap_or_default().opacity(opacity),
5059 wavy: false.into(),
5060 });
5061 }
5062
5063 pub fn paint_glyph(
5072 &mut self,
5073 origin: Point<Pixels>,
5074 font_id: FontId,
5075 glyph_id: GlyphId,
5076 font_size: Pixels,
5077 color: Hsla,
5078 ) -> Result<()> {
5079 self.invalidator.debug_assert_paint();
5080
5081 let element_opacity = self.element_opacity();
5082 let scale_factor = self.scale_factor();
5083 let glyph_origin = origin.scale(scale_factor);
5084
5085 let quantized_origin = Point::new(
5086 round_half_toward_zero(glyph_origin.x.0 * SUBPIXEL_VARIANTS_X as f32)
5087 / SUBPIXEL_VARIANTS_X as f32,
5088 round_half_toward_zero(glyph_origin.y.0 * SUBPIXEL_VARIANTS_Y as f32)
5089 / SUBPIXEL_VARIANTS_Y as f32,
5090 );
5091 let subpixel_variant = Point::new(
5092 (quantized_origin.x.fract() * SUBPIXEL_VARIANTS_X as f32) as u8,
5093 (quantized_origin.y.fract() * SUBPIXEL_VARIANTS_Y as f32) as u8,
5094 );
5095 let integer_origin = quantized_origin.map(|c| ScaledPixels(c.trunc()));
5096 let subpixel_rendering = self.should_use_subpixel_rendering(font_id, font_size);
5097 let dilation = self.text_system().glyph_dilation_for_color(color);
5098 let params = RenderGlyphParams {
5099 font_id,
5100 glyph_id,
5101 font_size,
5102 subpixel_variant,
5103 scale_factor,
5104 is_emoji: false,
5105 subpixel_rendering,
5106 dilation,
5107 };
5108
5109 let raster_bounds = self.text_system().raster_bounds(¶ms)?;
5110 if !raster_bounds.is_zero() {
5111 let tile = self
5112 .sprite_atlas
5113 .get_or_insert_with(params.clone().into(), &mut || {
5114 let (size, bytes) = self.text_system().rasterize_glyph(¶ms)?;
5115 Ok(Some((size, Cow::Owned(bytes))))
5116 })?
5117 .expect("Callback above only errors or returns Some");
5118 let bounds = Bounds {
5119 origin: integer_origin + raster_bounds.origin.map(Into::into),
5120 size: tile.bounds.size.map(Into::into),
5121 };
5122 let content_mask = self.snapped_content_mask();
5123
5124 if subpixel_rendering {
5125 self.next_frame.scene.insert_primitive(SubpixelSprite {
5126 order: 0,
5127 pad: 0,
5128 bounds,
5129 content_mask,
5130 color: color.opacity(element_opacity),
5131 tile,
5132 transformation: TransformationMatrix::unit(),
5133 });
5134 } else {
5135 self.next_frame.scene.insert_primitive(MonochromeSprite {
5136 order: 0,
5137 pad: 0,
5138 bounds,
5139 content_mask,
5140 color: color.opacity(element_opacity),
5141 tile,
5142 transformation: TransformationMatrix::unit(),
5143 });
5144 }
5145 }
5146 Ok(())
5147 }
5148
5149 fn should_use_subpixel_rendering(&self, font_id: FontId, font_size: Pixels) -> bool {
5150 if self.platform_window.background_appearance() != WindowBackgroundAppearance::Opaque {
5151 return false;
5152 }
5153
5154 if !self.platform_window.is_subpixel_rendering_supported() {
5155 return false;
5156 }
5157
5158 let mode = match self.text_rendering_mode.get() {
5159 TextRenderingMode::PlatformDefault => self
5160 .text_system()
5161 .recommended_rendering_mode(font_id, font_size),
5162 mode => mode,
5163 };
5164
5165 mode == TextRenderingMode::Subpixel
5166 }
5167
5168 pub fn paint_emoji(
5177 &mut self,
5178 origin: Point<Pixels>,
5179 font_id: FontId,
5180 glyph_id: GlyphId,
5181 font_size: Pixels,
5182 ) -> Result<()> {
5183 self.invalidator.debug_assert_paint();
5184
5185 let scale_factor = self.scale_factor();
5186 let glyph_origin = origin.scale(scale_factor);
5187 let integer_origin = glyph_origin.map(|c| ScaledPixels(round_half_toward_zero(c.0)));
5188 let params = RenderGlyphParams {
5189 font_id,
5190 glyph_id,
5191 font_size,
5192 subpixel_variant: Default::default(),
5193 scale_factor,
5194 is_emoji: true,
5195 subpixel_rendering: false,
5196 dilation: 0,
5197 };
5198
5199 let raster_bounds = self.text_system().raster_bounds(¶ms)?;
5200 if !raster_bounds.is_zero() {
5201 let tile = self
5202 .sprite_atlas
5203 .get_or_insert_with(params.clone().into(), &mut || {
5204 let (size, bytes) = self.text_system().rasterize_glyph(¶ms)?;
5205 Ok(Some((size, Cow::Owned(bytes))))
5206 })?
5207 .expect("Callback above only errors or returns Some");
5208
5209 let bounds = Bounds {
5210 origin: integer_origin + raster_bounds.origin.map(Into::into),
5211 size: tile.bounds.size.map(Into::into),
5212 };
5213 let content_mask = self.snapped_content_mask();
5214 let opacity = self.element_opacity();
5215
5216 self.next_frame.scene.insert_primitive(PolychromeSprite {
5217 order: 0,
5218 pad: 0,
5219 grayscale: false.into(),
5220 bounds,
5221 corner_radii: Default::default(),
5222 content_mask,
5223 tile,
5224 opacity,
5225 });
5226 }
5227 Ok(())
5228 }
5229
5230 pub fn paint_svg(
5234 &mut self,
5235 bounds: Bounds<Pixels>,
5236 path: SharedString,
5237 data: Option<&[u8]>,
5238 transformation: TransformationMatrix,
5239 color: Hsla,
5240 cx: &App,
5241 ) -> Result<()> {
5242 self.invalidator.debug_assert_paint();
5243
5244 let element_opacity = self.element_opacity();
5245 let bounds = self.snap_bounds(bounds);
5246
5247 let params = RenderSvgParams {
5248 path,
5249 size: bounds.size.map(|pixels| {
5250 DevicePixels::from((pixels.0 * SMOOTH_SVG_SCALE_FACTOR).ceil() as i32)
5251 }),
5252 };
5253
5254 let Some(tile) =
5255 self.sprite_atlas
5256 .get_or_insert_with(params.clone().into(), &mut || {
5257 let Some((size, bytes)) = cx.svg_renderer.render_alpha_mask(¶ms, data)?
5258 else {
5259 return Ok(None);
5260 };
5261 Ok(Some((size, Cow::Owned(bytes))))
5262 })?
5263 else {
5264 return Ok(());
5265 };
5266 let content_mask = self.snapped_content_mask();
5267 let svg_bounds = Bounds {
5268 origin: bounds.center()
5269 - Point::new(
5270 ScaledPixels(tile.bounds.size.width.0 as f32 / SMOOTH_SVG_SCALE_FACTOR / 2.),
5271 ScaledPixels(tile.bounds.size.height.0 as f32 / SMOOTH_SVG_SCALE_FACTOR / 2.),
5272 ),
5273 size: tile
5274 .bounds
5275 .size
5276 .map(|value| ScaledPixels(value.0 as f32 / SMOOTH_SVG_SCALE_FACTOR)),
5277 };
5278 let final_bounds = svg_bounds
5279 .map_origin(|value| ScaledPixels(round_half_toward_zero(value.0)))
5280 .map_size(|size| size.ceil());
5281
5282 self.next_frame.scene.insert_primitive(MonochromeSprite {
5283 order: 0,
5284 pad: 0,
5285 bounds: final_bounds,
5286 content_mask,
5287 color: color.opacity(element_opacity),
5288 tile,
5289 transformation,
5290 });
5291
5292 Ok(())
5293 }
5294
5295 pub fn paint_image(
5304 &mut self,
5305 bounds: Bounds<Pixels>,
5306 image_bounds: Bounds<Pixels>,
5307 corner_radii: Corners<Pixels>,
5308 data: Arc<RenderImage>,
5309 frame_index: usize,
5310 grayscale: bool,
5311 ) -> Result<()> {
5312 self.invalidator.debug_assert_paint();
5313
5314 let visible_bounds = bounds.intersect(&image_bounds);
5315 if visible_bounds.size.width <= Pixels::ZERO || visible_bounds.size.height <= Pixels::ZERO {
5316 return Ok(());
5317 }
5318 if image_bounds.size.width <= Pixels::ZERO || image_bounds.size.height <= Pixels::ZERO {
5319 return Ok(());
5320 }
5321
5322 let params = RenderImageParams {
5323 image_id: data.id,
5324 frame_index,
5325 };
5326
5327 let tile = self
5328 .sprite_atlas
5329 .get_or_insert_with(params.into(), &mut || {
5330 Ok(Some((
5331 data.size(frame_index),
5332 Cow::Borrowed(
5333 data.as_bytes(frame_index)
5334 .expect("It's the caller's job to pass a valid frame index"),
5335 ),
5336 )))
5337 })?
5338 .expect("Callback above only returns Some");
5339
5340 let visible_bounds_snapped = self.snap_bounds(visible_bounds);
5341
5342 let sub_tile = if visible_bounds == image_bounds {
5343 tile
5344 } else {
5345 let x_offset_ratio =
5346 (visible_bounds.origin.x - image_bounds.origin.x) / image_bounds.size.width;
5347 let y_offset_ratio =
5348 (visible_bounds.origin.y - image_bounds.origin.y) / image_bounds.size.height;
5349 let width_ratio = visible_bounds.size.width / image_bounds.size.width;
5350 let height_ratio = visible_bounds.size.height / image_bounds.size.height;
5351
5352 let tile_origin_x = tile.bounds.origin.x.0;
5353 let tile_origin_y = tile.bounds.origin.y.0;
5354 let tile_width = tile.bounds.size.width.0;
5355 let tile_height = tile.bounds.size.height.0;
5356
5357 let sub_origin_x = tile_origin_x + (x_offset_ratio * tile_width as f32).round() as i32;
5358 let sub_origin_y = tile_origin_y + (y_offset_ratio * tile_height as f32).round() as i32;
5359 let sub_width = (width_ratio * tile_width as f32).round() as i32;
5360 let sub_height = (height_ratio * tile_height as f32).round() as i32;
5361
5362 let max_x = tile_origin_x + tile_width;
5363 let max_y = tile_origin_y + tile_height;
5364
5365 let clamped_origin_x = sub_origin_x.clamp(tile_origin_x, max_x);
5366 let clamped_origin_y = sub_origin_y.clamp(tile_origin_y, max_y);
5367 let clamped_width = sub_width.min(max_x - clamped_origin_x).max(0);
5368 let clamped_height = sub_height.min(max_y - clamped_origin_y).max(0);
5369
5370 AtlasTile {
5371 bounds: Bounds {
5372 origin: point(
5373 DevicePixels(clamped_origin_x),
5374 DevicePixels(clamped_origin_y),
5375 ),
5376 size: size(DevicePixels(clamped_width), DevicePixels(clamped_height)),
5377 },
5378 ..tile
5379 }
5380 };
5381
5382 let content_mask = self.snapped_content_mask();
5383 let corner_radii = corner_radii
5384 .clamp_radii_for_quad_size(visible_bounds.size)
5385 .scale(self.scale_factor());
5386 let opacity = self.element_opacity();
5387
5388 self.next_frame.scene.insert_primitive(PolychromeSprite {
5389 order: 0,
5390 pad: 0,
5391 grayscale: grayscale.into(),
5392 bounds: visible_bounds_snapped,
5393 content_mask,
5394 corner_radii,
5395 tile: sub_tile,
5396 opacity,
5397 });
5398 Ok(())
5399 }
5400
5401 #[cfg(any(target_os = "macos", target_os = "ios"))]
5405 pub fn paint_surface(
5406 &mut self,
5407 bounds: Bounds<Pixels>,
5408 image_buffer: CFRetained<CVPixelBuffer>,
5409 ) {
5410 use crate::PaintSurface;
5411
5412 self.invalidator.debug_assert_paint();
5413
5414 let bounds = self.snap_bounds(bounds);
5415 let content_mask = self.snapped_content_mask();
5416 self.next_frame.scene.insert_primitive(PaintSurface {
5417 order: 0,
5418 bounds,
5419 content_mask,
5420 image_buffer,
5421 });
5422 }
5423
5424 pub fn drop_image(&mut self, data: Arc<RenderImage>) -> Result<()> {
5426 for frame_index in 0..data.frame_count() {
5427 let params = RenderImageParams {
5428 image_id: data.id,
5429 frame_index,
5430 };
5431
5432 self.sprite_atlas.remove(¶ms.clone().into());
5433 }
5434
5435 Ok(())
5436 }
5437
5438 #[cfg(any(test, feature = "test-support"))]
5440 pub fn has_image_atlas_entry(&self, data: &RenderImage) -> bool {
5441 data.frame_count() > 0
5442 && (0..data.frame_count()).all(|frame_index| {
5443 self.sprite_atlas.contains(
5444 &RenderImageParams {
5445 image_id: data.id,
5446 frame_index,
5447 }
5448 .into(),
5449 )
5450 })
5451 }
5452
5453 #[must_use]
5459 pub fn request_layout(
5460 &mut self,
5461 style: Style,
5462 children: impl IntoIterator<Item = LayoutId>,
5463 cx: &mut App,
5464 ) -> LayoutId {
5465 self.invalidator.debug_assert_prepaint();
5466
5467 cx.layout_id_buffer.clear();
5468 cx.layout_id_buffer.extend(children);
5469 let rem_size = self.rem_size();
5470 let scale_factor = self.scale_factor();
5471
5472 self.layout_engine.as_mut().unwrap().request_layout(
5473 style,
5474 rem_size,
5475 scale_factor,
5476 &cx.layout_id_buffer,
5477 )
5478 }
5479
5480 pub fn request_measured_layout<F>(&mut self, style: Style, measure: F) -> LayoutId
5489 where
5490 F: Fn(Size<Option<Pixels>>, Size<AvailableSpace>, &mut Window, &mut App) -> Size<Pixels>
5491 + 'static,
5492 {
5493 self.invalidator.debug_assert_prepaint();
5494
5495 let rem_size = self.rem_size();
5496 let scale_factor = self.scale_factor();
5497 self.layout_engine
5498 .as_mut()
5499 .unwrap()
5500 .request_measured_layout(style, rem_size, scale_factor, measure)
5501 }
5502
5503 pub fn compute_layout(
5509 &mut self,
5510 layout_id: LayoutId,
5511 available_space: Size<AvailableSpace>,
5512 cx: &mut App,
5513 ) {
5514 self.invalidator.debug_assert_prepaint();
5515
5516 let mut layout_engine = self.layout_engine.take().unwrap();
5517 layout_engine.compute_layout(layout_id, available_space, self, cx);
5518 self.layout_engine = Some(layout_engine);
5519 }
5520
5521 pub fn layout_bounds(&mut self, layout_id: LayoutId) -> Bounds<Pixels> {
5526 self.invalidator.debug_assert_prepaint();
5527
5528 let scale_factor = self.scale_factor();
5529 let mut bounds = self
5530 .layout_engine
5531 .as_mut()
5532 .unwrap()
5533 .layout_bounds(layout_id, scale_factor)
5534 .map(Into::into);
5535 let snapped_offset = self.pixel_snap_point(self.element_offset());
5536 bounds.origin += snapped_offset;
5537 bounds
5538 }
5539
5540 pub fn insert_hitbox(&mut self, bounds: Bounds<Pixels>, behavior: HitboxBehavior) -> Hitbox {
5546 self.invalidator.debug_assert_prepaint();
5547
5548 let content_mask = self.content_mask();
5549 let id = self.next_hitbox_id;
5550 self.next_hitbox_id = self.next_hitbox_id.next();
5551 let hitbox = Hitbox {
5552 id,
5553 bounds,
5554 content_mask,
5555 behavior,
5556 };
5557 self.next_frame.hitboxes.push(hitbox.clone());
5558 hitbox
5559 }
5560
5561 pub fn insert_window_control_hitbox(&mut self, area: WindowControlArea, hitbox: Hitbox) {
5565 self.invalidator.debug_assert_paint();
5566 self.next_frame.window_control_hitboxes.push((area, hitbox));
5567 }
5568
5569 pub fn set_key_context(&mut self, context: KeyContext) {
5574 self.invalidator.debug_assert_paint();
5575 self.next_frame.dispatch_tree.set_key_context(context);
5576 }
5577
5578 pub fn set_focus_handle(&mut self, focus_handle: &FocusHandle, _: &App) {
5583 self.invalidator.debug_assert_prepaint();
5584 if focus_handle.is_focused(self) {
5585 self.next_frame.focus = Some(focus_handle.id);
5586 }
5587 self.next_frame.dispatch_tree.set_focus_id(focus_handle.id);
5588 }
5589
5590 pub fn set_view_id(&mut self, view_id: EntityId) {
5596 self.invalidator.debug_assert_prepaint();
5597 self.next_frame.dispatch_tree.set_view_id(view_id);
5598 }
5599
5600 pub fn current_view(&self) -> EntityId {
5602 self.invalidator.debug_assert_paint_or_prepaint();
5603 self.rendered_entity_stack.last().copied().unwrap()
5604 }
5605
5606 #[inline]
5607 pub(crate) fn with_rendered_view<R>(
5608 &mut self,
5609 id: EntityId,
5610 f: impl FnOnce(&mut Self) -> R,
5611 ) -> R {
5612 self.rendered_entity_stack.push(id);
5613 let result = f(self);
5614 self.rendered_entity_stack.pop();
5615 result
5616 }
5617
5618 pub fn with_image_cache<F, R>(&mut self, image_cache: Option<AnyImageCache>, f: F) -> R
5620 where
5621 F: FnOnce(&mut Self) -> R,
5622 {
5623 if let Some(image_cache) = image_cache {
5624 self.image_cache_stack.push(image_cache);
5625 let result = f(self);
5626 self.image_cache_stack.pop();
5627 result
5628 } else {
5629 f(self)
5630 }
5631 }
5632
5633 pub fn handle_input(
5642 &mut self,
5643 focus_handle: &FocusHandle,
5644 input_handler: impl InputHandler,
5645 cx: &App,
5646 ) {
5647 self.invalidator.debug_assert_paint();
5648
5649 if focus_handle.is_focused(self) {
5650 let cx = self.to_async(cx);
5651 self.next_frame.input_handlers.push(Some(
5652 PlatformInputHandler::new(cx, Box::new(input_handler)).with_focus(focus_handle.id),
5653 ));
5654 }
5655 }
5656
5657 pub fn notify_text_input_changed(&self, focus_handle: &FocusHandle) {
5662 if self.focused_text_input == Some(focus_handle.id) && focus_handle.is_focused(self) {
5663 self.platform_window
5664 .text_input_state_changed(TextInputStateChange::ContentChanged);
5665 }
5666 }
5667
5668 pub fn notify_text_selection_changed(&self, focus_handle: &FocusHandle) {
5673 if self.focused_text_input == Some(focus_handle.id) && focus_handle.is_focused(self) {
5674 self.platform_window
5675 .text_input_state_changed(TextInputStateChange::SelectionChanged);
5676 }
5677 }
5678
5679 fn apply_text_input_configuration(&mut self, cx: &mut App) {
5684 let configuration = match self.platform_window.take_input_handler() {
5685 Some(mut input_handler) => {
5686 let configuration = input_handler.text_input_configuration(self, cx);
5687 self.platform_window.set_input_handler(input_handler);
5688 configuration
5689 }
5690 None => TextInputConfiguration::default(),
5691 };
5692 if self.last_text_input_configuration.as_ref() != Some(&configuration) {
5693 self.platform_window
5694 .set_text_input_configuration(configuration.clone());
5695 self.last_text_input_configuration = Some(configuration);
5696 }
5697 }
5698
5699 pub fn on_mouse_event<Event: MouseEvent>(
5705 &mut self,
5706 mut listener: impl FnMut(&Event, DispatchPhase, &mut Window, &mut App) + 'static,
5707 ) {
5708 self.invalidator.debug_assert_paint();
5709
5710 self.next_frame.mouse_listeners.push(Some(Box::new(
5711 move |event: &dyn Any, phase: DispatchPhase, window: &mut Window, cx: &mut App| {
5712 if let Some(event) = event.downcast_ref() {
5713 listener(event, phase, window, cx)
5714 }
5715 },
5716 )));
5717 }
5718
5719 pub fn on_key_event<Event: KeyEvent>(
5728 &mut self,
5729 listener: impl Fn(&Event, DispatchPhase, &mut Window, &mut App) + 'static,
5730 ) {
5731 self.invalidator.debug_assert_paint();
5732
5733 self.next_frame.dispatch_tree.on_key_event(Rc::new(
5734 move |event: &dyn Any, phase, window: &mut Window, cx: &mut App| {
5735 if let Some(event) = event.downcast_ref::<Event>() {
5736 listener(event, phase, window, cx)
5737 }
5738 },
5739 ));
5740 }
5741
5742 pub fn on_modifiers_changed(
5749 &mut self,
5750 listener: impl Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static,
5751 ) {
5752 self.invalidator.debug_assert_paint();
5753
5754 self.next_frame.dispatch_tree.on_modifiers_changed(Rc::new(
5755 move |event: &ModifiersChangedEvent, window: &mut Window, cx: &mut App| {
5756 listener(event, window, cx)
5757 },
5758 ));
5759 }
5760
5761 pub fn on_focus_in(
5765 &mut self,
5766 handle: &FocusHandle,
5767 cx: &mut App,
5768 mut listener: impl FnMut(&mut Window, &mut App) + 'static,
5769 ) -> Subscription {
5770 let focus_id = handle.id;
5771 let (subscription, activate) =
5772 self.new_focus_listener(Box::new(move |event, window, cx| {
5773 if event.is_focus_in(focus_id) {
5774 listener(window, cx);
5775 }
5776 true
5777 }));
5778 cx.defer(move |_| activate());
5779 subscription
5780 }
5781
5782 pub fn on_focus_out(
5785 &mut self,
5786 handle: &FocusHandle,
5787 cx: &mut App,
5788 mut listener: impl FnMut(FocusOutEvent, &mut Window, &mut App) + 'static,
5789 ) -> Subscription {
5790 let focus_id = handle.id;
5791 let (subscription, activate) =
5792 self.new_focus_listener(Box::new(move |event, window, cx| {
5793 if let Some(blurred_id) = event.previous_focus_path.last().copied()
5794 && event.is_focus_out(focus_id)
5795 {
5796 let event = FocusOutEvent {
5797 blurred: WeakFocusHandle {
5798 id: blurred_id,
5799 handles: Arc::downgrade(&cx.focus_handles),
5800 },
5801 };
5802 listener(event, window, cx)
5803 }
5804 true
5805 }));
5806 cx.defer(move |_| activate());
5807 subscription
5808 }
5809
5810 fn reset_cursor_style(&self, cx: &mut App) {
5811 if self.is_window_hovered() {
5813 let style = cx
5815 .active_drag_cursor_style()
5816 .or_else(|| self.rendered_frame.cursor_style(self))
5817 .unwrap_or(CursorStyle::Arrow);
5818 cx.platform.set_cursor_style(style);
5819 }
5820 }
5821
5822 pub fn dispatch_keystroke(&mut self, keystroke: Keystroke, cx: &mut App) -> bool {
5825 let keystroke = keystroke.with_simulated_ime();
5826 let result = self.dispatch_event(
5827 PlatformInput::KeyDown(KeyDownEvent {
5828 keystroke: keystroke.clone(),
5829 is_held: false,
5830 prefer_character_input: false,
5831 }),
5832 cx,
5833 );
5834 if !result.propagate {
5835 return true;
5836 }
5837
5838 if let Some(input) = keystroke.key_char
5839 && let Some(mut input_handler) = self.platform_window.take_input_handler()
5840 {
5841 input_handler.dispatch_input(&input, self, cx);
5842 self.platform_window.set_input_handler(input_handler);
5843 return true;
5844 }
5845
5846 false
5847 }
5848
5849 pub fn keystroke_text_for(&self, action: &dyn Action, cx: &App) -> String {
5852 self.highest_precedence_binding_for_action(action)
5853 .map(|binding| {
5854 binding
5855 .keystrokes()
5856 .iter()
5857 .map(|keystroke| keystroke.display(cx.platform_info().os).to_string())
5858 .collect::<Vec<_>>()
5859 .join(" ")
5860 })
5861 .unwrap_or_else(|| action.name().to_string())
5862 }
5863
5864 #[profiling::function]
5866 pub fn dispatch_event(&mut self, event: PlatformInput, cx: &mut App) -> DispatchEventResult {
5867 #[cfg(feature = "profiler")]
5868 self.window_profiler.begin_input(event.kind_name());
5869 let update_count_before = self.invalidator.update_count();
5870 let old_modality = self.last_input_modality;
5874 self.last_input_modality = match &event {
5875 PlatformInput::KeyDown(_) => InputModality::Keyboard,
5876 PlatformInput::MouseMove(_) | PlatformInput::MouseDown(_) => InputModality::Mouse,
5877 PlatformInput::Touch(_) => InputModality::Touch,
5878 _ => self.last_input_modality,
5879 };
5880 if self.last_input_modality != old_modality {
5881 self.refresh();
5882 }
5883
5884 cx.propagate_event = true;
5886 self.default_prevented = false;
5888
5889 let event = match event {
5890 PlatformInput::MouseMove(mouse_move) => {
5893 self.mouse_position = mouse_move.position;
5894 self.modifiers = mouse_move.modifiers;
5895 PlatformInput::MouseMove(mouse_move)
5896 }
5897 PlatformInput::MouseDown(mouse_down) => {
5898 self.mouse_position = mouse_down.position;
5899 self.modifiers = mouse_down.modifiers;
5900 PlatformInput::MouseDown(mouse_down)
5901 }
5902 PlatformInput::MouseUp(mouse_up) => {
5903 self.mouse_position = mouse_up.position;
5904 self.modifiers = mouse_up.modifiers;
5905 PlatformInput::MouseUp(mouse_up)
5906 }
5907 PlatformInput::MousePressure(mouse_pressure) => {
5908 PlatformInput::MousePressure(mouse_pressure)
5909 }
5910 PlatformInput::MouseExited(mouse_exited) => {
5911 self.modifiers = mouse_exited.modifiers;
5912 PlatformInput::MouseExited(mouse_exited)
5913 }
5914 PlatformInput::ModifiersChanged(modifiers_changed) => {
5915 self.modifiers = modifiers_changed.modifiers;
5916 self.capslock = modifiers_changed.capslock;
5917 PlatformInput::ModifiersChanged(modifiers_changed)
5918 }
5919 PlatformInput::Scroll(scroll_wheel) => {
5920 self.mouse_position = scroll_wheel.position;
5921 self.modifiers = scroll_wheel.modifiers;
5922 PlatformInput::Scroll(scroll_wheel)
5923 }
5924 PlatformInput::Pinch(pinch) => {
5925 self.mouse_position = pinch.position;
5926 self.modifiers = pinch.modifiers;
5927 PlatformInput::Pinch(pinch)
5928 }
5929 PlatformInput::FileDrop(file_drop) => match file_drop {
5932 FileDropEvent::Entered { position, paths } => {
5933 self.mouse_position = position;
5934 let source_window = self.handle.window_id();
5935 if !cx.restore_platform_drag(source_window) && cx.active_drag.is_none() {
5936 cx.active_drag = Some(AnyDrag {
5937 value: Arc::new(paths.clone()),
5938 view: cx.new(|_| paths).into(),
5939 cursor_offset: position,
5940 cursor_style: None,
5941 external_payload_source: None,
5942 });
5943 }
5944 PlatformInput::MouseMove(MouseMoveEvent {
5945 position,
5946 pressed_button: Some(MouseButton::Left),
5947 modifiers: Modifiers::default(),
5948 })
5949 }
5950 FileDropEvent::Pending { position } => {
5951 self.mouse_position = position;
5952 PlatformInput::MouseMove(MouseMoveEvent {
5953 position,
5954 pressed_button: Some(MouseButton::Left),
5955 modifiers: Modifiers::default(),
5956 })
5957 }
5958 FileDropEvent::Submit { position } => {
5959 cx.activate(true);
5960 self.mouse_position = position;
5961 PlatformInput::MouseUp(MouseUpEvent {
5962 button: MouseButton::Left,
5963 position,
5964 modifiers: Modifiers::default(),
5965 click_count: 1,
5966 })
5967 }
5968 FileDropEvent::Exited => {
5969 if !cx.hand_restored_drag_to_platform(self.handle.window_id()) {
5970 cx.active_drag.take();
5971 }
5972 self.refresh();
5973 PlatformInput::FileDrop(FileDropEvent::Exited)
5974 }
5975 FileDropEvent::Ended => {
5976 cx.end_platform_drag(self.handle.window_id());
5977 self.refresh();
5978 PlatformInput::FileDrop(FileDropEvent::Ended)
5979 }
5980 },
5981 PlatformInput::Touch(touch) => PlatformInput::Touch(touch),
5982 PlatformInput::LongPress(long_press) => {
5983 self.mouse_position = if long_press.phase == crate::TouchPhase::Started {
5984 long_press.start_position
5985 } else {
5986 long_press.position
5987 };
5988 if long_press.phase == crate::TouchPhase::Started {
5989 self.long_press_capture = None;
5990 }
5991 PlatformInput::LongPress(long_press)
5992 }
5993 PlatformInput::TouchDrag(touch_drag) => {
5994 self.mouse_position = touch_drag.start_position;
5995 PlatformInput::TouchDrag(touch_drag)
5996 }
5997 PlatformInput::BackGesture(back) => {
5998 self.mouse_position = back.start_position;
5999 PlatformInput::BackGesture(back)
6000 }
6001 PlatformInput::KeyDown(_) | PlatformInput::KeyUp(_) => event,
6002 };
6003
6004 if let Some(any_mouse_event) = event.mouse_event() {
6005 self.dispatch_mouse_event(any_mouse_event, cx);
6006 } else if let Some(any_key_event) = event.keyboard_event() {
6007 self.dispatch_key_event(any_key_event, cx);
6008 } else if let Some(touch_event) = event.touch_event() {
6009 self.dispatch_touch_event(touch_event, cx);
6010 }
6011 if let PlatformInput::LongPress(long_press) = &event {
6012 match long_press.phase {
6013 crate::TouchPhase::Started if !self.default_prevented => {
6014 self.long_press_capture = None;
6015 }
6016 crate::TouchPhase::Ended | crate::TouchPhase::Cancelled => {
6017 self.long_press_capture = None;
6018 }
6019 crate::TouchPhase::Started | crate::TouchPhase::Moved => {}
6020 }
6021 }
6022
6023 self.promote_external_drag_to_platform(&event, cx);
6026
6027 let caused_invalidation = self.invalidator.update_count() > update_count_before;
6028 if caused_invalidation {
6029 self.input_rate_tracker.borrow_mut().record_input();
6030 }
6031 #[cfg(feature = "profiler")]
6032 self.window_profiler.end_input(caused_invalidation);
6033
6034 DispatchEventResult {
6035 propagate: cx.propagate_event,
6036 default_prevented: self.default_prevented,
6037 }
6038 }
6039
6040 fn promote_external_drag_to_platform(&mut self, event: &PlatformInput, cx: &mut App) {
6041 let PlatformInput::MouseMove(mouse_move) = event else {
6042 return;
6043 };
6044 if mouse_move.pressed_button != Some(MouseButton::Left) {
6045 return;
6046 }
6047 if Bounds::new(Point::default(), self.viewport_size).contains(&mouse_move.position) {
6048 return;
6049 }
6050 if !self.platform_window.can_start_external_drag() {
6051 return;
6052 }
6053 let Some(payload_source) = cx
6054 .active_drag
6055 .as_mut()
6056 .and_then(|drag| drag.external_payload_source.take())
6057 else {
6058 return;
6059 };
6060 let Some(payload) = payload_source(self, cx) else {
6061 return;
6062 };
6063 if self.platform_window.start_external_drag(&payload)
6064 && cx.hand_active_drag_to_platform(self.handle.window_id())
6065 {
6066 self.refresh();
6067 }
6068 }
6069
6070 pub fn touch_prediction_enabled(&self) -> bool {
6074 self.touch_prediction_enabled
6075 }
6076
6077 pub fn set_touch_prediction_enabled(&mut self, enabled: bool) {
6081 self.touch_prediction_enabled = enabled;
6082 }
6083
6084 fn dispatch_touch_event(&mut self, event: &TouchEvent, cx: &mut App) {
6088 let mut event = event.clone();
6089 if !self.touch_prediction_enabled {
6090 event.predicted_position = None;
6091 }
6092 let recognized_gestures = self.touch_gestures.handle_event(&event);
6093 if event.phase == crate::TouchPhase::Started
6094 && let Some(touch_drag) = self.touch_gestures.offer_touch_drag(event.id)
6095 {
6096 self.dispatch_recognized_touch_gesture(touch_drag, cx);
6097 }
6098 if event.phase == crate::TouchPhase::Started
6099 && self.touch_gestures.pending_long_press().is_some()
6100 {
6101 self.long_press_capture = None;
6102 }
6103 let mut tapped = false;
6104 for gesture in recognized_gestures {
6105 tapped |= matches!(gesture, RecognizedTouchGesture::Tap { .. });
6106 self.dispatch_recognized_touch_gesture(gesture, cx);
6107 }
6108 if event.phase == crate::TouchPhase::Started {
6109 self.schedule_long_press_timer(cx);
6110 } else if self.touch_gestures.pending_long_press().is_none() {
6111 self.long_press_timer.take();
6112 }
6113 if tapped && self.invalidator.is_dirty() {
6119 self.draw(cx).clear(cx);
6120 }
6121 if self.touch_gestures.has_momentum() {
6122 self.schedule_touch_momentum_tick();
6123 }
6124 }
6125
6126 fn dispatch_recognized_touch_gesture(&mut self, gesture: RecognizedTouchGesture, cx: &mut App) {
6127 match gesture {
6128 RecognizedTouchGesture::Scroll(scroll_wheel) => {
6129 self.mouse_position = scroll_wheel.position;
6130 cx.propagate_event = true;
6131 self.dispatch_mouse_event(&scroll_wheel, cx);
6132 }
6133 RecognizedTouchGesture::Tap { down, up } => {
6134 self.mouse_position = up.position;
6135 cx.propagate_event = true;
6136 self.dispatch_mouse_event(&down, cx);
6137 cx.propagate_event = true;
6138 self.dispatch_mouse_event(&up, cx);
6139 }
6140 RecognizedTouchGesture::TouchDrag(touch_drag) => {
6141 self.mouse_position = touch_drag.start_position;
6142 cx.propagate_event = true;
6143 self.default_prevented = false;
6144 let started = touch_drag.phase == crate::TouchPhase::Started;
6145 self.dispatch_mouse_event(&touch_drag, cx);
6146 if started {
6147 self.touch_gestures
6148 .resolve_touch_drag(self.default_prevented);
6149 }
6150 }
6151 RecognizedTouchGesture::LongPress(long_press) => {
6152 self.mouse_position = if long_press.phase == crate::TouchPhase::Started {
6153 long_press.start_position
6154 } else {
6155 long_press.position
6156 };
6157 cx.propagate_event = true;
6158 self.default_prevented = false;
6159 let started = long_press.phase == crate::TouchPhase::Started;
6160 let ended = matches!(
6161 long_press.phase,
6162 crate::TouchPhase::Ended | crate::TouchPhase::Cancelled
6163 );
6164 self.dispatch_mouse_event(&long_press, cx);
6165 if started {
6166 let claimed = self.default_prevented;
6167 self.touch_gestures.resolve_long_press(claimed);
6168 if !claimed {
6169 self.long_press_capture = None;
6170 }
6171 }
6172 if ended {
6173 self.long_press_capture = None;
6174 }
6175 }
6176 }
6177 }
6178
6179 fn schedule_long_press_timer(&mut self, cx: &mut App) {
6180 self.long_press_timer.take();
6181 let Some((touch_id, duration)) = self.touch_gestures.pending_long_press() else {
6182 return;
6183 };
6184 self.long_press_timer = Some(self.spawn(cx, async move |cx| {
6185 cx.background_executor.timer(duration).await;
6186 cx.update(move |window, cx| {
6187 window.long_press_timer.take();
6188 if let Some(gesture) = window.touch_gestures.offer_long_press(touch_id) {
6189 window.dispatch_recognized_touch_gesture(gesture, cx);
6190 }
6191 })
6192 .with_context(|| {
6193 format!(
6194 "handling long-press timer for window {:?}",
6195 cx.window_handle().window_id()
6196 )
6197 })
6198 .log_err();
6199 }));
6200 }
6201
6202 fn schedule_touch_momentum_tick(&mut self) {
6203 self.on_next_frame(|window, cx| {
6204 if let Some(gesture) = window.touch_gestures.tick_momentum() {
6205 window.dispatch_recognized_touch_gesture(gesture, cx);
6206 }
6207 if window.touch_gestures.has_momentum() {
6208 window.schedule_touch_momentum_tick();
6209 }
6210 });
6211 }
6212
6213 fn dispatch_mouse_event(&mut self, event: &dyn Any, cx: &mut App) {
6214 self.mouse_hit_test = self.rendered_frame.hit_test(self.mouse_position());
6215
6216 #[cfg(any(feature = "inspector", debug_assertions))]
6217 if self.is_inspector_picking(cx) {
6218 self.reset_cursor_style(cx);
6219 self.handle_inspector_mouse_event(event, cx);
6220 return;
6222 }
6223
6224 let mut mouse_listeners = mem::take(&mut self.rendered_frame.mouse_listeners);
6225
6226 for listener in &mut mouse_listeners {
6229 let listener = listener.as_mut().unwrap();
6230 listener(event, DispatchPhase::Capture, self, cx);
6231 if !cx.propagate_event {
6232 break;
6233 }
6234 }
6235
6236 if cx.propagate_event {
6238 for listener in mouse_listeners.iter_mut().rev() {
6239 let listener = listener.as_mut().unwrap();
6240 listener(event, DispatchPhase::Bubble, self, cx);
6241 if !cx.propagate_event {
6242 break;
6243 }
6244 }
6245 }
6246
6247 self.rendered_frame.mouse_listeners = mouse_listeners;
6248
6249 if cx.has_active_drag() {
6250 if event.is::<MouseMoveEvent>() {
6251 self.refresh();
6254 } else if event.is::<MouseUpEvent>() {
6255 cx.active_drag = None;
6258 self.refresh();
6259 }
6260 }
6261
6262 if event.is::<MouseUpEvent>() && self.captured_hitbox.is_some() {
6264 self.captured_hitbox = None;
6265 }
6266
6267 self.reset_cursor_style(cx);
6270 }
6271
6272 fn input_preference(&mut self, event: &dyn Any, cx: &mut App) -> InputPreference {
6275 let prefer_character_input = event
6276 .downcast_ref::<KeyDownEvent>()
6277 .is_some_and(|key_down_event| key_down_event.prefer_character_input);
6278 if !prefer_character_input {
6279 return InputPreference::KeyBindings;
6280 }
6281 let Some(mut input_handler) = self.platform_window.take_input_handler() else {
6282 return InputPreference::KeyBindings;
6283 };
6284 let accepts_text_input = input_handler.accepts_text_input(self, cx);
6285 self.platform_window.set_input_handler(input_handler);
6286 if accepts_text_input {
6287 InputPreference::CharacterInput
6288 } else {
6289 InputPreference::KeyBindings
6290 }
6291 }
6292
6293 fn dispatch_key_event(&mut self, event: &dyn Any, cx: &mut App) {
6294 if self.invalidator.is_dirty() {
6295 self.draw(cx).clear(cx);
6296 }
6297
6298 let node_id = self.focus_node_id_in_rendered_frame(self.focus);
6299 let dispatch_path = self.rendered_frame.dispatch_tree.dispatch_path(node_id);
6300
6301 let mut keystroke: Option<Keystroke> = None;
6302
6303 if let Some(event) = event.downcast_ref::<ModifiersChangedEvent>() {
6304 if event.modifiers.number_of_modifiers() == 0
6305 && self.pending_modifier.modifiers.number_of_modifiers() == 1
6306 && !self.pending_modifier.saw_other_input
6307 {
6308 let key = match self.pending_modifier.modifiers {
6309 modifiers if modifiers.shift => Some("shift"),
6310 modifiers if modifiers.control => Some("control"),
6311 modifiers if modifiers.alt => Some("alt"),
6312 modifiers if modifiers.platform => Some("platform"),
6313 modifiers if modifiers.function => Some("function"),
6314 _ => None,
6315 };
6316 if let Some(key) = key {
6317 keystroke = Some(Keystroke {
6318 key: key.to_string(),
6319 key_char: None,
6320 modifiers: Modifiers::default(),
6321 });
6322 }
6323 }
6324
6325 if self.pending_modifier.modifiers.number_of_modifiers() == 0
6326 && event.modifiers.number_of_modifiers() == 1
6327 {
6328 self.pending_modifier.saw_other_input = false
6329 } else if event.modifiers.number_of_modifiers() > 1 {
6330 self.pending_modifier.saw_other_input = true
6331 }
6332 self.pending_modifier.modifiers = event.modifiers
6333 } else if let Some(key_down_event) = event.downcast_ref::<KeyDownEvent>() {
6334 self.pending_modifier.saw_other_input = true;
6335 keystroke = Some(key_down_event.keystroke.clone());
6336 if key_down_event.keystroke.key_char.is_some()
6337 && matches!(
6338 cx.cursor_hide_mode,
6339 CursorHideMode::OnTyping | CursorHideMode::OnTypingAndAction
6340 )
6341 {
6342 cx.platform.hide_cursor_until_mouse_moves();
6343 }
6344 }
6345
6346 let Some(keystroke) = keystroke else {
6347 self.finish_dispatch_key_event(
6348 event,
6349 None,
6350 InputPreference::KeyBindings,
6351 dispatch_path,
6352 self.context_stack(),
6353 cx,
6354 );
6355 return;
6356 };
6357
6358 let input_preference = self.input_preference(event, cx);
6359
6360 cx.propagate_event = true;
6361 self.dispatch_keystroke_interceptors(
6362 &keystroke,
6363 input_preference,
6364 self.context_stack(),
6365 cx,
6366 );
6367 if !cx.propagate_event {
6368 self.finish_dispatch_key_event(
6369 event,
6370 Some(&keystroke),
6371 input_preference,
6372 dispatch_path,
6373 self.context_stack(),
6374 cx,
6375 );
6376 return;
6377 }
6378
6379 let mut currently_pending = self.pending_input.take().unwrap_or_default();
6380 if currently_pending.focus.is_some() && currently_pending.focus != self.focus {
6381 currently_pending = PendingInput::default();
6382 }
6383
6384 let match_result = self.rendered_frame.dispatch_tree.dispatch_key(
6385 currently_pending.keystrokes,
6386 keystroke.clone(),
6387 &dispatch_path,
6388 );
6389
6390 if !match_result.to_replay.is_empty() {
6391 self.replay_pending_input(match_result.to_replay, cx);
6392 cx.propagate_event = true;
6393 }
6394
6395 if !match_result.pending.is_empty() {
6396 let previous_timeout = currently_pending.timeout.take();
6397 currently_pending.keystrokes = match_result.pending;
6398 currently_pending.focus = self.focus;
6399
6400 let text_input_requires_timeout = event
6401 .downcast_ref::<KeyDownEvent>()
6402 .filter(|key_down| key_down.keystroke.key_char.is_some())
6403 .and_then(|_| self.platform_window.take_input_handler())
6404 .map_or(false, |mut input_handler| {
6405 let accepts = input_handler.accepts_text_input(self, cx);
6406 self.platform_window.set_input_handler(input_handler);
6407 accepts
6408 });
6409
6410 let standalone_modifier = matches!(currently_pending.keystrokes.as_slice(), [keystroke] if keystroke.is_modifier_key());
6411 let needs_timeout = previous_timeout.is_some()
6412 || match_result.pending_has_binding
6413 || text_input_requires_timeout
6414 || standalone_modifier;
6415 currently_pending.timeout = if needs_timeout {
6416 match previous_timeout {
6417 Some(mut timeout) if timeout.is_paused() => {
6418 timeout.reset_duration(PENDING_INPUT_TIMEOUT);
6419 Some(timeout)
6420 }
6421 previous_timeout => {
6422 drop(previous_timeout);
6423 Some(self.new_pending_input_timeout(PENDING_INPUT_TIMEOUT, cx))
6424 }
6425 }
6426 } else {
6427 None
6428 };
6429 self.pending_input = Some(currently_pending);
6430 self.pending_input_changed(cx);
6431 cx.propagate_event = false;
6432 return;
6433 }
6434
6435 let input_preference = self.input_preference(event, cx);
6437 if input_preference == InputPreference::KeyBindings {
6438 for binding in match_result.bindings {
6439 self.dispatch_action_on_node(node_id, binding.action.as_ref(), cx);
6440 if !cx.propagate_event {
6441 self.dispatch_keystroke_observers(
6442 &keystroke,
6443 input_preference,
6444 Some(binding.action.as_ref()),
6445 match_result.context_stack,
6446 cx,
6447 );
6448 self.pending_input_changed(cx);
6449 return;
6450 }
6451 }
6452 }
6453
6454 self.finish_dispatch_key_event(
6455 event,
6456 Some(&keystroke),
6457 input_preference,
6458 dispatch_path,
6459 match_result.context_stack,
6460 cx,
6461 );
6462 self.pending_input_changed(cx);
6463 }
6464
6465 fn new_pending_input_timeout(&self, duration: Duration, cx: &App) -> PendingInputTimeout {
6466 let (started_at, task) = self.start_pending_input_timeout(duration, cx);
6467 PendingInputTimeout {
6468 duration,
6469 remaining: duration,
6470 state: PendingInputTimeoutState::Running { started_at, task },
6471 }
6472 }
6473
6474 fn start_pending_input_timeout(&self, remaining: Duration, cx: &App) -> (Instant, Task<()>) {
6475 let started_at = cx.background_executor().now();
6476 let task = self.spawn(cx, async move |cx| {
6477 cx.background_executor.timer(remaining).await;
6478 cx.update(move |window, cx| {
6479 let Some(currently_pending) = window
6480 .pending_input
6481 .take()
6482 .filter(|pending| pending.focus == window.focus)
6483 else {
6484 return;
6485 };
6486
6487 let node_id = window.focus_node_id_in_rendered_frame(window.focus);
6488 let dispatch_path = window.rendered_frame.dispatch_tree.dispatch_path(node_id);
6489
6490 let to_replay = window
6491 .rendered_frame
6492 .dispatch_tree
6493 .flush_dispatch(currently_pending.keystrokes, &dispatch_path);
6494
6495 window.pending_input_changed(cx);
6496 window.replay_pending_input(to_replay, cx)
6497 })
6498 .with_context(|| {
6499 format!(
6500 "handling pending input timeout for window {:?}",
6501 cx.window_handle().window_id()
6502 )
6503 })
6504 .log_err();
6505 });
6506 (started_at, task)
6507 }
6508
6509 fn finish_dispatch_key_event(
6510 &mut self,
6511 event: &dyn Any,
6512 recognized_keystroke: Option<&Keystroke>,
6513 input_preference: InputPreference,
6514 dispatch_path: SmallVec<[DispatchNodeId; 32]>,
6515 context_stack: Vec<KeyContext>,
6516 cx: &mut App,
6517 ) {
6518 self.dispatch_key_down_up_event(event, &dispatch_path, cx);
6519 if !cx.propagate_event {
6520 return;
6521 }
6522
6523 self.dispatch_modifiers_changed_event(event, &dispatch_path, cx);
6524 if !cx.propagate_event {
6525 return;
6526 }
6527
6528 if let Some(keystroke) = recognized_keystroke {
6529 self.dispatch_keystroke_observers(keystroke, input_preference, None, context_stack, cx);
6530 }
6531 }
6532
6533 pub(crate) fn pending_input_changed(&mut self, cx: &mut App) {
6534 self.pending_input_observers
6535 .clone()
6536 .retain(|callback| callback(self, cx));
6537 }
6538
6539 fn defer_pending_input_changed(&self, cx: &mut App) {
6540 let window_handle = self.handle;
6543 cx.defer(move |cx| {
6544 window_handle
6545 .update(cx, |_, window, cx| {
6546 window.pending_input_changed(cx);
6547 })
6548 .ok();
6549 });
6550 }
6551
6552 fn dispatch_key_down_up_event(
6553 &mut self,
6554 event: &dyn Any,
6555 dispatch_path: &SmallVec<[DispatchNodeId; 32]>,
6556 cx: &mut App,
6557 ) {
6558 for node_id in dispatch_path {
6560 let node = self.rendered_frame.dispatch_tree.node(*node_id);
6561
6562 for key_listener in node.key_listeners.clone() {
6563 key_listener(event, DispatchPhase::Capture, self, cx);
6564 if !cx.propagate_event {
6565 return;
6566 }
6567 }
6568 }
6569
6570 for node_id in dispatch_path.iter().rev() {
6572 let node = self.rendered_frame.dispatch_tree.node(*node_id);
6574 for key_listener in node.key_listeners.clone() {
6575 key_listener(event, DispatchPhase::Bubble, self, cx);
6576 if !cx.propagate_event {
6577 return;
6578 }
6579 }
6580 }
6581 }
6582
6583 fn dispatch_modifiers_changed_event(
6584 &mut self,
6585 event: &dyn Any,
6586 dispatch_path: &SmallVec<[DispatchNodeId; 32]>,
6587 cx: &mut App,
6588 ) {
6589 let Some(event) = event.downcast_ref::<ModifiersChangedEvent>() else {
6590 return;
6591 };
6592 for node_id in dispatch_path.iter().rev() {
6593 let node = self.rendered_frame.dispatch_tree.node(*node_id);
6594 for listener in node.modifiers_changed_listeners.clone() {
6595 listener(event, self, cx);
6596 if !cx.propagate_event {
6597 return;
6598 }
6599 }
6600 }
6601 }
6602
6603 pub fn has_pending_keystrokes(&self) -> bool {
6605 self.pending_input().is_some()
6606 }
6607
6608 #[cfg(test)]
6609 pub(crate) fn pending_input_is_none(&self) -> bool {
6610 self.pending_input.is_none()
6611 }
6612
6613 pub(crate) fn clear_pending_keystrokes(&mut self, cx: &mut App) {
6614 if self.pending_input.take().is_some() {
6615 self.defer_pending_input_changed(cx);
6616 }
6617 }
6618
6619 pub fn pending_input(&self) -> Option<PendingInputStatus<'_>> {
6622 self.pending_input
6623 .as_ref()
6624 .filter(|pending_input| pending_input.focus == self.focus)
6625 .map(|pending_input| PendingInputStatus {
6626 keystrokes: pending_input.keystrokes.as_slice(),
6627 timeout: pending_input
6628 .timeout
6629 .as_ref()
6630 .map(PendingInputTimeout::status),
6631 })
6632 }
6633
6634 pub fn set_pending_input_timeout_paused<T: 'static>(
6639 &mut self,
6640 owner: &Entity<T>,
6641 paused: bool,
6642 cx: &mut App,
6643 ) -> bool {
6644 let owner_id = owner.entity_id();
6645 if !paused {
6646 return self.resume_pending_input_timeout(owner_id, cx);
6647 }
6648
6649 let timeout = self
6650 .pending_input
6651 .as_ref()
6652 .filter(|pending_input| pending_input.focus == self.focus)
6653 .and_then(|pending_input| pending_input.timeout.as_ref());
6654 let Some(timeout) = timeout else {
6655 return false;
6656 };
6657 if timeout.is_paused() {
6658 return false;
6659 }
6660
6661 let release_subscription = self.observe_release(owner, cx, move |_, window, cx| {
6662 window.resume_pending_input_timeout(owner_id, cx);
6663 });
6664 let now = cx.background_executor().now();
6665 let changed = self
6666 .pending_input
6667 .as_mut()
6668 .filter(|pending_input| pending_input.focus == self.focus)
6669 .and_then(|pending_input| pending_input.timeout.as_mut())
6670 .is_some_and(|timeout| {
6671 timeout.pause(
6672 PendingInputTimeoutPause {
6673 owner_id,
6674 _release_subscription: release_subscription,
6675 },
6676 now,
6677 )
6678 });
6679
6680 if changed {
6681 self.defer_pending_input_changed(cx);
6682 }
6683 changed
6684 }
6685
6686 fn resume_pending_input_timeout(&mut self, owner_id: EntityId, cx: &mut App) -> bool {
6687 let Some(remaining) = self
6688 .pending_input
6689 .as_ref()
6690 .and_then(|pending_input| pending_input.timeout.as_ref())
6691 .filter(|timeout| timeout.pause_owner_id() == Some(owner_id))
6692 .map(|timeout| timeout.remaining)
6693 else {
6694 return false;
6695 };
6696
6697 let (started_at, task) = self.start_pending_input_timeout(remaining, cx);
6698 let changed = self
6699 .pending_input
6700 .as_mut()
6701 .and_then(|pending_input| pending_input.timeout.as_mut())
6702 .is_some_and(|timeout| timeout.resume(owner_id, started_at, task));
6703
6704 if changed {
6705 self.defer_pending_input_changed(cx);
6706 }
6707 changed
6708 }
6709
6710 pub fn pending_input_keystrokes(&self) -> Option<&[Keystroke]> {
6712 self.pending_input()
6713 .map(|pending_input| pending_input.keystrokes())
6714 }
6715
6716 fn replay_pending_input(&mut self, replays: SmallVec<[Replay; 1]>, cx: &mut App) {
6717 let node_id = self.focus_node_id_in_rendered_frame(self.focus);
6718 let dispatch_path = self.rendered_frame.dispatch_tree.dispatch_path(node_id);
6719
6720 'replay: for replay in replays {
6721 let event = KeyDownEvent {
6722 keystroke: replay.keystroke.clone(),
6723 is_held: false,
6724 prefer_character_input: true,
6725 };
6726
6727 cx.propagate_event = true;
6728 for binding in replay.bindings {
6729 self.dispatch_action_on_node(node_id, binding.action.as_ref(), cx);
6730 if !cx.propagate_event {
6731 self.dispatch_keystroke_observers(
6732 &replay.keystroke,
6733 InputPreference::KeyBindings,
6734 Some(binding.action.as_ref()),
6735 Vec::default(),
6736 cx,
6737 );
6738 continue 'replay;
6739 }
6740 }
6741
6742 self.dispatch_key_down_up_event(&event, &dispatch_path, cx);
6743 if !cx.propagate_event {
6744 continue 'replay;
6745 }
6746 if let Some(input) = replay.keystroke.key_char.as_ref().cloned()
6747 && let Some(mut input_handler) = self.platform_window.take_input_handler()
6748 {
6749 input_handler.dispatch_input(&input, self, cx);
6750 self.platform_window.set_input_handler(input_handler)
6751 }
6752 }
6753 }
6754
6755 fn focus_node_id_in_rendered_frame(&self, focus_id: Option<FocusId>) -> DispatchNodeId {
6756 focus_id
6757 .and_then(|focus_id| {
6758 self.rendered_frame
6759 .dispatch_tree
6760 .focusable_node_id(focus_id)
6761 })
6762 .unwrap_or_else(|| self.rendered_frame.dispatch_tree.root_node_id())
6763 }
6764
6765 fn dispatch_action_on_node(
6766 &mut self,
6767 node_id: DispatchNodeId,
6768 action: &dyn Action,
6769 cx: &mut App,
6770 ) {
6771 self.dispatch_action_on_node_inner(node_id, action, cx);
6772
6773 if !cx.propagate_event
6774 && cx.cursor_hide_mode == CursorHideMode::OnTypingAndAction
6775 && self.last_input_was_keyboard()
6776 {
6777 cx.platform.hide_cursor_until_mouse_moves();
6778 }
6779 }
6780
6781 fn dispatch_action_on_node_inner(
6782 &mut self,
6783 node_id: DispatchNodeId,
6784 action: &dyn Action,
6785 cx: &mut App,
6786 ) {
6787 let dispatch_path = self.rendered_frame.dispatch_tree.dispatch_path(node_id);
6788
6789 cx.propagate_event = true;
6791 if let Some(mut global_listeners) = cx
6792 .global_action_listeners
6793 .remove(&action.as_any().type_id())
6794 {
6795 for listener in &global_listeners {
6796 #[cfg(feature = "profiler")]
6797 self.window_profiler.begin_action_handler(action, cx);
6798 listener(action.as_any(), DispatchPhase::Capture, cx);
6799 #[cfg(feature = "profiler")]
6800 self.window_profiler.end_action_handler();
6801 if !cx.propagate_event {
6802 break;
6803 }
6804 }
6805
6806 global_listeners.extend(
6807 cx.global_action_listeners
6808 .remove(&action.as_any().type_id())
6809 .unwrap_or_default(),
6810 );
6811
6812 cx.global_action_listeners
6813 .insert(action.as_any().type_id(), global_listeners);
6814 }
6815
6816 if !cx.propagate_event {
6817 return;
6818 }
6819
6820 for node_id in &dispatch_path {
6822 let node = self.rendered_frame.dispatch_tree.node(*node_id);
6823 for DispatchActionListener {
6824 action_type,
6825 listener,
6826 } in node.action_listeners.clone()
6827 {
6828 let any_action = action.as_any();
6829 if action_type == any_action.type_id() {
6830 #[cfg(feature = "profiler")]
6831 self.window_profiler.begin_action_handler(action, cx);
6832 listener(any_action, DispatchPhase::Capture, self, cx);
6833 #[cfg(feature = "profiler")]
6834 self.window_profiler.end_action_handler();
6835
6836 if !cx.propagate_event {
6837 return;
6838 }
6839 }
6840 }
6841 }
6842
6843 for node_id in dispatch_path.iter().rev() {
6845 let node = self.rendered_frame.dispatch_tree.node(*node_id);
6846 for DispatchActionListener {
6847 action_type,
6848 listener,
6849 } in node.action_listeners.clone()
6850 {
6851 let any_action = action.as_any();
6852 if action_type == any_action.type_id() {
6853 cx.propagate_event = false; #[cfg(feature = "profiler")]
6855 self.window_profiler.begin_action_handler(action, cx);
6856 listener(any_action, DispatchPhase::Bubble, self, cx);
6857 #[cfg(feature = "profiler")]
6858 self.window_profiler.end_action_handler();
6859
6860 if !cx.propagate_event {
6861 return;
6862 }
6863 }
6864 }
6865 }
6866
6867 if let Some(mut global_listeners) = cx
6869 .global_action_listeners
6870 .remove(&action.as_any().type_id())
6871 {
6872 for listener in global_listeners.iter().rev() {
6873 cx.propagate_event = false; #[cfg(feature = "profiler")]
6876 self.window_profiler.begin_action_handler(action, cx);
6877 listener(action.as_any(), DispatchPhase::Bubble, cx);
6878 #[cfg(feature = "profiler")]
6879 self.window_profiler.end_action_handler();
6880 if !cx.propagate_event {
6881 break;
6882 }
6883 }
6884
6885 global_listeners.extend(
6886 cx.global_action_listeners
6887 .remove(&action.as_any().type_id())
6888 .unwrap_or_default(),
6889 );
6890
6891 cx.global_action_listeners
6892 .insert(action.as_any().type_id(), global_listeners);
6893 }
6894 }
6895
6896 pub fn observe_global<G: Global>(
6899 &mut self,
6900 cx: &mut App,
6901 f: impl Fn(&mut Window, &mut App) + 'static,
6902 ) -> Subscription {
6903 let window_handle = self.handle;
6904 let (subscription, activate) = cx.global_observers.insert_pending(
6905 TypeId::of::<G>(),
6906 Box::new(move |cx| {
6907 window_handle
6908 .update(cx, |_, window, cx| f(window, cx))
6909 .is_ok()
6910 }),
6911 );
6912 cx.defer(move |_| activate());
6913 subscription
6914 }
6915
6916 pub fn activate_window(&self) {
6918 self.platform_window.activate();
6919 }
6920
6921 pub fn request_attention(&self) {
6923 self.platform_window.request_attention();
6924 }
6925
6926 pub fn minimize_window(&self) {
6928 self.platform_window.minimize();
6929 }
6930
6931 pub fn toggle_fullscreen(&self) {
6933 self.platform_window.toggle_fullscreen();
6934 }
6935
6936 pub fn toggle_simple_fullscreen(&self) {
6941 self.platform_window.toggle_simple_fullscreen();
6942 }
6943
6944 pub fn invalidate_character_coordinates(&self) {
6946 self.on_next_frame(|window, cx| {
6947 if let Some(mut input_handler) = window.platform_window.take_input_handler() {
6948 if let Some(bounds) = input_handler.selected_bounds(window, cx) {
6949 window.platform_window.update_ime_position(bounds);
6950 }
6951 window.platform_window.set_input_handler(input_handler);
6952 }
6953 });
6954 }
6955
6956 pub fn prompt<T>(
6960 &mut self,
6961 level: PromptLevel,
6962 message: &str,
6963 detail: Option<&str>,
6964 answers: &[T],
6965 cx: &mut App,
6966 ) -> oneshot::Receiver<usize>
6967 where
6968 T: Clone + Into<PromptButton>,
6969 {
6970 let prompt_builder = cx.prompt_builder.take();
6971 let Some(prompt_builder) = prompt_builder else {
6972 unreachable!("Re-entrant window prompting is not supported by GPUI");
6973 };
6974
6975 let answers = answers
6976 .iter()
6977 .map(|answer| answer.clone().into())
6978 .collect::<Vec<_>>();
6979
6980 let receiver = match &prompt_builder {
6981 PromptBuilder::Default => self
6982 .platform_window
6983 .prompt(level, message, detail, &answers)
6984 .unwrap_or_else(|| {
6985 self.build_custom_prompt(&prompt_builder, level, message, detail, &answers, cx)
6986 }),
6987 PromptBuilder::Custom(_) => {
6988 self.build_custom_prompt(&prompt_builder, level, message, detail, &answers, cx)
6989 }
6990 };
6991
6992 cx.prompt_builder = Some(prompt_builder);
6993
6994 receiver
6995 }
6996
6997 fn build_custom_prompt(
6998 &mut self,
6999 prompt_builder: &PromptBuilder,
7000 level: PromptLevel,
7001 message: &str,
7002 detail: Option<&str>,
7003 answers: &[PromptButton],
7004 cx: &mut App,
7005 ) -> oneshot::Receiver<usize> {
7006 let (sender, receiver) = oneshot::channel();
7007 let handle = PromptHandle::new(sender);
7008 let handle = (prompt_builder)(level, message, detail, answers, handle, self, cx);
7009 self.prompt = Some(handle);
7010 receiver
7011 }
7012
7013 pub fn has_active_prompt(&self) -> bool {
7018 self.prompt.is_some()
7019 }
7020
7021 pub fn context_stack(&self) -> Vec<KeyContext> {
7023 let node_id = self.focus_node_id_in_rendered_frame(self.focus);
7024 let dispatch_tree = &self.rendered_frame.dispatch_tree;
7025 dispatch_tree
7026 .dispatch_path(node_id)
7027 .iter()
7028 .filter_map(move |&node_id| dispatch_tree.node(node_id).context.clone())
7029 .collect()
7030 }
7031
7032 pub fn available_actions(&self, cx: &App) -> Vec<Box<dyn Action>> {
7034 let node_id = self.focus_node_id_in_rendered_frame(self.focus);
7035 let mut actions = self.rendered_frame.dispatch_tree.available_actions(node_id);
7036 for action_type in cx.global_action_listeners.keys() {
7037 if let Err(ix) = actions.binary_search_by_key(action_type, |a| a.as_any().type_id()) {
7038 let action = cx.actions.build_action_type(action_type).ok();
7039 if let Some(action) = action {
7040 actions.insert(ix, action);
7041 }
7042 }
7043 }
7044 actions
7045 }
7046
7047 pub fn bindings_for_action(&self, action: &dyn Action) -> Vec<KeyBinding> {
7050 self.rendered_frame
7051 .dispatch_tree
7052 .bindings_for_action(action, &self.rendered_frame.dispatch_tree.context_stack)
7053 }
7054
7055 pub fn highest_precedence_binding_for_action(&self, action: &dyn Action) -> Option<KeyBinding> {
7058 self.rendered_frame
7059 .dispatch_tree
7060 .highest_precedence_binding_for_action(
7061 action,
7062 &self.rendered_frame.dispatch_tree.context_stack,
7063 )
7064 }
7065
7066 pub fn bindings_for_action_in_context(
7068 &self,
7069 action: &dyn Action,
7070 context: KeyContext,
7071 ) -> Vec<KeyBinding> {
7072 let dispatch_tree = &self.rendered_frame.dispatch_tree;
7073 dispatch_tree.bindings_for_action(action, &[context])
7074 }
7075
7076 pub fn highest_precedence_binding_for_action_in_context(
7079 &self,
7080 action: &dyn Action,
7081 context: KeyContext,
7082 ) -> Option<KeyBinding> {
7083 let dispatch_tree = &self.rendered_frame.dispatch_tree;
7084 dispatch_tree.highest_precedence_binding_for_action(action, &[context])
7085 }
7086
7087 pub fn bindings_for_action_in(
7091 &self,
7092 action: &dyn Action,
7093 focus_handle: &FocusHandle,
7094 ) -> Vec<KeyBinding> {
7095 let dispatch_tree = &self.rendered_frame.dispatch_tree;
7096 let Some(context_stack) = self.context_stack_for_focus_handle(focus_handle) else {
7097 return vec![];
7098 };
7099 dispatch_tree.bindings_for_action(action, &context_stack)
7100 }
7101
7102 pub fn highest_precedence_binding_for_action_in(
7106 &self,
7107 action: &dyn Action,
7108 focus_handle: &FocusHandle,
7109 ) -> Option<KeyBinding> {
7110 let dispatch_tree = &self.rendered_frame.dispatch_tree;
7111 let context_stack = self.context_stack_for_focus_handle(focus_handle)?;
7112 dispatch_tree.highest_precedence_binding_for_action(action, &context_stack)
7113 }
7114
7115 pub fn possible_bindings_for_input(&self, input: &[Keystroke]) -> Vec<KeyBinding> {
7117 self.rendered_frame
7118 .dispatch_tree
7119 .possible_next_bindings_for_input(input, &self.context_stack())
7120 }
7121
7122 fn context_stack_for_focus_handle(
7123 &self,
7124 focus_handle: &FocusHandle,
7125 ) -> Option<Vec<KeyContext>> {
7126 let dispatch_tree = &self.rendered_frame.dispatch_tree;
7127 let node_id = dispatch_tree.focusable_node_id(focus_handle.id)?;
7128 let context_stack: Vec<_> = dispatch_tree
7129 .dispatch_path(node_id)
7130 .into_iter()
7131 .filter_map(|node_id| dispatch_tree.node(node_id).context.clone())
7132 .collect();
7133 Some(context_stack)
7134 }
7135
7136 pub fn listener_for<T: 'static, E>(
7138 &self,
7139 view: &Entity<T>,
7140 f: impl Fn(&mut T, &E, &mut Window, &mut Context<T>) + 'static,
7141 ) -> impl Fn(&E, &mut Window, &mut App) + 'static {
7142 let view = view.downgrade();
7143 move |e: &E, window: &mut Window, cx: &mut App| {
7144 view.update(cx, |view, cx| f(view, e, window, cx)).ok();
7145 }
7146 }
7147
7148 pub fn handler_for<E: 'static, Callback: Fn(&mut E, &mut Window, &mut Context<E>) + 'static>(
7150 &self,
7151 entity: &Entity<E>,
7152 f: Callback,
7153 ) -> impl Fn(&mut Window, &mut App) + 'static {
7154 let entity = entity.downgrade();
7155 move |window: &mut Window, cx: &mut App| {
7156 entity.update(cx, |entity, cx| f(entity, window, cx)).ok();
7157 }
7158 }
7159
7160 pub fn on_window_should_close(
7163 &self,
7164 cx: &App,
7165 f: impl Fn(&mut Window, &mut App) -> bool + 'static,
7166 ) {
7167 let mut cx = self.to_async(cx);
7168 self.platform_window.on_should_close(Box::new(move || {
7169 cx.update(|window, cx| f(window, cx)).unwrap_or(true)
7170 }))
7171 }
7172
7173 pub fn on_action(
7182 &mut self,
7183 action_type: TypeId,
7184 listener: impl Fn(&dyn Any, DispatchPhase, &mut Window, &mut App) + 'static,
7185 ) {
7186 self.invalidator.debug_assert_paint();
7187
7188 self.next_frame
7189 .dispatch_tree
7190 .on_action(action_type, Rc::new(listener));
7191 }
7192
7193 pub fn on_action_when(
7202 &mut self,
7203 condition: bool,
7204 action_type: TypeId,
7205 listener: impl Fn(&dyn Any, DispatchPhase, &mut Window, &mut App) + 'static,
7206 ) {
7207 self.invalidator.debug_assert_paint();
7208
7209 if condition {
7210 self.next_frame
7211 .dispatch_tree
7212 .on_action(action_type, Rc::new(listener));
7213 }
7214 }
7215
7216 pub fn gpu_specs(&self) -> Option<GpuSpecs> {
7219 self.platform_window.gpu_specs()
7220 }
7221
7222 pub fn titlebar_double_click(&self) {
7225 self.platform_window
7226 .titlebar_double_click(self.is_resizable, self.is_minimizable);
7227 }
7228
7229 pub fn window_title(&self) -> String {
7232 self.platform_window.get_title()
7233 }
7234
7235 pub fn tabbed_windows(&self) -> Option<Vec<SystemWindowTab>> {
7238 self.platform_window.tabbed_windows()
7239 }
7240
7241 pub fn tab_bar_visible(&self) -> bool {
7244 self.platform_window.tab_bar_visible()
7245 }
7246
7247 pub fn merge_all_windows(&self) {
7250 self.platform_window.merge_all_windows()
7251 }
7252
7253 pub fn move_tab_to_new_window(&self) {
7256 self.platform_window.move_tab_to_new_window()
7257 }
7258
7259 pub fn toggle_window_tab_overview(&self) {
7262 self.platform_window.toggle_window_tab_overview()
7263 }
7264
7265 pub fn set_tabbing_identifier(&self, tabbing_identifier: Option<String>) {
7268 self.platform_window
7269 .set_tabbing_identifier(tabbing_identifier)
7270 }
7271
7272 pub fn play_system_bell(&self) {
7275 self.platform_window.play_system_bell()
7276 }
7277
7278 pub fn is_a11y_enabled(&self) -> bool {
7285 self.a11y.is_enabled()
7286 }
7287
7288 pub fn is_a11y_active(&self) -> bool {
7299 self.a11y.is_active()
7300 }
7301
7302 pub fn debug_a11y_tree_json(&self) -> Option<String> {
7304 self.a11y.debug_tree_json()
7305 }
7306
7307 pub fn on_a11y_action(
7313 &mut self,
7314 node_id: accesskit::NodeId,
7315 action: accesskit::Action,
7316 listener: impl FnMut(Option<&accesskit::ActionData>, &mut Window, &mut App) + 'static,
7317 ) {
7318 self.a11y
7319 .action_listeners
7320 .entry(node_id)
7321 .or_default()
7322 .push((action, Box::new(listener)));
7323 }
7324
7325 #[cfg(not(target_family = "wasm"))]
7326 pub(crate) fn handle_a11y_action(&mut self, request: accesskit::ActionRequest, cx: &mut App) {
7327 if let Some(mut listeners) = self.a11y.action_listeners.remove(&request.target_node) {
7330 let extra_data = request.data.as_ref();
7331 let mut matched = false;
7332 for (action, listener) in &mut listeners {
7333 if *action == request.action {
7334 listener(extra_data, self, cx);
7335 matched = true;
7336 }
7337 }
7338 self.a11y
7339 .action_listeners
7340 .insert(request.target_node, listeners);
7341 if matched {
7342 return;
7343 }
7344 }
7345
7346 match request.action {
7348 accesskit::Action::Click => {
7349 if let Some(bounds) = self.a11y.node_bounds.get(&request.target_node).copied() {
7350 let center = bounds.center();
7351 let mouse_down = PlatformInput::MouseDown(crate::MouseDownEvent {
7352 button: MouseButton::Left,
7353 position: center,
7354 modifiers: Modifiers::default(),
7355 click_count: 1,
7356 first_mouse: false,
7357 });
7358 let mouse_up = PlatformInput::MouseUp(MouseUpEvent {
7359 button: MouseButton::Left,
7360 position: center,
7361 modifiers: Modifiers::default(),
7362 click_count: 1,
7363 });
7364 self.dispatch_event(mouse_down, cx);
7365 self.dispatch_event(mouse_up, cx);
7366 }
7367 }
7368 accesskit::Action::Focus => {
7369 if let Some(focus_id) = self.a11y.focus_ids.get(&request.target_node).copied()
7370 && let Some(handle) = FocusHandle::for_id(focus_id, &cx.focus_handles)
7371 {
7372 self.focus(&handle, cx);
7373 }
7374 }
7375 accesskit::Action::Blur => {
7376 self.blur(cx);
7377 }
7378 _ => {
7379 log::debug!(
7380 "Unhandled a11y action: {:?} on {:?}",
7381 request.action,
7382 request.target_node
7383 );
7384 }
7385 }
7386 }
7387
7388 #[cfg(any(feature = "inspector", debug_assertions))]
7390 pub fn toggle_inspector(&mut self, cx: &mut App) {
7391 self.inspector = match self.inspector {
7392 None => Some(cx.new(|_| Inspector::new())),
7393 Some(_) => {
7394 self.rendered_frame.next_inspector_instance_ids = FxHashMap::default();
7395 self.rendered_frame.inspector_hitboxes = FxHashMap::default();
7396 self.next_frame.next_inspector_instance_ids = FxHashMap::default();
7397 self.next_frame.inspector_hitboxes = FxHashMap::default();
7398 None
7399 }
7400 };
7401 self.refresh();
7402 }
7403
7404 pub fn is_inspector_picking(&self, _cx: &App) -> bool {
7406 #[cfg(any(feature = "inspector", debug_assertions))]
7407 {
7408 if let Some(inspector) = &self.inspector {
7409 return inspector.read(_cx).is_picking();
7410 }
7411 }
7412 false
7413 }
7414
7415 #[cfg(any(feature = "inspector", debug_assertions))]
7417 pub fn with_inspector_state<T: 'static, R>(
7418 &mut self,
7419 inspector_id: Option<&crate::InspectorElementId>,
7420 cx: &mut App,
7421 f: impl FnOnce(&mut Option<T>, &mut Self) -> R,
7422 ) -> Option<R> {
7423 let inspector_id = inspector_id?;
7424 let inspector = self.inspector.as_ref()?;
7425 if inspector.read(cx).active_element_id() != Some(inspector_id) {
7426 return None;
7427 }
7428 let inspector = inspector.clone();
7429 Some(inspector.update(cx, |inspector, _cx| {
7430 inspector.with_active_element_state(self, f)
7431 }))
7432 }
7433
7434 #[cfg(any(feature = "inspector", debug_assertions))]
7435 pub(crate) fn inspector_enabled(&self) -> bool {
7436 self.inspector.is_some()
7437 }
7438
7439 #[cfg(any(feature = "inspector", debug_assertions))]
7440 pub(crate) fn build_inspector_element_id(
7441 &mut self,
7442 path: crate::InspectorElementPath,
7443 ) -> crate::InspectorElementId {
7444 self.invalidator.debug_assert_paint_or_prepaint();
7445 let path = Rc::new(path);
7446 let next_instance_id = self
7447 .next_frame
7448 .next_inspector_instance_ids
7449 .entry(path.clone())
7450 .or_insert(0);
7451 let instance_id = *next_instance_id;
7452 *next_instance_id += 1;
7453 crate::InspectorElementId { path, instance_id }
7454 }
7455
7456 #[cfg(any(feature = "inspector", debug_assertions))]
7457 fn prepaint_inspector(&mut self, inspector_width: Pixels, cx: &mut App) -> Option<AnyElement> {
7458 if let Some(inspector) = self.inspector.take() {
7459 let mut inspector_element = AnyView::from(inspector.clone()).into_any_element();
7460 inspector_element.prepaint_as_root(
7461 point(self.viewport_size.width - inspector_width, px(0.0)),
7462 size(inspector_width, self.viewport_size.height).into(),
7463 self,
7464 cx,
7465 );
7466 self.inspector = Some(inspector);
7467 Some(inspector_element)
7468 } else {
7469 None
7470 }
7471 }
7472
7473 #[cfg(any(feature = "inspector", debug_assertions))]
7474 fn paint_inspector(&mut self, inspector_element: Option<AnyElement>, cx: &mut App) {
7475 if let Some(mut inspector_element) = inspector_element {
7476 inspector_element.paint(self, cx);
7477 };
7478 }
7479
7480 #[cfg(any(feature = "inspector", debug_assertions))]
7483 pub fn insert_inspector_hitbox(
7484 &mut self,
7485 hitbox_id: HitboxId,
7486 inspector_id: Option<&crate::InspectorElementId>,
7487 cx: &App,
7488 ) {
7489 self.invalidator.debug_assert_paint_or_prepaint();
7490 if !self.is_inspector_picking(cx) {
7491 return;
7492 }
7493 if let Some(inspector_id) = inspector_id {
7494 self.next_frame
7495 .inspector_hitboxes
7496 .insert(hitbox_id, inspector_id.clone());
7497 }
7498 }
7499
7500 #[cfg(any(feature = "inspector", debug_assertions))]
7501 fn paint_inspector_hitbox(&mut self, cx: &App) {
7502 if let Some(inspector) = self.inspector.as_ref() {
7503 let inspector = inspector.read(cx);
7504 if let Some((hitbox_id, _)) = self.hovered_inspector_hitbox(inspector, &self.next_frame)
7505 && let Some(hitbox) = self
7506 .next_frame
7507 .hitboxes
7508 .iter()
7509 .find(|hitbox| hitbox.id == hitbox_id)
7510 {
7511 self.paint_quad(crate::fill(hitbox.bounds, crate::rgba(0x61afef4d)));
7512 }
7513 }
7514 }
7515
7516 #[cfg(any(feature = "inspector", debug_assertions))]
7517 fn handle_inspector_mouse_event(&mut self, event: &dyn Any, cx: &mut App) {
7518 let Some(inspector) = self.inspector.clone() else {
7519 return;
7520 };
7521 if event.downcast_ref::<MouseMoveEvent>().is_some() {
7522 inspector.update(cx, |inspector, _cx| {
7523 if let Some((_, inspector_id)) =
7524 self.hovered_inspector_hitbox(inspector, &self.rendered_frame)
7525 {
7526 inspector.hover(inspector_id, self);
7527 }
7528 });
7529 } else if event.downcast_ref::<crate::MouseDownEvent>().is_some() {
7530 inspector.update(cx, |inspector, _cx| {
7531 if let Some((_, inspector_id)) =
7532 self.hovered_inspector_hitbox(inspector, &self.rendered_frame)
7533 {
7534 inspector.select(inspector_id, self);
7535 }
7536 });
7537 } else if let Some(event) = event.downcast_ref::<crate::ScrollEvent>() {
7538 const SCROLL_LINES: f32 = 3.0;
7540 const SCROLL_PIXELS_PER_LAYER: f32 = 36.0;
7541 let delta_y = event
7542 .delta
7543 .pixel_delta(px(SCROLL_PIXELS_PER_LAYER / SCROLL_LINES))
7544 .y;
7545 if let Some(inspector) = self.inspector.clone() {
7546 inspector.update(cx, |inspector, _cx| {
7547 if let Some(depth) = inspector.pick_depth.as_mut() {
7548 *depth += f32::from(delta_y) / SCROLL_PIXELS_PER_LAYER;
7549 let max_depth = self.mouse_hit_test.ids.len() as f32 - 0.5;
7550 if *depth < 0.0 {
7551 *depth = 0.0;
7552 } else if *depth > max_depth {
7553 *depth = max_depth;
7554 }
7555 if let Some((_, inspector_id)) =
7556 self.hovered_inspector_hitbox(inspector, &self.rendered_frame)
7557 {
7558 inspector.set_active_element_id(inspector_id, self);
7559 }
7560 }
7561 });
7562 }
7563 }
7564 }
7565
7566 #[cfg(any(feature = "inspector", debug_assertions))]
7567 fn hovered_inspector_hitbox(
7568 &self,
7569 inspector: &Inspector,
7570 frame: &Frame,
7571 ) -> Option<(HitboxId, crate::InspectorElementId)> {
7572 if let Some(pick_depth) = inspector.pick_depth {
7573 let depth = (pick_depth as i64).try_into().unwrap_or(0);
7574 let max_skipped = self.mouse_hit_test.ids.len().saturating_sub(1);
7575 let skip_count = (depth as usize).min(max_skipped);
7576 for hitbox_id in self.mouse_hit_test.ids.iter().skip(skip_count) {
7577 if let Some(inspector_id) = frame.inspector_hitboxes.get(hitbox_id) {
7578 return Some((*hitbox_id, inspector_id.clone()));
7579 }
7580 }
7581 }
7582 None
7583 }
7584
7585 #[cfg(any(test, feature = "test-support"))]
7588 pub fn set_modifiers(&mut self, modifiers: Modifiers) {
7589 self.modifiers = modifiers;
7590 }
7591
7592 #[cfg(any(test, feature = "test-support"))]
7596 pub fn simulate_mouse_move(&mut self, position: Point<Pixels>, cx: &mut App) {
7597 let event = PlatformInput::MouseMove(MouseMoveEvent {
7598 position,
7599 modifiers: self.modifiers,
7600 pressed_button: None,
7601 });
7602 let _ = self.dispatch_event(event, cx);
7603 }
7604
7605 #[inline(never)]
7606 fn take_element_state(
7607 &mut self,
7608 global_id: &GlobalElementId,
7609 state_type: TypeId,
7610 ) -> ((GlobalElementId, TypeId), Option<ElementStateBox>) {
7611 let key = (global_id.clone(), state_type);
7612 self.next_frame.accessed_element_states.push(key.clone());
7613 let state = self
7614 .next_frame
7615 .element_states
7616 .remove(&key)
7617 .or_else(|| self.rendered_frame.element_states.remove(&key));
7618 (key, state)
7619 }
7620
7621 #[inline(never)]
7622 fn insert_element_state(
7623 &mut self,
7624 key: (GlobalElementId, TypeId),
7625 state: ElementStateBox,
7626 ) -> Option<ElementStateBox> {
7627 self.next_frame.element_states.insert(key, state)
7628 }
7629
7630 #[inline(never)]
7631 fn observe_keyed_state<S: 'static>(state: &Entity<S>, current_view: EntityId, cx: &mut App) {
7632 cx.observe(state, move |_, cx| {
7633 cx.notify(current_view);
7634 })
7635 .detach();
7636 }
7637}
7638
7639slotmap::new_key_type! {
7641 pub struct WindowId;
7643}
7644
7645impl WindowId {
7646 pub fn as_u64(&self) -> u64 {
7648 self.0.as_ffi()
7649 }
7650}
7651
7652impl From<u64> for WindowId {
7653 fn from(value: u64) -> Self {
7654 WindowId(slotmap::KeyData::from_ffi(value))
7655 }
7656}
7657
7658#[derive(Deref, DerefMut)]
7661pub struct WindowHandle<V> {
7662 #[deref]
7663 #[deref_mut]
7664 pub(crate) any_handle: AnyWindowHandle,
7665 state_type: PhantomData<fn(V) -> V>,
7666}
7667
7668impl<V> Debug for WindowHandle<V> {
7669 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
7670 f.debug_struct("WindowHandle")
7671 .field("any_handle", &self.any_handle.id.as_u64())
7672 .finish()
7673 }
7674}
7675
7676impl<V: 'static + Render> WindowHandle<V> {
7677 pub fn new(id: WindowId) -> Self {
7680 WindowHandle {
7681 any_handle: AnyWindowHandle {
7682 id,
7683 state_type: TypeId::of::<V>(),
7684 root_entity_type_name: std::any::type_name::<V>(),
7685 },
7686 state_type: PhantomData,
7687 }
7688 }
7689
7690 #[cfg(any(test, feature = "test-support"))]
7694 pub fn root<C>(&self, cx: &mut C) -> Result<Entity<V>>
7695 where
7696 C: AppContext,
7697 {
7698 cx.update_window(self.any_handle, |root_view, _, _| {
7699 root_view
7700 .downcast::<V>()
7701 .map_err(|_| anyhow!("the type of the window's root view has changed"))
7702 })?
7703 }
7704
7705 #[track_caller]
7709 pub fn update<C, R>(
7710 &self,
7711 cx: &mut C,
7712 update: impl FnOnce(&mut V, &mut Window, &mut Context<V>) -> R,
7713 ) -> Result<R>
7714 where
7715 C: AppContext,
7716 {
7717 cx.update_window(self.any_handle, |root_view, window, cx| {
7718 let view = root_view
7719 .downcast::<V>()
7720 .map_err(|_| anyhow!("the type of the window's root view has changed"))?;
7721
7722 Ok(view.update(cx, |view, cx| update(view, window, cx)))
7723 })?
7724 }
7725
7726 pub fn read<'a>(&self, cx: &'a App) -> Result<&'a V> {
7730 let x = cx
7731 .windows
7732 .get(self.id)
7733 .and_then(|window| {
7734 window
7735 .as_deref()
7736 .and_then(|window| window.root.clone())
7737 .map(|root_view| root_view.downcast::<V>())
7738 })
7739 .context("window not found")?
7740 .map_err(|_| anyhow!("the type of the window's root view has changed"))?;
7741
7742 Ok(x.read(cx))
7743 }
7744
7745 pub fn read_with<C, R>(&self, cx: &C, read_with: impl FnOnce(&V, &App) -> R) -> Result<R>
7749 where
7750 C: AppContext,
7751 {
7752 cx.read_window(self, |root_view, cx| read_with(root_view.read(cx), cx))
7753 }
7754
7755 pub fn entity<C>(&self, cx: &C) -> Result<Entity<V>>
7759 where
7760 C: AppContext,
7761 {
7762 cx.read_window(self, |root_view, _cx| root_view)
7763 }
7764
7765 pub fn is_active(&self, cx: &mut App) -> Option<bool> {
7770 cx.update_window(self.any_handle, |_, window, _| window.is_window_active())
7771 .ok()
7772 }
7773}
7774
7775impl<V> Copy for WindowHandle<V> {}
7776
7777impl<V> Clone for WindowHandle<V> {
7778 fn clone(&self) -> Self {
7779 *self
7780 }
7781}
7782
7783impl<V> PartialEq for WindowHandle<V> {
7784 fn eq(&self, other: &Self) -> bool {
7785 self.any_handle == other.any_handle
7786 }
7787}
7788
7789impl<V> Eq for WindowHandle<V> {}
7790
7791impl<V> Hash for WindowHandle<V> {
7792 fn hash<H: Hasher>(&self, state: &mut H) {
7793 self.any_handle.hash(state);
7794 }
7795}
7796
7797impl<V: 'static> From<WindowHandle<V>> for AnyWindowHandle {
7798 fn from(val: WindowHandle<V>) -> Self {
7799 val.any_handle
7800 }
7801}
7802
7803#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
7805pub struct AnyWindowHandle {
7806 pub(crate) id: WindowId,
7807 state_type: TypeId,
7808 root_entity_type_name: &'static str,
7809}
7810
7811impl AnyWindowHandle {
7812 pub fn window_id(&self) -> WindowId {
7814 self.id
7815 }
7816
7817 pub fn root_entity_type_name(&self) -> &'static str {
7819 self.root_entity_type_name
7820 }
7821
7822 pub fn downcast<T: 'static>(&self) -> Option<WindowHandle<T>> {
7825 if TypeId::of::<T>() == self.state_type {
7826 Some(WindowHandle {
7827 any_handle: *self,
7828 state_type: PhantomData,
7829 })
7830 } else {
7831 None
7832 }
7833 }
7834
7835 #[track_caller]
7839 pub fn update<C, R>(
7840 self,
7841 cx: &mut C,
7842 update: impl FnOnce(AnyView, &mut Window, &mut App) -> R,
7843 ) -> Result<R>
7844 where
7845 C: AppContext,
7846 {
7847 cx.update_window(self, update)
7848 }
7849
7850 pub fn read<T, C, R>(self, cx: &C, read: impl FnOnce(Entity<T>, &App) -> R) -> Result<R>
7854 where
7855 C: AppContext,
7856 T: 'static,
7857 {
7858 let view = self
7859 .downcast::<T>()
7860 .context("the type of the window's root view has changed")?;
7861
7862 cx.read_window(&view, read)
7863 }
7864}
7865
7866impl HasWindowHandle for Window {
7867 fn window_handle(&self) -> Result<raw_window_handle::WindowHandle<'_>, HandleError> {
7868 self.platform_window.window_handle()
7869 }
7870}
7871
7872impl HasDisplayHandle for Window {
7873 fn display_handle(
7874 &self,
7875 ) -> std::result::Result<raw_window_handle::DisplayHandle<'_>, HandleError> {
7876 self.platform_window.display_handle()
7877 }
7878}
7879
7880#[derive(Clone, Debug, Eq, PartialEq, Hash)]
7885pub enum ElementId {
7886 View(EntityId),
7888 Integer(u64),
7890 Name(SharedString),
7892 Uuid(Uuid),
7894 FocusHandle(FocusId),
7896 NamedInteger(SharedString, u64),
7898 Path(Arc<std::path::Path>),
7900 CodeLocation(core::panic::Location<'static>),
7902 NamedChild(Arc<ElementId>, SharedString),
7904 OpaqueId([u8; 20]),
7906}
7907
7908impl ElementId {
7909 pub fn named_usize(name: impl Into<SharedString>, integer: usize) -> ElementId {
7911 Self::NamedInteger(name.into(), integer as u64)
7912 }
7913}
7914
7915impl Display for ElementId {
7916 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
7917 match self {
7918 ElementId::View(entity_id) => write!(f, "view-{}", entity_id)?,
7919 ElementId::Integer(ix) => write!(f, "{}", ix)?,
7920 ElementId::Name(name) => write!(f, "{}", name)?,
7921 ElementId::FocusHandle(_) => write!(f, "FocusHandle")?,
7922 ElementId::NamedInteger(s, i) => write!(f, "{}-{}", s, i)?,
7923 ElementId::Uuid(uuid) => write!(f, "{}", uuid)?,
7924 ElementId::Path(path) => write!(f, "{}", path.display())?,
7925 ElementId::CodeLocation(location) => write!(f, "{}", location)?,
7926 ElementId::NamedChild(id, name) => write!(f, "{}-{}", id, name)?,
7927 ElementId::OpaqueId(opaque_id) => write!(f, "{:x?}", opaque_id)?,
7928 }
7929
7930 Ok(())
7931 }
7932}
7933
7934impl TryInto<SharedString> for ElementId {
7935 type Error = anyhow::Error;
7936
7937 fn try_into(self) -> anyhow::Result<SharedString> {
7938 if let ElementId::Name(name) = self {
7939 Ok(name)
7940 } else {
7941 anyhow::bail!("element id is not string")
7942 }
7943 }
7944}
7945
7946impl From<u64> for ElementId {
7947 fn from(id: u64) -> Self {
7948 ElementId::Integer(id)
7949 }
7950}
7951
7952impl From<usize> for ElementId {
7953 fn from(id: usize) -> Self {
7954 ElementId::Integer(id as u64)
7955 }
7956}
7957
7958impl From<i32> for ElementId {
7959 fn from(id: i32) -> Self {
7960 Self::Integer(id as u64)
7961 }
7962}
7963
7964impl From<SharedString> for ElementId {
7965 fn from(name: SharedString) -> Self {
7966 ElementId::Name(name)
7967 }
7968}
7969
7970impl From<String> for ElementId {
7971 fn from(name: String) -> Self {
7972 ElementId::Name(name.into())
7973 }
7974}
7975
7976impl From<Arc<str>> for ElementId {
7977 fn from(name: Arc<str>) -> Self {
7978 ElementId::Name(name.into())
7979 }
7980}
7981
7982impl From<Arc<std::path::Path>> for ElementId {
7983 fn from(path: Arc<std::path::Path>) -> Self {
7984 ElementId::Path(path)
7985 }
7986}
7987
7988impl From<&'static str> for ElementId {
7989 fn from(name: &'static str) -> Self {
7990 ElementId::Name(SharedString::new_static(name))
7991 }
7992}
7993
7994impl<'a> From<&'a FocusHandle> for ElementId {
7995 fn from(handle: &'a FocusHandle) -> Self {
7996 ElementId::FocusHandle(handle.id)
7997 }
7998}
7999
8000impl From<(&'static str, EntityId)> for ElementId {
8001 fn from((name, id): (&'static str, EntityId)) -> Self {
8002 ElementId::NamedInteger(SharedString::new_static(name), id.as_u64())
8003 }
8004}
8005
8006impl From<(&'static str, usize)> for ElementId {
8007 fn from((name, id): (&'static str, usize)) -> Self {
8008 ElementId::NamedInteger(SharedString::new_static(name), id as u64)
8009 }
8010}
8011
8012impl From<(SharedString, usize)> for ElementId {
8013 fn from((name, id): (SharedString, usize)) -> Self {
8014 ElementId::NamedInteger(name, id as u64)
8015 }
8016}
8017
8018impl From<(&'static str, u64)> for ElementId {
8019 fn from((name, id): (&'static str, u64)) -> Self {
8020 ElementId::NamedInteger(SharedString::new_static(name), id)
8021 }
8022}
8023
8024impl From<Uuid> for ElementId {
8025 fn from(value: Uuid) -> Self {
8026 Self::Uuid(value)
8027 }
8028}
8029
8030impl From<(&'static str, u32)> for ElementId {
8031 fn from((name, id): (&'static str, u32)) -> Self {
8032 ElementId::NamedInteger(SharedString::new_static(name), u64::from(id))
8033 }
8034}
8035
8036impl<T: Into<SharedString>> From<(ElementId, T)> for ElementId {
8037 fn from((id, name): (ElementId, T)) -> Self {
8038 ElementId::NamedChild(Arc::new(id), name.into())
8039 }
8040}
8041
8042impl From<&'static core::panic::Location<'static>> for ElementId {
8043 fn from(location: &'static core::panic::Location<'static>) -> Self {
8044 ElementId::CodeLocation(*location)
8045 }
8046}
8047
8048impl From<[u8; 20]> for ElementId {
8049 fn from(opaque_id: [u8; 20]) -> Self {
8050 ElementId::OpaqueId(opaque_id)
8051 }
8052}
8053
8054#[derive(Clone)]
8057pub struct PaintQuad {
8058 pub bounds: Bounds<Pixels>,
8060 pub corner_radii: Corners<Pixels>,
8062 pub background: Background,
8064 pub border_widths: Edges<Pixels>,
8066 pub border_color: Hsla,
8068 pub border_style: BorderStyle,
8070}
8071
8072impl PaintQuad {
8073 pub fn corner_radii(self, corner_radii: impl Into<Corners<Pixels>>) -> Self {
8075 PaintQuad {
8076 corner_radii: corner_radii.into(),
8077 ..self
8078 }
8079 }
8080
8081 pub fn border_widths(self, border_widths: impl Into<Edges<Pixels>>) -> Self {
8083 PaintQuad {
8084 border_widths: border_widths.into(),
8085 ..self
8086 }
8087 }
8088
8089 pub fn border_color(self, border_color: impl Into<Hsla>) -> Self {
8091 PaintQuad {
8092 border_color: border_color.into(),
8093 ..self
8094 }
8095 }
8096
8097 pub fn background(self, background: impl Into<Background>) -> Self {
8099 PaintQuad {
8100 background: background.into(),
8101 ..self
8102 }
8103 }
8104}
8105
8106pub fn quad(
8108 bounds: Bounds<Pixels>,
8109 corner_radii: impl Into<Corners<Pixels>>,
8110 background: impl Into<Background>,
8111 border_widths: impl Into<Edges<Pixels>>,
8112 border_color: impl Into<Hsla>,
8113 border_style: BorderStyle,
8114) -> PaintQuad {
8115 PaintQuad {
8116 bounds,
8117 corner_radii: corner_radii.into(),
8118 background: background.into(),
8119 border_widths: border_widths.into(),
8120 border_color: border_color.into(),
8121 border_style,
8122 }
8123}
8124
8125pub fn fill(bounds: impl Into<Bounds<Pixels>>, background: impl Into<Background>) -> PaintQuad {
8127 PaintQuad {
8128 bounds: bounds.into(),
8129 corner_radii: (0.).into(),
8130 background: background.into(),
8131 border_widths: (0.).into(),
8132 border_color: transparent_black(),
8133 border_style: BorderStyle::default(),
8134 }
8135}
8136
8137pub fn outline(
8139 bounds: impl Into<Bounds<Pixels>>,
8140 border_color: impl Into<Hsla>,
8141 border_style: BorderStyle,
8142) -> PaintQuad {
8143 PaintQuad {
8144 bounds: bounds.into(),
8145 corner_radii: (0.).into(),
8146 background: transparent_black().into(),
8147 border_widths: (1.).into(),
8148 border_color: border_color.into(),
8149 border_style,
8150 }
8151}
8152
8153#[inline(never)]
8154fn with_element_arena_erased(callback: &mut dyn FnMut(&mut Arena)) -> Result<(), AccessError> {
8155 if let Some(arena_pointer) = CURRENT_ELEMENT_ARENA.try_with(Cell::get)? {
8156 let arena_cell = unsafe { &*arena_pointer };
8159 callback(&mut arena_cell.borrow_mut());
8160 Ok(())
8161 } else {
8162 ELEMENT_ARENA.try_with(|arena| callback(&mut arena.borrow_mut()))
8163 }
8164}
8165
8166#[cfg(test)]
8167mod tests {
8168 use std::{
8169 cell::{Cell, RefCell},
8170 path::PathBuf,
8171 rc::Rc,
8172 time::Duration,
8173 };
8174
8175 use crate::{
8176 AnyWindowHandle, AppContext as _, BackGestureEvent, BackGesturePhase, Bounds, ContentMask,
8177 Context, CursorStyle, DispatchPhase, DragMoveEvent, Empty, ExternalDragPayload,
8178 ExternalPaths, FileDragPaths, FileDropEvent, FocusHandle, InputEvent as _,
8179 InteractiveElement as _, IntoElement, KeyDownEvent, Keystroke, LongPressEvent, MouseButton,
8180 MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, PlatformInput, Point,
8181 Render, RequestFrameOptions, ScaledPixels, StatefulInteractiveElement as _, Styled,
8182 TestAppContext, ThermalState, TouchDragEvent, TouchEvent, TouchId, TouchPhase, Underline,
8183 UnderlineStyle, Window, WindowAppearance, WindowOptions, canvas, div, hsla, point, px,
8184 size,
8185 };
8186
8187 #[gpui::test]
8188 async fn test_focus_handle_release(cx: &mut TestAppContext) {
8189 let (handle, survivor) = cx.update(|cx| (cx.focus_handle(), cx.focus_handle()));
8190 let handle_id = handle.id;
8191 let weak_handle = handle.downgrade();
8192 let retained = handle.clone();
8193 drop(handle);
8194
8195 cx.update(|_| {});
8196 cx.read(|cx| {
8197 let handles = cx.focus_handles.read();
8198 assert_eq!(handles.handles.len(), 2);
8199 assert!(handles.dropped_handles.is_empty());
8200 });
8201 assert_eq!(weak_handle.upgrade().as_ref(), Some(&retained));
8202
8203 cx.background_spawn(async move { drop(retained) }).await;
8204 assert_eq!(weak_handle.upgrade(), None);
8205 cx.read(|cx| {
8206 assert_eq!(cx.focus_handles.read().dropped_handles, [handle_id]);
8207 });
8208 cx.update(|_| {});
8209 cx.read(|cx| {
8210 let handles = cx.focus_handles.read();
8211 assert_eq!(handles.handles.keys().collect::<Vec<_>>(), [survivor.id]);
8212 assert!(handles.dropped_handles.is_empty());
8213 });
8214
8215 let replacements = cx.update(|cx| [cx.focus_handle(), cx.focus_handle()]);
8216 assert_eq!(weak_handle.upgrade(), None);
8217 cx.update(|cx| {
8218 drop(replacements);
8219 cx.defer(move |_| drop(survivor));
8220 });
8221 cx.read(|cx| {
8222 let handles = cx.focus_handles.read();
8223 assert_eq!(handles.handles.len(), 0);
8224 assert!(handles.dropped_handles.is_empty());
8225 });
8226 }
8227
8228 #[gpui::test]
8229 fn test_dropped_focus_handle_blurs_only_its_windows(cx: &mut TestAppContext) {
8230 let windows = [
8231 cx.add_window(|_, _| EmptyView),
8232 cx.add_window(|_, _| EmptyView),
8233 cx.add_window(|_, _| EmptyView),
8234 cx.add_window(|_, _| EmptyView),
8235 cx.add_window(|_, _| EmptyView),
8236 ];
8237 let (handle, other_handle, survivor) =
8238 cx.update(|cx| (cx.focus_handle(), cx.focus_handle(), cx.focus_handle()));
8239 for window in &windows[..2] {
8240 window
8241 .update(cx, |_, window, cx| window.focus(&handle, cx))
8242 .expect("window exists");
8243 }
8244 windows[2]
8245 .update(cx, |_, window, cx| window.focus(&other_handle, cx))
8246 .expect("window exists");
8247 windows[3]
8248 .update(cx, |_, window, cx| window.focus(&survivor, cx))
8249 .expect("window exists");
8250
8251 cx.update(|cx| {
8252 for _ in 0..1_000 {
8253 drop(cx.focus_handle());
8254 }
8255 drop((handle, other_handle));
8256 });
8257 for window in &windows[..3] {
8258 window
8259 .update(cx, |_, window, _| assert_eq!(window.focus, None))
8260 .expect("window exists");
8261 }
8262 windows[3]
8263 .update(cx, |_, window, cx| {
8264 assert_eq!(window.focused(cx).as_ref(), Some(&survivor));
8265 assert_eq!(window.focus_generation, 1);
8266 })
8267 .expect("window exists");
8268 windows[4]
8269 .update(cx, |_, window, _| {
8270 assert_eq!(window.focus, None);
8271 assert_eq!(window.focus_generation, 0);
8272 })
8273 .expect("window exists");
8274 }
8275
8276 #[gpui::test]
8277 fn test_native_close_defers_app_access(cx: &mut TestAppContext) {
8278 let window = cx.add_window(|_, _| EmptyView);
8279 let platform_window = cx.test_window(window.into());
8280 {
8281 let app = cx.app.borrow_mut();
8282 platform_window.simulate_close();
8283 assert!(app.windows.contains_key(window.window_id()));
8284 }
8285 cx.run_until_parked();
8286 assert!(!cx.app.borrow().windows.contains_key(window.window_id()));
8287 }
8288
8289 #[cfg(feature = "profiler")]
8290 #[gpui::test]
8291 fn test_frame_requests_and_skips_are_journaled(cx: &mut TestAppContext) {
8292 use crate::FrameRequestSource;
8293 use crate::profiler::journal::{
8294 self, ForegroundJournalEntry, FrameSkipReason, IntervalBoundary,
8295 };
8296 use scheduler::Instant;
8297
8298 cx.app.borrow_mut().mode = crate::app::GpuiMode::Production;
8299 let window = cx.add_window(|_, _| EmptyView);
8300 let platform_window = cx.test_window(window.into());
8301 platform_window.simulate_frame_request(RequestFrameOptions::default());
8302 let (journal, _guard) = journal::install_test_foreground_journal(64, 4);
8303 let mut collector = journal.collector();
8304
8305 for (signal_at, source) in [
8306 (Some(Instant::now()), FrameRequestSource::NativeCallback),
8307 (Some(Instant::now()), FrameRequestSource::LocalSchedule),
8308 (None, FrameRequestSource::NativeCallback),
8309 ] {
8310 platform_window.simulate_frame_request(RequestFrameOptions {
8311 signal_at,
8312 signal_source: source,
8313 require_presentation: true,
8314 ..Default::default()
8315 });
8316 let entries = collector.collect_unseen().entries;
8317 let signal_index = entries
8318 .iter()
8319 .position(|entry| {
8320 matches!(
8321 entry,
8322 ForegroundJournalEntry::PlatformSignal(signal)
8323 if signal.window_id == window.window_id()
8324 && signal.signal_at == signal_at
8325 && signal.source == source
8326 && signal_at.is_none_or(|at| signal.handled_at >= at)
8327 )
8328 })
8329 .expect("frame request metadata");
8330 let skip_index = entries
8331 .iter()
8332 .position(|entry| {
8333 matches!(
8334 entry,
8335 ForegroundJournalEntry::Boundary(IntervalBoundary::FrameSkipped(skipped))
8336 if skipped.window_id == window.window_id()
8337 && skipped.reason == FrameSkipReason::NoRenderNeeded
8338 )
8339 })
8340 .expect("completed no-render boundary");
8341 assert!(signal_index < skip_index);
8342 }
8343 }
8344
8345 #[cfg(feature = "profiler")]
8346 #[gpui::test]
8347 fn test_frame_demand_survives_throttling(cx: &mut TestAppContext) {
8348 use crate::profiler::journal::{
8349 self, ForegroundJournalEntry, FrameSkipReason, IntervalBoundary,
8350 };
8351 use scheduler::Instant;
8352
8353 cx.app.borrow_mut().mode = crate::app::GpuiMode::Production;
8355 let (journal, _guard) = journal::install_test_foreground_journal(256, 4);
8356 let mut collector = journal.collector();
8357 for (initially_dirty, schedule_callback) in [(true, false), (false, true)] {
8358 let window = cx
8360 .app
8361 .borrow_mut()
8362 .open_window(
8363 WindowOptions {
8364 inactive_frame_interval: Some(Duration::from_secs(3600)),
8365 ..Default::default()
8366 },
8367 |_, cx| cx.new(|_| EmptyView),
8368 )
8369 .expect("open inactive test window");
8370 let platform_window = cx.test_window(window.into());
8371 platform_window.simulate_active_status_change(false);
8372 platform_window.simulate_frame_request(RequestFrameOptions::default());
8373 let callback_ran = Rc::new(Cell::new(false));
8374 let draws_before = window
8375 .update(cx, |_, window, _| {
8376 let draws_before = window
8377 .frame_duration_snapshot()
8378 .draw_duration_histogram
8379 .len();
8380 if initially_dirty {
8381 window.refresh();
8382 }
8383 if schedule_callback {
8384 window.on_next_frame({
8385 let callback_ran = callback_ran.clone();
8386 move |window, _| {
8387 callback_ran.set(true);
8388 window.refresh();
8389 }
8390 });
8391 }
8392 draws_before
8393 })
8394 .expect("schedule throttled frame");
8395 let input_at = Instant::now();
8397 journal::record_input(journal::InputTiming {
8398 kind: "test",
8399 start: input_at,
8400 end: input_at,
8401 caused_invalidation: false,
8402 });
8403 collector.collect_unseen();
8404 let wakes_before = platform_window.frame_wake_count();
8405 platform_window.simulate_frame_request(RequestFrameOptions {
8406 signal_at: Some(Instant::now()),
8407 ..Default::default()
8408 });
8409 window
8410 .update(cx, |_, window, _| {
8411 assert_eq!(
8412 window
8413 .frame_duration_snapshot()
8414 .draw_duration_histogram
8415 .len(),
8416 draws_before,
8417 );
8418 })
8419 .expect("inspect deferred frame");
8420 assert!(!callback_ran.get());
8421 assert!(platform_window.frame_scheduled());
8422 assert!(platform_window.frame_wake_count() > wakes_before);
8423 assert!(collector.collect_unseen().entries.iter().any(|entry| {
8424 matches!(
8425 entry,
8426 ForegroundJournalEntry::Boundary(IntervalBoundary::FrameSkipped(skipped))
8427 if skipped.window_id == window.window_id()
8428 && skipped.reason == FrameSkipReason::InactiveFrameRateLimit
8429 )
8430 }));
8431 platform_window.simulate_frame_request(RequestFrameOptions {
8432 signal_at: Some(Instant::now()),
8433 require_presentation: true,
8434 ..Default::default()
8435 });
8436 window
8437 .update(cx, |_, window, _| {
8438 assert_eq!(
8439 window
8440 .frame_duration_snapshot()
8441 .draw_duration_histogram
8442 .len(),
8443 draws_before + 1,
8444 );
8445 })
8446 .expect("inspect presented frame");
8447 assert_eq!(callback_ran.get(), schedule_callback);
8448 }
8449 }
8450
8451 #[gpui::test]
8455 fn test_window_visibility(cx: &mut TestAppContext) {
8456 use crate::WindowVisibility;
8457
8458 let window = cx.add_window(|_, _| EmptyView);
8459 let observed = Rc::new(RefCell::new(Vec::new()));
8460 let _subscription = window
8461 .update(cx, {
8462 let observed = observed.clone();
8463 move |_, window, _| {
8464 assert_eq!(window.visibility(), WindowVisibility::Visible);
8465 assert!(window.is_visible());
8466 window.observe_window_visibility(move |visibility, window, _| {
8467 assert_eq!(window.visibility(), visibility);
8468 observed.borrow_mut().push(visibility);
8469 })
8470 }
8471 })
8472 .unwrap();
8473 let test_window = cx.test_window(window.into());
8474 let frame_wake_count = test_window.frame_wake_count();
8475
8476 test_window.simulate_visibility_change(WindowVisibility::Hidden);
8477 assert_eq!(*observed.borrow(), [WindowVisibility::Hidden]);
8478 window
8479 .update(cx, |_, window, _| assert!(!window.is_visible()))
8480 .unwrap();
8481
8482 test_window.simulate_visibility_change(WindowVisibility::Hidden);
8484 assert_eq!(observed.borrow().len(), 1);
8485
8486 test_window.simulate_visibility_change(WindowVisibility::Visible);
8487 assert_eq!(
8488 *observed.borrow(),
8489 [WindowVisibility::Hidden, WindowVisibility::Visible]
8490 );
8491 window
8492 .update(cx, |_, window, _| assert!(window.is_visible()))
8493 .unwrap();
8494 assert_eq!(test_window.frame_wake_count(), frame_wake_count);
8495 }
8496
8497 #[gpui::test]
8500 fn test_display_tracking(cx: &mut TestAppContext) {
8501 use crate::{DisplayChanges, DisplayEvent, DisplayId, Subscription};
8502
8503 let sixty_hertz = Duration::from_secs(1) / 60;
8504 let one_hundred_twenty_hertz = Duration::from_secs(1) / 120;
8505 let one_hundred_forty_four_hertz = Duration::from_secs(1) / 144;
8506
8507 let display_events = Rc::new(RefCell::new(Vec::new()));
8508 let _display_subscription = cx.update({
8509 let display_events = display_events.clone();
8510 move |cx| cx.observe_displays(move |event, _| display_events.borrow_mut().push(event))
8511 });
8512
8513 let window = cx.add_window(|window, cx| DisplayTracker {
8514 refresh_intervals: Vec::new(),
8515 _subscription: cx.observe_window_display(
8516 window,
8517 |tracker: &mut DisplayTracker, window, cx| {
8518 tracker.refresh_intervals.push(window.refresh_interval(cx));
8519 },
8520 ),
8521 });
8522 let window_notifications = Rc::new(RefCell::new(Vec::new()));
8523 let _window_subscription = window
8524 .update(cx, {
8525 let window_notifications = window_notifications.clone();
8526 move |_, window, cx| {
8527 assert_eq!(window.refresh_interval(cx), Some(sixty_hertz));
8528 window.observe_window_display(move |window, cx| {
8529 window_notifications
8530 .borrow_mut()
8531 .push((window.display_id, window.refresh_interval(cx)));
8532 })
8533 }
8534 })
8535 .unwrap();
8536 let test_window = cx.test_window(window.into());
8537
8538 cx.test_platform()
8543 .connect_display(DisplayId(2), Some(one_hundred_twenty_hertz));
8544 test_window.simulate_move_to_display(DisplayId(2));
8545 assert_eq!(
8546 window_notifications.take(),
8547 [(Some(DisplayId(2)), Some(one_hundred_twenty_hertz))]
8548 );
8549 assert_eq!(display_events.take(), [DisplayEvent::Added(DisplayId(2))]);
8550 cx.simulate_display_refresh_interval_change(DisplayId(2), Some(one_hundred_twenty_hertz));
8551 assert!(window_notifications.borrow().is_empty());
8552
8553 test_window.simulate_move_to_display(DisplayId(2));
8555 assert!(window_notifications.borrow().is_empty());
8556
8557 cx.simulate_display_refresh_interval_change(
8558 DisplayId(2),
8559 Some(one_hundred_forty_four_hertz),
8560 );
8561 assert_eq!(
8562 window_notifications.take(),
8563 [(Some(DisplayId(2)), Some(one_hundred_forty_four_hertz))]
8564 );
8565
8566 cx.simulate_display_refresh_interval_change(DisplayId(1), Some(one_hundred_twenty_hertz));
8568 cx.simulate_display_removed(DisplayId(1));
8569 assert!(window_notifications.borrow().is_empty());
8570
8571 assert_eq!(
8572 display_events.take(),
8573 [
8574 DisplayEvent::Changed {
8575 id: DisplayId(2),
8576 changes: DisplayChanges::REFRESH_INTERVAL,
8577 },
8578 DisplayEvent::Changed {
8579 id: DisplayId(1),
8580 changes: DisplayChanges::REFRESH_INTERVAL,
8581 },
8582 DisplayEvent::Removed(DisplayId(1)),
8583 ]
8584 );
8585 window
8586 .update(cx, |tracker, _, _| {
8587 assert_eq!(
8588 tracker.refresh_intervals,
8589 [
8590 Some(one_hundred_twenty_hertz),
8591 Some(one_hundred_forty_four_hertz)
8592 ]
8593 );
8594 })
8595 .unwrap();
8596
8597 struct DisplayTracker {
8598 refresh_intervals: Vec<Option<Duration>>,
8599 _subscription: Subscription,
8600 }
8601
8602 impl Render for DisplayTracker {
8603 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
8604 Empty
8605 }
8606 }
8607 }
8608
8609 #[gpui::test]
8610 fn test_fully_visible_bounds_preserve_layout_viewport(cx: &mut TestAppContext) {
8611 let window = cx.add_window(|_, _| EmptyView);
8612 let mut platform_window = cx.test_window(window.into());
8613 platform_window.simulate_resize(size(px(400.), px(800.)));
8614 window
8615 .update(cx, |_, window, _| {
8616 assert_eq!(
8617 window.visual_viewport_bounds(),
8618 Bounds::new(Point::default(), size(px(400.), px(800.)))
8619 );
8620 assert_eq!(
8621 window.fully_visible_bounds(),
8622 window.visual_viewport_bounds()
8623 );
8624 })
8625 .unwrap();
8626
8627 platform_window.simulate_frame_request(RequestFrameOptions::default());
8628 let wakes = platform_window.frame_wake_count();
8629 let visual_bounds = Bounds::new(point(px(10.), px(40.)), size(px(380.), px(460.)));
8630 platform_window.simulate_visual_viewport_change(visual_bounds);
8631 assert!(platform_window.frame_wake_count() > wakes);
8632 platform_window.simulate_frame_request(RequestFrameOptions::default());
8633 let wakes = platform_window.frame_wake_count();
8634 let insets = crate::WindowInsets {
8635 safe_area: crate::Edges {
8636 top: px(60.),
8637 right: px(20.),
8638 bottom: px(30.),
8639 left: px(-10.),
8640 },
8641 ime: crate::Edges {
8642 bottom: px(350.),
8643 ..Default::default()
8644 },
8645 ..Default::default()
8646 };
8647 platform_window.simulate_insets_change(insets.clone());
8648 assert!(platform_window.frame_wake_count() > wakes);
8649 window
8650 .update(cx, |_, window, cx| {
8651 let focus = cx.focus_handle();
8652 window.focus(&focus, cx);
8653 assert_eq!(window.insets(), insets);
8654 assert_eq!(window.viewport_size(), size(px(400.), px(800.)));
8655 assert_eq!(window.visual_viewport_bounds(), visual_bounds);
8656 assert_eq!(
8657 window.fully_visible_bounds(),
8658 Bounds::new(point(px(10.), px(60.)), size(px(370.), px(390.)))
8659 );
8660 window.request_virtual_keyboard();
8661 window.dismiss_virtual_keyboard();
8662 assert!(focus.is_focused(window));
8663 assert_eq!(window.viewport_size(), size(px(400.), px(800.)));
8664 })
8665 .unwrap();
8666 assert_eq!(platform_window.virtual_keyboard_requests(), 1);
8667 assert_eq!(platform_window.virtual_keyboard_dismissals(), 1);
8668
8669 platform_window.simulate_insets_change(crate::WindowInsets {
8670 safe_area: crate::Edges {
8671 top: px(900.),
8672 left: px(500.),
8673 ..Default::default()
8674 },
8675 ..Default::default()
8676 });
8677 window
8678 .update(cx, |_, window, _| {
8679 assert_eq!(
8680 window.fully_visible_bounds().size,
8681 size(Pixels::ZERO, Pixels::ZERO)
8682 );
8683 })
8684 .unwrap();
8685
8686 use crate::PlatformWindow as _;
8688 platform_window.on_insets_changed(Box::new(|_| {}));
8689 platform_window.simulate_insets_change(crate::WindowInsets::default());
8690 window
8691 .update(cx, |_, window, _| {
8692 assert_eq!(window.insets(), crate::WindowInsets::default());
8693 })
8694 .unwrap();
8695 }
8696
8697 #[test]
8698 fn frame_throttle_tracks_display_refreshes() {
8699 let start = super::Instant::now();
8700 for (elapsed, interval) in [
8701 (Duration::from_micros(16_666), Duration::from_micros(16_667)),
8702 (Duration::from_micros(33_332), Duration::from_micros(33_333)),
8703 ] {
8704 let mut pacer = super::FramePacer::default();
8705 assert!(!pacer.should_defer(start, Some(interval)));
8706 assert!(pacer.should_defer(start + elapsed / 2, Some(interval)));
8707 assert!(!pacer.should_defer(start + elapsed, Some(interval)));
8708 }
8709 }
8710
8711 #[test]
8712 fn frame_throttle_preserves_cadence_without_catching_up_after_stalls() {
8713 let start = super::Instant::now();
8714 let interval = Duration::from_micros(33_333);
8715 let mut pacer = super::FramePacer::default();
8716 assert!(!pacer.should_defer(start, Some(interval)));
8717 let delayed_frame = start + interval + Duration::from_millis(5);
8718 assert!(!pacer.should_defer(delayed_frame, Some(interval)));
8719 assert!(pacer.should_defer(start + interval + interval / 2, Some(interval)));
8720 assert!(!pacer.should_defer(start + interval * 2, Some(interval)));
8721
8722 let after_stall = start + interval * 4 - Duration::from_millis(1);
8724 assert!(!pacer.should_defer(after_stall, Some(interval)));
8725 assert!(pacer.should_defer(after_stall + interval / 2, Some(interval)));
8726 assert!(!pacer.should_defer(after_stall + interval, Some(interval)));
8727 let mandatory_frame = after_stall + interval + Duration::from_millis(2);
8729 assert!(!pacer.should_defer(mandatory_frame, None));
8730 assert!(pacer.should_defer(mandatory_frame + interval / 2, Some(interval)));
8731 }
8732
8733 #[test]
8734 fn power_modes_preserve_frame_limits() {
8735 let options = WindowOptions::default();
8736 for (power_mode, active_interval, thermal_interval) in [
8737 (
8738 crate::PowerMode::LowPower,
8739 Some(Duration::from_micros(33_333)),
8740 Duration::from_micros(33_333),
8741 ),
8742 (
8743 crate::PowerMode::Unknown,
8744 None,
8745 Duration::from_micros(16_667),
8746 ),
8747 (
8748 crate::PowerMode::Balanced,
8749 None,
8750 Duration::from_micros(16_667),
8751 ),
8752 (
8753 crate::PowerMode::HighPower,
8754 None,
8755 Duration::from_micros(16_667),
8756 ),
8757 ] {
8758 assert_eq!(
8759 super::minimum_frame_interval(
8760 true,
8761 false,
8762 options.inactive_frame_interval,
8763 options.low_power_frame_interval,
8764 options.thermal_frame_interval,
8765 ThermalState::Nominal,
8766 power_mode,
8767 ),
8768 active_interval,
8769 );
8770 assert_eq!(
8771 super::minimum_frame_interval(
8772 true,
8773 false,
8774 options.inactive_frame_interval,
8775 options.low_power_frame_interval,
8776 options.thermal_frame_interval,
8777 ThermalState::Serious,
8778 power_mode,
8779 ),
8780 Some(thermal_interval),
8781 );
8782 }
8783 }
8784
8785 #[test]
8786 fn frame_limits_combine_enabled_intervals() {
8787 let short = Some(Duration::from_millis(10));
8788 let long = Some(Duration::from_millis(100));
8789 for (inactive, low_power, thermal, expected) in [
8790 (long, short, short, long),
8791 (short, long, short, long),
8792 (short, short, long, long),
8793 (long, None, None, long),
8794 (None, long, None, long),
8795 (None, None, long, long),
8796 (None, None, None, None),
8797 ] {
8798 assert_eq!(
8799 super::minimum_frame_interval(
8800 true,
8801 true,
8802 inactive,
8803 low_power,
8804 thermal,
8805 ThermalState::Critical,
8806 crate::PowerMode::LowPower,
8807 ),
8808 expected,
8809 );
8810 }
8811 assert_eq!(
8812 super::minimum_frame_interval(
8813 false,
8814 true,
8815 long,
8816 long,
8817 long,
8818 ThermalState::Critical,
8819 crate::PowerMode::LowPower,
8820 ),
8821 None,
8822 );
8823 }
8824
8825 struct EmptyView;
8826
8827 impl Render for EmptyView {
8828 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
8829 div()
8830 }
8831 }
8832
8833 struct OpensWindowOnPaint {
8834 opened: Rc<Cell<bool>>,
8835 }
8836
8837 impl Render for OpensWindowOnPaint {
8838 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
8839 let opened = self.opened.clone();
8840 div()
8841 .size_full()
8842 .child(canvas(
8843 |_, _, _| {},
8844 move |_, _, _window, cx| {
8845 if !opened.replace(true) {
8846 cx.open_window(WindowOptions::default(), |_, cx| cx.new(|_| EmptyView))
8847 .unwrap();
8848 }
8849 },
8850 ))
8851 .child(div().child("after"))
8855 }
8856 }
8857
8858 #[test]
8864 fn test_window_opened_during_draw_defers_arena_clear() {
8865 let mut cx = TestAppContext::single();
8866
8867 let opened = Rc::new(Cell::new(false));
8868 let window = cx.add_window({
8870 let opened = opened.clone();
8871 move |_, _| OpensWindowOnPaint { opened }
8872 });
8873
8874 assert!(opened.get());
8875 assert_eq!(cx.windows().len(), 2);
8876
8877 cx.update_window(window.into(), |_, window, cx| window.draw(cx).clear(cx))
8880 .unwrap();
8881 }
8882
8883 #[test]
8884 fn test_scale_factor_change_preserves_bounds_and_survives_resize() {
8885 let mut cx = TestAppContext::single();
8886 let window = cx.add_window(|_, _| EmptyView);
8887 let handle: AnyWindowHandle = window.into();
8888 let window_state = |cx: &mut TestAppContext| {
8889 cx.update_window(handle, |_, window, _| {
8890 (
8891 window.scale_factor(),
8892 window.bounds(),
8893 window.viewport_size(),
8894 )
8895 })
8896 .unwrap()
8897 };
8898
8899 let (scale_factor, mut expected_bounds, _) = window_state(&mut cx);
8900 assert_eq!(scale_factor, 2.0);
8901
8902 for (scale_factor, resized_size) in [
8903 (1.0, size(px(800.), px(600.))),
8904 (1.25, size(px(640.), px(480.))),
8905 (2.0, size(px(1024.), px(768.))),
8906 ] {
8907 cx.simulate_window_scale_factor_change(handle, scale_factor);
8908 assert_eq!(
8909 window_state(&mut cx),
8910 (scale_factor, expected_bounds, expected_bounds.size)
8911 );
8912
8913 cx.simulate_window_resize(handle, resized_size);
8914 expected_bounds.size = resized_size;
8915 assert_eq!(
8916 window_state(&mut cx),
8917 (scale_factor, expected_bounds, resized_size)
8918 );
8919 }
8920 }
8921
8922 #[gpui::test]
8927 fn test_frame_waker_fires_on_frame_demand(cx: &mut TestAppContext) {
8928 let window = cx.add_window(|_, _| EmptyView);
8929 let test_window = cx.test_window(window.into());
8930 test_window.simulate_active_status_change(true);
8932
8933 assert!(
8937 test_window.frame_wake_count() >= 1,
8938 "opening a window must wake the frame source for the initial frame"
8939 );
8940
8941 test_window.simulate_frame_request(RequestFrameOptions::default());
8943
8944 let baseline = test_window.frame_wake_count();
8947 test_window.simulate_frame_request(RequestFrameOptions::default());
8948 window.update(cx, |_, _, _| {}).unwrap();
8949 assert_eq!(
8950 test_window.frame_wake_count(),
8951 baseline,
8952 "clean frames and non-notifying updates must not wake the frame source"
8953 );
8954
8955 window.update(cx, |_, _, cx| cx.notify()).unwrap();
8957 assert!(
8958 test_window.frame_wake_count() > baseline,
8959 "notifying a view in an idle window must wake the frame source"
8960 );
8961
8962 test_window.simulate_frame_request(RequestFrameOptions::default());
8964 let baseline = test_window.frame_wake_count();
8965 test_window.simulate_frame_request(RequestFrameOptions::default());
8966 assert_eq!(
8967 test_window.frame_wake_count(),
8968 baseline,
8969 "serving demand must return the window to idle"
8970 );
8971
8972 window
8974 .update(cx, |_, window, _| window.on_next_frame(|_, _| {}))
8975 .unwrap();
8976 assert!(
8977 test_window.frame_wake_count() > baseline,
8978 "scheduling a next-frame callback in an idle window must wake the frame source"
8979 );
8980 }
8981
8982 #[gpui::test]
8987 fn test_pending_next_frame_callbacks_are_not_stranded(cx: &mut TestAppContext) {
8988 let window = cx.add_window(|_, _| EmptyView);
8989 let test_window = cx.test_window(window.into());
8990 test_window.simulate_frame_request(RequestFrameOptions::default());
8993
8994 let callback_ran = Rc::new(Cell::new(false));
8995 window
8996 .update(cx, {
8997 let callback_ran = callback_ran.clone();
8998 move |_, window, _| {
8999 window.on_next_frame(move |_, _| callback_ran.set(true));
9000 }
9001 })
9002 .unwrap();
9003
9004 let baseline = test_window.frame_wake_count();
9005 test_window.simulate_frame_request(RequestFrameOptions::default());
9006 assert!(
9013 test_window.frame_wake_count() > baseline || callback_ran.get(),
9014 "a frame request with pending next-frame callbacks must either run them or re-arm the frame source"
9015 );
9016 }
9017
9018 #[gpui::test]
9019 fn test_window_reports_no_raw_handle_instead_of_panicking(cx: &mut TestAppContext) {
9020 use raw_window_handle::{HandleError, HasDisplayHandle as _, HasWindowHandle as _};
9021
9022 let window = cx.add_window(|_, _| EmptyView);
9023 window
9024 .update(cx, |_, window, _| {
9025 assert!(matches!(
9026 window.window_handle(),
9027 Err(HandleError::NotSupported)
9028 ));
9029 assert!(matches!(
9030 window.display_handle(),
9031 Err(HandleError::NotSupported)
9032 ));
9033 })
9034 .unwrap();
9035 }
9036
9037 #[gpui::test]
9038 fn test_appearance_change_runs_after_app_update(cx: &mut TestAppContext) {
9039 let window = cx.add_window(|_, _| EmptyView);
9040 let observed_appearance = Rc::new(Cell::new(None));
9041 let _subscription = window
9042 .update(cx, {
9043 let observed_appearance = observed_appearance.clone();
9044 move |_, window, _| {
9045 window.observe_window_appearance(move |window, _| {
9046 observed_appearance.set(Some(window.appearance()));
9047 })
9048 }
9049 })
9050 .unwrap();
9051 let test_window = cx.test_window(window.into());
9052
9053 cx.update(|_| {
9054 test_window.simulate_appearance_change(WindowAppearance::Dark);
9055 assert_eq!(observed_appearance.get(), None);
9056 });
9057 cx.run_until_parked();
9058
9059 assert_eq!(observed_appearance.get(), Some(WindowAppearance::Dark));
9060 }
9061
9062 #[gpui::test]
9063 fn queued_frame_callback_wakes_a_parked_render_loop(cx: &mut TestAppContext) {
9064 let window = cx.add_window(|_, _| Empty);
9065 let test_window = cx.test_window(window.into());
9066
9067 assert!(test_window.simulate_scheduled_frame());
9068 assert!(test_window.simulate_scheduled_frame());
9069 assert!(!test_window.frame_scheduled());
9070
9071 cx.update_window(window.into(), |_, window, _| {
9072 window.active.set(true);
9073 window.on_next_frame(|_, _| {});
9074 })
9075 .unwrap();
9076 assert!(
9077 test_window.frame_scheduled(),
9078 "queuing work on a parked window must wake the render loop"
9079 );
9080
9081 assert!(test_window.simulate_scheduled_frame());
9082 assert!(
9083 test_window.frame_scheduled(),
9084 "presenting the frame must await one compositor callback"
9085 );
9086 assert!(test_window.simulate_scheduled_frame());
9087 assert!(!test_window.frame_scheduled());
9088 }
9089
9090 #[gpui::test]
9091 fn pending_presentation_wakes_a_parked_render_loop(cx: &mut TestAppContext) {
9092 let window = cx.add_window(|_, _| Empty);
9093 let test_window = cx.test_window(window.into());
9094
9095 assert!(test_window.simulate_scheduled_frame());
9096 assert!(test_window.simulate_scheduled_frame());
9097 assert!(!test_window.frame_scheduled());
9098
9099 cx.update_window(window.into(), |_, window, cx| window.draw(cx).clear(cx))
9100 .unwrap();
9101
9102 assert!(
9103 test_window.frame_scheduled(),
9104 "a rendered scene awaiting presentation must wake the render loop"
9105 );
9106 }
9107
9108 #[gpui::test]
9109 fn callback_queued_during_a_frame_requests_a_follow_up(cx: &mut TestAppContext) {
9110 let window = cx.add_window(|_, _| Empty);
9111 let test_window = cx.test_window(window.into());
9112
9113 let callback_ran = Rc::new(Cell::new(false));
9114 cx.update_window(window.into(), |_, window, _| {
9115 window.active.set(true);
9118 let callback_ran = callback_ran.clone();
9119 window.on_next_frame(move |window, _| {
9120 window.on_next_frame(move |_, _| callback_ran.set(true));
9121 });
9122 })
9123 .unwrap();
9124
9125 assert!(test_window.simulate_scheduled_frame());
9126 assert!(!callback_ran.get());
9127 assert!(
9128 test_window.frame_scheduled(),
9129 "a callback queued mid-frame must schedule a follow-up before the loop parks"
9130 );
9131
9132 assert!(test_window.simulate_scheduled_frame());
9133 assert!(callback_ran.get());
9134 }
9135
9136 struct RootView {
9137 explicit_size: bool,
9138 child_bounds: Rc<Cell<Bounds<Pixels>>>,
9139 }
9140
9141 impl Render for RootView {
9142 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
9143 let child_bounds = self.child_bounds.clone();
9144 let root = div().flex().flex_col().child(
9145 canvas(
9146 move |bounds, _, _| child_bounds.set(bounds),
9147 |_, _, _, _| {},
9148 )
9149 .size_full(),
9150 );
9151 if self.explicit_size {
9152 root.w(px(300.)).h(px(200.))
9153 } else {
9154 root
9155 }
9156 }
9157 }
9158
9159 #[test]
9160 fn auto_sized_window_root_fills_the_window() {
9161 let mut cx = TestAppContext::single();
9162 let child_bounds = Rc::new(Cell::new(Bounds::default()));
9163 let window = cx.add_window({
9164 let child_bounds = child_bounds.clone();
9165 move |_, _| RootView {
9166 explicit_size: false,
9167 child_bounds,
9168 }
9169 });
9170
9171 let viewport_size = cx
9172 .update_window(window.into(), |_, window, cx| {
9173 window.draw(cx).clear(cx);
9174 window.viewport_size()
9175 })
9176 .unwrap();
9177
9178 assert_eq!(child_bounds.get().size, viewport_size);
9179 }
9180
9181 #[test]
9182 fn explicitly_sized_window_root_keeps_its_size() {
9183 let mut cx = TestAppContext::single();
9184 let child_bounds = Rc::new(Cell::new(Bounds::default()));
9185 let window = cx.add_window({
9186 let child_bounds = child_bounds.clone();
9187 move |_, _| RootView {
9188 explicit_size: true,
9189 child_bounds,
9190 }
9191 });
9192
9193 cx.update_window(window.into(), |_, window, cx| {
9194 window.draw(cx).clear(cx);
9195 })
9196 .unwrap();
9197
9198 assert_eq!(child_bounds.get().size, size(px(300.), px(200.)));
9199 }
9200
9201 struct FileDragView {
9202 path: PathBuf,
9203 observed_drag_moves: Rc<RefCell<Vec<Point<Pixels>>>>,
9204 observed_drops: Rc<RefCell<Vec<PathBuf>>>,
9205 }
9206
9207 struct FileDropExitView(Rc<Cell<usize>>);
9208
9209 impl Render for FileDropExitView {
9210 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
9211 div().size_full().on_file_drop_exit({
9212 let observed_file_drop_exit = self.0.clone();
9213 move |_, _, _| observed_file_drop_exit.set(observed_file_drop_exit.get() + 1)
9214 })
9215 }
9216 }
9217
9218 impl Render for FileDragView {
9219 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
9220 div()
9221 .id("file-drag")
9222 .size_full()
9223 .on_drag(self.path.clone(), |_, _, _, cx| cx.new(|_| Empty))
9224 .external_drag_payload(|path: &PathBuf, _, _| {
9225 Some(ExternalDragPayload::Files(FileDragPaths::new([(
9226 path.clone(),
9227 true,
9228 )])))
9229 })
9230 .on_drag_move({
9231 let observed_drag_moves = self.observed_drag_moves.clone();
9232 move |event: &DragMoveEvent<PathBuf>, _, _| {
9233 observed_drag_moves.borrow_mut().push(event.event.position);
9234 }
9235 })
9236 .on_drop({
9237 let observed_drops = self.observed_drops.clone();
9238 move |path: &PathBuf, _, _| observed_drops.borrow_mut().push(path.clone())
9239 })
9240 }
9241 }
9242
9243 #[gpui::test]
9244 fn file_drag_is_promoted_once_and_restored_in_source_window(cx: &mut TestAppContext) {
9245 struct Drag {
9246 window: AnyWindowHandle,
9247 observed_drag_moves: Rc<RefCell<Vec<Point<Pixels>>>>,
9248 observed_drops: Rc<RefCell<Vec<PathBuf>>>,
9249 }
9250
9251 fn start_drag(cx: &mut TestAppContext, path: PathBuf, platform_result: bool) -> Drag {
9252 let observed_drag_moves = Rc::new(RefCell::new(Vec::new()));
9253 let observed_drops = Rc::new(RefCell::new(Vec::new()));
9254 let window: AnyWindowHandle = cx
9255 .add_window({
9256 let observed_drag_moves = observed_drag_moves.clone();
9257 let observed_drops = observed_drops.clone();
9258 move |_, _| FileDragView {
9259 path,
9260 observed_drag_moves,
9261 observed_drops,
9262 }
9263 })
9264 .into();
9265 cx.test_window(window)
9266 .set_start_external_drag_result(platform_result);
9267
9268 let update_result = cx.update_window(window, |_, window, cx| {
9269 window.draw(cx).clear(cx);
9270 window.dispatch_event(
9271 MouseDownEvent {
9272 position: point(px(10.), px(10.)),
9273 button: MouseButton::Left,
9274 modifiers: Default::default(),
9275 click_count: 1,
9276 first_mouse: false,
9277 }
9278 .to_platform_input(),
9279 cx,
9280 );
9281 window.dispatch_event(
9282 MouseMoveEvent {
9283 position: point(px(20.), px(20.)),
9284 pressed_button: Some(MouseButton::Left),
9285 modifiers: Default::default(),
9286 }
9287 .to_platform_input(),
9288 cx,
9289 );
9290 assert!(cx.active_drag.is_some());
9291 });
9292 assert!(
9293 update_result.is_ok(),
9294 "failed to start drag: {update_result:?}"
9295 );
9296
9297 assert!(cx.test_window(window).external_drag_files().is_empty());
9298 Drag {
9299 window,
9300 observed_drag_moves,
9301 observed_drops,
9302 }
9303 }
9304
9305 let successful_path = PathBuf::from("/tmp/successful-drag");
9306 let successful = start_drag(cx, successful_path.clone(), true);
9307 let outside_position = point(px(-1.), px(20.));
9308 let update_result = cx.update_window(successful.window, |_, window, cx| {
9309 window.dispatch_event(
9310 MouseMoveEvent {
9311 position: outside_position,
9312 pressed_button: Some(MouseButton::Left),
9313 modifiers: Default::default(),
9314 }
9315 .to_platform_input(),
9316 cx,
9317 );
9318 assert!(cx.active_drag.is_none());
9319 });
9320 assert!(
9321 update_result.is_ok(),
9322 "failed to promote drag: {update_result:?}"
9323 );
9324 assert_eq!(
9325 cx.test_window(successful.window).external_drag_files(),
9326 [(successful_path.clone(), true)]
9327 );
9328 assert_eq!(
9331 successful.observed_drag_moves.borrow().last(),
9332 Some(&outside_position)
9333 );
9334
9335 let first_destination_exit_count = Rc::new(Cell::new(0));
9336 let first_destination: AnyWindowHandle = cx
9337 .add_window({
9338 let first_destination_exit_count = first_destination_exit_count.clone();
9339 move |_, _| FileDropExitView(first_destination_exit_count)
9340 })
9341 .into();
9342 let second_destination_exit_count = Rc::new(Cell::new(0));
9343 let second_destination: AnyWindowHandle = cx
9344 .add_window({
9345 let second_destination_exit_count = second_destination_exit_count.clone();
9346 move |_, _| FileDropExitView(second_destination_exit_count)
9347 })
9348 .into();
9349 let reentry_position = point(px(30.), px(30.));
9350 let external_paths = || ExternalPaths([successful_path.clone()].into_iter().collect());
9351 let update_result = cx.update_window(first_destination, |_, window, cx| {
9352 window.draw(cx).clear(cx);
9353 window.dispatch_event(
9354 FileDropEvent::Entered {
9355 position: reentry_position,
9356 paths: external_paths(),
9357 }
9358 .to_platform_input(),
9359 cx,
9360 );
9361 assert!(
9362 cx.active_drag
9363 .as_ref()
9364 .is_some_and(|drag| drag.value.downcast_ref::<ExternalPaths>().is_some())
9365 );
9366 window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx);
9367 assert!(cx.active_drag.is_none());
9368 assert_eq!(first_destination_exit_count.get(), 1);
9369 assert_eq!(second_destination_exit_count.get(), 0);
9370 });
9371 assert!(
9372 update_result.is_ok(),
9373 "failed to handle drag in first destination window: {update_result:?}"
9374 );
9375
9376 let update_result = cx.update_window(second_destination, |_, window, cx| {
9377 window.draw(cx).clear(cx);
9378 window.dispatch_event(
9379 PlatformInput::KeyDown(KeyDownEvent {
9380 keystroke: Keystroke::parse("down").expect("valid keystroke"),
9381 is_held: false,
9382 prefer_character_input: false,
9383 }),
9384 cx,
9385 );
9386 window.dispatch_event(
9387 FileDropEvent::Entered {
9388 position: reentry_position,
9389 paths: external_paths(),
9390 }
9391 .to_platform_input(),
9392 cx,
9393 );
9394 assert!(
9395 cx.active_drag
9396 .as_ref()
9397 .is_some_and(|drag| drag.value.downcast_ref::<ExternalPaths>().is_some())
9398 );
9399 assert_eq!(first_destination_exit_count.get(), 1);
9400 assert_eq!(second_destination_exit_count.get(), 0);
9401
9402 window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx);
9403 assert!(cx.active_drag.is_none());
9404 assert_eq!(first_destination_exit_count.get(), 1);
9405 assert_eq!(second_destination_exit_count.get(), 1);
9406 });
9407 assert!(
9408 update_result.is_ok(),
9409 "failed to handle drag in second destination window: {update_result:?}"
9410 );
9411
9412 let update_result = cx.update_window(successful.window, |_, window, cx| {
9413 window.dispatch_event(
9414 FileDropEvent::Entered {
9415 position: reentry_position,
9416 paths: external_paths(),
9417 }
9418 .to_platform_input(),
9419 cx,
9420 );
9421 assert!(
9422 cx.active_drag
9423 .as_ref()
9424 .is_some_and(|drag| drag.value.downcast_ref::<PathBuf>().is_some())
9425 );
9426 assert_eq!(
9427 successful.observed_drag_moves.borrow().last(),
9428 Some(&reentry_position)
9429 );
9430
9431 window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx);
9432 assert!(cx.active_drag.is_none());
9433
9434 window.dispatch_event(
9435 FileDropEvent::Entered {
9436 position: reentry_position,
9437 paths: external_paths(),
9438 }
9439 .to_platform_input(),
9440 cx,
9441 );
9442 assert!(
9443 cx.active_drag
9444 .as_ref()
9445 .is_some_and(|drag| drag.value.downcast_ref::<PathBuf>().is_some())
9446 );
9447
9448 window.dispatch_event(
9449 FileDropEvent::Submit {
9450 position: reentry_position,
9451 }
9452 .to_platform_input(),
9453 cx,
9454 );
9455 assert_eq!(
9456 successful.observed_drops.borrow().as_slice(),
9457 std::slice::from_ref(&successful_path)
9458 );
9459 assert!(cx.active_drag.is_none());
9460
9461 window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx);
9462 assert!(cx.active_drag.is_none());
9463 window.dispatch_event(FileDropEvent::Ended.to_platform_input(), cx);
9464 assert!(cx.active_drag.is_none());
9465
9466 window.dispatch_event(
9467 FileDropEvent::Entered {
9468 position: reentry_position,
9469 paths: external_paths(),
9470 }
9471 .to_platform_input(),
9472 cx,
9473 );
9474 assert!(
9475 cx.active_drag
9476 .as_ref()
9477 .is_some_and(|drag| drag.value.downcast_ref::<ExternalPaths>().is_some())
9478 );
9479 window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx);
9480 });
9481 assert!(
9482 update_result.is_ok(),
9483 "failed to restore drag in source window: {update_result:?}"
9484 );
9485
9486 let cancelled_path = PathBuf::from("/tmp/cancelled-drag");
9487 let cancelled = start_drag(cx, cancelled_path.clone(), true);
9488 let update_result = cx.update_window(cancelled.window, |_, window, cx| {
9489 window.dispatch_event(
9490 MouseMoveEvent {
9491 position: outside_position,
9492 pressed_button: Some(MouseButton::Left),
9493 modifiers: Default::default(),
9494 }
9495 .to_platform_input(),
9496 cx,
9497 );
9498 assert!(cx.active_drag.is_none());
9499
9500 window.dispatch_event(
9501 FileDropEvent::Entered {
9502 position: reentry_position,
9503 paths: ExternalPaths([cancelled_path].into_iter().collect()),
9504 }
9505 .to_platform_input(),
9506 cx,
9507 );
9508 assert!(
9509 cx.active_drag
9510 .as_ref()
9511 .is_some_and(|drag| drag.value.downcast_ref::<PathBuf>().is_some())
9512 );
9513 assert!(cx.stop_active_drag(window));
9514 assert!(cx.active_drag.is_none());
9515 });
9516 assert!(
9517 update_result.is_ok(),
9518 "failed to cancel restored drag: {update_result:?}"
9519 );
9520 assert!(!cx.update(|cx| cx.end_platform_drag(cancelled.window.window_id())));
9521
9522 let removed_path = PathBuf::from("/tmp/removed-window-drag");
9523 let removed = start_drag(cx, removed_path, true);
9524 let removed_window_id = removed.window.window_id();
9525 let update_result = cx.update_window(removed.window, |_, window, cx| {
9526 window.dispatch_event(
9527 MouseMoveEvent {
9528 position: outside_position,
9529 pressed_button: Some(MouseButton::Left),
9530 modifiers: Default::default(),
9531 }
9532 .to_platform_input(),
9533 cx,
9534 );
9535 assert!(cx.active_drag.is_none());
9536 window.remove_window();
9537 });
9538 assert!(
9539 update_result.is_ok(),
9540 "failed to remove drag source window: {update_result:?}"
9541 );
9542 assert!(!cx.update(|cx| cx.end_platform_drag(removed_window_id)));
9543
9544 let failed_path = PathBuf::from("/tmp/failed-drag");
9545 let failed = start_drag(cx, failed_path.clone(), false);
9546 let update_result = cx.update_window(failed.window, |_, window, cx| {
9547 for x_position in [-1., -2.] {
9548 window.dispatch_event(
9549 MouseMoveEvent {
9550 position: point(px(x_position), px(20.)),
9551 pressed_button: Some(MouseButton::Left),
9552 modifiers: Default::default(),
9553 }
9554 .to_platform_input(),
9555 cx,
9556 );
9557 }
9558 assert!(cx.active_drag.is_some());
9559 });
9560 assert!(
9561 update_result.is_ok(),
9562 "failed to retain drag after platform failure: {update_result:?}"
9563 );
9564 assert_eq!(
9565 cx.test_window(failed.window).external_drag_files(),
9566 [(failed_path, true)]
9567 );
9568 }
9569
9570 struct FocusForwarder {
9571 a: FocusHandle,
9572 b: FocusHandle,
9573 }
9574
9575 impl Render for FocusForwarder {
9576 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
9577 div()
9578 .size_full()
9579 .child(div().w(px(50.)).h(px(50.)).track_focus(&self.a))
9580 .child(div().w(px(50.)).h(px(50.)).track_focus(&self.b))
9581 }
9582 }
9583
9584 #[gpui::test]
9588 fn test_focus_moved_by_focus_listener_is_dispatched(cx: &mut TestAppContext) {
9589 let b_focus_count = Rc::new(Cell::new(0));
9590 let window = cx.add_window({
9591 let b_focus_count = b_focus_count.clone();
9592 move |window, cx| {
9593 let a = cx.focus_handle();
9594 let b = cx.focus_handle();
9595 cx.on_focus(&a, window, |this: &mut FocusForwarder, window, cx| {
9596 let b = this.b.clone();
9597 window.focus(&b, cx);
9598 })
9599 .detach();
9600 cx.on_focus(&b, window, move |_, _, _| {
9601 b_focus_count.set(b_focus_count.get() + 1);
9602 })
9603 .detach();
9604 FocusForwarder { a, b }
9605 }
9606 });
9607
9608 window
9609 .update(cx, |_, window, _| window.activate_window())
9610 .unwrap();
9611 cx.executor().run_until_parked();
9612
9613 window
9614 .update(cx, |this, window, cx| {
9615 let a = this.a.clone();
9616 window.focus(&a, cx);
9617 })
9618 .unwrap();
9619 cx.executor().run_until_parked();
9620
9621 window
9622 .update(cx, |this, window, _| {
9623 assert!(this.b.is_focused(window));
9624 })
9625 .unwrap();
9626 assert_eq!(b_focus_count.get(), 1);
9627 }
9628
9629 #[gpui::test]
9630 fn claimed_touch_drag_receives_movement_and_release(cx: &mut TestAppContext) {
9631 let events = Rc::new(RefCell::new(Vec::new()));
9632 let window = cx.add_window({
9633 let events = events.clone();
9634 move |_, _| TouchDragListener { events }
9635 });
9636 let touch = TouchId(1);
9637
9638 dispatch_touch(window, cx, touch, TouchPhase::Started, 10.);
9639 dispatch_touch(window, cx, touch, TouchPhase::Moved, 30.);
9640 dispatch_touch(window, cx, touch, TouchPhase::Ended, 40.);
9641
9642 assert_eq!(
9643 events.borrow().as_slice(),
9644 [
9645 (TouchPhase::Started, px(10.)),
9646 (TouchPhase::Moved, px(30.)),
9647 (TouchPhase::Ended, px(40.)),
9648 ]
9649 );
9650 }
9651
9652 #[gpui::test]
9653 fn back_gesture_is_claimed_by_the_innermost_listener(cx: &mut TestAppContext) {
9654 let claims = Rc::new(RefCell::new(Vec::new()));
9655 let window = cx.add_window({
9656 let claims = claims.clone();
9657 move |_, _| BackListeners { claims }
9658 });
9659 let dispatch_back = |cx: &mut TestAppContext, x: f32| {
9660 window
9661 .update(cx, |_, window, cx| {
9662 window
9663 .dispatch_event(
9664 BackGestureEvent {
9665 phase: BackGesturePhase::Started,
9666 start_position: point(px(x), px(10.)),
9667 progress: 0.,
9668 }
9669 .to_platform_input(),
9670 cx,
9671 )
9672 .default_prevented
9673 })
9674 .unwrap()
9675 };
9676 assert!(dispatch_back(cx, 10.));
9677 assert!(dispatch_back(cx, 80.));
9678 assert!(!dispatch_back(cx, 150.));
9679 assert_eq!(claims.borrow().as_slice(), ["inner", "outer"]);
9680 }
9681
9682 struct BackListeners {
9683 claims: Rc<RefCell<Vec<&'static str>>>,
9684 }
9685
9686 impl Render for BackListeners {
9687 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
9688 let claim = |name: &'static str| {
9689 let claims = self.claims.clone();
9690 move |_: &BackGestureEvent, _: &mut Window, _: &mut crate::App| {
9691 claims.borrow_mut().push(name)
9692 }
9693 };
9694 div().size_full().child(
9695 div()
9696 .w(px(100.))
9697 .h_full()
9698 .on_back_gesture(claim("outer"))
9699 .child(div().w(px(50.)).h_full().on_back_gesture(claim("inner"))),
9700 )
9701 }
9702 }
9703
9704 struct TouchDragListener {
9705 events: Rc<RefCell<Vec<(TouchPhase, Pixels)>>>,
9706 }
9707
9708 impl Render for TouchDragListener {
9709 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
9710 let events = self.events.clone();
9711 canvas(
9712 |_, _, _| {},
9713 move |_, _, window, _| {
9714 window.on_mouse_event(move |event: &TouchDragEvent, phase, window, _cx| {
9715 if phase != DispatchPhase::Bubble {
9716 return;
9717 }
9718 events.borrow_mut().push((event.phase, event.position.x));
9719 if event.phase == TouchPhase::Started {
9720 window.prevent_default();
9721 }
9722 });
9723 },
9724 )
9725 }
9726 }
9727
9728 #[gpui::test]
9729 fn long_press_is_claimed_only_when_started_prevents_default(cx: &mut TestAppContext) {
9730 for response in [
9731 LongPressResponse::PreventDefault,
9732 LongPressResponse::StopPropagation,
9733 LongPressResponse::None,
9734 ] {
9735 let phases = Rc::new(RefCell::new(Vec::new()));
9736 let window = cx.add_window({
9737 let phases = phases.clone();
9738 move |_, _| LongPressListener { phases, response }
9739 });
9740 dispatch_touch(window, cx, TouchId(1), TouchPhase::Started, 0.);
9741 cx.executor().advance_clock(Duration::from_millis(501));
9742 cx.executor().run_until_parked();
9743 window
9744 .update(cx, |_, window, _| {
9745 assert_eq!(
9746 window.long_press_capture.is_some(),
9747 response == LongPressResponse::PreventDefault
9748 );
9749 })
9750 .unwrap();
9751 dispatch_touch(window, cx, TouchId(1), TouchPhase::Moved, 2.);
9752 dispatch_touch(window, cx, TouchId(1), TouchPhase::Ended, 2.);
9753 window
9754 .update(cx, |_, window, _| {
9755 assert!(window.long_press_capture.is_none());
9756 })
9757 .unwrap();
9758
9759 let phases = phases.borrow();
9760 if response == LongPressResponse::PreventDefault {
9761 assert_eq!(
9762 phases.as_slice(),
9763 [TouchPhase::Started, TouchPhase::Moved, TouchPhase::Ended]
9764 );
9765 } else {
9766 assert_eq!(phases.as_slice(), [TouchPhase::Started]);
9767 }
9768 }
9769 }
9770
9771 #[gpui::test]
9772 fn stale_default_prevention_does_not_claim_long_press(cx: &mut TestAppContext) {
9773 let phases = Rc::new(RefCell::new(Vec::new()));
9774 let window = cx.add_window({
9775 let phases = phases.clone();
9776 move |_, _| LongPressListener {
9777 phases,
9778 response: LongPressResponse::None,
9779 }
9780 });
9781 window
9782 .update(cx, |_, window, _| {
9783 window.prevent_default();
9784 })
9785 .unwrap();
9786
9787 dispatch_touch(window, cx, TouchId(1), TouchPhase::Started, 0.);
9788 cx.executor().advance_clock(Duration::from_millis(501));
9789 cx.executor().run_until_parked();
9790 dispatch_touch(window, cx, TouchId(1), TouchPhase::Moved, 2.);
9791
9792 assert_eq!(phases.borrow().as_slice(), [TouchPhase::Started]);
9793 }
9794
9795 #[gpui::test]
9796 fn resolved_touch_cancels_scheduled_long_press(cx: &mut TestAppContext) {
9797 for (phase, position) in [
9798 (TouchPhase::Ended, 0.),
9799 (TouchPhase::Cancelled, 0.),
9800 (TouchPhase::Moved, 20.),
9801 ] {
9802 let phases = Rc::new(RefCell::new(Vec::new()));
9803 let window = cx.add_window({
9804 let phases = phases.clone();
9805 move |_, _| LongPressListener {
9806 phases,
9807 response: LongPressResponse::PreventDefault,
9808 }
9809 });
9810 dispatch_touch(window, cx, TouchId(1), TouchPhase::Started, 0.);
9811 dispatch_touch(window, cx, TouchId(1), phase, position);
9812 cx.executor().advance_clock(Duration::from_millis(501));
9813 cx.executor().run_until_parked();
9814
9815 assert!(phases.borrow().is_empty(), "{phase:?} allowed long press");
9816 }
9817 }
9818
9819 #[gpui::test]
9820 fn stale_long_press_timer_cannot_affect_replacement_touch(cx: &mut TestAppContext) {
9821 let phases = Rc::new(RefCell::new(Vec::new()));
9822 let window = cx.add_window({
9823 let phases = phases.clone();
9824 move |_, _| LongPressListener {
9825 phases,
9826 response: LongPressResponse::PreventDefault,
9827 }
9828 });
9829 let first_touch = TouchId(1);
9830 dispatch_touch(window, cx, first_touch, TouchPhase::Started, 0.);
9831 cx.executor().advance_clock(Duration::from_millis(250));
9832 dispatch_touch(window, cx, first_touch, TouchPhase::Cancelled, 0.);
9833 dispatch_touch(window, cx, TouchId(2), TouchPhase::Started, 10.);
9834
9835 cx.executor().advance_clock(Duration::from_millis(251));
9836 cx.executor().run_until_parked();
9837 assert!(phases.borrow().is_empty());
9838
9839 cx.executor().advance_clock(Duration::from_millis(250));
9840 cx.executor().run_until_parked();
9841 assert_eq!(phases.borrow().as_slice(), [TouchPhase::Started]);
9842 }
9843
9844 #[gpui::test]
9845 fn test_underline_exclusions_preserve_device_geometry(cx: &mut TestAppContext) {
9846 test_underline_paint_at_scales(cx, |window| {
9847 let (numerator, denominator) = match window.scale_factor() {
9848 1.0 => (1, 1),
9849 1.25 => (5, 4),
9850 1.5 => (3, 2),
9851 2.0 => (2, 1),
9852 3.0 => (3, 1),
9853 scale => panic!("unexpected scale {scale}"),
9854 };
9855 let snap = |quarters: i32| {
9856 let scaled = quarters * numerator;
9857 let divisor = 4 * denominator;
9858 let rounded =
9859 scaled.abs() / divisor + i32::from(2 * (scaled.abs() % divisor) > divisor);
9860 ScaledPixels((scaled.signum() * rounded) as f32)
9861 };
9862 let stroke = |quarters| {
9863 if quarters == 0 {
9864 ScaledPixels(0.)
9865 } else {
9866 snap(quarters).max(ScaledPixels(1.))
9867 }
9868 };
9869
9870 for origin in [-101, -3, -2, -1, 0, 1, 2, 3, 101] {
9871 for width in [0, 1, 3, 84] {
9872 for thickness in [0, 1, 4, 7] {
9873 for wavy in [false, true] {
9874 let style = UnderlineStyle {
9875 thickness: px(thickness as f32 / 4.),
9876 color: Some(hsla(0.25, 0.5, 0.75, 0.5)),
9877 wavy,
9878 };
9879 let bounds = Bounds::new(
9880 point(snap(origin), snap(-origin)),
9881 size(
9882 stroke(width),
9883 stroke(thickness) * if wavy { 3. } else { 1. },
9884 ),
9885 );
9886 let origin = point(px(origin as f32 / 4.), px(-origin as f32 / 4.));
9887 let width = px(width as f32 / 4.);
9888 assert_eq!(window.underline_bounds(origin, width, &style), bounds);
9889 let original = paint_test_underlines(window, |window| {
9890 window.paint_underline(origin, width, &style);
9891 })
9892 .to_vec();
9893 assert_eq!(original.len(), usize::from(!bounds.is_empty()));
9894 if let Some(underline) = original.first() {
9895 assert_eq!(underline.bounds, bounds);
9896 assert_eq!(underline.thickness, stroke(thickness));
9897 assert_eq!(underline.wavy, wavy.into());
9898 assert_eq!(underline.color, hsla(0.25, 0.5, 0.75, 0.5));
9899 }
9900
9901 let underlines = paint_test_underlines(window, |window| {
9902 window.paint_underline_with_exclusions(origin, width, &style, &[]);
9903 });
9904 assert_eq!(underlines.len(), original.len());
9905 for (actual, original) in underlines.iter().zip(&original) {
9906 assert_same_underline_geometry(actual, original);
9907 assert_eq!(actual.content_mask, original.content_mask);
9908 }
9909
9910 let left = bounds.left().0 as i32;
9911 let right = bounds.right().0 as i32;
9912 let exclusions = [
9913 (15, 25),
9914 (3, 7),
9915 (5, 10),
9916 (10, 12),
9917 (0, 0),
9918 (4, 2),
9919 (-50, -1),
9920 (70, 100),
9921 ]
9922 .map(|(start, end)| {
9923 ScaledPixels((left + start) as f32)
9924 ..ScaledPixels((left + end) as f32)
9925 });
9926 let underlines = paint_test_underlines(window, |window| {
9927 window.paint_underline_with_exclusions(
9928 origin,
9929 width,
9930 &style,
9931 &exclusions,
9932 );
9933 });
9934 let expected = (left..right)
9935 .filter(|column| {
9936 !bounds.is_empty()
9937 && !exclusions.iter().any(|span| {
9938 span.contains(&ScaledPixels(*column as f32))
9939 })
9940 })
9941 .collect::<Vec<_>>();
9942 assert_eq!(underline_device_columns(underlines), expected);
9943 for underline in underlines {
9944 assert_same_underline_geometry(underline, &original[0]);
9945 }
9946 }
9947 }
9948 }
9949 }
9950 });
9951 }
9952
9953 #[gpui::test]
9954 fn test_underline_exclusions_preserve_helvetica_warning_edge(cx: &mut TestAppContext) {
9955 test_underline_paint_at_scales(cx, |window| {
9956 if window.scale_factor() != 2.0 {
9957 return;
9958 }
9959 let style = UnderlineStyle {
9960 thickness: px(1.),
9961 color: None,
9962 wavy: true,
9963 };
9964 let origin = point(px(2.888_183_8), px(15.357_917));
9965 let width = px(23.848_145) - origin.x;
9966 let original = paint_test_underlines(window, |window| {
9967 window.paint_underline(origin, width, &style);
9968 })[0];
9969 assert_eq!(original.bounds.left(), ScaledPixels(6.));
9970 assert_eq!(original.bounds.right(), ScaledPixels(48.));
9971 let point_bounds = window.underline_bounds(origin, px(10.829_102), &style);
9972 assert_eq!(point_bounds.left(), ScaledPixels(6.));
9973 assert_eq!(point_bounds.right(), ScaledPixels(28.));
9974
9975 let underlines = paint_test_underlines(window, |window| {
9976 window.paint_underline_with_exclusions(
9977 origin,
9978 width,
9979 &style,
9980 &[point_bounds.left()..point_bounds.right()],
9981 );
9982 });
9983 assert_eq!(underlines.len(), 1);
9984 assert_same_underline_geometry(&underlines[0], &original);
9985 assert_eq!(
9986 underline_device_columns(underlines),
9987 (28..48).collect::<Vec<_>>()
9988 );
9989 });
9990 }
9991
9992 #[gpui::test]
9993 fn test_underline_exclusions_preserve_content_mask_and_opacity(cx: &mut TestAppContext) {
9994 test_underline_paint_at_scales(cx, |window| {
9995 let scale = window.scale_factor();
9996 let original_mask = window.content_mask();
9997 let original_opacity = window.element_opacity();
9998 let mask = ContentMask {
9999 bounds: Bounds::from_corners(point(px(4.), px(10.)), point(px(16.), px(12.))),
10000 };
10001 let style = UnderlineStyle {
10002 thickness: px(2.),
10003 color: Some(hsla(0.25, 0.5, 0.75, 0.5)),
10004 wavy: true,
10005 };
10006 let origin = point(px(-1.25), px(9.25));
10007 let width = px(24.);
10008 window.with_content_mask(Some(mask), |window| {
10009 window.with_element_opacity(Some(0.5), |window| {
10010 window.with_element_opacity(Some(0.5), |window| {
10011 let original = paint_test_underlines(window, |window| {
10012 window.paint_underline(origin, width, &style);
10013 })[0];
10014 assert_eq!(original.color, hsla(0.25, 0.5, 0.75, 0.125));
10015 let underlines = paint_test_underlines(window, |window| {
10016 window.paint_underline_with_exclusions(
10017 origin,
10018 width,
10019 &style,
10020 &[ScaledPixels(6. * scale)..ScaledPixels(8. * scale)],
10021 );
10022 });
10023 assert_eq!(underlines.len(), 2);
10024 let expected = [(4. * scale, 6. * scale), (8. * scale, 16. * scale)].map(
10025 |(left, right)| {
10026 Bounds::from_corners(
10027 point(ScaledPixels(left), ScaledPixels((10. * scale).floor())),
10028 point(ScaledPixels(right), ScaledPixels((12. * scale).ceil())),
10029 )
10030 },
10031 );
10032 assert_eq!(
10033 underlines
10034 .iter()
10035 .map(|underline| underline.content_mask.bounds)
10036 .collect::<Vec<_>>(),
10037 expected
10038 );
10039 for underline in underlines {
10040 assert_same_underline_geometry(underline, &original);
10041 }
10042 });
10043 });
10044 });
10045 assert_eq!(window.content_mask(), original_mask);
10046 assert_eq!(window.element_opacity(), original_opacity);
10047 });
10048 }
10049
10050 #[gpui::test]
10051 fn test_underline_exclusions_adversarial_permutations(cx: &mut TestAppContext) {
10052 test_underline_paint_at_scales(cx, |window| {
10053 let style = UnderlineStyle {
10054 thickness: px(1.),
10055 color: None,
10056 wavy: true,
10057 };
10058 let origin = point(px(-4.), px(-1.));
10059 let width = px(8.);
10060 let left = (-4. * window.scale_factor()) as i32;
10061 let right = (4. * window.scale_factor()) as i32;
10062 let endpoints = [-16, -8, -1, 0, 1, 8, 16];
10063 for first_start in endpoints {
10064 for first_end in endpoints {
10065 for second_start in endpoints {
10066 for second_end in endpoints {
10067 let exclusions = [first_start..first_end, second_start..second_end];
10068 let expected = (left..right)
10069 .filter(|column| {
10070 !exclusions.iter().any(|span| span.contains(column))
10071 })
10072 .collect::<Vec<_>>();
10073 let exclusions = exclusions.map(|span| {
10074 ScaledPixels(span.start as f32)..ScaledPixels(span.end as f32)
10075 });
10076 let underlines = paint_test_underlines(window, |window| {
10077 window.paint_underline_with_exclusions(
10078 origin,
10079 width,
10080 &style,
10081 &exclusions,
10082 );
10083 });
10084 assert_eq!(
10085 underline_device_columns(underlines),
10086 expected,
10087 "{exclusions:?}"
10088 );
10089 }
10090 }
10091 }
10092 }
10093 });
10094 }
10095
10096 #[derive(Clone, Copy, PartialEq)]
10097 enum LongPressResponse {
10098 PreventDefault,
10099 StopPropagation,
10100 None,
10101 }
10102
10103 struct LongPressListener {
10104 phases: Rc<RefCell<Vec<TouchPhase>>>,
10105 response: LongPressResponse,
10106 }
10107
10108 impl Render for LongPressListener {
10109 fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
10110 let entity = cx.entity();
10111 let phases = self.phases.clone();
10112 let response = self.response;
10113 canvas(
10114 |_, _, _| {},
10115 move |_, _, window, _| {
10116 window.on_mouse_event(move |event: &LongPressEvent, phase, window, cx| {
10117 if phase != DispatchPhase::Bubble {
10118 return;
10119 }
10120 phases.borrow_mut().push(event.phase);
10121 match response {
10122 LongPressResponse::PreventDefault => {
10123 window.capture_long_press(&entity);
10124 window.prevent_default();
10125 }
10126 LongPressResponse::StopPropagation => cx.stop_propagation(),
10127 LongPressResponse::None => {}
10128 }
10129 });
10130 },
10131 )
10132 }
10133 }
10134
10135 struct UnderlineTestView(Rc<dyn Fn(&mut Window)>);
10136
10137 impl Render for UnderlineTestView {
10138 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
10139 let paint = self.0.clone();
10140 canvas(
10141 |_, _, _| {},
10142 move |_, _, window, _| {
10143 window.content_mask_stack.push(ContentMask {
10144 bounds: Bounds::from_corners(
10145 point(px(-1000.), px(-1000.)),
10146 point(px(1000.), px(1000.)),
10147 ),
10148 });
10149 paint(window);
10150 window.content_mask_stack.pop();
10151 },
10152 )
10153 .size_full()
10154 }
10155 }
10156
10157 fn test_underline_paint_at_scales(
10158 cx: &mut TestAppContext,
10159 paint: impl Fn(&mut Window) + 'static,
10160 ) {
10161 let window = cx.add_window(move |_, _| UnderlineTestView(Rc::new(paint)));
10162 for scale in [1., 1.25, 1.5, 2., 3.] {
10163 cx.simulate_window_scale_factor_change(window.into(), scale);
10164 cx.update_window(window.into(), |_, window, cx| window.draw(cx).clear(cx))
10165 .unwrap();
10166 }
10167 }
10168
10169 fn paint_test_underlines(window: &mut Window, paint: impl FnOnce(&mut Window)) -> &[Underline] {
10170 window.next_frame.scene.clear();
10171 paint(window);
10172 &window.next_frame.scene.underlines
10173 }
10174
10175 fn assert_same_underline_geometry(actual: &Underline, expected: &Underline) {
10176 assert_eq!(actual.bounds, expected.bounds);
10177 assert_eq!(actual.thickness, expected.thickness);
10178 assert_eq!(actual.wavy, expected.wavy);
10179 assert_eq!(actual.color, expected.color);
10180 assert_eq!(actual.pad, expected.pad);
10181 }
10182
10183 fn underline_device_columns(underlines: &[Underline]) -> Vec<i32> {
10184 underlines
10185 .iter()
10186 .flat_map(|underline| {
10187 let bounds = underline.bounds.intersect(&underline.content_mask.bounds);
10188 bounds.left().0 as i32..bounds.right().0 as i32
10189 })
10190 .collect()
10191 }
10192
10193 actions!(window_test, [StandaloneModifierAction]);
10194
10195 struct StandaloneModifierView {
10196 focus_handle: FocusHandle,
10197 action_count: usize,
10198 }
10199
10200 impl Render for StandaloneModifierView {
10201 fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
10202 div()
10203 .key_context("StandaloneModifier")
10204 .track_focus(&self.focus_handle)
10205 .on_action(cx.listener(|this, _: &StandaloneModifierAction, _, _| {
10206 this.action_count += 1;
10207 }))
10208 }
10209 }
10210
10211 #[gpui::test]
10212 fn test_standalone_modifier_sequence_timeout_and_recovery(cx: &mut TestAppContext) {
10213 use crate::{KeyBinding, Modifiers};
10214
10215 cx.update(|cx| {
10216 cx.bind_keys([KeyBinding::new(
10217 "shift shift",
10218 StandaloneModifierAction,
10219 Some("StandaloneModifier"),
10220 )]);
10221 });
10222 let (view, cx) = cx.add_window_view(|window, cx| {
10223 let focus_handle = cx.focus_handle();
10224 window.focus(&focus_handle, cx);
10225 StandaloneModifierView {
10226 focus_handle,
10227 action_count: 0,
10228 }
10229 });
10230 let shift = Modifiers {
10231 shift: true,
10232 ..Modifiers::default()
10233 };
10234 cx.simulate_modifiers_change(shift);
10235 cx.simulate_modifiers_change(Modifiers::default());
10236 cx.update(|window, _| {
10237 assert_eq!(
10238 window.pending_input_keystrokes(),
10239 Some([Keystroke::parse("shift").expect("valid keystroke")].as_slice())
10240 );
10241 });
10242 cx.background_executor.advance_clock(Duration::from_secs(2));
10243 cx.run_until_parked();
10244 cx.update(|window, cx| {
10245 assert!(!window.has_pending_keystrokes());
10246 assert_eq!(view.read(cx).action_count, 0);
10247 });
10248
10249 cx.simulate_modifiers_change(shift);
10250 cx.simulate_modifiers_change(Modifiers::default());
10251 cx.update(|window, cx| {
10252 assert!(window.has_pending_keystrokes());
10253 assert_eq!(view.read(cx).action_count, 0);
10254 });
10255
10256 cx.simulate_modifiers_change(shift);
10257 cx.simulate_modifiers_change(Modifiers::default());
10258 cx.update(|window, cx| {
10259 assert!(!window.has_pending_keystrokes());
10260 assert_eq!(view.read(cx).action_count, 1);
10261 });
10262 }
10263
10264 #[gpui::test]
10265 fn test_standalone_modifier_sequence_dispatches_before_timeout(cx: &mut TestAppContext) {
10266 use crate::{KeyBinding, Modifiers};
10267
10268 cx.update(|cx| {
10269 cx.bind_keys([KeyBinding::new(
10270 "shift shift",
10271 StandaloneModifierAction,
10272 Some("StandaloneModifier"),
10273 )]);
10274 });
10275 let (view, cx) = cx.add_window_view(|window, cx| {
10276 let focus_handle = cx.focus_handle();
10277 window.focus(&focus_handle, cx);
10278 StandaloneModifierView {
10279 focus_handle,
10280 action_count: 0,
10281 }
10282 });
10283 let shift = Modifiers {
10284 shift: true,
10285 ..Modifiers::default()
10286 };
10287 cx.simulate_modifiers_change(shift);
10288 cx.simulate_modifiers_change(Modifiers::default());
10289 cx.background_executor
10290 .advance_clock(Duration::from_millis(500));
10291 cx.update(|window, cx| {
10292 assert!(window.has_pending_keystrokes());
10293 assert_eq!(view.read(cx).action_count, 0);
10294 });
10295 cx.simulate_modifiers_change(shift);
10296 cx.simulate_modifiers_change(Modifiers::default());
10297 cx.update(|window, cx| {
10298 assert!(!window.has_pending_keystrokes());
10299 assert_eq!(view.read(cx).action_count, 1);
10300 });
10301 cx.background_executor.advance_clock(Duration::from_secs(2));
10302 cx.run_until_parked();
10303 cx.update(|window, cx| {
10304 assert!(!window.has_pending_keystrokes());
10305 assert_eq!(view.read(cx).action_count, 1);
10306 });
10307 }
10308
10309 fn dispatch_touch<T: 'static>(
10310 window: crate::WindowHandle<T>,
10311 cx: &mut TestAppContext,
10312 id: TouchId,
10313 phase: TouchPhase,
10314 x: f32,
10315 ) {
10316 window
10317 .update(cx, |_, window, cx| {
10318 window.dispatch_event(
10319 TouchEvent {
10320 id,
10321 phase,
10322 position: point(px(x), px(0.)),
10323 predicted_position: None,
10324 force: None,
10325 }
10326 .to_platform_input(),
10327 cx,
10328 );
10329 })
10330 .unwrap();
10331 }
10332
10333 #[gpui::test]
10334 fn cached_view_follows_inherited_opacity(cx: &mut TestAppContext) {
10335 struct Child {
10336 renders: usize,
10337 }
10338
10339 impl Render for Child {
10340 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
10341 self.renders += 1;
10342 div().size_full().bg(crate::rgb(0xff0000))
10343 }
10344 }
10345
10346 struct Root {
10347 opacity: f32,
10348 width: Pixels,
10349 child: crate::Entity<Child>,
10350 }
10351
10352 impl Render for Root {
10353 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
10354 div()
10355 .w(self.width)
10356 .h(px(200.))
10357 .opacity(self.opacity)
10358 .hover(|style| style.opacity(1.))
10359 .child(
10360 self.child
10361 .clone()
10362 .cached(crate::StyleRefinement::default().size(px(100.))),
10363 )
10364 }
10365 }
10366
10367 fn update_and_draw(
10370 window: crate::WindowHandle<Root>,
10371 child: &crate::Entity<Child>,
10372 cx: &mut TestAppContext,
10373 update: impl FnOnce(&mut Root, &mut Window, &mut Context<Root>),
10374 ) -> (f32, usize) {
10375 window
10376 .update(cx, |root, window, cx| {
10377 update(root, window, cx);
10378 cx.notify();
10379 })
10380 .expect("update root");
10381 for _ in 0..3 {
10382 let (alpha, renders, requested_frames) = cx
10383 .update_window(window.into(), |_, window, cx| {
10384 window.draw(cx).clear(cx);
10385 let quads = &window.rendered_frame.scene.quads;
10386 assert_eq!(quads.len(), 1);
10387 let alpha = quads.first().expect("child quad").background.solid.a;
10388 (
10389 alpha,
10390 child.read(cx).renders,
10391 window.simulate_next_frame(cx),
10392 )
10393 })
10394 .expect("draw window");
10395 if requested_frames == 0 {
10396 return (alpha, renders);
10397 }
10398 }
10399 panic!("cached child kept requesting frames");
10400 }
10401
10402 let child = cx.new(|_| Child { renders: 0 });
10403 let window = cx.add_window(|_, _| Root {
10404 opacity: 1.,
10405 width: px(200.),
10406 child: child.clone(),
10407 });
10408 let outside = point(px(500.), px(500.));
10409 let inside = point(px(50.), px(50.));
10410 let beside = point(px(250.), px(50.));
10411
10412 let move_outside = |_: &mut Root, window: &mut Window, cx: &mut Context<Root>| {
10414 window.simulate_mouse_move(outside, cx)
10415 };
10416 assert_eq!(update_and_draw(window, &child, cx, move_outside), (1., 1));
10417 assert_eq!(update_and_draw(window, &child, cx, |_, _, _| {}), (1., 1));
10418 let fade = |root: &mut Root, _: &mut Window, _: &mut Context<Root>| root.opacity = 0.5;
10419 assert_eq!(update_and_draw(window, &child, cx, fade), (0.5, 2));
10420 assert_eq!(update_and_draw(window, &child, cx, |_, _, _| {}), (0.5, 2));
10421
10422 let move_inside = |_: &mut Root, window: &mut Window, cx: &mut Context<Root>| {
10424 window.simulate_mouse_move(inside, cx)
10425 };
10426 assert_eq!(update_and_draw(window, &child, cx, move_inside).0, 1.);
10427 let move_beside = |_: &mut Root, window: &mut Window, cx: &mut Context<Root>| {
10428 window.simulate_mouse_move(beside, cx)
10429 };
10430 assert_eq!(update_and_draw(window, &child, cx, move_beside).0, 0.5);
10431
10432 let widen = |root: &mut Root, _: &mut Window, _: &mut Context<Root>| root.width = px(300.);
10435 assert_eq!(update_and_draw(window, &child, cx, widen).0, 1.);
10436 }
10437
10438 #[gpui::test]
10439 fn test_drag_cursor_applies_before_next_paint(cx: &mut TestAppContext) {
10440 struct ResizeHandle;
10441
10442 impl Render for ResizeHandle {
10443 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
10444 div().size_full().cursor_text().child(
10445 div()
10446 .id("resize-handle")
10447 .w(px(4.))
10448 .h_full()
10449 .cursor_col_resize()
10450 .on_drag((), |_, _, _, cx| cx.new(|_| Empty)),
10451 )
10452 }
10453 }
10454
10455 let window = cx.add_window(|_, _| ResizeHandle);
10456 let handle_position = point(px(1.), px(10.));
10457 let drag_position = point(px(20.), px(10.));
10458 let cursor_observer = cx.clone();
10459
10460 let update_result = cx.update_window(window.into(), |_, window, cx| {
10461 window.hovered.set(true);
10462 window.active.set(true);
10463 window.draw(cx).clear(cx);
10464 window.dispatch_event(
10465 MouseMoveEvent {
10466 position: handle_position,
10467 ..Default::default()
10468 }
10469 .to_platform_input(),
10470 cx,
10471 );
10472 window.dispatch_event(
10473 MouseDownEvent {
10474 position: handle_position,
10475 button: MouseButton::Left,
10476 ..Default::default()
10477 }
10478 .to_platform_input(),
10479 cx,
10480 );
10481 window.dispatch_event(
10482 MouseMoveEvent {
10483 position: drag_position,
10484 pressed_button: Some(MouseButton::Left),
10485 ..Default::default()
10486 }
10487 .to_platform_input(),
10488 cx,
10489 );
10490 assert!(cx.has_active_drag());
10491 assert_eq!(
10493 cursor_observer.test_platform().cursor_style(),
10494 CursorStyle::ResizeColumn
10495 );
10496
10497 window.draw(cx).clear(cx);
10498 window.dispatch_event(
10499 MouseUpEvent {
10500 position: drag_position,
10501 button: MouseButton::Left,
10502 ..Default::default()
10503 }
10504 .to_platform_input(),
10505 cx,
10506 );
10507 window.draw(cx).clear(cx);
10508 });
10509 assert!(
10510 update_result.is_ok(),
10511 "failed to run drag: {update_result:?}"
10512 );
10513 assert_eq!(
10514 cursor_observer.test_platform().cursor_style(),
10515 CursorStyle::IBeam
10516 );
10517 }
10518}
10519
10520#[cfg(all(test, any(feature = "inspector", debug_assertions)))]
10521mod inspector_tests {
10522 use super::*;
10523 use crate::{
10524 DivInspectorState, InspectorElementId, MouseDownEvent, StyleRefinement, TestAppContext, div,
10525 };
10526
10527 #[gpui::test]
10528 fn inspector_only_tracks_its_open_window(cx: &mut TestAppContext) {
10529 let windows = [
10530 cx.add_window(|_, cx| InspectorTestRoot {
10531 child: cx.new(|_| InspectorTestView::default()),
10532 }),
10533 cx.add_window(|_, cx| InspectorTestRoot {
10534 child: cx.new(|_| InspectorTestView::default()),
10535 }),
10536 ];
10537 for window in windows {
10538 assert_closed_inspector(window.into(), cx);
10539 }
10540 for _ in 0..2 {
10541 cx.update_window(windows[0].into(), |root, window, cx| {
10542 let root = root.downcast::<InspectorTestRoot>().expect("test root");
10543 let child_widths = root.read(cx).child.read(cx).child_widths.clone();
10544 window.toggle_inspector(cx);
10545 window.draw(cx).clear(cx);
10546 assert_eq!(child_widths.borrow().as_slice(), &[px(10.); 3]);
10547 let path = window
10548 .rendered_frame
10549 .next_inspector_instance_ids
10550 .iter()
10551 .find_map(|(path, count)| (*count == 3).then(|| path.clone()))
10552 .expect("anonymous siblings share an inspector path");
10553 let selected_id = InspectorElementId {
10554 path,
10555 instance_id: 1,
10556 };
10557 let position = window
10558 .rendered_frame
10559 .hitboxes
10560 .iter()
10561 .find(|hitbox| {
10562 window.rendered_frame.inspector_hitboxes.get(&hitbox.id)
10563 == Some(&selected_id)
10564 })
10565 .expect("middle sibling is pickable")
10566 .bounds
10567 .center();
10568 window.simulate_mouse_move(position, cx);
10569 window.dispatch_event(
10570 PlatformInput::MouseDown(MouseDownEvent {
10571 position,
10572 button: MouseButton::Left,
10573 modifiers: Modifiers::default(),
10574 click_count: 1,
10575 first_mouse: false,
10576 }),
10577 cx,
10578 );
10579 window.dispatch_event(
10580 PlatformInput::MouseUp(MouseUpEvent {
10581 position,
10582 button: MouseButton::Left,
10583 modifiers: Modifiers::default(),
10584 click_count: 1,
10585 }),
10586 cx,
10587 );
10588 assert!(!window.is_inspector_picking(cx));
10589 assert_eq!(
10590 window
10591 .inspector
10592 .as_ref()
10593 .expect("open inspector")
10594 .read(cx)
10595 .active_element_id(),
10596 Some(&selected_id)
10597 );
10598 window.draw(cx).clear(cx);
10599 window.with_inspector_state::<DivInspectorState, _>(
10600 Some(&selected_id),
10601 cx,
10602 |state, _| {
10603 let state = state.as_mut().expect("selected div has style state");
10604 assert_eq!(
10605 state.base_style.size.width,
10606 Some(crate::Length::from(px(10.)))
10607 );
10608 state.base_style.size.width = Some(crate::Length::from(px(25.)));
10609 },
10610 );
10611 window.refresh();
10612 window.draw(cx).clear(cx);
10613 assert_eq!(
10614 child_widths.borrow().as_slice(),
10615 &[px(10.), px(25.), px(10.)]
10616 );
10617 })
10618 .expect("pick and edit a cached child");
10619 assert_closed_inspector(windows[1].into(), cx);
10620 windows[0]
10621 .update(cx, |_, window, cx| window.toggle_inspector(cx))
10622 .expect("close inspector");
10623 assert_closed_inspector(windows[0].into(), cx);
10624 }
10625 }
10626
10627 struct InspectorTestRoot {
10628 child: Entity<InspectorTestView>,
10629 }
10630
10631 impl Render for InspectorTestRoot {
10632 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
10633 self.child
10634 .clone()
10635 .cached(StyleRefinement::default().size(px(100.)))
10636 }
10637 }
10638
10639 #[derive(Default)]
10640 struct InspectorTestView {
10641 child_widths: Rc<RefCell<Vec<Pixels>>>,
10642 }
10643
10644 impl Render for InspectorTestView {
10645 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
10646 let child_widths = self.child_widths.clone();
10647 div()
10648 .on_children_prepainted(move |bounds, _, _| {
10649 *child_widths.borrow_mut() =
10650 bounds.iter().map(|bounds| bounds.size.width).collect();
10651 })
10652 .with_dynamic_prepaint_order(|_, _| SmallVec::from_iter([2, 0, 1]))
10653 .id("inspector-root")
10654 .flex()
10655 .children((0..3).map(|_| div().size(px(10.)).flex_shrink_0()))
10656 }
10657 }
10658
10659 fn assert_closed_inspector(window: AnyWindowHandle, cx: &mut TestAppContext) {
10660 cx.update_window(window, |root, window, cx| {
10661 window.draw(cx).clear(cx);
10662 window.draw(cx).clear(cx);
10663 let root = root.downcast::<InspectorTestRoot>().expect("test root");
10664 assert_eq!(
10665 root.read(cx)
10666 .child
10667 .read(cx)
10668 .child_widths
10669 .borrow()
10670 .as_slice(),
10671 &[px(10.); 3]
10672 );
10673 for frame in [&window.rendered_frame, &window.next_frame] {
10674 assert_eq!(frame.next_inspector_instance_ids.capacity(), 0);
10675 assert_eq!(frame.inspector_hitboxes.capacity(), 0);
10676 }
10677 })
10678 .expect("closed inspector has no bookkeeping and no style overrides");
10679 }
10680}