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