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