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