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