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) {
2802 let entity = self.current_view();
2803 self.on_next_frame(move |_, cx| cx.notify(entity));
2804 }
2805
2806 #[cfg(any(test, feature = "test-support"))]
2811 pub fn simulate_next_frame(&mut self, cx: &mut App) -> usize {
2812 let callbacks = self.next_frame_callbacks.take();
2813 let count = callbacks.len();
2814 for callback in callbacks {
2815 callback(self, cx);
2816 }
2817 count
2818 }
2819
2820 #[track_caller]
2824 pub fn spawn<AsyncFn, R>(&self, cx: &App, f: AsyncFn) -> Task<R>
2825 where
2826 R: 'static,
2827 AsyncFn: AsyncFnOnce(&mut AsyncWindowContext) -> R + 'static,
2828 {
2829 let handle = self.handle;
2830 cx.spawn(async move |app| {
2831 let mut async_window_cx = AsyncWindowContext::new_context(app.clone(), handle);
2832 f(&mut async_window_cx).await
2833 })
2834 }
2835
2836 #[track_caller]
2840 pub fn spawn_with_priority<AsyncFn, R>(
2841 &self,
2842 priority: Priority,
2843 cx: &App,
2844 f: AsyncFn,
2845 ) -> Task<R>
2846 where
2847 R: 'static,
2848 AsyncFn: AsyncFnOnce(&mut AsyncWindowContext) -> R + 'static,
2849 {
2850 let handle = self.handle;
2851 cx.spawn_with_priority(priority, async move |app| {
2852 let mut async_window_cx = AsyncWindowContext::new_context(app.clone(), handle);
2853 f(&mut async_window_cx).await
2854 })
2855 }
2856
2857 pub fn bounds_changed(&mut self, cx: &mut App) {
2863 self.scale_factor = self.platform_window.scale_factor();
2864 self.viewport_size = self.platform_window.content_size();
2865 self.mouse_position = self.platform_window.mouse_position();
2866
2867 self.refresh();
2868
2869 self.bounds_observers
2870 .clone()
2871 .retain(|callback| callback(self, cx));
2872 }
2873
2874 fn display_changed(handle: AnyWindowHandle, cx: &mut App) {
2875 let moved_to = handle
2876 .update(cx, |_, window, _| {
2877 let display_id = window.platform_window.display().map(|display| display.id());
2878 let moved = display_id != window.display_id;
2879 window.display_id = display_id;
2880 moved.then_some(display_id)
2881 })
2882 .log_err()
2883 .flatten();
2884 let Some(display_id) = moved_to else {
2885 return;
2886 };
2887 if let Some(display_id) = display_id
2891 && !cx.knows_display(display_id)
2892 {
2893 cx.displays_changed();
2894 }
2895 handle
2896 .update(cx, |_, window, cx| window.notify_display_observers(cx))
2897 .log_err();
2898 }
2899
2900 pub(crate) fn notify_display_observers(&mut self, cx: &mut App) {
2901 self.display_observers
2902 .clone()
2903 .retain(|callback| callback(self, cx));
2904 }
2905
2906 pub fn refresh_interval(&self, cx: &App) -> Option<Duration> {
2911 cx.display_refresh_interval(self.display_id?)
2912 }
2913
2914 pub fn observe_window_display(
2919 &self,
2920 mut callback: impl FnMut(&mut Window, &mut App) + 'static,
2921 ) -> Subscription {
2922 self.display_observers.insert(Box::new(move |window, cx| {
2923 callback(window, cx);
2924 true
2925 }))
2926 }
2927
2928 pub fn bounds(&self) -> Bounds<Pixels> {
2930 self.platform_window.bounds()
2931 }
2932
2933 pub fn insets(&self) -> WindowInsets {
2935 self.platform_window.insets()
2936 }
2937
2938 #[cfg(any(test, feature = "test-support"))]
2942 pub fn render_to_image(&self) -> anyhow::Result<image::RgbaImage> {
2943 self.platform_window
2944 .render_to_image(&self.rendered_frame.scene)
2945 }
2946
2947 #[cfg(any(test, feature = "test-support"))]
2951 pub fn render_to_linear_p3(&self) -> anyhow::Result<crate::LinearP3Image> {
2952 self.platform_window
2953 .render_to_linear_p3(&self.rendered_frame.scene)
2954 }
2955
2956 #[cfg(any(test, feature = "test-support"))]
2961 pub fn painted_quads(&self) -> Vec<Quad> {
2962 self.rendered_frame.scene.quads.clone()
2963 }
2964
2965 #[cfg(any(test, feature = "test-support"))]
2967 pub fn painted_underlines(&self) -> Vec<Underline> {
2968 self.rendered_frame.scene.underlines.clone()
2969 }
2970
2971 pub fn resize(&mut self, size: Size<Pixels>) {
2973 self.platform_window.resize(size);
2974 }
2975
2976 pub fn is_fullscreen(&self) -> bool {
2978 self.platform_window.is_fullscreen()
2979 }
2980
2981 pub fn is_simple_fullscreen(&self) -> bool {
2985 self.platform_window.is_simple_fullscreen()
2986 }
2987
2988 pub(crate) fn appearance_changed(&mut self, cx: &mut App) {
2989 self.appearance = self.platform_window.appearance();
2990
2991 self.appearance_observers
2992 .clone()
2993 .retain(|callback| callback(self, cx));
2994 }
2995
2996 pub(crate) fn button_layout_changed(&mut self, cx: &mut App) {
2997 self.button_layout_observers
2998 .clone()
2999 .retain(|callback| callback(self, cx));
3000 }
3001
3002 pub fn appearance(&self) -> WindowAppearance {
3004 self.appearance
3005 }
3006
3007 pub fn viewport_size(&self) -> Size<Pixels> {
3009 self.viewport_size
3010 }
3011
3012 pub fn visual_viewport_bounds(&self) -> Bounds<Pixels> {
3018 self.platform_window.visual_viewport_bounds()
3019 }
3020
3021 pub fn fully_visible_bounds(&self) -> Bounds<Pixels> {
3026 let insets = self.platform_window.insets().effective();
3027 let viewport = self.viewport_size();
3028 let left = insets.left.max(Pixels::ZERO).min(viewport.width);
3029 let top = insets.top.max(Pixels::ZERO).min(viewport.height);
3030 let right = (viewport.width - insets.right.max(Pixels::ZERO)).max(left);
3031 let bottom = (viewport.height - insets.bottom.max(Pixels::ZERO)).max(top);
3032 let safe_bounds = Bounds::from_corners(point(left, top), point(right, bottom));
3033 let mut visible = safe_bounds.intersect(&self.visual_viewport_bounds());
3034 visible.size.width = visible.size.width.max(Pixels::ZERO);
3035 visible.size.height = visible.size.height.max(Pixels::ZERO);
3036 visible
3037 }
3038
3039 pub fn request_virtual_keyboard(&self) {
3044 self.platform_window.show_soft_keyboard();
3045 }
3046
3047 pub fn dismiss_virtual_keyboard(&self) {
3049 self.platform_window.hide_soft_keyboard();
3050 }
3051
3052 pub fn is_window_active(&self) -> bool {
3054 self.active.get()
3055 }
3056
3057 pub fn is_window_hovered(&self) -> bool {
3061 if cfg!(any(
3062 target_os = "windows",
3063 target_os = "linux",
3064 target_os = "freebsd"
3065 )) {
3066 self.hovered.get()
3067 } else {
3068 self.is_window_active()
3069 }
3070 }
3071
3072 pub fn zoom_window(&self) {
3074 self.platform_window.zoom();
3075 }
3076
3077 pub fn show_window_menu(&self, position: Point<Pixels>) {
3079 self.platform_window.show_window_menu(position)
3080 }
3081
3082 pub fn start_window_move(&self) {
3087 self.platform_window.start_window_move()
3088 }
3089
3090 pub fn set_client_inset(&mut self, inset: Pixels) {
3092 self.client_inset = Some(inset);
3093 self.platform_window.set_client_inset(inset);
3094 }
3095
3096 pub fn client_inset(&self) -> Option<Pixels> {
3098 self.client_inset
3099 }
3100
3101 pub fn window_decorations(&self) -> Decorations {
3103 self.platform_window.window_decorations()
3104 }
3105
3106 pub fn is_resizable(&self) -> bool {
3108 self.is_resizable
3109 }
3110
3111 pub fn is_minimizable(&self) -> bool {
3113 self.is_minimizable
3114 }
3115
3116 pub fn window_controls(&self) -> WindowControls {
3118 self.platform_window.window_controls()
3119 }
3120
3121 pub fn set_window_title(&mut self, title: &str) {
3123 self.platform_window.set_title(title);
3124 self.a11y.set_window_title(title.to_string());
3125 }
3126
3127 #[cfg(target_os = "macos")]
3129 pub fn set_traffic_light_position(&self, position: Point<Pixels>) {
3130 self.platform_window.set_traffic_light_position(position);
3131 }
3132
3133 pub fn set_app_id(&mut self, app_id: &str) {
3135 self.platform_window.set_app_id(app_id);
3136 }
3137
3138 pub fn set_color_mode(
3142 &mut self,
3143 mode: crate::WindowColorMode,
3144 ) -> anyhow::Result<crate::WindowColorStatus> {
3145 anyhow::ensure!(
3146 self.invalidator.not_drawing(),
3147 "cannot change color presentation during drawing"
3148 );
3149 let status = self.platform_window.set_color_mode(mode)?;
3150 self.refresh();
3151 Ok(status)
3152 }
3153
3154 pub fn color_status(&self) -> crate::WindowColorStatus {
3156 self.platform_window.color_status()
3157 }
3158
3159 pub fn set_background_appearance(&self, background_appearance: WindowBackgroundAppearance) {
3163 self.platform_window
3164 .set_background_appearance(background_appearance);
3165 }
3166
3167 pub fn set_window_edited(&mut self, edited: bool) {
3169 self.platform_window.set_edited(edited);
3170 }
3171
3172 pub fn set_document_path(&self, path: Option<&std::path::Path>) {
3175 self.platform_window.set_document_path(path);
3176 }
3177
3178 pub fn display(&self, cx: &App) -> Option<Rc<dyn PlatformDisplay>> {
3180 cx.platform
3181 .displays()
3182 .into_iter()
3183 .find(|display| Some(display.id()) == self.display_id)
3184 }
3185
3186 pub fn show_character_palette(&self) {
3188 self.platform_window.show_character_palette();
3189 }
3190
3191 pub fn scale_factor(&self) -> f32 {
3195 self.scale_factor
3196 }
3197
3198 #[cfg(any(test, feature = "test-support"))]
3200 pub fn set_scale_factor(&mut self, scale_factor: f32) {
3201 self.scale_factor = scale_factor;
3202 self.refresh();
3203 }
3204
3205 pub fn rem_size(&self) -> Pixels {
3208 self.rem_size_override_stack
3209 .last()
3210 .copied()
3211 .unwrap_or(self.rem_size)
3212 }
3213
3214 pub fn set_rem_size(&mut self, rem_size: impl Into<Pixels>) {
3217 self.rem_size = rem_size.into();
3218 }
3219
3220 pub fn with_global_id<R>(
3223 &mut self,
3224 element_id: ElementId,
3225 f: impl FnOnce(&GlobalElementId, &mut Self) -> R,
3226 ) -> R {
3227 self.with_id(element_id, |this| {
3228 let global_id = this.element_id_stack.global_id();
3229
3230 f(&global_id, this)
3231 })
3232 }
3233
3234 #[inline]
3236 pub fn with_id<R>(
3237 &mut self,
3238 element_id: impl Into<ElementId>,
3239 f: impl FnOnce(&mut Self) -> R,
3240 ) -> R {
3241 self.element_id_stack.push(element_id.into());
3242 let result = f(self);
3243 self.element_id_stack.pop();
3244 result
3245 }
3246
3247 #[inline]
3253 pub fn with_rem_size<F, R>(&mut self, rem_size: Option<impl Into<Pixels>>, f: F) -> R
3254 where
3255 F: FnOnce(&mut Self) -> R,
3256 {
3257 self.invalidator.debug_assert_paint_or_prepaint();
3258
3259 if let Some(rem_size) = rem_size {
3260 self.rem_size_override_stack.push(rem_size.into());
3261 let result = f(self);
3262 self.rem_size_override_stack.pop();
3263 result
3264 } else {
3265 f(self)
3266 }
3267 }
3268
3269 pub fn line_height(&self) -> Pixels {
3271 self.text_style().line_height_in_pixels(self.rem_size())
3272 }
3273
3274 #[inline]
3276 pub fn pixel_snap(&self, value: Pixels) -> Pixels {
3277 px(round_to_device_pixel(value.0, self.scale_factor()) / self.scale_factor())
3278 }
3279
3280 #[inline]
3282 pub fn pixel_snap_f64(&self, value: f64) -> f64 {
3283 let scale_factor = f64::from(self.scale_factor());
3284 round_half_toward_zero_f64(value * scale_factor) / scale_factor
3285 }
3286
3287 #[inline]
3289 pub fn pixel_snap_bounds(&self, bounds: Bounds<Pixels>) -> Bounds<Pixels> {
3290 bounds.map(|c| self.pixel_snap(c))
3291 }
3292
3293 #[inline]
3295 pub fn pixel_snap_point(&self, position: Point<Pixels>) -> Point<Pixels> {
3296 position.map(|c| self.pixel_snap(c))
3297 }
3298
3299 pub fn underline_bounds(
3301 &self,
3302 origin: Point<Pixels>,
3303 width: Pixels,
3304 style: &UnderlineStyle,
3305 ) -> Bounds<ScaledPixels> {
3306 let scale_factor = self.scale_factor();
3307 let thickness = self.snap_stroke(style.thickness);
3308 let height = if style.wavy {
3309 ScaledPixels(thickness.0 * 3.)
3310 } else {
3311 thickness
3312 };
3313 Bounds {
3314 origin: origin.map(|c| ScaledPixels(round_to_device_pixel(c.0, scale_factor))),
3315 size: size(self.snap_stroke(width), height),
3316 }
3317 }
3318
3319 pub fn paint_underline_with_exclusions(
3321 &mut self,
3322 origin: Point<Pixels>,
3323 width: Pixels,
3324 style: &UnderlineStyle,
3325 exclusions: &[Range<ScaledPixels>],
3326 ) {
3327 self.invalidator.debug_assert_paint();
3328 let underline = self.underline(origin, width, style);
3329 if exclusions.is_empty() {
3330 self.next_frame.scene.insert_primitive(underline);
3331 return;
3332 }
3333
3334 let bounds = underline.bounds.intersect(&underline.content_mask.bounds);
3335 if bounds.is_empty() {
3336 return;
3337 }
3338
3339 let mut exclusions = exclusions
3340 .iter()
3341 .filter_map(|span| {
3342 let start = span.start.max(bounds.left());
3343 let end = span.end.min(bounds.right());
3344 (start < end).then_some(start..end)
3345 })
3346 .collect::<SmallVec<[_; 4]>>();
3347 if exclusions.is_empty() {
3348 self.next_frame.scene.insert_primitive(underline);
3349 return;
3350 }
3351 exclusions.sort_unstable_by_key(|span| span.start);
3352
3353 let mut paint_span = |start, end| {
3354 self.next_frame.scene.insert_primitive(Underline {
3355 content_mask: ContentMask {
3356 bounds: Bounds::from_corners(
3357 point(start, bounds.top()),
3358 point(end, bounds.bottom()),
3359 ),
3360 },
3361 ..underline
3362 });
3363 };
3364 let mut start = bounds.left();
3365 for exclusion in exclusions {
3366 if start < exclusion.start {
3367 paint_span(start, exclusion.start);
3368 }
3369 start = start.max(exclusion.end);
3370 }
3371 if start < bounds.right() {
3372 paint_span(start, bounds.right());
3373 }
3374 }
3375
3376 fn underline(&self, origin: Point<Pixels>, width: Pixels, style: &UnderlineStyle) -> Underline {
3377 Underline {
3378 order: 0,
3379 pad: 0,
3380 bounds: self.underline_bounds(origin, width, style),
3381 content_mask: self.snapped_content_mask(),
3382 color: style
3383 .color
3384 .unwrap_or_default()
3385 .opacity(self.element_opacity()),
3386 thickness: self.snap_stroke(style.thickness),
3387 wavy: style.wavy.into(),
3388 }
3389 }
3390
3391 #[inline]
3392 fn snap_bounds(&self, bounds: Bounds<Pixels>) -> Bounds<ScaledPixels> {
3393 let scale_factor = self.scale_factor();
3394 let left = round_to_device_pixel(bounds.left().0, scale_factor);
3395 let top = round_to_device_pixel(bounds.top().0, scale_factor);
3396 let right = round_to_device_pixel(bounds.right().0, scale_factor).max(left);
3397 let bottom = round_to_device_pixel(bounds.bottom().0, scale_factor).max(top);
3398 Bounds::from_corners(
3399 point(ScaledPixels(left), ScaledPixels(top)),
3400 point(ScaledPixels(right), ScaledPixels(bottom)),
3401 )
3402 }
3403
3404 #[inline]
3406 fn snap_stroke(&self, value: Pixels) -> ScaledPixels {
3407 ScaledPixels(round_stroke_to_device_pixel(value.0, self.scale_factor()))
3408 }
3409
3410 #[inline]
3411 fn snap_border_widths(&self, edges: Edges<Pixels>) -> Edges<ScaledPixels> {
3412 edges.map(|e| self.snap_stroke(*e))
3413 }
3414
3415 #[inline]
3417 fn cover_bounds(&self, bounds: Bounds<Pixels>) -> Bounds<ScaledPixels> {
3418 let scale_factor = self.scale_factor();
3419 if bounds.is_empty() {
3422 return Bounds::new(bounds.origin.scale(scale_factor), Size::default());
3423 }
3424 let left = floor_to_device_pixel(bounds.left().0, scale_factor);
3425 let top = floor_to_device_pixel(bounds.top().0, scale_factor);
3426 let right = ceil_to_device_pixel(bounds.right().0, scale_factor).max(left);
3427 let bottom = ceil_to_device_pixel(bounds.bottom().0, scale_factor).max(top);
3428 Bounds::from_corners(
3429 point(ScaledPixels(left), ScaledPixels(top)),
3430 point(ScaledPixels(right), ScaledPixels(bottom)),
3431 )
3432 }
3433
3434 #[inline]
3435 fn snapped_content_mask(&self) -> ContentMask<ScaledPixels> {
3436 ContentMask {
3437 bounds: self.cover_bounds(self.content_mask().bounds),
3438 }
3439 }
3440
3441 pub fn prevent_default(&mut self) {
3444 self.default_prevented = true;
3445 }
3446
3447 pub fn default_prevented(&self) -> bool {
3449 self.default_prevented
3450 }
3451
3452 pub fn is_action_available(&self, action: &dyn Action, cx: &App) -> bool {
3454 let node_id =
3455 self.focus_node_id_in_rendered_frame(self.focused(cx).map(|handle| handle.id));
3456 self.rendered_frame
3457 .dispatch_tree
3458 .is_action_available(action, node_id)
3459 }
3460
3461 pub fn is_action_available_in(&self, action: &dyn Action, focus_handle: &FocusHandle) -> bool {
3463 let node_id = self.focus_node_id_in_rendered_frame(Some(focus_handle.id));
3464 self.rendered_frame
3465 .dispatch_tree
3466 .is_action_available(action, node_id)
3467 }
3468
3469 pub fn mouse_position(&self) -> Point<Pixels> {
3471 self.mouse_position
3472 }
3473
3474 pub fn capture_pointer(&mut self, hitbox_id: HitboxId) {
3481 self.captured_hitbox = Some(hitbox_id);
3482 }
3483
3484 pub fn release_pointer(&mut self) {
3486 self.captured_hitbox = None;
3487 }
3488
3489 pub fn captured_hitbox(&self) -> Option<HitboxId> {
3491 self.captured_hitbox
3492 }
3493
3494 pub fn capture_long_press<T: 'static>(&mut self, entity: &Entity<T>) {
3500 self.long_press_capture = Some(entity.entity_id());
3501 }
3502
3503 pub fn has_long_press_capture<T: 'static>(&self, entity: &Entity<T>) -> bool {
3505 self.long_press_capture == Some(entity.entity_id())
3506 }
3507
3508 pub fn modifiers(&self) -> Modifiers {
3510 self.modifiers
3511 }
3512
3513 pub fn last_input_was_keyboard(&self) -> bool {
3516 self.last_input_modality == InputModality::Keyboard
3517 }
3518
3519 pub fn last_input_was_touch(&self) -> bool {
3521 self.last_input_modality == InputModality::Touch
3522 }
3523
3524 pub fn capslock(&self) -> Capslock {
3526 self.capslock
3527 }
3528
3529 #[profiling::function]
3532 pub fn draw(&mut self, cx: &mut App) -> ArenaClearNeeded {
3533 #[cfg(feature = "profiler")]
3536 let frame_dirty = self.invalidator.take_frame_dirty();
3537 #[cfg(feature = "profiler")]
3538 self.window_profiler.begin_draw();
3539
3540 let arena_scope = ElementArenaScope::enter(&cx.element_arena);
3543
3544 if self.platform_window.prepare_frame() {
3545 self.refresh();
3546 }
3547 self.invalidate_entities();
3548 cx.entities.clear_accessed();
3549 debug_assert!(self.rendered_entity_stack.is_empty());
3550 self.invalidator.set_dirty(false);
3551 self.requested_autoscroll = None;
3552
3553 if let Some(input_handler) = self.platform_window.take_input_handler() {
3558 if let Some(slot) = self
3559 .rendered_frame
3560 .input_handlers
3561 .iter_mut()
3562 .rev()
3563 .find(|h| h.is_none())
3564 {
3565 *slot = Some(input_handler);
3566 } else {
3567 self.rendered_frame.input_handlers.push(Some(input_handler));
3568 }
3569 }
3570 if !cx.mode.skip_drawing() {
3571 self.draw_roots(cx);
3572 #[cfg(feature = "profiler")]
3573 {
3574 let viewport_size = self.viewport_size;
3575 let scale_factor = self.scale_factor();
3576 self.debug_frame_overlay.paint(
3577 &mut self.next_frame.scene,
3578 viewport_size,
3579 scale_factor,
3580 );
3581 }
3582 }
3583 self.dirty_views.clear();
3584 self.next_frame.window_active = self.active.get();
3585
3586 let focused_text_input = if let Some(mut input_handler) = self
3592 .next_frame
3593 .input_handlers
3594 .iter_mut()
3595 .rev()
3596 .find_map(|h| h.take())
3597 {
3598 let accepts_text_input = input_handler.accepts_text_input(self, cx);
3599 self.platform_window.set_input_handler(input_handler);
3600 self.focus.filter(|_| accepts_text_input)
3601 } else {
3602 None
3603 };
3604 self.apply_text_input_configuration(cx);
3605 let previous = mem::replace(&mut self.focused_text_input, focused_text_input);
3606 if previous.is_some() != focused_text_input.is_some() {
3607 self.platform_window
3608 .text_input_state_changed(if focused_text_input.is_some() {
3609 TextInputStateChange::FocusGained
3610 } else {
3611 TextInputStateChange::FocusLost
3612 });
3613 }
3614
3615 self.layout_engine.as_mut().unwrap().clear();
3616 self.text_system().finish_frame();
3617 self.next_frame.finish(&mut self.rendered_frame);
3618
3619 self.invalidator.set_phase(DrawPhase::Focus);
3620 let previous_focus_path = self.rendered_frame.focus_path();
3621 let previous_window_active = self.rendered_frame.window_active;
3622 mem::swap(&mut self.rendered_frame, &mut self.next_frame);
3623 self.next_frame.clear();
3624 let current_focus_path = self.rendered_frame.focus_path();
3625 let current_window_active = self.rendered_frame.window_active;
3626 let mut focus_before_listeners = self.focus;
3627
3628 if previous_focus_path != current_focus_path
3629 || previous_window_active != current_window_active
3630 {
3631 if !previous_focus_path.is_empty() && current_focus_path.is_empty() {
3632 self.focus_lost_path = previous_focus_path.clone();
3633 self.focus_lost_listeners
3634 .clone()
3635 .retain(|listener| listener(self, cx));
3636 self.focus_lost_path = SmallVec::new();
3637 focus_before_listeners = self.focus;
3642 }
3643
3644 let event = WindowFocusEvent {
3645 previous_focus_path: if previous_window_active {
3646 previous_focus_path
3647 } else {
3648 Default::default()
3649 },
3650 current_focus_path: if current_window_active {
3651 current_focus_path
3652 } else {
3653 Default::default()
3654 },
3655 };
3656 self.focus_listeners
3657 .clone()
3658 .retain(|listener| listener(&event, self, cx));
3659 }
3660
3661 debug_assert!(self.rendered_entity_stack.is_empty());
3662 self.record_entities_accessed(cx);
3663 self.reset_cursor_style(cx);
3664 self.refreshing = false;
3665 self.invalidator.set_phase(DrawPhase::None);
3666 if self.focus != focus_before_listeners {
3671 self.refresh();
3672 }
3673 self.needs_present.set(true);
3674
3675 #[cfg(feature = "profiler")]
3676 {
3677 self.window_profiler
3678 .set_refresh_interval(self.refresh_interval(cx));
3679 let draw_duration = self
3680 .window_profiler
3681 .end_draw(frame_dirty.dirty_at, frame_dirty.invalidations);
3682 self.debug_frame_overlay.record_frame(draw_duration);
3683 }
3684
3685 arena_scope.exit(&cx.element_arena)
3688 }
3689
3690 fn record_entities_accessed(&mut self, cx: &mut App) {
3691 let mut entities_ref = cx.entities.accessed_entities.get_mut();
3692 let mut entities = mem::take(entities_ref.deref_mut());
3693 let handle = self.handle;
3694 cx.record_entities_accessed(
3695 handle,
3696 self.invalidator.clone(),
3698 &entities,
3699 );
3700 let mut entities_ref = cx.entities.accessed_entities.get_mut();
3701 mem::swap(&mut entities, entities_ref.deref_mut());
3702 }
3703
3704 fn invalidate_entities(&mut self) {
3705 let mut views = self.invalidator.take_views();
3706 for entity in views.drain() {
3707 self.mark_view_dirty(entity);
3708 }
3709 self.invalidator.replace_views(views);
3710 }
3711
3712 #[cfg(any(test, feature = "test-support"))]
3715 pub fn submit_frame_for_validation(&self) -> anyhow::Result<()> {
3716 self.platform_window
3717 .submit_scene_for_validation(&self.rendered_frame.scene)
3718 }
3719
3720 #[profiling::function]
3721 fn present(&mut self) {
3722 #[cfg(feature = "profiler")]
3723 let _foreground_turn = profiler::journal::foreground_turn();
3724 #[cfg(feature = "profiler")]
3725 let present_start = Instant::now();
3726 self.platform_window.draw(&self.rendered_frame.scene);
3727 #[cfg(feature = "profiler")]
3728 self.window_profiler.record_present(
3729 present_start,
3730 Instant::now(),
3731 self.active.get(),
3732 !self.next_frame_callbacks.borrow().is_empty(),
3733 );
3734 self.needs_present.set(false);
3735 profiling::finish_frame!();
3736 }
3737
3738 #[cfg(all(test, feature = "profiler"))]
3740 pub fn present_if_needed(&mut self) {
3741 if self.needs_present.get() {
3742 self.present();
3743 }
3744 }
3745
3746 #[cfg(feature = "profiler")]
3748 pub fn input_latency_snapshot(&self) -> profiler::InputLatencySnapshot {
3749 self.window_profiler.input_latency_snapshot()
3750 }
3751
3752 #[cfg(feature = "profiler")]
3754 pub fn frame_duration_snapshot(&self) -> profiler::FrameDurationSnapshot {
3755 self.window_profiler.frame_duration_snapshot()
3756 }
3757
3758 #[cfg(feature = "profiler")]
3760 pub fn debug_frame_overlay_mode(&self) -> DebugFrameOverlayMode {
3761 self.debug_frame_overlay.mode()
3762 }
3763
3764 #[cfg(feature = "profiler")]
3766 pub fn set_debug_frame_overlay_mode(&mut self, mode: DebugFrameOverlayMode) {
3767 self.debug_frame_overlay.set_mode(mode);
3768 self.refresh();
3769 }
3770
3771 #[cfg(feature = "profiler")]
3774 pub fn cycle_debug_frame_overlay_mode(&mut self) {
3775 self.set_debug_frame_overlay_mode(self.debug_frame_overlay.mode().next());
3776 }
3777
3778 #[cfg(feature = "profiler")]
3781 pub fn reset_debug_frame_overlay_stats(&mut self) {
3782 self.debug_frame_overlay.reset_stats();
3783 self.refresh();
3784 }
3785
3786 fn draw_roots(&mut self, cx: &mut App) {
3787 self.invalidator.set_phase(DrawPhase::Prepaint);
3788 self.tooltip_bounds.take();
3789
3790 self.a11y.sync_active_flag();
3791 if self.a11y.is_active() {
3792 self.a11y.begin_frame();
3793 }
3794
3795 let _inspector_width: Pixels = rems(30.0).to_pixels(self.rem_size());
3796 let root_size = {
3797 #[cfg(debug_assertions)]
3798 {
3799 if self.inspector.is_some() {
3800 let mut size = self.viewport_size;
3801 size.width = (size.width - _inspector_width).max(px(0.0));
3802 size
3803 } else {
3804 self.viewport_size
3805 }
3806 }
3807 #[cfg(not(debug_assertions))]
3808 {
3809 self.viewport_size
3810 }
3811 };
3812
3813 let scale_factor = self.scale_factor();
3817 #[cfg(feature = "profiler")]
3818 let request_layout_start = Instant::now();
3819 let mut root_element = self.root.as_ref().unwrap().clone().into_any_element();
3820 let root_layout_id = root_element.request_layout(self, cx);
3821 #[cfg(feature = "profiler")]
3822 let prepaint_start = Instant::now();
3823 self.layout_engine
3824 .as_mut()
3825 .unwrap()
3826 .stretch_auto_size_to_fill(root_layout_id, root_size, scale_factor);
3827 root_element.prepaint_as_root(Point::default(), root_size.into(), self, cx);
3828
3829 #[cfg(debug_assertions)]
3830 let inspector_element = self.prepaint_inspector(_inspector_width, cx);
3831
3832 self.prepaint_deferred_draws(cx);
3833
3834 let mut prompt_element = None;
3835 let mut active_drag_element = None;
3836 let mut tooltip_element = None;
3837 if let Some(prompt) = self.prompt.take() {
3838 let mut element = prompt.view.any_view().into_any_element();
3839 let prompt_layout_id = element.request_layout(self, cx);
3840 self.layout_engine
3841 .as_mut()
3842 .unwrap()
3843 .stretch_auto_size_to_fill(prompt_layout_id, root_size, scale_factor);
3844 element.prepaint_as_root(Point::default(), root_size.into(), self, cx);
3845 prompt_element = Some(element);
3846 self.prompt = Some(prompt);
3847 } else if let Some(active_drag) = cx.active_drag.take() {
3848 let mut element = active_drag.view.clone().into_any_element();
3849 let offset = self.mouse_position() - active_drag.cursor_offset;
3850 element.prepaint_as_root(offset, AvailableSpace::min_size(), self, cx);
3851 active_drag_element = Some(element);
3852 cx.active_drag = Some(active_drag);
3853 } else {
3854 tooltip_element = self.prepaint_tooltip(cx);
3855 }
3856
3857 self.mouse_hit_test = self.next_frame.hit_test(self.mouse_position);
3858
3859 #[cfg(feature = "profiler")]
3861 let paint_start = Instant::now();
3862 self.invalidator.set_phase(DrawPhase::Paint);
3863 root_element.paint(self, cx);
3864
3865 #[cfg(debug_assertions)]
3866 self.paint_inspector(inspector_element, cx);
3867
3868 self.paint_deferred_draws(cx);
3869
3870 if let Some(mut prompt_element) = prompt_element {
3871 prompt_element.paint(self, cx);
3872 } else if let Some(mut drag_element) = active_drag_element {
3873 drag_element.paint(self, cx);
3874 } else if let Some(mut tooltip_element) = tooltip_element {
3875 tooltip_element.paint(self, cx);
3876 }
3877
3878 #[cfg(debug_assertions)]
3879 self.paint_inspector_hitbox(cx);
3880
3881 #[cfg(feature = "profiler")]
3882 self.window_profiler
3883 .record_draw_phases(profiler::DrawPhases {
3884 request_layout: prepaint_start.duration_since(request_layout_start),
3885 prepaint: paint_start.duration_since(prepaint_start),
3886 paint: Instant::now().duration_since(paint_start),
3887 });
3888
3889 let a11y_active_start_of_frame = self.a11y.is_active();
3891 self.a11y.sync_active_flag();
3892 let a11y_active_end_of_frame = self.a11y.is_active();
3893
3894 let should_send_a11y_update = a11y_active_start_of_frame && a11y_active_end_of_frame;
3895
3896 if a11y_active_start_of_frame {
3897 let frame_info = crate::window::a11y::debug::FrameDebugInfo {
3900 viewport_size: self.viewport_size,
3901 scale_factor: self.scale_factor,
3902 tab_stop_count: self.next_frame.tab_stops.tab_stop_count(),
3903 };
3904 let tree_update = self.a11y.end_frame(frame_info);
3906
3907 if should_send_a11y_update {
3908 log::debug!(
3909 "Sending a11y tree update: {} nodes",
3910 tree_update.nodes.len()
3911 );
3912 self.platform_window.a11y_tree_update(tree_update);
3913 }
3914 }
3915 }
3916
3917 fn prepaint_tooltip(&mut self, cx: &mut App) -> Option<AnyElement> {
3918 for tooltip_request_index in (0..self.next_frame.tooltip_requests.len()).rev() {
3920 let Some(Some(tooltip_request)) = self
3921 .next_frame
3922 .tooltip_requests
3923 .get(tooltip_request_index)
3924 .cloned()
3925 else {
3926 log::error!("Unexpectedly absent TooltipRequest");
3927 continue;
3928 };
3929 let mut element = tooltip_request.tooltip.view.clone().into_any_element();
3930 let mouse_position = tooltip_request.tooltip.mouse_position;
3931 let tooltip_size = element.layout_as_root(AvailableSpace::min_size(), self, cx);
3932
3933 let mut tooltip_bounds =
3934 Bounds::new(mouse_position + point(px(1.), px(1.)), tooltip_size);
3935 let window_bounds = Bounds {
3936 origin: Point::default(),
3937 size: self.viewport_size(),
3938 };
3939
3940 if tooltip_bounds.right() > window_bounds.right() {
3941 let new_x = mouse_position.x - tooltip_bounds.size.width - px(1.);
3942 if new_x >= Pixels::ZERO {
3943 tooltip_bounds.origin.x = new_x;
3944 } else {
3945 tooltip_bounds.origin.x = cmp::max(
3946 Pixels::ZERO,
3947 tooltip_bounds.origin.x - tooltip_bounds.right() - window_bounds.right(),
3948 );
3949 }
3950 }
3951
3952 if tooltip_bounds.bottom() > window_bounds.bottom() {
3953 let new_y = mouse_position.y - tooltip_bounds.size.height - px(1.);
3954 if new_y >= Pixels::ZERO {
3955 tooltip_bounds.origin.y = new_y;
3956 } else {
3957 tooltip_bounds.origin.y = cmp::max(
3958 Pixels::ZERO,
3959 tooltip_bounds.origin.y - tooltip_bounds.bottom() - window_bounds.bottom(),
3960 );
3961 }
3962 }
3963
3964 let is_visible =
3968 (tooltip_request.tooltip.check_visible_and_update)(tooltip_bounds, self, cx);
3969 if !is_visible {
3970 continue;
3971 }
3972
3973 self.with_absolute_element_offset(tooltip_bounds.origin, |window| {
3974 element.prepaint(window, cx)
3975 });
3976
3977 self.tooltip_bounds = Some(TooltipBounds {
3978 id: tooltip_request.id,
3979 bounds: tooltip_bounds,
3980 });
3981 return Some(element);
3982 }
3983 None
3984 }
3985
3986 fn prepaint_deferred_draws(&mut self, cx: &mut App) {
3987 assert_eq!(self.element_id_stack.len(), 0);
3988
3989 let mut round_start = 0;
4000 let mut depth = 0;
4001 loop {
4002 let round_end = self.next_frame.deferred_draws.len();
4003 if round_start == round_end {
4004 break;
4005 }
4006 assert!(depth < 10, "Exceeded maximum (10) deferred depth");
4008 depth += 1;
4009
4010 let mut traversal_order = (round_start..round_end).collect::<SmallVec<[usize; 8]>>();
4012 traversal_order.sort_by_key(|ix| self.next_frame.deferred_draws[*ix].priority);
4013
4014 for deferred_draw_ix in traversal_order {
4015 let (element, parent_node, current_view, rem_size, absolute_offset, prepaint_range) = {
4016 let deferred_draw = &mut self.next_frame.deferred_draws[deferred_draw_ix];
4017 self.element_id_stack
4018 .clone_from(&deferred_draw.element_id_stack);
4019 self.text_style_stack
4020 .clone_from(&deferred_draw.text_style_stack);
4021 self.glyph_brush_stack
4022 .clone_from(&deferred_draw.glyph_brush_stack);
4023 (
4024 deferred_draw.element.take(),
4025 deferred_draw.parent_node,
4026 deferred_draw.current_view,
4027 deferred_draw.rem_size,
4028 deferred_draw.absolute_offset,
4029 deferred_draw.prepaint_range.clone(),
4030 )
4031 };
4032 self.next_frame.dispatch_tree.set_active_node(parent_node);
4033
4034 let prepaint_start = self.prepaint_index();
4035 if let Some(mut element) = element {
4036 self.with_rendered_view(current_view, |window| {
4037 window.with_rem_size(Some(rem_size), |window| {
4038 window.with_absolute_element_offset(absolute_offset, |window| {
4039 element.prepaint(window, cx);
4040 });
4041 });
4042 });
4043 self.next_frame.deferred_draws[deferred_draw_ix].element = Some(element);
4044 } else {
4045 self.reuse_prepaint(prepaint_range);
4046 }
4047 let prepaint_end = self.prepaint_index();
4048 self.next_frame.deferred_draws[deferred_draw_ix].prepaint_range =
4049 prepaint_start..prepaint_end;
4050 }
4051
4052 self.element_id_stack.clear();
4053 self.text_style_stack.clear();
4054 self.glyph_brush_stack.clear();
4055 round_start = round_end;
4056 }
4057 }
4058
4059 fn paint_deferred_draws(&mut self, cx: &mut App) {
4060 assert_eq!(self.element_id_stack.len(), 0);
4061
4062 if self.next_frame.deferred_draws.len() == 0 {
4065 return;
4066 }
4067
4068 let traversal_order = self.deferred_draw_traversal_order();
4069 let mut deferred_draws = mem::take(&mut self.next_frame.deferred_draws);
4070 for deferred_draw_ix in traversal_order {
4071 let deferred_draw = &mut deferred_draws[deferred_draw_ix];
4072 self.element_id_stack
4073 .clone_from(&deferred_draw.element_id_stack);
4074 self.glyph_brush_stack
4075 .clone_from(&deferred_draw.glyph_brush_stack);
4076 self.next_frame
4077 .dispatch_tree
4078 .set_active_node(deferred_draw.parent_node);
4079
4080 let paint_start = self.paint_index();
4081 let content_mask = deferred_draw.content_mask;
4082 if let Some(element) = deferred_draw.element.as_mut() {
4083 self.with_rendered_view(deferred_draw.current_view, |window| {
4084 window.with_content_mask(content_mask, |window| {
4085 window.with_rem_size(Some(deferred_draw.rem_size), |window| {
4086 element.paint(window, cx);
4087 });
4088 })
4089 })
4090 } else {
4091 self.reuse_paint(deferred_draw.paint_range.clone());
4092 }
4093 let paint_end = self.paint_index();
4094 deferred_draw.paint_range = paint_start..paint_end;
4095 }
4096 self.next_frame.deferred_draws = deferred_draws;
4097 self.element_id_stack.clear();
4098 self.glyph_brush_stack.clear();
4099 }
4100
4101 fn deferred_draw_traversal_order(&mut self) -> SmallVec<[usize; 8]> {
4102 let deferred_count = self.next_frame.deferred_draws.len();
4103 let mut sorted_indices = (0..deferred_count).collect::<SmallVec<[_; 8]>>();
4104 sorted_indices.sort_by_key(|ix| self.next_frame.deferred_draws[*ix].priority);
4105 sorted_indices
4106 }
4107
4108 pub(crate) fn prepaint_index(&self) -> PrepaintStateIndex {
4109 PrepaintStateIndex {
4110 hitboxes_index: self.next_frame.hitboxes.len(),
4111 tooltips_index: self.next_frame.tooltip_requests.len(),
4112 deferred_draws_index: self.next_frame.deferred_draws.len(),
4113 dispatch_tree_index: self.next_frame.dispatch_tree.len(),
4114 accessed_element_states_index: self.next_frame.accessed_element_states.len(),
4115 line_layout_index: self.text_system.layout_index(),
4116 }
4117 }
4118
4119 pub(crate) fn reuse_prepaint(&mut self, range: Range<PrepaintStateIndex>) {
4120 self.next_frame.hitboxes.extend(
4121 self.rendered_frame.hitboxes[range.start.hitboxes_index..range.end.hitboxes_index]
4122 .iter()
4123 .cloned(),
4124 );
4125 self.next_frame.tooltip_requests.extend(
4126 self.rendered_frame.tooltip_requests
4127 [range.start.tooltips_index..range.end.tooltips_index]
4128 .iter_mut()
4129 .map(|request| request.take()),
4130 );
4131 self.next_frame.accessed_element_states.extend(
4132 self.rendered_frame.accessed_element_states[range.start.accessed_element_states_index
4133 ..range.end.accessed_element_states_index]
4134 .iter()
4135 .map(|(id, type_id)| (id.clone(), *type_id)),
4136 );
4137 self.text_system
4138 .reuse_layouts(range.start.line_layout_index..range.end.line_layout_index);
4139
4140 let reused_subtree = self.next_frame.dispatch_tree.reuse_subtree(
4141 range.start.dispatch_tree_index..range.end.dispatch_tree_index,
4142 &mut self.rendered_frame.dispatch_tree,
4143 self.focus,
4144 );
4145
4146 if reused_subtree.contains_focus() {
4147 self.next_frame.focus = self.focus;
4148 }
4149
4150 self.next_frame.deferred_draws.extend(
4151 self.rendered_frame.deferred_draws
4152 [range.start.deferred_draws_index..range.end.deferred_draws_index]
4153 .iter()
4154 .map(|deferred_draw| DeferredDraw {
4155 current_view: deferred_draw.current_view,
4156 parent_node: reused_subtree.refresh_node_id(deferred_draw.parent_node),
4157 element_id_stack: deferred_draw.element_id_stack.clone(),
4158 text_style_stack: deferred_draw.text_style_stack.clone(),
4159 glyph_brush_stack: deferred_draw.glyph_brush_stack.clone(),
4160 content_mask: deferred_draw.content_mask,
4161 rem_size: deferred_draw.rem_size,
4162 priority: deferred_draw.priority,
4163 element: None,
4164 absolute_offset: deferred_draw.absolute_offset,
4165 prepaint_range: deferred_draw.prepaint_range.clone(),
4166 paint_range: deferred_draw.paint_range.clone(),
4167 }),
4168 );
4169 }
4170
4171 pub(crate) fn paint_index(&self) -> PaintIndex {
4172 PaintIndex {
4173 scene_index: self.next_frame.scene.len(),
4174 #[cfg(any(test, feature = "test-support"))]
4175 debug_bounds_index: self.next_frame.debug_bounds_records.len(),
4176 mouse_listeners_index: self.next_frame.mouse_listeners.len(),
4177 input_handlers_index: self.next_frame.input_handlers.len(),
4178 cursor_styles_index: self.next_frame.cursor_styles.len(),
4179 accessed_element_states_index: self.next_frame.accessed_element_states.len(),
4180 tab_handle_index: self.next_frame.tab_stops.paint_index(),
4181 line_layout_index: self.text_system.layout_index(),
4182 }
4183 }
4184
4185 pub(crate) fn reuse_paint(&mut self, range: Range<PaintIndex>) {
4186 #[cfg(any(test, feature = "test-support"))]
4188 for (selector, bounds) in &self.rendered_frame.debug_bounds_records
4189 [range.start.debug_bounds_index..range.end.debug_bounds_index]
4190 {
4191 self.next_frame
4192 .record_debug_bounds(selector.clone(), *bounds);
4193 }
4194 self.next_frame.cursor_styles.extend(
4195 self.rendered_frame.cursor_styles
4196 [range.start.cursor_styles_index..range.end.cursor_styles_index]
4197 .iter()
4198 .cloned(),
4199 );
4200 self.next_frame.input_handlers.extend(
4201 self.rendered_frame.input_handlers
4202 [range.start.input_handlers_index..range.end.input_handlers_index]
4203 .iter_mut()
4204 .map(|handler| handler.take()),
4205 );
4206 self.next_frame.mouse_listeners.extend(
4207 self.rendered_frame.mouse_listeners
4208 [range.start.mouse_listeners_index..range.end.mouse_listeners_index]
4209 .iter_mut()
4210 .map(|listener| listener.take()),
4211 );
4212 self.next_frame.accessed_element_states.extend(
4213 self.rendered_frame.accessed_element_states[range.start.accessed_element_states_index
4214 ..range.end.accessed_element_states_index]
4215 .iter()
4216 .map(|(id, type_id)| (id.clone(), *type_id)),
4217 );
4218 self.next_frame.tab_stops.replay(
4219 &self.rendered_frame.tab_stops.insertion_history
4220 [range.start.tab_handle_index..range.end.tab_handle_index],
4221 );
4222
4223 self.text_system
4224 .reuse_layouts(range.start.line_layout_index..range.end.line_layout_index);
4225 self.next_frame.scene.replay(
4226 range.start.scene_index..range.end.scene_index,
4227 &self.rendered_frame.scene,
4228 );
4229 }
4230
4231 pub fn with_text_style<F, R>(&mut self, style: Option<TextStyleRefinement>, f: F) -> R
4235 where
4236 F: FnOnce(&mut Self) -> R,
4237 {
4238 self.invalidator.debug_assert_paint_or_prepaint();
4239 if let Some(style) = style {
4240 self.text_style_stack.push(style);
4241 let result = f(self);
4242 self.text_style_stack.pop();
4243 result
4244 } else {
4245 f(self)
4246 }
4247 }
4248
4249 pub fn set_cursor_style(&mut self, style: CursorStyle, hitbox: &Hitbox) {
4252 self.invalidator.debug_assert_paint();
4253 self.next_frame.cursor_styles.push(CursorStyleRequest {
4254 hitbox_id: Some(hitbox.id),
4255 style,
4256 });
4257 }
4258
4259 pub fn set_window_cursor_style(&mut self, style: CursorStyle) {
4264 self.invalidator.debug_assert_paint();
4265 self.next_frame.cursor_styles.push(CursorStyleRequest {
4266 hitbox_id: None,
4267 style,
4268 })
4269 }
4270
4271 pub fn set_tooltip(&mut self, tooltip: AnyTooltip) -> TooltipId {
4274 self.invalidator.debug_assert_prepaint();
4275 let id = TooltipId(post_inc(&mut self.next_tooltip_id.0));
4276 self.next_frame
4277 .tooltip_requests
4278 .push(Some(TooltipRequest { id, tooltip }));
4279 id
4280 }
4281
4282 #[inline]
4287 pub fn with_content_mask<R>(
4288 &mut self,
4289 mask: Option<ContentMask<Pixels>>,
4290 f: impl FnOnce(&mut Self) -> R,
4291 ) -> R {
4292 self.invalidator.debug_assert_paint_or_prepaint();
4293 if let Some(mask) = mask {
4294 let mask = mask.intersect(&self.content_mask());
4295 self.content_mask_stack.push(mask);
4296 let result = f(self);
4297 self.content_mask_stack.pop();
4298 result
4299 } else {
4300 f(self)
4301 }
4302 }
4303
4304 pub fn with_element_offset<R>(
4307 &mut self,
4308 offset: Point<Pixels>,
4309 f: impl FnOnce(&mut Self) -> R,
4310 ) -> R {
4311 self.invalidator.debug_assert_prepaint();
4312
4313 if offset.is_zero() {
4314 return f(self);
4315 };
4316
4317 let abs_offset = self.element_offset() + offset;
4318 self.with_absolute_element_offset(abs_offset, f)
4319 }
4320
4321 pub fn with_absolute_element_offset<R>(
4325 &mut self,
4326 offset: Point<Pixels>,
4327 f: impl FnOnce(&mut Self) -> R,
4328 ) -> R {
4329 self.invalidator.debug_assert_prepaint();
4330 self.element_offset_stack.push(offset);
4331 let result = f(self);
4332 self.element_offset_stack.pop();
4333 result
4334 }
4335
4336 pub fn with_element_opacity<R>(
4338 &mut self,
4339 opacity: Option<f32>,
4340 f: impl FnOnce(&mut Self) -> R,
4341 ) -> R {
4342 self.invalidator.debug_assert_paint_or_prepaint();
4343
4344 let Some(opacity) = opacity else {
4345 return f(self);
4346 };
4347
4348 let previous_opacity = self.element_opacity;
4349 self.element_opacity = previous_opacity * opacity;
4350 let result = f(self);
4351 self.element_opacity = previous_opacity;
4352 result
4353 }
4354
4355 pub fn transact<T, U>(&mut self, f: impl FnOnce(&mut Self) -> Result<T, U>) -> Result<T, U> {
4361 self.invalidator.debug_assert_prepaint();
4362 let index = self.prepaint_index();
4363 let result = f(self);
4364 if result.is_err() {
4365 self.next_frame.hitboxes.truncate(index.hitboxes_index);
4366 self.next_frame
4367 .tooltip_requests
4368 .truncate(index.tooltips_index);
4369 self.next_frame
4370 .deferred_draws
4371 .truncate(index.deferred_draws_index);
4372 self.next_frame
4373 .dispatch_tree
4374 .truncate(index.dispatch_tree_index);
4375 self.next_frame
4376 .accessed_element_states
4377 .truncate(index.accessed_element_states_index);
4378 self.text_system.truncate_layouts(index.line_layout_index);
4379 }
4380 result
4381 }
4382
4383 pub fn request_autoscroll(&mut self, bounds: Bounds<Pixels>) {
4389 self.invalidator.debug_assert_prepaint();
4390 self.requested_autoscroll = Some(bounds);
4391 }
4392
4393 pub fn take_autoscroll(&mut self) -> Option<Bounds<Pixels>> {
4396 self.invalidator.debug_assert_prepaint();
4397 self.requested_autoscroll.take()
4398 }
4399
4400 pub fn use_asset<A: Asset>(&mut self, source: &A::Source, cx: &mut App) -> Option<A::Output> {
4406 cx.asset_entry::<A>(source).use_by(self.current_view())
4407 }
4408
4409 pub fn get_asset<A: Asset>(&mut self, source: &A::Source, cx: &mut App) -> Option<A::Output> {
4415 cx.fetch_asset::<A>(source)
4416 }
4417 pub fn element_offset(&self) -> Point<Pixels> {
4420 self.invalidator.debug_assert_prepaint();
4421 self.element_offset_stack
4422 .last()
4423 .copied()
4424 .unwrap_or_default()
4425 }
4426
4427 #[inline]
4430 pub fn element_opacity(&self) -> f32 {
4431 self.invalidator.debug_assert_paint_or_prepaint();
4432 self.element_opacity
4433 }
4434
4435 pub fn content_mask(&self) -> ContentMask<Pixels> {
4437 self.invalidator.debug_assert_paint_or_prepaint();
4438 self.content_mask_stack
4439 .last()
4440 .cloned()
4441 .unwrap_or_else(|| ContentMask {
4442 bounds: Bounds {
4443 origin: Point::default(),
4444 size: self.viewport_size,
4445 },
4446 })
4447 }
4448
4449 pub fn with_element_namespace<R>(
4452 &mut self,
4453 element_id: impl Into<ElementId>,
4454 f: impl FnOnce(&mut Self) -> R,
4455 ) -> R {
4456 self.element_id_stack.push(element_id.into());
4457 let result = f(self);
4458 self.element_id_stack.pop();
4459 result
4460 }
4461
4462 pub fn use_keyed_state<S: 'static>(
4464 &mut self,
4465 key: impl Into<ElementId>,
4466 cx: &mut App,
4467 init: impl FnOnce(&mut Self, &mut Context<S>) -> S,
4468 ) -> Entity<S> {
4469 let current_view = self.current_view();
4470 self.with_global_id(key.into(), |global_id, window| {
4471 window.with_element_state(global_id, |state: Option<Entity<S>>, window| {
4472 if let Some(state) = state {
4473 (state.clone(), state)
4474 } else {
4475 let new_state = cx.new(|cx| init(window, cx));
4476 Self::observe_keyed_state(&new_state, current_view, cx);
4477 (new_state.clone(), new_state)
4478 }
4479 })
4480 })
4481 }
4482
4483 #[track_caller]
4489 pub fn use_state<S: 'static>(
4490 &mut self,
4491 cx: &mut App,
4492 init: impl FnOnce(&mut Self, &mut Context<S>) -> S,
4493 ) -> Entity<S> {
4494 self.use_keyed_state(
4495 ElementId::CodeLocation(*core::panic::Location::caller()),
4496 cx,
4497 init,
4498 )
4499 }
4500
4501 #[inline(always)]
4506 pub fn with_element_state<S, R>(
4507 &mut self,
4508 global_id: &GlobalElementId,
4509 f: impl FnOnce(Option<S>, &mut Self) -> (R, S),
4510 ) -> R
4511 where
4512 S: 'static,
4513 {
4514 self.invalidator.debug_assert_paint_or_prepaint();
4515
4516 let (key, state) = self.take_element_state(global_id, TypeId::of::<S>());
4517
4518 if let Some(any) = state {
4519 let ElementStateBox {
4520 inner,
4521 #[cfg(debug_assertions)]
4522 type_name,
4523 } = any;
4524 let mut state_box = inner
4526 .downcast::<Option<S>>()
4527 .map_err(|_| {
4528 #[cfg(debug_assertions)]
4529 {
4530 anyhow::anyhow!(
4531 "invalid element state type for id, requested {:?}, actual: {:?}",
4532 std::any::type_name::<S>(),
4533 type_name
4534 )
4535 }
4536
4537 #[cfg(not(debug_assertions))]
4538 {
4539 anyhow::anyhow!(
4540 "invalid element state type for id, requested {:?}",
4541 std::any::type_name::<S>(),
4542 )
4543 }
4544 })
4545 .unwrap();
4546
4547 let state = state_box.take().expect(
4548 "reentrant call to with_element_state for the same state type and element id",
4549 );
4550 let (result, state) = f(Some(state), self);
4551 state_box.replace(state);
4552 self.insert_element_state(
4553 key,
4554 ElementStateBox {
4555 inner: state_box,
4556 #[cfg(debug_assertions)]
4557 type_name,
4558 },
4559 );
4560 result
4561 } else {
4562 let (result, state) = f(None, self);
4563 self.insert_element_state(
4564 key,
4565 ElementStateBox {
4566 inner: Box::new(Some(state)),
4567 #[cfg(debug_assertions)]
4568 type_name: std::any::type_name::<S>(),
4569 },
4570 );
4571 result
4572 }
4573 }
4574
4575 pub fn with_optional_element_state<S, R>(
4582 &mut self,
4583 global_id: Option<&GlobalElementId>,
4584 f: impl FnOnce(Option<Option<S>>, &mut Self) -> (R, Option<S>),
4585 ) -> R
4586 where
4587 S: 'static,
4588 {
4589 self.invalidator.debug_assert_paint_or_prepaint();
4590
4591 if let Some(global_id) = global_id {
4592 self.with_element_state(global_id, |state, cx| {
4593 let (result, state) = f(Some(state), cx);
4594 let state =
4595 state.expect("you must return some state when you pass some element id");
4596 (result, state)
4597 })
4598 } else {
4599 let (result, state) = f(None, self);
4600 debug_assert!(
4601 state.is_none(),
4602 "you must not return an element state when passing None for the global id"
4603 );
4604 result
4605 }
4606 }
4607
4608 #[inline]
4610 pub fn with_tab_group<R>(&mut self, index: Option<isize>, f: impl FnOnce(&mut Self) -> R) -> R {
4611 if let Some(index) = index {
4612 self.next_frame.tab_stops.begin_group(index);
4613 let result = f(self);
4614 self.next_frame.tab_stops.end_group();
4615 result
4616 } else {
4617 f(self)
4618 }
4619 }
4620
4621 pub fn defer_draw(
4630 &mut self,
4631 element: AnyElement,
4632 absolute_offset: Point<Pixels>,
4633 priority: usize,
4634 content_mask: Option<ContentMask<Pixels>>,
4635 ) {
4636 self.invalidator.debug_assert_prepaint();
4637 let parent_node = self.next_frame.dispatch_tree.active_node_id().unwrap();
4638 self.next_frame.deferred_draws.push(DeferredDraw {
4639 current_view: self.current_view(),
4640 parent_node,
4641 element_id_stack: self.element_id_stack.clone(),
4642 text_style_stack: self.text_style_stack.clone(),
4643 glyph_brush_stack: self.glyph_brush_stack.clone(),
4644 content_mask,
4645 rem_size: self.rem_size(),
4646 priority,
4647 element: Some(element),
4648 absolute_offset,
4649 prepaint_range: PrepaintStateIndex::default()..PrepaintStateIndex::default(),
4650 paint_range: PaintIndex::default()..PaintIndex::default(),
4651 });
4652 }
4653
4654 pub fn paint_layer<R>(&mut self, bounds: Bounds<Pixels>, f: impl FnOnce(&mut Self) -> R) -> R {
4660 self.invalidator.debug_assert_paint();
4661
4662 let content_mask = self.content_mask();
4663 let clipped_bounds = bounds.intersect(&content_mask.bounds);
4664 if !clipped_bounds.is_empty() {
4665 self.next_frame
4666 .scene
4667 .push_layer(self.cover_bounds(clipped_bounds));
4668 }
4669
4670 let result = f(self);
4671
4672 if !clipped_bounds.is_empty() {
4673 self.next_frame.scene.pop_layer();
4674 }
4675
4676 result
4677 }
4678
4679 pub fn paint_drop_shadows(
4685 &mut self,
4686 bounds: Bounds<Pixels>,
4687 corner_radii: Corners<Pixels>,
4688 shadows: &[BoxShadow],
4689 ) {
4690 self.invalidator.debug_assert_paint();
4691
4692 let scale_factor = self.scale_factor();
4693 let content_mask = self.snapped_content_mask();
4694 let opacity = self.element_opacity();
4695 let element_bounds = self.cover_bounds(bounds);
4696 let element_corner_radii = corner_radii.scale(scale_factor);
4697 for shadow in shadows {
4698 if shadow.inset {
4699 continue;
4700 }
4701 let shadow_bounds = (bounds + shadow.offset).dilate(shadow.spread_radius);
4702 self.next_frame.scene.insert_primitive(Shadow {
4703 order: 0,
4704 blur_radius: shadow.blur_radius.scale(scale_factor),
4705 bounds: self.cover_bounds(shadow_bounds),
4706 content_mask,
4707 corner_radii: corner_radii.scale(scale_factor),
4708 color: shadow.color.opacity(opacity),
4709 element_bounds,
4710 element_corner_radii,
4711 inset: 0,
4712 pad: 0,
4713 });
4714 }
4715 }
4716
4717 pub fn paint_inset_shadows(
4721 &mut self,
4722 bounds: Bounds<Pixels>,
4723 corner_radii: Corners<Pixels>,
4724 shadows: &[BoxShadow],
4725 ) {
4726 self.invalidator.debug_assert_paint();
4727
4728 let scale_factor = self.scale_factor();
4729 let content_mask = self.snapped_content_mask();
4730 let opacity = self.element_opacity();
4731 let element_bounds = self.cover_bounds(bounds);
4732 let element_corner_radii = corner_radii.scale(scale_factor);
4733 for shadow in shadows {
4734 if !shadow.inset {
4735 continue;
4736 }
4737 let hole = (bounds + shadow.offset).dilate(-shadow.spread_radius);
4738 let zero = Pixels::ZERO;
4741 let hole_corner_radii = Corners {
4742 top_left: (corner_radii.top_left - shadow.spread_radius).max(zero),
4743 top_right: (corner_radii.top_right - shadow.spread_radius).max(zero),
4744 bottom_right: (corner_radii.bottom_right - shadow.spread_radius).max(zero),
4745 bottom_left: (corner_radii.bottom_left - shadow.spread_radius).max(zero),
4746 };
4747 self.next_frame.scene.insert_primitive(Shadow {
4748 order: 0,
4749 blur_radius: shadow.blur_radius.scale(scale_factor),
4750 bounds: self.cover_bounds(hole),
4751 content_mask,
4752 corner_radii: hole_corner_radii.scale(scale_factor),
4753 color: shadow.color.opacity(opacity),
4754 element_bounds,
4755 element_corner_radii,
4756 inset: 1,
4757 pad: 0,
4758 });
4759 }
4760 }
4761
4762 fn largest_border_interior(quad: &Quad) -> Bounds<ScaledPixels> {
4763 let radii = &quad.corner_radii;
4764 let widths = &quad.border_widths;
4765 let edge_radii = Edges {
4766 top: radii.top_left.max(radii.top_right),
4767 right: radii.top_right.max(radii.bottom_right),
4768 bottom: radii.bottom_left.max(radii.bottom_right),
4769 left: radii.top_left.max(radii.bottom_left),
4770 };
4771
4772 let antialias_inset = point(ScaledPixels(1.0), ScaledPixels(1.0));
4773 let inset_bounds = |top_left_inset, bottom_right_inset| {
4774 Bounds::from_corners(
4775 quad.bounds.origin + top_left_inset + antialias_inset,
4776 quad.bounds.bottom_right() - bottom_right_inset - antialias_inset,
4777 )
4778 };
4779
4780 let horizontal_band = inset_bounds(
4783 point(widths.left, widths.top.max(edge_radii.top)),
4784 point(widths.right, widths.bottom.max(edge_radii.bottom)),
4785 );
4786 let vertical_band = inset_bounds(
4787 point(widths.left.max(edge_radii.left), widths.top),
4788 point(widths.right.max(edge_radii.right), widths.bottom),
4789 );
4790
4791 let area = |bounds: &Bounds<ScaledPixels>| {
4792 bounds.size.width.0.max(0.) * bounds.size.height.0.max(0.)
4793 };
4794 if area(&horizontal_band) >= area(&vertical_band) {
4795 horizontal_band
4796 } else {
4797 vertical_band
4798 }
4799 }
4800
4801 pub fn paint_quad(&mut self, quad: PaintQuad) {
4811 self.invalidator.debug_assert_paint();
4812
4813 let opacity = self.element_opacity();
4814 let snapped_bounds = self.snap_bounds(quad.bounds);
4815 let snapped_border_widths = self.snap_border_widths(quad.border_widths);
4816 let quad = Quad {
4817 order: 0,
4818 bounds: snapped_bounds,
4819 content_mask: self.snapped_content_mask(),
4820 background: quad.background.opacity(opacity),
4821 border_color: quad.border_color.opacity(opacity),
4822 corner_radii: quad.corner_radii.scale(self.scale_factor()),
4823 border_widths: snapped_border_widths,
4824 border_style: quad.border_style,
4825 };
4826
4827 if !quad.background.is_transparent() {
4828 self.next_frame.scene.insert_primitive(quad);
4829 return;
4830 }
4831
4832 let outer_bounds = quad.bounds;
4835 let inner_bounds = Self::largest_border_interior(&quad);
4836
4837 if inner_bounds.is_empty() {
4838 self.next_frame.scene.insert_primitive(quad);
4839 return;
4840 }
4841
4842 let strips = [
4843 Bounds::from_corners(
4845 outer_bounds.origin,
4846 point(outer_bounds.right(), inner_bounds.top()),
4847 ),
4848 Bounds::from_corners(
4850 point(outer_bounds.left(), inner_bounds.bottom()),
4851 outer_bounds.bottom_right(),
4852 ),
4853 Bounds::from_corners(
4855 point(outer_bounds.left(), inner_bounds.top()),
4856 inner_bounds.bottom_left(),
4857 ),
4858 Bounds::from_corners(
4860 inner_bounds.top_right(),
4861 point(outer_bounds.right(), inner_bounds.bottom()),
4862 ),
4863 ];
4864
4865 for strip in strips {
4866 let content_mask_bounds = quad.content_mask.bounds.intersect(&strip);
4867 if !content_mask_bounds.is_empty() {
4868 self.next_frame.scene.insert_primitive(Quad {
4869 content_mask: ContentMask {
4870 bounds: content_mask_bounds,
4871 },
4872 ..quad
4873 });
4874 }
4875 }
4876 }
4877
4878 pub fn paint_path(&mut self, mut path: Path<Pixels>, color: impl Into<Background>) {
4882 self.invalidator.debug_assert_paint();
4883
4884 let scale_factor = self.scale_factor();
4885 let content_mask = self.content_mask();
4886 let opacity = self.element_opacity();
4887 path.content_mask = content_mask;
4888 let color: Background = color.into();
4889 path.color = color.opacity(opacity);
4890 self.next_frame
4891 .scene
4892 .insert_primitive(path.scale(scale_factor));
4893 }
4894
4895 pub fn paint_brushed_path(&mut self, mut draw: crate::ShaderDraw) {
4898 self.invalidator.debug_assert_paint();
4899 draw.opacity *= self.element_opacity();
4900 draw.content_mask.bounds = draw
4901 .content_mask
4902 .bounds
4903 .intersect(&self.content_mask().scale(self.scale_factor()).bounds);
4904 if draw.opacity <= 0. || draw.bounds.intersect(&draw.content_mask.bounds).is_empty() {
4905 return;
4906 }
4907 self.next_frame
4908 .scene
4909 .insert_primitive(crate::Primitive::Shader(draw));
4910 }
4911
4912 pub fn paint_underline(
4916 &mut self,
4917 origin: Point<Pixels>,
4918 width: Pixels,
4919 style: &UnderlineStyle,
4920 ) {
4921 self.invalidator.debug_assert_paint();
4922 let underline = self.underline(origin, width, style);
4923 self.next_frame.scene.insert_primitive(underline);
4924 }
4925
4926 pub fn paint_strikethrough(
4930 &mut self,
4931 origin: Point<Pixels>,
4932 width: Pixels,
4933 style: &StrikethroughStyle,
4934 ) {
4935 self.invalidator.debug_assert_paint();
4936
4937 let scale_factor = self.scale_factor();
4938 let height = style.thickness;
4939 let bounds = Bounds {
4940 origin: origin.map(|c| ScaledPixels(round_to_device_pixel(c.0, scale_factor))),
4941 size: size(self.snap_stroke(width), self.snap_stroke(height)),
4942 };
4943 let opacity = self.element_opacity();
4944
4945 self.next_frame.scene.insert_primitive(Underline {
4946 order: 0,
4947 pad: 0,
4948 bounds,
4949 content_mask: self.snapped_content_mask(),
4950 thickness: self.snap_stroke(style.thickness),
4951 color: style.color.unwrap_or_default().opacity(opacity),
4952 wavy: false.into(),
4953 });
4954 }
4955
4956 pub fn paint_glyph(
4965 &mut self,
4966 origin: Point<Pixels>,
4967 font_id: FontId,
4968 glyph_id: GlyphId,
4969 font_size: Pixels,
4970 color: Hsla,
4971 ) -> Result<()> {
4972 self.invalidator.debug_assert_paint();
4973
4974 let element_opacity = self.element_opacity();
4975 let scale_factor = self.scale_factor();
4976 let glyph_origin = origin.scale(scale_factor);
4977
4978 let quantized_origin = Point::new(
4979 round_half_toward_zero(glyph_origin.x.0 * SUBPIXEL_VARIANTS_X as f32)
4980 / SUBPIXEL_VARIANTS_X as f32,
4981 round_half_toward_zero(glyph_origin.y.0 * SUBPIXEL_VARIANTS_Y as f32)
4982 / SUBPIXEL_VARIANTS_Y as f32,
4983 );
4984 let subpixel_variant = Point::new(
4985 (quantized_origin.x.fract() * SUBPIXEL_VARIANTS_X as f32) as u8,
4986 (quantized_origin.y.fract() * SUBPIXEL_VARIANTS_Y as f32) as u8,
4987 );
4988 let integer_origin = quantized_origin.map(|c| ScaledPixels(c.trunc()));
4989 let glyph_brush = self.glyph_brush_stack.last().cloned();
4990 let subpixel_rendering = glyph_brush.is_none()
4993 && self.layer_capture_depth == 0
4994 && self.should_use_subpixel_rendering(font_id, font_size);
4995 let dilation = self
4996 .text_system()
4997 .glyph_dilation_for_color(if glyph_brush.is_some() {
4998 crate::white()
4999 } else {
5000 color
5001 });
5002 let params = RenderGlyphParams {
5003 font_id,
5004 glyph_id,
5005 font_size,
5006 subpixel_variant,
5007 scale_factor,
5008 is_emoji: false,
5009 subpixel_rendering,
5010 dilation,
5011 };
5012
5013 let raster_bounds = self.text_system().raster_bounds(¶ms)?;
5014 if !raster_bounds.is_zero() {
5015 let tile = self
5016 .sprite_atlas
5017 .get_or_insert_with(params.clone().into(), &mut || {
5018 let (size, bytes) = self.text_system().rasterize_glyph(¶ms)?;
5019 Ok(Some((size, Cow::Owned(bytes))))
5020 })?
5021 .expect("Callback above only errors or returns Some");
5022 let bounds = Bounds {
5023 origin: integer_origin + raster_bounds.origin.map(Into::into),
5024 size: tile.bounds.size.map(Into::into),
5025 };
5026 let content_mask = self.snapped_content_mask();
5027
5028 if let Some((brush, brush_bounds)) = glyph_brush {
5029 brush.paint_glyph(
5030 self,
5031 MonochromeSprite {
5032 order: 0,
5033 pad: 0,
5034 bounds,
5035 content_mask,
5036 color: crate::white(),
5037 tile,
5038 transformation: TransformationMatrix::unit(),
5039 },
5040 brush_bounds,
5041 element_opacity,
5042 )?;
5043 } else if subpixel_rendering {
5044 self.next_frame.scene.insert_primitive(SubpixelSprite {
5045 order: 0,
5046 pad: 0,
5047 bounds,
5048 content_mask,
5049 color: color.opacity(element_opacity),
5050 tile,
5051 transformation: TransformationMatrix::unit(),
5052 });
5053 } else {
5054 self.next_frame.scene.insert_primitive(MonochromeSprite {
5055 order: 0,
5056 pad: 0,
5057 bounds,
5058 content_mask,
5059 color: color.opacity(element_opacity),
5060 tile,
5061 transformation: TransformationMatrix::unit(),
5062 });
5063 }
5064 }
5065 Ok(())
5066 }
5067
5068 fn should_use_subpixel_rendering(&self, font_id: FontId, font_size: Pixels) -> bool {
5069 if self.platform_window.background_appearance() != WindowBackgroundAppearance::Opaque {
5070 return false;
5071 }
5072
5073 if !self.platform_window.is_subpixel_rendering_supported() {
5074 return false;
5075 }
5076
5077 let mode = match self.text_rendering_mode.get() {
5078 TextRenderingMode::PlatformDefault => self
5079 .text_system()
5080 .recommended_rendering_mode(font_id, font_size),
5081 mode => mode,
5082 };
5083
5084 mode == TextRenderingMode::Subpixel
5085 }
5086
5087 pub fn paint_emoji(
5096 &mut self,
5097 origin: Point<Pixels>,
5098 font_id: FontId,
5099 glyph_id: GlyphId,
5100 font_size: Pixels,
5101 ) -> Result<()> {
5102 self.invalidator.debug_assert_paint();
5103
5104 let scale_factor = self.scale_factor();
5105 let glyph_origin = origin.scale(scale_factor);
5106 let integer_origin = glyph_origin.map(|c| ScaledPixels(round_half_toward_zero(c.0)));
5107 let params = RenderGlyphParams {
5108 font_id,
5109 glyph_id,
5110 font_size,
5111 subpixel_variant: Default::default(),
5112 scale_factor,
5113 is_emoji: true,
5114 subpixel_rendering: false,
5115 dilation: 0,
5116 };
5117
5118 let raster_bounds = self.text_system().raster_bounds(¶ms)?;
5119 if !raster_bounds.is_zero() {
5120 let tile = self
5121 .sprite_atlas
5122 .get_or_insert_with(params.clone().into(), &mut || {
5123 let (size, bytes) = self.text_system().rasterize_glyph(¶ms)?;
5124 Ok(Some((size, Cow::Owned(bytes))))
5125 })?
5126 .expect("Callback above only errors or returns Some");
5127
5128 let bounds = Bounds {
5129 origin: integer_origin + raster_bounds.origin.map(Into::into),
5130 size: tile.bounds.size.map(Into::into),
5131 };
5132 let content_mask = self.snapped_content_mask();
5133 let opacity = self.element_opacity();
5134
5135 self.next_frame.scene.insert_primitive(PolychromeSprite {
5136 order: 0,
5137 pad: 0,
5138 grayscale: false.into(),
5139 bounds,
5140 corner_radii: Default::default(),
5141 content_mask,
5142 tile,
5143 opacity,
5144 });
5145 }
5146 Ok(())
5147 }
5148
5149 pub fn paint_svg(
5153 &mut self,
5154 bounds: Bounds<Pixels>,
5155 path: SharedString,
5156 data: Option<&[u8]>,
5157 transformation: TransformationMatrix,
5158 color: Hsla,
5159 cx: &App,
5160 ) -> Result<()> {
5161 self.invalidator.debug_assert_paint();
5162
5163 let element_opacity = self.element_opacity();
5164 let bounds = self.snap_bounds(bounds);
5165
5166 let params = RenderSvgParams {
5167 path,
5168 size: bounds.size.map(|pixels| {
5169 DevicePixels::from((pixels.0 * SMOOTH_SVG_SCALE_FACTOR).ceil() as i32)
5170 }),
5171 };
5172
5173 let Some(tile) =
5174 self.sprite_atlas
5175 .get_or_insert_with(params.clone().into(), &mut || {
5176 let Some((size, bytes)) = cx.svg_renderer.render_alpha_mask(¶ms, data)?
5177 else {
5178 return Ok(None);
5179 };
5180 Ok(Some((size, Cow::Owned(bytes))))
5181 })?
5182 else {
5183 return Ok(());
5184 };
5185 let content_mask = self.snapped_content_mask();
5186 let svg_bounds = Bounds {
5187 origin: bounds.center()
5188 - Point::new(
5189 ScaledPixels(tile.bounds.size.width.0 as f32 / SMOOTH_SVG_SCALE_FACTOR / 2.),
5190 ScaledPixels(tile.bounds.size.height.0 as f32 / SMOOTH_SVG_SCALE_FACTOR / 2.),
5191 ),
5192 size: tile
5193 .bounds
5194 .size
5195 .map(|value| ScaledPixels(value.0 as f32 / SMOOTH_SVG_SCALE_FACTOR)),
5196 };
5197 let final_bounds = svg_bounds
5198 .map_origin(|value| ScaledPixels(round_half_toward_zero(value.0)))
5199 .map_size(|size| size.ceil());
5200
5201 self.next_frame.scene.insert_primitive(MonochromeSprite {
5202 order: 0,
5203 pad: 0,
5204 bounds: final_bounds,
5205 content_mask,
5206 color: color.opacity(element_opacity),
5207 tile,
5208 transformation,
5209 });
5210
5211 Ok(())
5212 }
5213
5214 pub fn paint_image(
5223 &mut self,
5224 bounds: Bounds<Pixels>,
5225 image_bounds: Bounds<Pixels>,
5226 corner_radii: Corners<Pixels>,
5227 data: Arc<RenderImage>,
5228 frame_index: usize,
5229 grayscale: bool,
5230 ) -> Result<()> {
5231 self.invalidator.debug_assert_paint();
5232
5233 let visible_bounds = bounds.intersect(&image_bounds);
5234 if visible_bounds.size.width <= Pixels::ZERO || visible_bounds.size.height <= Pixels::ZERO {
5235 return Ok(());
5236 }
5237 if image_bounds.size.width <= Pixels::ZERO || image_bounds.size.height <= Pixels::ZERO {
5238 return Ok(());
5239 }
5240
5241 let params = RenderImageParams {
5242 image_id: data.id,
5243 frame_index,
5244 };
5245
5246 let tile = self
5247 .sprite_atlas
5248 .get_or_insert_with(params.into(), &mut || {
5249 Ok(Some((
5250 data.size(frame_index),
5251 Cow::Borrowed(
5252 data.as_bytes(frame_index)
5253 .expect("It's the caller's job to pass a valid frame index"),
5254 ),
5255 )))
5256 })?
5257 .expect("Callback above only returns Some");
5258
5259 let visible_bounds_snapped = self.snap_bounds(visible_bounds);
5260
5261 let sub_tile = if visible_bounds == image_bounds {
5262 tile
5263 } else {
5264 let x_offset_ratio =
5265 (visible_bounds.origin.x - image_bounds.origin.x) / image_bounds.size.width;
5266 let y_offset_ratio =
5267 (visible_bounds.origin.y - image_bounds.origin.y) / image_bounds.size.height;
5268 let width_ratio = visible_bounds.size.width / image_bounds.size.width;
5269 let height_ratio = visible_bounds.size.height / image_bounds.size.height;
5270
5271 let tile_origin_x = tile.bounds.origin.x.0;
5272 let tile_origin_y = tile.bounds.origin.y.0;
5273 let tile_width = tile.bounds.size.width.0;
5274 let tile_height = tile.bounds.size.height.0;
5275
5276 let sub_origin_x = tile_origin_x + (x_offset_ratio * tile_width as f32).round() as i32;
5277 let sub_origin_y = tile_origin_y + (y_offset_ratio * tile_height as f32).round() as i32;
5278 let sub_width = (width_ratio * tile_width as f32).round() as i32;
5279 let sub_height = (height_ratio * tile_height as f32).round() as i32;
5280
5281 let max_x = tile_origin_x + tile_width;
5282 let max_y = tile_origin_y + tile_height;
5283
5284 let clamped_origin_x = sub_origin_x.clamp(tile_origin_x, max_x);
5285 let clamped_origin_y = sub_origin_y.clamp(tile_origin_y, max_y);
5286 let clamped_width = sub_width.min(max_x - clamped_origin_x).max(0);
5287 let clamped_height = sub_height.min(max_y - clamped_origin_y).max(0);
5288
5289 AtlasTile {
5290 bounds: Bounds {
5291 origin: point(
5292 DevicePixels(clamped_origin_x),
5293 DevicePixels(clamped_origin_y),
5294 ),
5295 size: size(DevicePixels(clamped_width), DevicePixels(clamped_height)),
5296 },
5297 ..tile
5298 }
5299 };
5300
5301 let content_mask = self.snapped_content_mask();
5302 let corner_radii = corner_radii
5303 .clamp_radii_for_quad_size(visible_bounds.size)
5304 .scale(self.scale_factor());
5305 let opacity = self.element_opacity();
5306
5307 self.next_frame.scene.insert_primitive(PolychromeSprite {
5308 order: 0,
5309 pad: 0,
5310 grayscale: grayscale.into(),
5311 bounds: visible_bounds_snapped,
5312 content_mask,
5313 corner_radii,
5314 tile: sub_tile,
5315 opacity,
5316 });
5317 Ok(())
5318 }
5319
5320 pub(crate) fn glyph_brush_cache_key(&self) -> Option<GlyphBrushCacheKey> {
5321 self.glyph_brush_stack
5322 .last()
5323 .map(|(brush, bounds)| GlyphBrushCacheKey {
5324 identity: brush.cache_key(),
5325 bounds: *bounds,
5326 })
5327 }
5328 pub fn with_glyph_brush<R>(
5331 &mut self,
5332 brush: Arc<dyn crate::GlyphBrush>,
5333 bounds: Bounds<Pixels>,
5334 f: impl FnOnce(&mut Self) -> R,
5335 ) -> R {
5336 self.glyph_brush_stack.push((brush, bounds));
5337 let result = f(self);
5338 self.glyph_brush_stack.pop();
5339 result
5340 }
5341 pub fn paint_glyph_shader(
5344 &mut self,
5345 glyph: MonochromeSprite,
5346 program: Arc<crate::SceneShaderProgram>,
5347 data: [[f32; 4]; crate::SHADER_DATA_SLOTS],
5348 opacity: f32,
5349 ) {
5350 self.invalidator.debug_assert_paint();
5351 assert!(
5352 program.uses_glyph_mask(),
5353 "glyph drawing requires an atlas-masked program"
5354 );
5355 self.next_frame
5356 .scene
5357 .insert_primitive(crate::Primitive::Shader(crate::ShaderDraw {
5358 order: 0,
5359 bounds: glyph.bounds,
5360 content_mask: glyph.content_mask,
5361 opacity,
5362 scale_factor: self.scale_factor(),
5363 program,
5364 data,
5365 path_mask: None,
5366 glyph_mask: Some(glyph.tile),
5367 }));
5368 }
5369 pub fn is_layer_captured(&self) -> bool {
5371 self.layer_capture_depth > 0
5372 }
5373
5374 pub fn with_layer_capture_scope<R>(
5377 &mut self,
5378 bounds: Bounds<Pixels>,
5379 f: impl FnOnce(&mut Self) -> R,
5380 ) -> R {
5381 let source = bounds.intersect(&self.content_mask().bounds);
5382 self.with_layer_capture_masks(source, source, f)
5383 }
5384
5385 pub fn with_layer_effect_scope<R>(
5389 &mut self,
5390 bounds: Bounds<Pixels>,
5391 f: impl FnOnce(&mut Self) -> R,
5392 ) -> R {
5393 let (source, clip) = self.layer_capture_regions(bounds);
5394 self.with_layer_capture_masks(source, clip, f)
5395 }
5396 fn layer_capture_regions(&self, bounds: Bounds<Pixels>) -> (Bounds<Pixels>, Bounds<Pixels>) {
5397 let viewport = Bounds::new(Point::default(), self.viewport_size);
5398 let clip = bounds
5399 .intersect(&self.content_mask().bounds)
5400 .intersect(&viewport);
5401 if clip.is_empty() {
5402 return (clip, clip);
5403 }
5404 let levels = crate::MAX_LAYER_GROUPS
5405 .saturating_sub(self.layer_capture_depth)
5406 .max(1) as f32;
5407 let halo = px((16. + 8. / self.scale_factor()) * levels);
5410 let mut source = clip.dilate(halo).intersect(&viewport);
5411 if let Some(parent) = self.layer_capture_sources.last() {
5412 source = source.intersect(parent);
5413 }
5414 (source, clip)
5415 }
5416 pub(crate) fn layer_capture_visible_bounds(&self) -> Option<Bounds<Pixels>> {
5417 self.layer_capture_clips.last().copied()
5418 }
5419 fn with_layer_capture_masks<R>(
5420 &mut self,
5421 source: Bounds<Pixels>,
5422 clip: Bounds<Pixels>,
5423 f: impl FnOnce(&mut Self) -> R,
5424 ) -> R {
5425 self.invalidator.debug_assert_paint_or_prepaint();
5426 let previous = self.element_opacity;
5427 self.element_opacity = 1.;
5428 self.layer_capture_depth += 1;
5429 self.layer_capture_sources.push(source);
5430 let visible = self
5431 .layer_capture_clips
5432 .last()
5433 .map_or(clip, |parent| clip.intersect(parent));
5434 self.layer_capture_clips.push(visible);
5435 self.content_mask_stack.push(ContentMask { bounds: source });
5438 let result = f(self);
5439 self.content_mask_stack.pop();
5440 self.layer_capture_clips.pop();
5441 self.layer_capture_sources.pop();
5442 self.layer_capture_depth -= 1;
5443 self.element_opacity = previous;
5444 result
5445 }
5446
5447 pub fn with_layer_opacity<R>(
5450 &mut self,
5451 bounds: Bounds<Pixels>,
5452 opacity: f32,
5453 program: Arc<crate::SceneShaderProgram>,
5454 f: impl FnOnce(&mut Self) -> R,
5455 ) -> R {
5456 self.with_layer_effect(bounds, opacity, 1., program, f)
5457 }
5458
5459 pub fn with_layer_effect<R>(
5462 &mut self,
5463 bounds: Bounds<Pixels>,
5464 opacity: f32,
5465 saturation: f32,
5466 program: Arc<crate::SceneShaderProgram>,
5467 f: impl FnOnce(&mut Self) -> R,
5468 ) -> R {
5469 self.with_layer_color_filter(
5470 bounds,
5471 opacity,
5472 crate::LayerColorFilter {
5473 saturation,
5474 ..Default::default()
5475 },
5476 program,
5477 f,
5478 )
5479 }
5480
5481 pub fn with_layer_color_filter<R>(
5484 &mut self,
5485 bounds: Bounds<Pixels>,
5486 opacity: f32,
5487 color_filter: crate::LayerColorFilter,
5488 program: Arc<crate::SceneShaderProgram>,
5489 f: impl FnOnce(&mut Self) -> R,
5490 ) -> R {
5491 self.invalidator.debug_assert_paint();
5492 let viewport = Bounds::new(Point::default(), self.viewport_size);
5493 let (source, clipped) = self.layer_capture_regions(bounds);
5494 let effective = opacity * self.element_opacity();
5495 if clipped.is_empty() {
5496 return self.with_layer_capture_scope(clipped, f);
5497 }
5498 let scale = self.scale_factor();
5499 let capture = Arc::new(crate::LayerCapture {
5500 blur_quality: color_filter.blur_quality,
5501 blur_radius: crate::ScaledPixels(f32::from(color_filter.blur_radius) * scale),
5502 blend_mode: color_filter.blend_mode,
5503 bounds: source.scale(scale),
5504 clip: Some(clipped.scale(scale)),
5505 opacity: effective,
5506 saturation: color_filter.saturation,
5507 brightness: color_filter.brightness,
5508 contrast: color_filter.contrast,
5509 invert: color_filter.invert,
5510 program,
5511 });
5512 let fence = ContentMask {
5513 bounds: viewport.scale(scale),
5514 };
5515 self.next_frame
5516 .scene
5517 .begin_layer_capture(capture.clone(), fence);
5518 let result = self.with_layer_capture_masks(source, clipped, f);
5519 self.next_frame.scene.end_layer_capture(capture, fence);
5520 result
5521 }
5522
5523 pub fn paint_backdrop_blur(
5526 &mut self,
5527 bounds: Bounds<Pixels>,
5528 radii: Corners<Pixels>,
5529 radius: Pixels,
5530 opacity: f32,
5531 program: Arc<crate::SceneShaderProgram>,
5532 ) {
5533 self.invalidator.debug_assert_paint();
5534 let viewport = Bounds::new(Point::default(), self.viewport_size);
5535 let clip = self.content_mask();
5536 if radius <= px(0.)
5537 || bounds
5538 .intersect(&clip.bounds)
5539 .intersect(&viewport)
5540 .is_empty()
5541 || opacity <= 0.
5542 {
5543 return;
5544 }
5545 let scale = self.scale_factor();
5546 let halo = radius + px(crate::BACKDROP_DOWNSAMPLE as f32 / scale);
5547 let footprint = Bounds::new(
5548 bounds.origin - point(halo, halo),
5549 bounds.size + size(halo * 2., halo * 2.),
5550 );
5551 self.next_frame
5552 .scene
5553 .insert_primitive(crate::Primitive::Backdrop(crate::BackdropDraw {
5554 order: 0,
5555 bounds: footprint.scale(scale),
5556 content_mask: ContentMask {
5557 bounds: viewport.scale(scale),
5558 },
5559 surface_bounds: bounds.scale(scale),
5560 clip: clip.scale(scale),
5561 corner_radii: radii.scale(scale),
5562 radius: f32::from(radius) * scale,
5563 opacity: opacity * self.element_opacity(),
5564 program,
5565 }));
5566 }
5567 pub fn paint_shader(
5570 &mut self,
5571 bounds: Bounds<Pixels>,
5572 program: std::sync::Arc<crate::SceneShaderProgram>,
5573 data: [[f32; 4]; crate::SHADER_DATA_SLOTS],
5574 ) {
5575 self.invalidator.debug_assert_paint();
5576 if bounds.is_empty() || self.element_opacity() <= 0.0 {
5577 return;
5578 }
5579 self.next_frame
5580 .scene
5581 .insert_primitive(crate::Primitive::Shader(crate::ShaderDraw {
5582 path_mask: None,
5583 glyph_mask: None,
5584 order: 0,
5585 bounds: bounds.scale(self.scale_factor()),
5586 content_mask: self.snapped_content_mask(),
5587 opacity: self.element_opacity(),
5588 scale_factor: self.scale_factor(),
5589 program,
5590 data,
5591 }));
5592 }
5593
5594 pub fn paint_custom(
5597 &mut self,
5598 geometry: crate::CustomGeometry,
5599 content: std::sync::Arc<dyn crate::CustomContent>,
5600 ) {
5601 self.invalidator.debug_assert_paint();
5602 if self.element_opacity() <= 0.0 {
5603 return;
5604 }
5605 let (bounds, corner_radii, coverage) = match geometry {
5606 crate::CustomGeometry::RoundedRect {
5607 bounds,
5608 corner_radii,
5609 } => (bounds, corner_radii, None),
5610 crate::CustomGeometry::Border(quad) => {
5611 if quad.border_widths == Edges::default() {
5612 return;
5613 }
5614 let coverage = crate::CustomCoverage::Border {
5615 widths: self.snap_border_widths(quad.border_widths),
5616 style: quad.border_style,
5617 };
5618 (quad.bounds, quad.corner_radii, Some(coverage))
5619 }
5620 crate::CustomGeometry::Path(path) => {
5621 if path.bounds.is_empty() || path.vertices.is_empty() {
5622 return;
5623 }
5624 let bounds = path.bounds;
5625 (
5626 bounds,
5627 Corners::default(),
5628 Some(crate::CustomCoverage::Path(std::sync::Arc::new(
5629 path.scale(self.scale_factor()),
5630 ))),
5631 )
5632 }
5633 };
5634 if bounds.is_empty() {
5635 return;
5636 }
5637 let scaled_bounds = if matches!(&coverage, Some(crate::CustomCoverage::Path(_))) {
5640 bounds.scale(self.scale_factor())
5641 } else {
5642 self.snap_bounds(bounds)
5643 };
5644 self.next_frame.scene.insert_primitive(crate::CustomDraw {
5645 order: 0,
5646 bounds: scaled_bounds,
5647 content_mask: self.snapped_content_mask(),
5648 corner_radii: corner_radii
5649 .clamp_radii_for_quad_size(bounds.size)
5650 .scale(self.scale_factor()),
5651 opacity: self.element_opacity(),
5652 content: crate::scene::content_with_preparation_geometry(
5653 content,
5654 bounds.size,
5655 self.scale_factor(),
5656 ),
5657 coverage,
5658 });
5659 }
5660
5661 #[cfg(any(target_os = "macos", target_os = "ios"))]
5665 pub fn paint_surface(
5666 &mut self,
5667 bounds: Bounds<Pixels>,
5668 image_buffer: CFRetained<CVPixelBuffer>,
5669 ) {
5670 use crate::PaintSurface;
5671
5672 self.invalidator.debug_assert_paint();
5673
5674 let bounds = self.snap_bounds(bounds);
5675 let content_mask = self.snapped_content_mask();
5676 self.next_frame.scene.insert_primitive(PaintSurface {
5677 order: 0,
5678 bounds,
5679 content_mask,
5680 image_buffer,
5681 });
5682 }
5683
5684 pub fn drop_image(&mut self, data: Arc<RenderImage>) -> Result<()> {
5686 for frame_index in 0..data.frame_count() {
5687 let params = RenderImageParams {
5688 image_id: data.id,
5689 frame_index,
5690 };
5691
5692 self.sprite_atlas.remove(¶ms.clone().into());
5693 }
5694
5695 Ok(())
5696 }
5697
5698 #[cfg(any(test, feature = "test-support"))]
5700 pub fn has_image_atlas_entry(&self, data: &RenderImage) -> bool {
5701 data.frame_count() > 0
5702 && (0..data.frame_count()).all(|frame_index| {
5703 self.sprite_atlas.contains(
5704 &RenderImageParams {
5705 image_id: data.id,
5706 frame_index,
5707 }
5708 .into(),
5709 )
5710 })
5711 }
5712
5713 #[must_use]
5719 pub fn request_layout(
5720 &mut self,
5721 style: Style,
5722 children: impl IntoIterator<Item = LayoutId>,
5723 cx: &mut App,
5724 ) -> LayoutId {
5725 self.invalidator.debug_assert_prepaint();
5726
5727 cx.layout_id_buffer.clear();
5728 cx.layout_id_buffer.extend(children);
5729 let rem_size = self.rem_size();
5730 let scale_factor = self.scale_factor();
5731
5732 self.layout_engine.as_mut().unwrap().request_layout(
5733 style,
5734 rem_size,
5735 scale_factor,
5736 &cx.layout_id_buffer,
5737 )
5738 }
5739
5740 pub fn request_measured_layout<F>(&mut self, style: Style, measure: F) -> LayoutId
5749 where
5750 F: Fn(Size<Option<Pixels>>, Size<AvailableSpace>, &mut Window, &mut App) -> Size<Pixels>
5751 + 'static,
5752 {
5753 self.invalidator.debug_assert_prepaint();
5754
5755 let rem_size = self.rem_size();
5756 let scale_factor = self.scale_factor();
5757 self.layout_engine
5758 .as_mut()
5759 .unwrap()
5760 .request_measured_layout(style, rem_size, scale_factor, measure)
5761 }
5762
5763 pub fn compute_layout(
5769 &mut self,
5770 layout_id: LayoutId,
5771 available_space: Size<AvailableSpace>,
5772 cx: &mut App,
5773 ) {
5774 self.invalidator.debug_assert_prepaint();
5775
5776 let mut layout_engine = self.layout_engine.take().unwrap();
5777 layout_engine.compute_layout(layout_id, available_space, self, cx);
5778 self.layout_engine = Some(layout_engine);
5779 }
5780
5781 pub fn layout_bounds(&mut self, layout_id: LayoutId) -> Bounds<Pixels> {
5786 self.invalidator.debug_assert_prepaint();
5787
5788 let scale_factor = self.scale_factor();
5789 let mut bounds = self
5790 .layout_engine
5791 .as_mut()
5792 .unwrap()
5793 .layout_bounds(layout_id, scale_factor)
5794 .map(Into::into);
5795 let snapped_offset = self.pixel_snap_point(self.element_offset());
5796 bounds.origin += snapped_offset;
5797 bounds
5798 }
5799
5800 pub fn insert_hitbox(&mut self, bounds: Bounds<Pixels>, behavior: HitboxBehavior) -> Hitbox {
5806 self.invalidator.debug_assert_prepaint();
5807
5808 let mut content_mask = self.content_mask();
5809 if let Some(clip) = self.layer_capture_visible_bounds() {
5810 content_mask.bounds = content_mask.bounds.intersect(&clip);
5811 }
5812 let id = self.next_hitbox_id;
5813 self.next_hitbox_id = self.next_hitbox_id.next();
5814 let hitbox = Hitbox {
5815 id,
5816 bounds,
5817 content_mask,
5818 behavior,
5819 };
5820 self.next_frame.hitboxes.push(hitbox.clone());
5821 hitbox
5822 }
5823
5824 pub fn insert_window_control_hitbox(&mut self, area: WindowControlArea, hitbox: Hitbox) {
5828 self.invalidator.debug_assert_paint();
5829 self.next_frame.window_control_hitboxes.push((area, hitbox));
5830 }
5831
5832 pub fn set_key_context(&mut self, context: KeyContext) {
5837 self.invalidator.debug_assert_paint();
5838 self.next_frame.dispatch_tree.set_key_context(context);
5839 }
5840
5841 pub fn set_focus_handle(&mut self, focus_handle: &FocusHandle, _: &App) {
5846 self.invalidator.debug_assert_prepaint();
5847 if focus_handle.is_focused(self) {
5848 self.next_frame.focus = Some(focus_handle.id);
5849 }
5850 self.next_frame.dispatch_tree.set_focus_id(focus_handle.id);
5851 }
5852
5853 pub fn set_view_id(&mut self, view_id: EntityId) {
5859 self.invalidator.debug_assert_prepaint();
5860 self.next_frame.dispatch_tree.set_view_id(view_id);
5861 }
5862
5863 pub fn current_view(&self) -> EntityId {
5865 self.invalidator.debug_assert_paint_or_prepaint();
5866 self.rendered_entity_stack.last().copied().unwrap()
5867 }
5868
5869 #[inline]
5870 pub(crate) fn with_rendered_view<R>(
5871 &mut self,
5872 id: EntityId,
5873 f: impl FnOnce(&mut Self) -> R,
5874 ) -> R {
5875 self.rendered_entity_stack.push(id);
5876 let result = f(self);
5877 self.rendered_entity_stack.pop();
5878 result
5879 }
5880
5881 pub fn with_image_cache<F, R>(&mut self, image_cache: Option<AnyImageCache>, f: F) -> R
5883 where
5884 F: FnOnce(&mut Self) -> R,
5885 {
5886 if let Some(image_cache) = image_cache {
5887 self.image_cache_stack.push(image_cache);
5888 let result = f(self);
5889 self.image_cache_stack.pop();
5890 result
5891 } else {
5892 f(self)
5893 }
5894 }
5895
5896 pub fn handle_input(
5905 &mut self,
5906 focus_handle: &FocusHandle,
5907 input_handler: impl InputHandler,
5908 cx: &App,
5909 ) {
5910 self.invalidator.debug_assert_paint();
5911
5912 if focus_handle.is_focused(self) {
5913 let cx = self.to_async(cx);
5914 self.next_frame.input_handlers.push(Some(
5915 PlatformInputHandler::new(cx, Box::new(input_handler)).with_focus(focus_handle.id),
5916 ));
5917 }
5918 }
5919
5920 pub fn notify_text_input_changed(&self, focus_handle: &FocusHandle) {
5925 if self.focused_text_input == Some(focus_handle.id) && focus_handle.is_focused(self) {
5926 self.platform_window
5927 .text_input_state_changed(TextInputStateChange::ContentChanged);
5928 }
5929 }
5930
5931 pub fn notify_text_selection_changed(&self, focus_handle: &FocusHandle) {
5936 if self.focused_text_input == Some(focus_handle.id) && focus_handle.is_focused(self) {
5937 self.platform_window
5938 .text_input_state_changed(TextInputStateChange::SelectionChanged);
5939 }
5940 }
5941
5942 fn apply_text_input_configuration(&mut self, cx: &mut App) {
5947 let configuration = match self.platform_window.take_input_handler() {
5948 Some(mut input_handler) => {
5949 let configuration = input_handler.text_input_configuration(self, cx);
5950 self.platform_window.set_input_handler(input_handler);
5951 configuration
5952 }
5953 None => TextInputConfiguration::default(),
5954 };
5955 if self.last_text_input_configuration.as_ref() != Some(&configuration) {
5956 self.platform_window
5957 .set_text_input_configuration(configuration.clone());
5958 self.last_text_input_configuration = Some(configuration);
5959 }
5960 }
5961
5962 pub fn on_mouse_event<Event: MouseEvent>(
5968 &mut self,
5969 mut listener: impl FnMut(&Event, DispatchPhase, &mut Window, &mut App) + 'static,
5970 ) {
5971 self.invalidator.debug_assert_paint();
5972
5973 self.next_frame.mouse_listeners.push(Some(Box::new(
5974 move |event: &dyn Any, phase: DispatchPhase, window: &mut Window, cx: &mut App| {
5975 if let Some(event) = event.downcast_ref() {
5976 listener(event, phase, window, cx)
5977 }
5978 },
5979 )));
5980 }
5981
5982 pub fn on_key_event<Event: KeyEvent>(
5991 &mut self,
5992 listener: impl Fn(&Event, DispatchPhase, &mut Window, &mut App) + 'static,
5993 ) {
5994 self.invalidator.debug_assert_paint();
5995
5996 self.next_frame.dispatch_tree.on_key_event(Rc::new(
5997 move |event: &dyn Any, phase, window: &mut Window, cx: &mut App| {
5998 if let Some(event) = event.downcast_ref::<Event>() {
5999 listener(event, phase, window, cx)
6000 }
6001 },
6002 ));
6003 }
6004
6005 pub fn on_modifiers_changed(
6012 &mut self,
6013 listener: impl Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static,
6014 ) {
6015 self.invalidator.debug_assert_paint();
6016
6017 self.next_frame.dispatch_tree.on_modifiers_changed(Rc::new(
6018 move |event: &ModifiersChangedEvent, window: &mut Window, cx: &mut App| {
6019 listener(event, window, cx)
6020 },
6021 ));
6022 }
6023
6024 pub fn on_focus_in(
6028 &mut self,
6029 handle: &FocusHandle,
6030 cx: &mut App,
6031 mut listener: impl FnMut(&mut Window, &mut App) + 'static,
6032 ) -> Subscription {
6033 let focus_id = handle.id;
6034 let (subscription, activate) =
6035 self.new_focus_listener(Box::new(move |event, window, cx| {
6036 if event.is_focus_in(focus_id) {
6037 listener(window, cx);
6038 }
6039 true
6040 }));
6041 cx.defer(move |_| activate());
6042 subscription
6043 }
6044
6045 pub fn on_focus_out(
6048 &mut self,
6049 handle: &FocusHandle,
6050 cx: &mut App,
6051 mut listener: impl FnMut(FocusOutEvent, &mut Window, &mut App) + 'static,
6052 ) -> Subscription {
6053 let focus_id = handle.id;
6054 let (subscription, activate) =
6055 self.new_focus_listener(Box::new(move |event, window, cx| {
6056 if let Some(blurred_id) = event.previous_focus_path.last().copied()
6057 && event.is_focus_out(focus_id)
6058 {
6059 let event = FocusOutEvent {
6060 blurred: WeakFocusHandle {
6061 id: blurred_id,
6062 handles: Arc::downgrade(&cx.focus_handles),
6063 },
6064 };
6065 listener(event, window, cx)
6066 }
6067 true
6068 }));
6069 cx.defer(move |_| activate());
6070 subscription
6071 }
6072
6073 fn reset_cursor_style(&self, cx: &mut App) {
6074 if self.is_window_hovered() {
6076 let style = self
6077 .rendered_frame
6078 .cursor_style(self)
6079 .unwrap_or(CursorStyle::Arrow);
6080 cx.platform.set_cursor_style(style);
6081 }
6082 }
6083
6084 pub fn dispatch_keystroke(&mut self, keystroke: Keystroke, cx: &mut App) -> bool {
6087 let keystroke = keystroke.with_simulated_ime();
6088 let result = self.dispatch_event(
6089 PlatformInput::KeyDown(KeyDownEvent {
6090 keystroke: keystroke.clone(),
6091 is_held: false,
6092 prefer_character_input: false,
6093 }),
6094 cx,
6095 );
6096 if !result.propagate {
6097 return true;
6098 }
6099
6100 if let Some(input) = keystroke.key_char
6101 && let Some(mut input_handler) = self.platform_window.take_input_handler()
6102 {
6103 input_handler.dispatch_input(&input, self, cx);
6104 self.platform_window.set_input_handler(input_handler);
6105 return true;
6106 }
6107
6108 false
6109 }
6110
6111 pub fn keystroke_text_for(&self, action: &dyn Action, cx: &App) -> String {
6114 self.highest_precedence_binding_for_action(action)
6115 .map(|binding| {
6116 binding
6117 .keystrokes()
6118 .iter()
6119 .map(|keystroke| keystroke.display(cx.platform_info().os).to_string())
6120 .collect::<Vec<_>>()
6121 .join(" ")
6122 })
6123 .unwrap_or_else(|| action.name().to_string())
6124 }
6125
6126 #[profiling::function]
6128 pub fn dispatch_event(&mut self, event: PlatformInput, cx: &mut App) -> DispatchEventResult {
6129 #[cfg(feature = "profiler")]
6130 self.window_profiler.begin_input(event.kind_name());
6131 let update_count_before = self.invalidator.update_count();
6132 let old_modality = self.last_input_modality;
6136 self.last_input_modality = match &event {
6137 PlatformInput::KeyDown(_) => InputModality::Keyboard,
6138 PlatformInput::MouseMove(_) | PlatformInput::MouseDown(_) => InputModality::Mouse,
6139 PlatformInput::Touch(_) => InputModality::Touch,
6140 _ => self.last_input_modality,
6141 };
6142 if self.last_input_modality != old_modality {
6143 self.refresh();
6144 }
6145
6146 cx.propagate_event = true;
6148 self.default_prevented = false;
6150
6151 let event = match event {
6152 PlatformInput::MouseMove(mouse_move) => {
6155 self.mouse_position = mouse_move.position;
6156 self.modifiers = mouse_move.modifiers;
6157 PlatformInput::MouseMove(mouse_move)
6158 }
6159 PlatformInput::MouseDown(mouse_down) => {
6160 self.mouse_position = mouse_down.position;
6161 self.modifiers = mouse_down.modifiers;
6162 PlatformInput::MouseDown(mouse_down)
6163 }
6164 PlatformInput::MouseUp(mouse_up) => {
6165 self.mouse_position = mouse_up.position;
6166 self.modifiers = mouse_up.modifiers;
6167 PlatformInput::MouseUp(mouse_up)
6168 }
6169 PlatformInput::MousePressure(mouse_pressure) => {
6170 PlatformInput::MousePressure(mouse_pressure)
6171 }
6172 PlatformInput::MouseExited(mouse_exited) => {
6173 self.modifiers = mouse_exited.modifiers;
6174 PlatformInput::MouseExited(mouse_exited)
6175 }
6176 PlatformInput::ModifiersChanged(modifiers_changed) => {
6177 self.modifiers = modifiers_changed.modifiers;
6178 self.capslock = modifiers_changed.capslock;
6179 PlatformInput::ModifiersChanged(modifiers_changed)
6180 }
6181 PlatformInput::Scroll(scroll_wheel) => {
6182 self.mouse_position = scroll_wheel.position;
6183 self.modifiers = scroll_wheel.modifiers;
6184 PlatformInput::Scroll(scroll_wheel)
6185 }
6186 PlatformInput::Pinch(pinch) => {
6187 self.mouse_position = pinch.position;
6188 self.modifiers = pinch.modifiers;
6189 PlatformInput::Pinch(pinch)
6190 }
6191 PlatformInput::FileDrop(file_drop) => match file_drop {
6194 FileDropEvent::Entered { position, paths } => {
6195 self.mouse_position = position;
6196 let source_window = self.handle.window_id();
6197 if !cx.restore_platform_drag(source_window) && cx.active_drag.is_none() {
6198 cx.active_drag = Some(AnyDrag {
6199 value: Arc::new(paths.clone()),
6200 view: cx.new(|_| paths).into(),
6201 cursor_offset: position,
6202 cursor_style: None,
6203 external_payload_source: None,
6204 });
6205 }
6206 PlatformInput::MouseMove(MouseMoveEvent {
6207 position,
6208 pressed_button: Some(MouseButton::Left),
6209 modifiers: Modifiers::default(),
6210 })
6211 }
6212 FileDropEvent::Pending { position } => {
6213 self.mouse_position = position;
6214 PlatformInput::MouseMove(MouseMoveEvent {
6215 position,
6216 pressed_button: Some(MouseButton::Left),
6217 modifiers: Modifiers::default(),
6218 })
6219 }
6220 FileDropEvent::Submit { position } => {
6221 cx.activate(true);
6222 self.mouse_position = position;
6223 PlatformInput::MouseUp(MouseUpEvent {
6224 button: MouseButton::Left,
6225 position,
6226 modifiers: Modifiers::default(),
6227 click_count: 1,
6228 })
6229 }
6230 FileDropEvent::Exited => {
6231 if !cx.hand_restored_drag_to_platform(self.handle.window_id()) {
6232 cx.active_drag.take();
6233 }
6234 self.refresh();
6235 PlatformInput::FileDrop(FileDropEvent::Exited)
6236 }
6237 FileDropEvent::Ended => {
6238 cx.end_platform_drag(self.handle.window_id());
6239 self.refresh();
6240 PlatformInput::FileDrop(FileDropEvent::Ended)
6241 }
6242 },
6243 PlatformInput::Touch(touch) => PlatformInput::Touch(touch),
6244 PlatformInput::LongPress(long_press) => {
6245 self.mouse_position = if long_press.phase == crate::TouchPhase::Started {
6246 long_press.start_position
6247 } else {
6248 long_press.position
6249 };
6250 if long_press.phase == crate::TouchPhase::Started {
6251 self.long_press_capture = None;
6252 }
6253 PlatformInput::LongPress(long_press)
6254 }
6255 PlatformInput::TouchDrag(touch_drag) => {
6256 self.mouse_position = touch_drag.start_position;
6257 PlatformInput::TouchDrag(touch_drag)
6258 }
6259 PlatformInput::BackGesture(back) => {
6260 self.mouse_position = back.start_position;
6261 PlatformInput::BackGesture(back)
6262 }
6263 PlatformInput::KeyDown(_) | PlatformInput::KeyUp(_) => event,
6264 };
6265
6266 if let Some(any_mouse_event) = event.mouse_event() {
6267 self.dispatch_mouse_event(any_mouse_event, cx);
6268 } else if let Some(any_key_event) = event.keyboard_event() {
6269 self.dispatch_key_event(any_key_event, cx);
6270 } else if let Some(touch_event) = event.touch_event() {
6271 self.dispatch_touch_event(touch_event, cx);
6272 }
6273 if let PlatformInput::LongPress(long_press) = &event {
6274 match long_press.phase {
6275 crate::TouchPhase::Started if !self.default_prevented => {
6276 self.long_press_capture = None;
6277 }
6278 crate::TouchPhase::Ended | crate::TouchPhase::Cancelled => {
6279 self.long_press_capture = None;
6280 }
6281 crate::TouchPhase::Started | crate::TouchPhase::Moved => {}
6282 }
6283 }
6284
6285 self.promote_external_drag_to_platform(&event, cx);
6288
6289 let caused_invalidation = self.invalidator.update_count() > update_count_before;
6290 if caused_invalidation {
6291 self.input_rate_tracker.borrow_mut().record_input();
6292 }
6293 #[cfg(feature = "profiler")]
6294 self.window_profiler.end_input(caused_invalidation);
6295
6296 DispatchEventResult {
6297 propagate: cx.propagate_event,
6298 default_prevented: self.default_prevented,
6299 }
6300 }
6301
6302 fn promote_external_drag_to_platform(&mut self, event: &PlatformInput, cx: &mut App) {
6303 let PlatformInput::MouseMove(mouse_move) = event else {
6304 return;
6305 };
6306 if mouse_move.pressed_button != Some(MouseButton::Left) {
6307 return;
6308 }
6309 if Bounds::new(Point::default(), self.viewport_size).contains(&mouse_move.position) {
6310 return;
6311 }
6312 if !self.platform_window.can_start_external_drag() {
6313 return;
6314 }
6315 let Some(payload_source) = cx
6316 .active_drag
6317 .as_mut()
6318 .and_then(|drag| drag.external_payload_source.take())
6319 else {
6320 return;
6321 };
6322 let Some(payload) = payload_source(self, cx) else {
6323 return;
6324 };
6325 if self.platform_window.start_external_drag(&payload)
6326 && cx.hand_active_drag_to_platform(self.handle.window_id())
6327 {
6328 self.refresh();
6329 }
6330 }
6331
6332 pub fn touch_prediction_enabled(&self) -> bool {
6336 self.touch_prediction_enabled
6337 }
6338
6339 pub fn set_touch_prediction_enabled(&mut self, enabled: bool) {
6343 self.touch_prediction_enabled = enabled;
6344 }
6345
6346 fn dispatch_touch_event(&mut self, event: &TouchEvent, cx: &mut App) {
6350 let mut event = event.clone();
6351 if !self.touch_prediction_enabled {
6352 event.predicted_position = None;
6353 }
6354 let recognized_gestures = self.touch_gestures.handle_event(&event);
6355 if event.phase == crate::TouchPhase::Started
6356 && let Some(touch_drag) = self.touch_gestures.offer_touch_drag(event.id)
6357 {
6358 self.dispatch_recognized_touch_gesture(touch_drag, cx);
6359 }
6360 if event.phase == crate::TouchPhase::Started
6361 && self.touch_gestures.pending_long_press().is_some()
6362 {
6363 self.long_press_capture = None;
6364 }
6365 let mut tapped = false;
6366 for gesture in recognized_gestures {
6367 tapped |= matches!(gesture, RecognizedTouchGesture::Tap { .. });
6368 self.dispatch_recognized_touch_gesture(gesture, cx);
6369 }
6370 if event.phase == crate::TouchPhase::Started {
6371 self.schedule_long_press_timer(cx);
6372 } else if self.touch_gestures.pending_long_press().is_none() {
6373 self.long_press_timer.take();
6374 }
6375 if tapped && self.invalidator.is_dirty() {
6381 self.draw(cx).clear(cx);
6382 }
6383 if self.touch_gestures.has_momentum() {
6384 self.schedule_touch_momentum_tick();
6385 }
6386 }
6387
6388 fn dispatch_recognized_touch_gesture(&mut self, gesture: RecognizedTouchGesture, cx: &mut App) {
6389 match gesture {
6390 RecognizedTouchGesture::Scroll(scroll_wheel) => {
6391 self.mouse_position = scroll_wheel.position;
6392 cx.propagate_event = true;
6393 self.dispatch_mouse_event(&scroll_wheel, cx);
6394 }
6395 RecognizedTouchGesture::Tap { down, up } => {
6396 self.mouse_position = up.position;
6397 cx.propagate_event = true;
6398 self.dispatch_mouse_event(&down, cx);
6399 cx.propagate_event = true;
6400 self.dispatch_mouse_event(&up, cx);
6401 }
6402 RecognizedTouchGesture::TouchDrag(touch_drag) => {
6403 self.mouse_position = touch_drag.start_position;
6404 cx.propagate_event = true;
6405 self.default_prevented = false;
6406 let started = touch_drag.phase == crate::TouchPhase::Started;
6407 self.dispatch_mouse_event(&touch_drag, cx);
6408 if started {
6409 self.touch_gestures
6410 .resolve_touch_drag(self.default_prevented);
6411 }
6412 }
6413 RecognizedTouchGesture::LongPress(long_press) => {
6414 self.mouse_position = if long_press.phase == crate::TouchPhase::Started {
6415 long_press.start_position
6416 } else {
6417 long_press.position
6418 };
6419 cx.propagate_event = true;
6420 self.default_prevented = false;
6421 let started = long_press.phase == crate::TouchPhase::Started;
6422 let ended = matches!(
6423 long_press.phase,
6424 crate::TouchPhase::Ended | crate::TouchPhase::Cancelled
6425 );
6426 self.dispatch_mouse_event(&long_press, cx);
6427 if started {
6428 let claimed = self.default_prevented;
6429 self.touch_gestures.resolve_long_press(claimed);
6430 if !claimed {
6431 self.long_press_capture = None;
6432 }
6433 }
6434 if ended {
6435 self.long_press_capture = None;
6436 }
6437 }
6438 }
6439 }
6440
6441 fn schedule_long_press_timer(&mut self, cx: &mut App) {
6442 self.long_press_timer.take();
6443 let Some((touch_id, duration)) = self.touch_gestures.pending_long_press() else {
6444 return;
6445 };
6446 self.long_press_timer = Some(self.spawn(cx, async move |cx| {
6447 cx.background_executor.timer(duration).await;
6448 cx.update(move |window, cx| {
6449 window.long_press_timer.take();
6450 if let Some(gesture) = window.touch_gestures.offer_long_press(touch_id) {
6451 window.dispatch_recognized_touch_gesture(gesture, cx);
6452 }
6453 })
6454 .log_err();
6455 }));
6456 }
6457
6458 fn schedule_touch_momentum_tick(&mut self) {
6459 self.on_next_frame(|window, cx| {
6460 if let Some(gesture) = window.touch_gestures.tick_momentum() {
6461 window.dispatch_recognized_touch_gesture(gesture, cx);
6462 }
6463 if window.touch_gestures.has_momentum() {
6464 window.schedule_touch_momentum_tick();
6465 }
6466 });
6467 }
6468
6469 fn dispatch_mouse_event(&mut self, event: &dyn Any, cx: &mut App) {
6470 let hit_test = self.rendered_frame.hit_test(self.mouse_position());
6471 if hit_test != self.mouse_hit_test {
6472 self.mouse_hit_test = hit_test;
6473 self.reset_cursor_style(cx);
6474 }
6475
6476 #[cfg(debug_assertions)]
6477 if self.is_inspector_picking(cx) {
6478 self.handle_inspector_mouse_event(event, cx);
6479 return;
6481 }
6482
6483 let mut mouse_listeners = mem::take(&mut self.rendered_frame.mouse_listeners);
6484
6485 for listener in &mut mouse_listeners {
6488 let listener = listener.as_mut().unwrap();
6489 listener(event, DispatchPhase::Capture, self, cx);
6490 if !cx.propagate_event {
6491 break;
6492 }
6493 }
6494
6495 if cx.propagate_event {
6497 for listener in mouse_listeners.iter_mut().rev() {
6498 let listener = listener.as_mut().unwrap();
6499 listener(event, DispatchPhase::Bubble, self, cx);
6500 if !cx.propagate_event {
6501 break;
6502 }
6503 }
6504 }
6505
6506 self.rendered_frame.mouse_listeners = mouse_listeners;
6507
6508 if cx.has_active_drag() {
6509 if event.is::<MouseMoveEvent>() {
6510 self.refresh();
6513 } else if event.is::<MouseUpEvent>() {
6514 cx.active_drag = None;
6517 self.refresh();
6518 }
6519 }
6520
6521 if event.is::<MouseUpEvent>() && self.captured_hitbox.is_some() {
6523 self.captured_hitbox = None;
6524 }
6525 }
6526
6527 fn input_preference(&mut self, event: &dyn Any, cx: &mut App) -> InputPreference {
6530 let prefer_character_input = event
6531 .downcast_ref::<KeyDownEvent>()
6532 .is_some_and(|key_down_event| key_down_event.prefer_character_input);
6533 if !prefer_character_input {
6534 return InputPreference::KeyBindings;
6535 }
6536 let Some(mut input_handler) = self.platform_window.take_input_handler() else {
6537 return InputPreference::KeyBindings;
6538 };
6539 let accepts_text_input = input_handler.accepts_text_input(self, cx);
6540 self.platform_window.set_input_handler(input_handler);
6541 if accepts_text_input {
6542 InputPreference::CharacterInput
6543 } else {
6544 InputPreference::KeyBindings
6545 }
6546 }
6547
6548 fn dispatch_key_event(&mut self, event: &dyn Any, cx: &mut App) {
6549 if self.invalidator.is_dirty() {
6550 self.draw(cx).clear(cx);
6551 }
6552
6553 let node_id = self.focus_node_id_in_rendered_frame(self.focus);
6554 let dispatch_path = self.rendered_frame.dispatch_tree.dispatch_path(node_id);
6555
6556 let mut keystroke: Option<Keystroke> = None;
6557
6558 if let Some(event) = event.downcast_ref::<ModifiersChangedEvent>() {
6559 if event.modifiers.number_of_modifiers() == 0
6560 && self.pending_modifier.modifiers.number_of_modifiers() == 1
6561 && !self.pending_modifier.saw_other_input
6562 {
6563 let key = match self.pending_modifier.modifiers {
6564 modifiers if modifiers.shift => Some("shift"),
6565 modifiers if modifiers.control => Some("control"),
6566 modifiers if modifiers.alt => Some("alt"),
6567 modifiers if modifiers.platform => Some("platform"),
6568 modifiers if modifiers.function => Some("function"),
6569 _ => None,
6570 };
6571 if let Some(key) = key {
6572 keystroke = Some(Keystroke {
6573 key: key.to_string(),
6574 key_char: None,
6575 modifiers: Modifiers::default(),
6576 });
6577 }
6578 }
6579
6580 if self.pending_modifier.modifiers.number_of_modifiers() == 0
6581 && event.modifiers.number_of_modifiers() == 1
6582 {
6583 self.pending_modifier.saw_other_input = false
6584 } else if event.modifiers.number_of_modifiers() > 1 {
6585 self.pending_modifier.saw_other_input = true
6586 }
6587 self.pending_modifier.modifiers = event.modifiers
6588 } else if let Some(key_down_event) = event.downcast_ref::<KeyDownEvent>() {
6589 self.pending_modifier.saw_other_input = true;
6590 keystroke = Some(key_down_event.keystroke.clone());
6591 if key_down_event.keystroke.key_char.is_some()
6592 && matches!(
6593 cx.cursor_hide_mode,
6594 CursorHideMode::OnTyping | CursorHideMode::OnTypingAndAction
6595 )
6596 {
6597 cx.platform.hide_cursor_until_mouse_moves();
6598 }
6599 }
6600
6601 let Some(keystroke) = keystroke else {
6602 self.finish_dispatch_key_event(
6603 event,
6604 None,
6605 InputPreference::KeyBindings,
6606 dispatch_path,
6607 self.context_stack(),
6608 cx,
6609 );
6610 return;
6611 };
6612
6613 let input_preference = self.input_preference(event, cx);
6614
6615 cx.propagate_event = true;
6616 self.dispatch_keystroke_interceptors(
6617 &keystroke,
6618 input_preference,
6619 self.context_stack(),
6620 cx,
6621 );
6622 if !cx.propagate_event {
6623 self.finish_dispatch_key_event(
6624 event,
6625 Some(&keystroke),
6626 input_preference,
6627 dispatch_path,
6628 self.context_stack(),
6629 cx,
6630 );
6631 return;
6632 }
6633
6634 let mut currently_pending = self.pending_input.take().unwrap_or_default();
6635 if currently_pending.focus.is_some() && currently_pending.focus != self.focus {
6636 currently_pending = PendingInput::default();
6637 }
6638
6639 let match_result = self.rendered_frame.dispatch_tree.dispatch_key(
6640 currently_pending.keystrokes,
6641 keystroke.clone(),
6642 &dispatch_path,
6643 );
6644
6645 if !match_result.to_replay.is_empty() {
6646 self.replay_pending_input(match_result.to_replay, cx);
6647 cx.propagate_event = true;
6648 }
6649
6650 if !match_result.pending.is_empty() {
6651 let previous_timeout = currently_pending.timeout.take();
6652 currently_pending.keystrokes = match_result.pending;
6653 currently_pending.focus = self.focus;
6654
6655 let text_input_requires_timeout = event
6656 .downcast_ref::<KeyDownEvent>()
6657 .filter(|key_down| key_down.keystroke.key_char.is_some())
6658 .and_then(|_| self.platform_window.take_input_handler())
6659 .map_or(false, |mut input_handler| {
6660 let accepts = input_handler.accepts_text_input(self, cx);
6661 self.platform_window.set_input_handler(input_handler);
6662 accepts
6663 });
6664
6665 let needs_timeout = previous_timeout.is_some()
6666 || match_result.pending_has_binding
6667 || text_input_requires_timeout;
6668 currently_pending.timeout = if needs_timeout {
6669 match previous_timeout {
6670 Some(mut timeout) if timeout.is_paused() => {
6671 timeout.reset_duration(PENDING_INPUT_TIMEOUT);
6672 Some(timeout)
6673 }
6674 previous_timeout => {
6675 drop(previous_timeout);
6676 Some(self.new_pending_input_timeout(PENDING_INPUT_TIMEOUT, cx))
6677 }
6678 }
6679 } else {
6680 None
6681 };
6682 self.pending_input = Some(currently_pending);
6683 self.pending_input_changed(cx);
6684 cx.propagate_event = false;
6685 return;
6686 }
6687
6688 let input_preference = self.input_preference(event, cx);
6690 if input_preference == InputPreference::KeyBindings {
6691 for binding in match_result.bindings {
6692 self.dispatch_action_on_node(node_id, binding.action.as_ref(), cx);
6693 if !cx.propagate_event {
6694 self.dispatch_keystroke_observers(
6695 &keystroke,
6696 input_preference,
6697 Some(binding.action.as_ref()),
6698 match_result.context_stack,
6699 cx,
6700 );
6701 self.pending_input_changed(cx);
6702 return;
6703 }
6704 }
6705 }
6706
6707 self.finish_dispatch_key_event(
6708 event,
6709 Some(&keystroke),
6710 input_preference,
6711 dispatch_path,
6712 match_result.context_stack,
6713 cx,
6714 );
6715 self.pending_input_changed(cx);
6716 }
6717
6718 fn new_pending_input_timeout(&self, duration: Duration, cx: &App) -> PendingInputTimeout {
6719 let (started_at, task) = self.start_pending_input_timeout(duration, cx);
6720 PendingInputTimeout {
6721 duration,
6722 remaining: duration,
6723 state: PendingInputTimeoutState::Running { started_at, task },
6724 }
6725 }
6726
6727 fn start_pending_input_timeout(&self, remaining: Duration, cx: &App) -> (Instant, Task<()>) {
6728 let started_at = cx.background_executor().now();
6729 let task = self.spawn(cx, async move |cx| {
6730 cx.background_executor.timer(remaining).await;
6731 cx.update(move |window, cx| {
6732 let Some(currently_pending) = window
6733 .pending_input
6734 .take()
6735 .filter(|pending| pending.focus == window.focus)
6736 else {
6737 return;
6738 };
6739
6740 let node_id = window.focus_node_id_in_rendered_frame(window.focus);
6741 let dispatch_path = window.rendered_frame.dispatch_tree.dispatch_path(node_id);
6742
6743 let to_replay = window
6744 .rendered_frame
6745 .dispatch_tree
6746 .flush_dispatch(currently_pending.keystrokes, &dispatch_path);
6747
6748 window.pending_input_changed(cx);
6749 window.replay_pending_input(to_replay, cx)
6750 })
6751 .log_err();
6752 });
6753 (started_at, task)
6754 }
6755
6756 fn finish_dispatch_key_event(
6757 &mut self,
6758 event: &dyn Any,
6759 recognized_keystroke: Option<&Keystroke>,
6760 input_preference: InputPreference,
6761 dispatch_path: SmallVec<[DispatchNodeId; 32]>,
6762 context_stack: Vec<KeyContext>,
6763 cx: &mut App,
6764 ) {
6765 self.dispatch_key_down_up_event(event, &dispatch_path, cx);
6766 if !cx.propagate_event {
6767 return;
6768 }
6769
6770 self.dispatch_modifiers_changed_event(event, &dispatch_path, cx);
6771 if !cx.propagate_event {
6772 return;
6773 }
6774
6775 if let Some(keystroke) = recognized_keystroke {
6776 self.dispatch_keystroke_observers(keystroke, input_preference, None, context_stack, cx);
6777 }
6778 }
6779
6780 pub(crate) fn pending_input_changed(&mut self, cx: &mut App) {
6781 self.pending_input_observers
6782 .clone()
6783 .retain(|callback| callback(self, cx));
6784 }
6785
6786 fn defer_pending_input_changed(&self, cx: &mut App) {
6787 let window_handle = self.handle;
6790 cx.defer(move |cx| {
6791 window_handle
6792 .update(cx, |_, window, cx| {
6793 window.pending_input_changed(cx);
6794 })
6795 .ok();
6796 });
6797 }
6798
6799 fn dispatch_key_down_up_event(
6800 &mut self,
6801 event: &dyn Any,
6802 dispatch_path: &SmallVec<[DispatchNodeId; 32]>,
6803 cx: &mut App,
6804 ) {
6805 for node_id in dispatch_path {
6807 let node = self.rendered_frame.dispatch_tree.node(*node_id);
6808
6809 for key_listener in node.key_listeners.clone() {
6810 key_listener(event, DispatchPhase::Capture, self, cx);
6811 if !cx.propagate_event {
6812 return;
6813 }
6814 }
6815 }
6816
6817 for node_id in dispatch_path.iter().rev() {
6819 let node = self.rendered_frame.dispatch_tree.node(*node_id);
6821 for key_listener in node.key_listeners.clone() {
6822 key_listener(event, DispatchPhase::Bubble, self, cx);
6823 if !cx.propagate_event {
6824 return;
6825 }
6826 }
6827 }
6828 }
6829
6830 fn dispatch_modifiers_changed_event(
6831 &mut self,
6832 event: &dyn Any,
6833 dispatch_path: &SmallVec<[DispatchNodeId; 32]>,
6834 cx: &mut App,
6835 ) {
6836 let Some(event) = event.downcast_ref::<ModifiersChangedEvent>() else {
6837 return;
6838 };
6839 for node_id in dispatch_path.iter().rev() {
6840 let node = self.rendered_frame.dispatch_tree.node(*node_id);
6841 for listener in node.modifiers_changed_listeners.clone() {
6842 listener(event, self, cx);
6843 if !cx.propagate_event {
6844 return;
6845 }
6846 }
6847 }
6848 }
6849
6850 pub fn has_pending_keystrokes(&self) -> bool {
6852 self.pending_input().is_some()
6853 }
6854
6855 #[cfg(test)]
6856 pub(crate) fn pending_input_is_none(&self) -> bool {
6857 self.pending_input.is_none()
6858 }
6859
6860 pub(crate) fn clear_pending_keystrokes(&mut self, cx: &mut App) {
6861 if self.pending_input.take().is_some() {
6862 self.defer_pending_input_changed(cx);
6863 }
6864 }
6865
6866 pub fn pending_input(&self) -> Option<PendingInputStatus<'_>> {
6869 self.pending_input
6870 .as_ref()
6871 .filter(|pending_input| pending_input.focus == self.focus)
6872 .map(|pending_input| PendingInputStatus {
6873 keystrokes: pending_input.keystrokes.as_slice(),
6874 timeout: pending_input
6875 .timeout
6876 .as_ref()
6877 .map(PendingInputTimeout::status),
6878 })
6879 }
6880
6881 pub fn set_pending_input_timeout_paused<T: 'static>(
6886 &mut self,
6887 owner: &Entity<T>,
6888 paused: bool,
6889 cx: &mut App,
6890 ) -> bool {
6891 let owner_id = owner.entity_id();
6892 if !paused {
6893 return self.resume_pending_input_timeout(owner_id, cx);
6894 }
6895
6896 let timeout = self
6897 .pending_input
6898 .as_ref()
6899 .filter(|pending_input| pending_input.focus == self.focus)
6900 .and_then(|pending_input| pending_input.timeout.as_ref());
6901 let Some(timeout) = timeout else {
6902 return false;
6903 };
6904 if timeout.is_paused() {
6905 return false;
6906 }
6907
6908 let release_subscription = self.observe_release(owner, cx, move |_, window, cx| {
6909 window.resume_pending_input_timeout(owner_id, cx);
6910 });
6911 let now = cx.background_executor().now();
6912 let changed = self
6913 .pending_input
6914 .as_mut()
6915 .filter(|pending_input| pending_input.focus == self.focus)
6916 .and_then(|pending_input| pending_input.timeout.as_mut())
6917 .is_some_and(|timeout| {
6918 timeout.pause(
6919 PendingInputTimeoutPause {
6920 owner_id,
6921 _release_subscription: release_subscription,
6922 },
6923 now,
6924 )
6925 });
6926
6927 if changed {
6928 self.defer_pending_input_changed(cx);
6929 }
6930 changed
6931 }
6932
6933 fn resume_pending_input_timeout(&mut self, owner_id: EntityId, cx: &mut App) -> bool {
6934 let Some(remaining) = self
6935 .pending_input
6936 .as_ref()
6937 .and_then(|pending_input| pending_input.timeout.as_ref())
6938 .filter(|timeout| timeout.pause_owner_id() == Some(owner_id))
6939 .map(|timeout| timeout.remaining)
6940 else {
6941 return false;
6942 };
6943
6944 let (started_at, task) = self.start_pending_input_timeout(remaining, cx);
6945 let changed = self
6946 .pending_input
6947 .as_mut()
6948 .and_then(|pending_input| pending_input.timeout.as_mut())
6949 .is_some_and(|timeout| timeout.resume(owner_id, started_at, task));
6950
6951 if changed {
6952 self.defer_pending_input_changed(cx);
6953 }
6954 changed
6955 }
6956
6957 pub fn pending_input_keystrokes(&self) -> Option<&[Keystroke]> {
6959 self.pending_input()
6960 .map(|pending_input| pending_input.keystrokes())
6961 }
6962
6963 fn replay_pending_input(&mut self, replays: SmallVec<[Replay; 1]>, cx: &mut App) {
6964 let node_id = self.focus_node_id_in_rendered_frame(self.focus);
6965 let dispatch_path = self.rendered_frame.dispatch_tree.dispatch_path(node_id);
6966
6967 'replay: for replay in replays {
6968 let event = KeyDownEvent {
6969 keystroke: replay.keystroke.clone(),
6970 is_held: false,
6971 prefer_character_input: true,
6972 };
6973
6974 cx.propagate_event = true;
6975 for binding in replay.bindings {
6976 self.dispatch_action_on_node(node_id, binding.action.as_ref(), cx);
6977 if !cx.propagate_event {
6978 self.dispatch_keystroke_observers(
6979 &replay.keystroke,
6980 InputPreference::KeyBindings,
6981 Some(binding.action.as_ref()),
6982 Vec::default(),
6983 cx,
6984 );
6985 continue 'replay;
6986 }
6987 }
6988
6989 self.dispatch_key_down_up_event(&event, &dispatch_path, cx);
6990 if !cx.propagate_event {
6991 continue 'replay;
6992 }
6993 if let Some(input) = replay.keystroke.key_char.as_ref().cloned()
6994 && let Some(mut input_handler) = self.platform_window.take_input_handler()
6995 {
6996 input_handler.dispatch_input(&input, self, cx);
6997 self.platform_window.set_input_handler(input_handler)
6998 }
6999 }
7000 }
7001
7002 fn focus_node_id_in_rendered_frame(&self, focus_id: Option<FocusId>) -> DispatchNodeId {
7003 focus_id
7004 .and_then(|focus_id| {
7005 self.rendered_frame
7006 .dispatch_tree
7007 .focusable_node_id(focus_id)
7008 })
7009 .unwrap_or_else(|| self.rendered_frame.dispatch_tree.root_node_id())
7010 }
7011
7012 fn dispatch_action_on_node(
7013 &mut self,
7014 node_id: DispatchNodeId,
7015 action: &dyn Action,
7016 cx: &mut App,
7017 ) {
7018 self.dispatch_action_on_node_inner(node_id, action, cx);
7019
7020 if !cx.propagate_event
7021 && cx.cursor_hide_mode == CursorHideMode::OnTypingAndAction
7022 && self.last_input_was_keyboard()
7023 {
7024 cx.platform.hide_cursor_until_mouse_moves();
7025 }
7026 }
7027
7028 fn dispatch_action_on_node_inner(
7029 &mut self,
7030 node_id: DispatchNodeId,
7031 action: &dyn Action,
7032 cx: &mut App,
7033 ) {
7034 let dispatch_path = self.rendered_frame.dispatch_tree.dispatch_path(node_id);
7035
7036 cx.propagate_event = true;
7038 if let Some(mut global_listeners) = cx
7039 .global_action_listeners
7040 .remove(&action.as_any().type_id())
7041 {
7042 for listener in &global_listeners {
7043 #[cfg(feature = "profiler")]
7044 self.window_profiler.begin_action_handler(action, cx);
7045 listener(action.as_any(), DispatchPhase::Capture, cx);
7046 #[cfg(feature = "profiler")]
7047 self.window_profiler.end_action_handler();
7048 if !cx.propagate_event {
7049 break;
7050 }
7051 }
7052
7053 global_listeners.extend(
7054 cx.global_action_listeners
7055 .remove(&action.as_any().type_id())
7056 .unwrap_or_default(),
7057 );
7058
7059 cx.global_action_listeners
7060 .insert(action.as_any().type_id(), global_listeners);
7061 }
7062
7063 if !cx.propagate_event {
7064 return;
7065 }
7066
7067 for node_id in &dispatch_path {
7069 let node = self.rendered_frame.dispatch_tree.node(*node_id);
7070 for DispatchActionListener {
7071 action_type,
7072 listener,
7073 } in node.action_listeners.clone()
7074 {
7075 let any_action = action.as_any();
7076 if action_type == any_action.type_id() {
7077 #[cfg(feature = "profiler")]
7078 self.window_profiler.begin_action_handler(action, cx);
7079 listener(any_action, DispatchPhase::Capture, self, cx);
7080 #[cfg(feature = "profiler")]
7081 self.window_profiler.end_action_handler();
7082
7083 if !cx.propagate_event {
7084 return;
7085 }
7086 }
7087 }
7088 }
7089
7090 for node_id in dispatch_path.iter().rev() {
7092 let node = self.rendered_frame.dispatch_tree.node(*node_id);
7093 for DispatchActionListener {
7094 action_type,
7095 listener,
7096 } in node.action_listeners.clone()
7097 {
7098 let any_action = action.as_any();
7099 if action_type == any_action.type_id() {
7100 cx.propagate_event = false; #[cfg(feature = "profiler")]
7102 self.window_profiler.begin_action_handler(action, cx);
7103 listener(any_action, DispatchPhase::Bubble, self, cx);
7104 #[cfg(feature = "profiler")]
7105 self.window_profiler.end_action_handler();
7106
7107 if !cx.propagate_event {
7108 return;
7109 }
7110 }
7111 }
7112 }
7113
7114 if let Some(mut global_listeners) = cx
7116 .global_action_listeners
7117 .remove(&action.as_any().type_id())
7118 {
7119 for listener in global_listeners.iter().rev() {
7120 cx.propagate_event = false; #[cfg(feature = "profiler")]
7123 self.window_profiler.begin_action_handler(action, cx);
7124 listener(action.as_any(), DispatchPhase::Bubble, cx);
7125 #[cfg(feature = "profiler")]
7126 self.window_profiler.end_action_handler();
7127 if !cx.propagate_event {
7128 break;
7129 }
7130 }
7131
7132 global_listeners.extend(
7133 cx.global_action_listeners
7134 .remove(&action.as_any().type_id())
7135 .unwrap_or_default(),
7136 );
7137
7138 cx.global_action_listeners
7139 .insert(action.as_any().type_id(), global_listeners);
7140 }
7141 }
7142
7143 pub fn observe_global<G: Global>(
7146 &mut self,
7147 cx: &mut App,
7148 f: impl Fn(&mut Window, &mut App) + 'static,
7149 ) -> Subscription {
7150 let window_handle = self.handle;
7151 let (subscription, activate) = cx.global_observers.insert_pending(
7152 TypeId::of::<G>(),
7153 Box::new(move |cx| {
7154 window_handle
7155 .update(cx, |_, window, cx| f(window, cx))
7156 .is_ok()
7157 }),
7158 );
7159 cx.defer(move |_| activate());
7160 subscription
7161 }
7162
7163 pub fn activate_window(&self) {
7165 self.platform_window.activate();
7166 }
7167
7168 pub fn request_attention(&self) {
7170 self.platform_window.request_attention();
7171 }
7172
7173 pub fn minimize_window(&self) {
7175 self.platform_window.minimize();
7176 }
7177
7178 pub fn toggle_fullscreen(&self) {
7180 self.platform_window.toggle_fullscreen();
7181 }
7182
7183 pub fn toggle_simple_fullscreen(&self) {
7188 self.platform_window.toggle_simple_fullscreen();
7189 }
7190
7191 pub fn invalidate_character_coordinates(&self) {
7193 self.on_next_frame(|window, cx| {
7194 if let Some(mut input_handler) = window.platform_window.take_input_handler() {
7195 if let Some(bounds) = input_handler.selected_bounds(window, cx) {
7196 window.platform_window.update_ime_position(bounds);
7197 }
7198 window.platform_window.set_input_handler(input_handler);
7199 }
7200 });
7201 }
7202
7203 pub fn prompt<T>(
7207 &mut self,
7208 level: PromptLevel,
7209 message: &str,
7210 detail: Option<&str>,
7211 answers: &[T],
7212 cx: &mut App,
7213 ) -> oneshot::Receiver<usize>
7214 where
7215 T: Clone + Into<PromptButton>,
7216 {
7217 let prompt_builder = cx.prompt_builder.take();
7218 let Some(prompt_builder) = prompt_builder else {
7219 unreachable!("Re-entrant window prompting is not supported by GPUI");
7220 };
7221
7222 let answers = answers
7223 .iter()
7224 .map(|answer| answer.clone().into())
7225 .collect::<Vec<_>>();
7226
7227 let receiver = match &prompt_builder {
7228 PromptBuilder::Default => self
7229 .platform_window
7230 .prompt(level, message, detail, &answers)
7231 .unwrap_or_else(|| {
7232 self.build_custom_prompt(&prompt_builder, level, message, detail, &answers, cx)
7233 }),
7234 PromptBuilder::Custom(_) => {
7235 self.build_custom_prompt(&prompt_builder, level, message, detail, &answers, cx)
7236 }
7237 };
7238
7239 cx.prompt_builder = Some(prompt_builder);
7240
7241 receiver
7242 }
7243
7244 fn build_custom_prompt(
7245 &mut self,
7246 prompt_builder: &PromptBuilder,
7247 level: PromptLevel,
7248 message: &str,
7249 detail: Option<&str>,
7250 answers: &[PromptButton],
7251 cx: &mut App,
7252 ) -> oneshot::Receiver<usize> {
7253 let (sender, receiver) = oneshot::channel();
7254 let handle = PromptHandle::new(sender);
7255 let handle = (prompt_builder)(level, message, detail, answers, handle, self, cx);
7256 self.prompt = Some(handle);
7257 receiver
7258 }
7259
7260 pub fn has_active_prompt(&self) -> bool {
7265 self.prompt.is_some()
7266 }
7267
7268 pub fn context_stack(&self) -> Vec<KeyContext> {
7270 let node_id = self.focus_node_id_in_rendered_frame(self.focus);
7271 let dispatch_tree = &self.rendered_frame.dispatch_tree;
7272 dispatch_tree
7273 .dispatch_path(node_id)
7274 .iter()
7275 .filter_map(move |&node_id| dispatch_tree.node(node_id).context.clone())
7276 .collect()
7277 }
7278
7279 pub fn available_actions(&self, cx: &App) -> Vec<Box<dyn Action>> {
7281 let node_id = self.focus_node_id_in_rendered_frame(self.focus);
7282 let mut actions = self.rendered_frame.dispatch_tree.available_actions(node_id);
7283 for action_type in cx.global_action_listeners.keys() {
7284 if let Err(ix) = actions.binary_search_by_key(action_type, |a| a.as_any().type_id()) {
7285 let action = cx.actions.build_action_type(action_type).ok();
7286 if let Some(action) = action {
7287 actions.insert(ix, action);
7288 }
7289 }
7290 }
7291 actions
7292 }
7293
7294 pub fn bindings_for_action(&self, action: &dyn Action) -> Vec<KeyBinding> {
7297 self.rendered_frame
7298 .dispatch_tree
7299 .bindings_for_action(action, &self.rendered_frame.dispatch_tree.context_stack)
7300 }
7301
7302 pub fn highest_precedence_binding_for_action(&self, action: &dyn Action) -> Option<KeyBinding> {
7305 self.rendered_frame
7306 .dispatch_tree
7307 .highest_precedence_binding_for_action(
7308 action,
7309 &self.rendered_frame.dispatch_tree.context_stack,
7310 )
7311 }
7312
7313 pub fn bindings_for_action_in_context(
7315 &self,
7316 action: &dyn Action,
7317 context: KeyContext,
7318 ) -> Vec<KeyBinding> {
7319 let dispatch_tree = &self.rendered_frame.dispatch_tree;
7320 dispatch_tree.bindings_for_action(action, &[context])
7321 }
7322
7323 pub fn highest_precedence_binding_for_action_in_context(
7326 &self,
7327 action: &dyn Action,
7328 context: KeyContext,
7329 ) -> Option<KeyBinding> {
7330 let dispatch_tree = &self.rendered_frame.dispatch_tree;
7331 dispatch_tree.highest_precedence_binding_for_action(action, &[context])
7332 }
7333
7334 pub fn bindings_for_action_in(
7338 &self,
7339 action: &dyn Action,
7340 focus_handle: &FocusHandle,
7341 ) -> Vec<KeyBinding> {
7342 let dispatch_tree = &self.rendered_frame.dispatch_tree;
7343 let Some(context_stack) = self.context_stack_for_focus_handle(focus_handle) else {
7344 return vec![];
7345 };
7346 dispatch_tree.bindings_for_action(action, &context_stack)
7347 }
7348
7349 pub fn highest_precedence_binding_for_action_in(
7353 &self,
7354 action: &dyn Action,
7355 focus_handle: &FocusHandle,
7356 ) -> Option<KeyBinding> {
7357 let dispatch_tree = &self.rendered_frame.dispatch_tree;
7358 let context_stack = self.context_stack_for_focus_handle(focus_handle)?;
7359 dispatch_tree.highest_precedence_binding_for_action(action, &context_stack)
7360 }
7361
7362 pub fn possible_bindings_for_input(&self, input: &[Keystroke]) -> Vec<KeyBinding> {
7364 self.rendered_frame
7365 .dispatch_tree
7366 .possible_next_bindings_for_input(input, &self.context_stack())
7367 }
7368
7369 fn context_stack_for_focus_handle(
7370 &self,
7371 focus_handle: &FocusHandle,
7372 ) -> Option<Vec<KeyContext>> {
7373 let dispatch_tree = &self.rendered_frame.dispatch_tree;
7374 let node_id = dispatch_tree.focusable_node_id(focus_handle.id)?;
7375 let context_stack: Vec<_> = dispatch_tree
7376 .dispatch_path(node_id)
7377 .into_iter()
7378 .filter_map(|node_id| dispatch_tree.node(node_id).context.clone())
7379 .collect();
7380 Some(context_stack)
7381 }
7382
7383 pub fn listener_for<T: 'static, E>(
7385 &self,
7386 view: &Entity<T>,
7387 f: impl Fn(&mut T, &E, &mut Window, &mut Context<T>) + 'static,
7388 ) -> impl Fn(&E, &mut Window, &mut App) + 'static {
7389 let view = view.downgrade();
7390 move |e: &E, window: &mut Window, cx: &mut App| {
7391 view.update(cx, |view, cx| f(view, e, window, cx)).ok();
7392 }
7393 }
7394
7395 pub fn handler_for<E: 'static, Callback: Fn(&mut E, &mut Window, &mut Context<E>) + 'static>(
7397 &self,
7398 entity: &Entity<E>,
7399 f: Callback,
7400 ) -> impl Fn(&mut Window, &mut App) + 'static {
7401 let entity = entity.downgrade();
7402 move |window: &mut Window, cx: &mut App| {
7403 entity.update(cx, |entity, cx| f(entity, window, cx)).ok();
7404 }
7405 }
7406
7407 pub fn on_window_should_close(
7410 &self,
7411 cx: &App,
7412 f: impl Fn(&mut Window, &mut App) -> bool + 'static,
7413 ) {
7414 let mut cx = self.to_async(cx);
7415 self.platform_window.on_should_close(Box::new(move || {
7416 cx.update(|window, cx| f(window, cx)).unwrap_or(true)
7417 }))
7418 }
7419
7420 pub fn on_action(
7429 &mut self,
7430 action_type: TypeId,
7431 listener: impl Fn(&dyn Any, DispatchPhase, &mut Window, &mut App) + 'static,
7432 ) {
7433 self.invalidator.debug_assert_paint();
7434
7435 self.next_frame
7436 .dispatch_tree
7437 .on_action(action_type, Rc::new(listener));
7438 }
7439
7440 pub fn on_action_when(
7449 &mut self,
7450 condition: bool,
7451 action_type: TypeId,
7452 listener: impl Fn(&dyn Any, DispatchPhase, &mut Window, &mut App) + 'static,
7453 ) {
7454 self.invalidator.debug_assert_paint();
7455
7456 if condition {
7457 self.next_frame
7458 .dispatch_tree
7459 .on_action(action_type, Rc::new(listener));
7460 }
7461 }
7462
7463 pub fn gpu_specs(&self) -> Option<GpuSpecs> {
7466 self.platform_window.gpu_specs()
7467 }
7468
7469 pub fn titlebar_double_click(&self) {
7472 self.platform_window
7473 .titlebar_double_click(self.is_resizable, self.is_minimizable);
7474 }
7475
7476 pub fn window_title(&self) -> String {
7479 self.platform_window.get_title()
7480 }
7481
7482 pub fn tabbed_windows(&self) -> Option<Vec<SystemWindowTab>> {
7485 self.platform_window.tabbed_windows()
7486 }
7487
7488 pub fn tab_bar_visible(&self) -> bool {
7491 self.platform_window.tab_bar_visible()
7492 }
7493
7494 pub fn merge_all_windows(&self) {
7497 self.platform_window.merge_all_windows()
7498 }
7499
7500 pub fn move_tab_to_new_window(&self) {
7503 self.platform_window.move_tab_to_new_window()
7504 }
7505
7506 pub fn toggle_window_tab_overview(&self) {
7509 self.platform_window.toggle_window_tab_overview()
7510 }
7511
7512 pub fn set_tabbing_identifier(&self, tabbing_identifier: Option<String>) {
7515 self.platform_window
7516 .set_tabbing_identifier(tabbing_identifier)
7517 }
7518
7519 pub fn play_system_bell(&self) {
7522 self.platform_window.play_system_bell()
7523 }
7524
7525 pub fn is_a11y_enabled(&self) -> bool {
7532 self.a11y.is_enabled()
7533 }
7534
7535 pub fn is_a11y_active(&self) -> bool {
7546 self.a11y.is_active()
7547 }
7548
7549 pub fn debug_a11y_tree_json(&self) -> Option<String> {
7551 self.a11y.debug_tree_json()
7552 }
7553
7554 pub fn on_a11y_action(
7560 &mut self,
7561 node_id: accesskit::NodeId,
7562 action: accesskit::Action,
7563 listener: impl FnMut(Option<&accesskit::ActionData>, &mut Window, &mut App) + 'static,
7564 ) {
7565 self.a11y
7566 .action_listeners
7567 .entry(node_id)
7568 .or_default()
7569 .push((action, Box::new(listener)));
7570 }
7571
7572 #[cfg(not(target_family = "wasm"))]
7573 pub(crate) fn handle_a11y_action(&mut self, request: accesskit::ActionRequest, cx: &mut App) {
7574 if let Some(mut listeners) = self.a11y.action_listeners.remove(&request.target_node) {
7577 let extra_data = request.data.as_ref();
7578 let mut matched = false;
7579 for (action, listener) in &mut listeners {
7580 if *action == request.action {
7581 listener(extra_data, self, cx);
7582 matched = true;
7583 }
7584 }
7585 self.a11y
7586 .action_listeners
7587 .insert(request.target_node, listeners);
7588 if matched {
7589 return;
7590 }
7591 }
7592
7593 match request.action {
7595 accesskit::Action::Click => {
7596 if let Some(bounds) = self.a11y.node_bounds.get(&request.target_node).copied() {
7597 let center = bounds.center();
7598 let mouse_down = PlatformInput::MouseDown(crate::MouseDownEvent {
7599 button: MouseButton::Left,
7600 position: center,
7601 modifiers: Modifiers::default(),
7602 click_count: 1,
7603 first_mouse: false,
7604 });
7605 let mouse_up = PlatformInput::MouseUp(MouseUpEvent {
7606 button: MouseButton::Left,
7607 position: center,
7608 modifiers: Modifiers::default(),
7609 click_count: 1,
7610 });
7611 self.dispatch_event(mouse_down, cx);
7612 self.dispatch_event(mouse_up, cx);
7613 }
7614 }
7615 accesskit::Action::Focus => {
7616 if let Some(focus_id) = self.a11y.focus_ids.get(&request.target_node).copied()
7617 && let Some(handle) = FocusHandle::for_id(focus_id, &cx.focus_handles)
7618 {
7619 self.focus(&handle, cx);
7620 }
7621 }
7622 accesskit::Action::Blur => {
7623 self.blur(cx);
7624 }
7625 _ => {
7626 log::debug!(
7627 "Unhandled a11y action: {:?} on {:?}",
7628 request.action,
7629 request.target_node
7630 );
7631 }
7632 }
7633 }
7634
7635 #[cfg(debug_assertions)]
7637 pub fn toggle_inspector(&mut self, cx: &mut App) {
7638 self.inspector = match self.inspector {
7639 None => Some(cx.new(|_| Inspector::new())),
7640 Some(_) => {
7641 self.rendered_frame.next_inspector_instance_ids = FxHashMap::default();
7642 self.rendered_frame.inspector_hitboxes = FxHashMap::default();
7643 self.next_frame.next_inspector_instance_ids = FxHashMap::default();
7644 self.next_frame.inspector_hitboxes = FxHashMap::default();
7645 None
7646 }
7647 };
7648 self.refresh();
7649 }
7650
7651 pub fn is_inspector_picking(&self, _cx: &App) -> bool {
7653 #[cfg(debug_assertions)]
7654 {
7655 if let Some(inspector) = &self.inspector {
7656 return inspector.read(_cx).is_picking();
7657 }
7658 }
7659 false
7660 }
7661
7662 #[cfg(debug_assertions)]
7664 pub fn with_inspector_state<T: 'static, R>(
7665 &mut self,
7666 inspector_id: Option<&crate::InspectorElementId>,
7667 cx: &mut App,
7668 f: impl FnOnce(&mut Option<T>, &mut Self) -> R,
7669 ) -> Option<R> {
7670 let inspector_id = inspector_id?;
7671 let inspector = self.inspector.as_ref()?;
7672 if inspector.read(cx).active_element_id() != Some(inspector_id) {
7673 return None;
7674 }
7675 let inspector = inspector.clone();
7676 Some(inspector.update(cx, |inspector, _cx| {
7677 inspector.with_active_element_state(self, f)
7678 }))
7679 }
7680
7681 #[cfg(debug_assertions)]
7682 pub(crate) fn inspector_enabled(&self) -> bool {
7683 self.inspector.is_some()
7684 }
7685
7686 #[cfg(debug_assertions)]
7687 pub(crate) fn build_inspector_element_id(
7688 &mut self,
7689 path: crate::InspectorElementPath,
7690 ) -> crate::InspectorElementId {
7691 self.invalidator.debug_assert_paint_or_prepaint();
7692 let path = Rc::new(path);
7693 let next_instance_id = self
7694 .next_frame
7695 .next_inspector_instance_ids
7696 .entry(path.clone())
7697 .or_insert(0);
7698 let instance_id = *next_instance_id;
7699 *next_instance_id += 1;
7700 crate::InspectorElementId { path, instance_id }
7701 }
7702
7703 #[cfg(debug_assertions)]
7704 fn prepaint_inspector(&mut self, inspector_width: Pixels, cx: &mut App) -> Option<AnyElement> {
7705 if let Some(inspector) = self.inspector.take() {
7706 let mut inspector_element = AnyView::from(inspector.clone()).into_any_element();
7707 inspector_element.prepaint_as_root(
7708 point(self.viewport_size.width - inspector_width, px(0.0)),
7709 size(inspector_width, self.viewport_size.height).into(),
7710 self,
7711 cx,
7712 );
7713 self.inspector = Some(inspector);
7714 Some(inspector_element)
7715 } else {
7716 None
7717 }
7718 }
7719
7720 #[cfg(debug_assertions)]
7721 fn paint_inspector(&mut self, inspector_element: Option<AnyElement>, cx: &mut App) {
7722 if let Some(mut inspector_element) = inspector_element {
7723 inspector_element.paint(self, cx);
7724 };
7725 }
7726
7727 #[cfg(debug_assertions)]
7730 pub fn insert_inspector_hitbox(
7731 &mut self,
7732 hitbox_id: HitboxId,
7733 inspector_id: Option<&crate::InspectorElementId>,
7734 cx: &App,
7735 ) {
7736 self.invalidator.debug_assert_paint_or_prepaint();
7737 if !self.is_inspector_picking(cx) {
7738 return;
7739 }
7740 if let Some(inspector_id) = inspector_id {
7741 self.next_frame
7742 .inspector_hitboxes
7743 .insert(hitbox_id, inspector_id.clone());
7744 }
7745 }
7746
7747 #[cfg(debug_assertions)]
7748 fn paint_inspector_hitbox(&mut self, cx: &App) {
7749 if let Some(inspector) = self.inspector.as_ref() {
7750 let inspector = inspector.read(cx);
7751 if let Some((hitbox_id, _)) = self.hovered_inspector_hitbox(inspector, &self.next_frame)
7752 && let Some(hitbox) = self
7753 .next_frame
7754 .hitboxes
7755 .iter()
7756 .find(|hitbox| hitbox.id == hitbox_id)
7757 {
7758 self.paint_quad(crate::fill(hitbox.bounds, crate::rgba(0x61afef4d)));
7759 }
7760 }
7761 }
7762
7763 #[cfg(debug_assertions)]
7764 fn handle_inspector_mouse_event(&mut self, event: &dyn Any, cx: &mut App) {
7765 let Some(inspector) = self.inspector.clone() else {
7766 return;
7767 };
7768 if event.downcast_ref::<MouseMoveEvent>().is_some() {
7769 inspector.update(cx, |inspector, _cx| {
7770 if let Some((_, inspector_id)) =
7771 self.hovered_inspector_hitbox(inspector, &self.rendered_frame)
7772 {
7773 inspector.hover(inspector_id, self);
7774 }
7775 });
7776 } else if event.downcast_ref::<crate::MouseDownEvent>().is_some() {
7777 inspector.update(cx, |inspector, _cx| {
7778 if let Some((_, inspector_id)) =
7779 self.hovered_inspector_hitbox(inspector, &self.rendered_frame)
7780 {
7781 inspector.select(inspector_id, self);
7782 }
7783 });
7784 } else if let Some(event) = event.downcast_ref::<crate::ScrollEvent>() {
7785 const SCROLL_LINES: f32 = 3.0;
7787 const SCROLL_PIXELS_PER_LAYER: f32 = 36.0;
7788 let delta_y = event
7789 .delta
7790 .pixel_delta(px(SCROLL_PIXELS_PER_LAYER / SCROLL_LINES))
7791 .y;
7792 if let Some(inspector) = self.inspector.clone() {
7793 inspector.update(cx, |inspector, _cx| {
7794 if let Some(depth) = inspector.pick_depth.as_mut() {
7795 *depth += f32::from(delta_y) / SCROLL_PIXELS_PER_LAYER;
7796 let max_depth = self.mouse_hit_test.ids.len() as f32 - 0.5;
7797 if *depth < 0.0 {
7798 *depth = 0.0;
7799 } else if *depth > max_depth {
7800 *depth = max_depth;
7801 }
7802 if let Some((_, inspector_id)) =
7803 self.hovered_inspector_hitbox(inspector, &self.rendered_frame)
7804 {
7805 inspector.set_active_element_id(inspector_id, self);
7806 }
7807 }
7808 });
7809 }
7810 }
7811 }
7812
7813 #[cfg(debug_assertions)]
7814 fn hovered_inspector_hitbox(
7815 &self,
7816 inspector: &Inspector,
7817 frame: &Frame,
7818 ) -> Option<(HitboxId, crate::InspectorElementId)> {
7819 if let Some(pick_depth) = inspector.pick_depth {
7820 let depth = (pick_depth as i64).try_into().unwrap_or(0);
7821 let max_skipped = self.mouse_hit_test.ids.len().saturating_sub(1);
7822 let skip_count = (depth as usize).min(max_skipped);
7823 for hitbox_id in self.mouse_hit_test.ids.iter().skip(skip_count) {
7824 if let Some(inspector_id) = frame.inspector_hitboxes.get(hitbox_id) {
7825 return Some((*hitbox_id, inspector_id.clone()));
7826 }
7827 }
7828 }
7829 None
7830 }
7831
7832 #[cfg(any(test, feature = "test-support"))]
7835 pub fn set_modifiers(&mut self, modifiers: Modifiers) {
7836 self.modifiers = modifiers;
7837 }
7838
7839 #[cfg(any(test, feature = "test-support"))]
7843 pub fn simulate_mouse_move(&mut self, position: Point<Pixels>, cx: &mut App) {
7844 let event = PlatformInput::MouseMove(MouseMoveEvent {
7845 position,
7846 modifiers: self.modifiers,
7847 pressed_button: None,
7848 });
7849 let _ = self.dispatch_event(event, cx);
7850 }
7851
7852 #[inline(never)]
7853 fn take_element_state(
7854 &mut self,
7855 global_id: &GlobalElementId,
7856 state_type: TypeId,
7857 ) -> ((GlobalElementId, TypeId), Option<ElementStateBox>) {
7858 let key = (global_id.clone(), state_type);
7859 self.next_frame.accessed_element_states.push(key.clone());
7860 let state = self
7861 .next_frame
7862 .element_states
7863 .remove(&key)
7864 .or_else(|| self.rendered_frame.element_states.remove(&key));
7865 (key, state)
7866 }
7867
7868 #[inline(never)]
7869 fn insert_element_state(
7870 &mut self,
7871 key: (GlobalElementId, TypeId),
7872 state: ElementStateBox,
7873 ) -> Option<ElementStateBox> {
7874 self.next_frame.element_states.insert(key, state)
7875 }
7876
7877 #[inline(never)]
7878 fn observe_keyed_state<S: 'static>(state: &Entity<S>, current_view: EntityId, cx: &mut App) {
7879 cx.observe(state, move |_, cx| {
7880 cx.notify(current_view);
7881 })
7882 .detach();
7883 }
7884}
7885
7886slotmap::new_key_type! {
7888 pub struct WindowId;
7890}
7891
7892impl WindowId {
7893 pub fn as_u64(&self) -> u64 {
7895 self.0.as_ffi()
7896 }
7897}
7898
7899impl From<u64> for WindowId {
7900 fn from(value: u64) -> Self {
7901 WindowId(slotmap::KeyData::from_ffi(value))
7902 }
7903}
7904
7905#[derive(Deref, DerefMut)]
7908pub struct WindowHandle<V> {
7909 #[deref]
7910 #[deref_mut]
7911 pub(crate) any_handle: AnyWindowHandle,
7912 state_type: PhantomData<fn(V) -> V>,
7913}
7914
7915impl<V> Debug for WindowHandle<V> {
7916 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
7917 f.debug_struct("WindowHandle")
7918 .field("any_handle", &self.any_handle.id.as_u64())
7919 .finish()
7920 }
7921}
7922
7923impl<V: 'static + Render> WindowHandle<V> {
7924 pub fn new(id: WindowId) -> Self {
7927 WindowHandle {
7928 any_handle: AnyWindowHandle {
7929 id,
7930 state_type: TypeId::of::<V>(),
7931 root_entity_type_name: std::any::type_name::<V>(),
7932 },
7933 state_type: PhantomData,
7934 }
7935 }
7936
7937 #[cfg(any(test, feature = "test-support"))]
7941 pub fn root<C>(&self, cx: &mut C) -> Result<Entity<V>>
7942 where
7943 C: AppContext,
7944 {
7945 cx.update_window(self.any_handle, |root_view, _, _| {
7946 root_view
7947 .downcast::<V>()
7948 .map_err(|_| anyhow!("the type of the window's root view has changed"))
7949 })?
7950 }
7951
7952 pub fn update<C, R>(
7956 &self,
7957 cx: &mut C,
7958 update: impl FnOnce(&mut V, &mut Window, &mut Context<V>) -> R,
7959 ) -> Result<R>
7960 where
7961 C: AppContext,
7962 {
7963 cx.update_window(self.any_handle, |root_view, window, cx| {
7964 let view = root_view
7965 .downcast::<V>()
7966 .map_err(|_| anyhow!("the type of the window's root view has changed"))?;
7967
7968 Ok(view.update(cx, |view, cx| update(view, window, cx)))
7969 })?
7970 }
7971
7972 pub fn read<'a>(&self, cx: &'a App) -> Result<&'a V> {
7976 let x = cx
7977 .windows
7978 .get(self.id)
7979 .and_then(|window| {
7980 window
7981 .as_deref()
7982 .and_then(|window| window.root.clone())
7983 .map(|root_view| root_view.downcast::<V>())
7984 })
7985 .context("window not found")?
7986 .map_err(|_| anyhow!("the type of the window's root view has changed"))?;
7987
7988 Ok(x.read(cx))
7989 }
7990
7991 pub fn read_with<C, R>(&self, cx: &C, read_with: impl FnOnce(&V, &App) -> R) -> Result<R>
7995 where
7996 C: AppContext,
7997 {
7998 cx.read_window(self, |root_view, cx| read_with(root_view.read(cx), cx))
7999 }
8000
8001 pub fn entity<C>(&self, cx: &C) -> Result<Entity<V>>
8005 where
8006 C: AppContext,
8007 {
8008 cx.read_window(self, |root_view, _cx| root_view)
8009 }
8010
8011 pub fn is_active(&self, cx: &mut App) -> Option<bool> {
8016 cx.update_window(self.any_handle, |_, window, _| window.is_window_active())
8017 .ok()
8018 }
8019}
8020
8021impl<V> Copy for WindowHandle<V> {}
8022
8023impl<V> Clone for WindowHandle<V> {
8024 fn clone(&self) -> Self {
8025 *self
8026 }
8027}
8028
8029impl<V> PartialEq for WindowHandle<V> {
8030 fn eq(&self, other: &Self) -> bool {
8031 self.any_handle == other.any_handle
8032 }
8033}
8034
8035impl<V> Eq for WindowHandle<V> {}
8036
8037impl<V> Hash for WindowHandle<V> {
8038 fn hash<H: Hasher>(&self, state: &mut H) {
8039 self.any_handle.hash(state);
8040 }
8041}
8042
8043impl<V: 'static> From<WindowHandle<V>> for AnyWindowHandle {
8044 fn from(val: WindowHandle<V>) -> Self {
8045 val.any_handle
8046 }
8047}
8048
8049#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
8051pub struct AnyWindowHandle {
8052 pub(crate) id: WindowId,
8053 state_type: TypeId,
8054 root_entity_type_name: &'static str,
8055}
8056
8057impl AnyWindowHandle {
8058 pub fn window_id(&self) -> WindowId {
8060 self.id
8061 }
8062
8063 pub fn root_entity_type_name(&self) -> &'static str {
8065 self.root_entity_type_name
8066 }
8067
8068 pub fn downcast<T: 'static>(&self) -> Option<WindowHandle<T>> {
8071 if TypeId::of::<T>() == self.state_type {
8072 Some(WindowHandle {
8073 any_handle: *self,
8074 state_type: PhantomData,
8075 })
8076 } else {
8077 None
8078 }
8079 }
8080
8081 pub fn update<C, R>(
8085 self,
8086 cx: &mut C,
8087 update: impl FnOnce(AnyView, &mut Window, &mut App) -> R,
8088 ) -> Result<R>
8089 where
8090 C: AppContext,
8091 {
8092 cx.update_window(self, update)
8093 }
8094
8095 pub fn read<T, C, R>(self, cx: &C, read: impl FnOnce(Entity<T>, &App) -> R) -> Result<R>
8099 where
8100 C: AppContext,
8101 T: 'static,
8102 {
8103 let view = self
8104 .downcast::<T>()
8105 .context("the type of the window's root view has changed")?;
8106
8107 cx.read_window(&view, read)
8108 }
8109}
8110
8111impl HasWindowHandle for Window {
8112 fn window_handle(&self) -> Result<raw_window_handle::WindowHandle<'_>, HandleError> {
8113 self.platform_window.window_handle()
8114 }
8115}
8116
8117impl HasDisplayHandle for Window {
8118 fn display_handle(
8119 &self,
8120 ) -> std::result::Result<raw_window_handle::DisplayHandle<'_>, HandleError> {
8121 self.platform_window.display_handle()
8122 }
8123}
8124
8125#[derive(Clone, Debug, Eq, PartialEq, Hash)]
8130pub enum ElementId {
8131 View(EntityId),
8133 Integer(u64),
8135 Name(SharedString),
8137 Uuid(Uuid),
8139 FocusHandle(FocusId),
8141 NamedInteger(SharedString, u64),
8143 Path(Arc<std::path::Path>),
8145 CodeLocation(core::panic::Location<'static>),
8147 NamedChild(Arc<ElementId>, SharedString),
8149 OpaqueId([u8; 20]),
8151}
8152
8153impl ElementId {
8154 pub fn named_usize(name: impl Into<SharedString>, integer: usize) -> ElementId {
8156 Self::NamedInteger(name.into(), integer as u64)
8157 }
8158}
8159
8160impl Display for ElementId {
8161 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
8162 match self {
8163 ElementId::View(entity_id) => write!(f, "view-{}", entity_id)?,
8164 ElementId::Integer(ix) => write!(f, "{}", ix)?,
8165 ElementId::Name(name) => write!(f, "{}", name)?,
8166 ElementId::FocusHandle(_) => write!(f, "FocusHandle")?,
8167 ElementId::NamedInteger(s, i) => write!(f, "{}-{}", s, i)?,
8168 ElementId::Uuid(uuid) => write!(f, "{}", uuid)?,
8169 ElementId::Path(path) => write!(f, "{}", path.display())?,
8170 ElementId::CodeLocation(location) => write!(f, "{}", location)?,
8171 ElementId::NamedChild(id, name) => write!(f, "{}-{}", id, name)?,
8172 ElementId::OpaqueId(opaque_id) => write!(f, "{:x?}", opaque_id)?,
8173 }
8174
8175 Ok(())
8176 }
8177}
8178
8179impl TryInto<SharedString> for ElementId {
8180 type Error = anyhow::Error;
8181
8182 fn try_into(self) -> anyhow::Result<SharedString> {
8183 if let ElementId::Name(name) = self {
8184 Ok(name)
8185 } else {
8186 anyhow::bail!("element id is not string")
8187 }
8188 }
8189}
8190
8191impl From<u64> for ElementId {
8192 fn from(id: u64) -> Self {
8193 ElementId::Integer(id)
8194 }
8195}
8196
8197impl From<usize> for ElementId {
8198 fn from(id: usize) -> Self {
8199 ElementId::Integer(id as u64)
8200 }
8201}
8202
8203impl From<i32> for ElementId {
8204 fn from(id: i32) -> Self {
8205 Self::Integer(id as u64)
8206 }
8207}
8208
8209impl From<SharedString> for ElementId {
8210 fn from(name: SharedString) -> Self {
8211 ElementId::Name(name)
8212 }
8213}
8214
8215impl From<String> for ElementId {
8216 fn from(name: String) -> Self {
8217 ElementId::Name(name.into())
8218 }
8219}
8220
8221impl From<Arc<str>> for ElementId {
8222 fn from(name: Arc<str>) -> Self {
8223 ElementId::Name(name.into())
8224 }
8225}
8226
8227impl From<Arc<std::path::Path>> for ElementId {
8228 fn from(path: Arc<std::path::Path>) -> Self {
8229 ElementId::Path(path)
8230 }
8231}
8232
8233impl From<&'static str> for ElementId {
8234 fn from(name: &'static str) -> Self {
8235 ElementId::Name(SharedString::new_static(name))
8236 }
8237}
8238
8239impl<'a> From<&'a FocusHandle> for ElementId {
8240 fn from(handle: &'a FocusHandle) -> Self {
8241 ElementId::FocusHandle(handle.id)
8242 }
8243}
8244
8245impl From<(&'static str, EntityId)> for ElementId {
8246 fn from((name, id): (&'static str, EntityId)) -> Self {
8247 ElementId::NamedInteger(SharedString::new_static(name), id.as_u64())
8248 }
8249}
8250
8251impl From<(&'static str, usize)> for ElementId {
8252 fn from((name, id): (&'static str, usize)) -> Self {
8253 ElementId::NamedInteger(SharedString::new_static(name), id as u64)
8254 }
8255}
8256
8257impl From<(SharedString, usize)> for ElementId {
8258 fn from((name, id): (SharedString, usize)) -> Self {
8259 ElementId::NamedInteger(name, id as u64)
8260 }
8261}
8262
8263impl From<(&'static str, u64)> for ElementId {
8264 fn from((name, id): (&'static str, u64)) -> Self {
8265 ElementId::NamedInteger(SharedString::new_static(name), id)
8266 }
8267}
8268
8269impl From<Uuid> for ElementId {
8270 fn from(value: Uuid) -> Self {
8271 Self::Uuid(value)
8272 }
8273}
8274
8275impl From<(&'static str, u32)> for ElementId {
8276 fn from((name, id): (&'static str, u32)) -> Self {
8277 ElementId::NamedInteger(SharedString::new_static(name), u64::from(id))
8278 }
8279}
8280
8281impl<T: Into<SharedString>> From<(ElementId, T)> for ElementId {
8282 fn from((id, name): (ElementId, T)) -> Self {
8283 ElementId::NamedChild(Arc::new(id), name.into())
8284 }
8285}
8286
8287impl From<&'static core::panic::Location<'static>> for ElementId {
8288 fn from(location: &'static core::panic::Location<'static>) -> Self {
8289 ElementId::CodeLocation(*location)
8290 }
8291}
8292
8293impl From<[u8; 20]> for ElementId {
8294 fn from(opaque_id: [u8; 20]) -> Self {
8295 ElementId::OpaqueId(opaque_id)
8296 }
8297}
8298
8299#[derive(Clone)]
8302pub struct PaintQuad {
8303 pub bounds: Bounds<Pixels>,
8305 pub corner_radii: Corners<Pixels>,
8307 pub background: Background,
8309 pub border_widths: Edges<Pixels>,
8311 pub border_color: Hsla,
8313 pub border_style: BorderStyle,
8315}
8316
8317impl PaintQuad {
8318 pub fn corner_radii(self, corner_radii: impl Into<Corners<Pixels>>) -> Self {
8320 PaintQuad {
8321 corner_radii: corner_radii.into(),
8322 ..self
8323 }
8324 }
8325
8326 pub fn border_widths(self, border_widths: impl Into<Edges<Pixels>>) -> Self {
8328 PaintQuad {
8329 border_widths: border_widths.into(),
8330 ..self
8331 }
8332 }
8333
8334 pub fn border_color(self, border_color: impl Into<Hsla>) -> Self {
8336 PaintQuad {
8337 border_color: border_color.into(),
8338 ..self
8339 }
8340 }
8341
8342 pub fn background(self, background: impl Into<Background>) -> Self {
8344 PaintQuad {
8345 background: background.into(),
8346 ..self
8347 }
8348 }
8349}
8350
8351pub fn quad(
8353 bounds: Bounds<Pixels>,
8354 corner_radii: impl Into<Corners<Pixels>>,
8355 background: impl Into<Background>,
8356 border_widths: impl Into<Edges<Pixels>>,
8357 border_color: impl Into<Hsla>,
8358 border_style: BorderStyle,
8359) -> PaintQuad {
8360 PaintQuad {
8361 bounds,
8362 corner_radii: corner_radii.into(),
8363 background: background.into(),
8364 border_widths: border_widths.into(),
8365 border_color: border_color.into(),
8366 border_style,
8367 }
8368}
8369
8370pub fn fill(bounds: impl Into<Bounds<Pixels>>, background: impl Into<Background>) -> PaintQuad {
8372 PaintQuad {
8373 bounds: bounds.into(),
8374 corner_radii: (0.).into(),
8375 background: background.into(),
8376 border_widths: (0.).into(),
8377 border_color: transparent_black(),
8378 border_style: BorderStyle::default(),
8379 }
8380}
8381
8382pub fn outline(
8384 bounds: impl Into<Bounds<Pixels>>,
8385 border_color: impl Into<Hsla>,
8386 border_style: BorderStyle,
8387) -> PaintQuad {
8388 PaintQuad {
8389 bounds: bounds.into(),
8390 corner_radii: (0.).into(),
8391 background: transparent_black().into(),
8392 border_widths: (1.).into(),
8393 border_color: border_color.into(),
8394 border_style,
8395 }
8396}
8397
8398#[inline(never)]
8399fn with_element_arena_erased(callback: &mut dyn FnMut(&mut Arena)) -> Result<(), AccessError> {
8400 if let Some(arena_pointer) = CURRENT_ELEMENT_ARENA.try_with(Cell::get)? {
8401 let arena_cell = unsafe { &*arena_pointer };
8404 callback(&mut arena_cell.borrow_mut());
8405 Ok(())
8406 } else {
8407 ELEMENT_ARENA.try_with(|arena| callback(&mut arena.borrow_mut()))
8408 }
8409}
8410
8411#[cfg(test)]
8412mod tests {
8413 use std::{
8414 cell::{Cell, RefCell},
8415 path::PathBuf,
8416 rc::Rc,
8417 time::Duration,
8418 };
8419
8420 use crate::{
8421 AnyWindowHandle, AppContext as _, BackGestureEvent, BackGesturePhase, Bounds, ContentMask,
8422 Context, DispatchPhase, DragMoveEvent, Empty, ExternalDragPayload, ExternalPaths,
8423 FileDragPaths, FileDropEvent, FocusHandle, InputEvent as _, InteractiveElement as _,
8424 IntoElement, KeyDownEvent, Keystroke, LongPressEvent, MouseButton, MouseDownEvent,
8425 MouseMoveEvent, ParentElement, Pixels, PlatformInput, Point, Render, RequestFrameOptions,
8426 ScaledPixels, StatefulInteractiveElement as _, Styled, TestAppContext, ThermalState,
8427 TouchDragEvent, TouchEvent, TouchId, TouchPhase, Underline, UnderlineStyle, Window,
8428 WindowAppearance, WindowOptions, canvas, div, hsla, point, px, size,
8429 };
8430
8431 #[gpui::test]
8432 fn test_cover_bounds_preserves_empty_fractional_clips(cx: &mut TestAppContext) {
8433 let window = cx.add_window(|_, _| EmptyView);
8434 window
8435 .update(cx, |_, window, _| {
8436 for scale in [1., 1.25, 2.] {
8437 window.scale_factor = scale;
8438 let tile = Bounds::new(point(px(10.), px(10.)), size(px(20.), px(20.)));
8439 let glyph = Bounds::new(point(px(40.25), px(15.25)), size(px(4.), px(4.)));
8440 for clip in [
8441 tile.intersect(&glyph),
8442 Bounds::new(point(px(40.25), px(15.25)), size(px(0.), px(4.))),
8443 Bounds::new(point(px(15.25), px(40.25)), size(px(4.), px(0.))),
8444 ] {
8445 assert!(clip.is_empty());
8446 assert!(window.cover_bounds(clip).is_empty());
8447 }
8448 let visible = Bounds::new(point(px(15.25), px(15.25)), size(px(0.1), px(0.1)));
8449 assert!(!window.cover_bounds(visible).is_empty());
8450 }
8451 })
8452 .unwrap();
8453 }
8454
8455 #[gpui::test]
8456 async fn test_focus_handle_release(cx: &mut TestAppContext) {
8457 let (handle, survivor) = cx.update(|cx| (cx.focus_handle(), cx.focus_handle()));
8458 let handle_id = handle.id;
8459 let weak_handle = handle.downgrade();
8460 let retained = handle.clone();
8461 drop(handle);
8462
8463 cx.update(|_| {});
8464 cx.read(|cx| {
8465 let handles = cx.focus_handles.read();
8466 assert_eq!(handles.handles.len(), 2);
8467 assert!(handles.dropped_handles.is_empty());
8468 });
8469 assert_eq!(weak_handle.upgrade().as_ref(), Some(&retained));
8470
8471 cx.background_spawn(async move { drop(retained) }).await;
8472 assert_eq!(weak_handle.upgrade(), None);
8473 cx.read(|cx| {
8474 assert_eq!(cx.focus_handles.read().dropped_handles, [handle_id]);
8475 });
8476 cx.update(|_| {});
8477 cx.read(|cx| {
8478 let handles = cx.focus_handles.read();
8479 assert_eq!(handles.handles.keys().collect::<Vec<_>>(), [survivor.id]);
8480 assert!(handles.dropped_handles.is_empty());
8481 });
8482
8483 let replacements = cx.update(|cx| [cx.focus_handle(), cx.focus_handle()]);
8484 assert_eq!(weak_handle.upgrade(), None);
8485 cx.update(|cx| {
8486 drop(replacements);
8487 cx.defer(move |_| drop(survivor));
8488 });
8489 cx.read(|cx| {
8490 let handles = cx.focus_handles.read();
8491 assert_eq!(handles.handles.len(), 0);
8492 assert!(handles.dropped_handles.is_empty());
8493 });
8494 }
8495
8496 #[gpui::test]
8497 fn test_dropped_focus_handle_blurs_only_its_windows(cx: &mut TestAppContext) {
8498 let windows = [
8499 cx.add_window(|_, _| EmptyView),
8500 cx.add_window(|_, _| EmptyView),
8501 cx.add_window(|_, _| EmptyView),
8502 cx.add_window(|_, _| EmptyView),
8503 cx.add_window(|_, _| EmptyView),
8504 ];
8505 let (handle, other_handle, survivor) =
8506 cx.update(|cx| (cx.focus_handle(), cx.focus_handle(), cx.focus_handle()));
8507 for window in &windows[..2] {
8508 window
8509 .update(cx, |_, window, cx| window.focus(&handle, cx))
8510 .expect("window exists");
8511 }
8512 windows[2]
8513 .update(cx, |_, window, cx| window.focus(&other_handle, cx))
8514 .expect("window exists");
8515 windows[3]
8516 .update(cx, |_, window, cx| window.focus(&survivor, cx))
8517 .expect("window exists");
8518
8519 cx.update(|cx| {
8520 for _ in 0..1_000 {
8521 drop(cx.focus_handle());
8522 }
8523 drop((handle, other_handle));
8524 });
8525 for window in &windows[..3] {
8526 window
8527 .update(cx, |_, window, _| assert_eq!(window.focus, None))
8528 .expect("window exists");
8529 }
8530 windows[3]
8531 .update(cx, |_, window, cx| {
8532 assert_eq!(window.focused(cx).as_ref(), Some(&survivor));
8533 assert_eq!(window.focus_generation, 1);
8534 })
8535 .expect("window exists");
8536 windows[4]
8537 .update(cx, |_, window, _| {
8538 assert_eq!(window.focus, None);
8539 assert_eq!(window.focus_generation, 0);
8540 })
8541 .expect("window exists");
8542 }
8543
8544 #[gpui::test]
8545 fn test_native_close_defers_app_access(cx: &mut TestAppContext) {
8546 let window = cx.add_window(|_, _| EmptyView);
8547 let platform_window = cx.test_window(window.into());
8548 {
8549 let app = cx.app.borrow_mut();
8550 platform_window.simulate_close();
8551 assert!(app.windows.contains_key(window.window_id()));
8552 }
8553 cx.run_until_parked();
8554 assert!(!cx.app.borrow().windows.contains_key(window.window_id()));
8555 }
8556
8557 #[cfg(feature = "profiler")]
8558 #[gpui::test]
8559 fn test_frame_requests_and_skips_are_journaled(cx: &mut TestAppContext) {
8560 use crate::FrameRequestSource;
8561 use crate::profiler::journal::{
8562 self, ForegroundJournalEntry, FrameSkipReason, IntervalBoundary,
8563 };
8564 use scheduler::Instant;
8565
8566 cx.app.borrow_mut().mode = crate::app::GpuiMode::Production;
8567 let window = cx.add_window(|_, _| EmptyView);
8568 let platform_window = cx.test_window(window.into());
8569 platform_window.simulate_frame_request(RequestFrameOptions::default());
8570 let (journal, _guard) = journal::install_test_foreground_journal(64, 4);
8571 let mut collector = journal.collector();
8572
8573 for (signal_at, source) in [
8574 (Some(Instant::now()), FrameRequestSource::NativeCallback),
8575 (Some(Instant::now()), FrameRequestSource::LocalSchedule),
8576 (None, FrameRequestSource::NativeCallback),
8577 ] {
8578 platform_window.simulate_frame_request(RequestFrameOptions {
8579 signal_at,
8580 signal_source: source,
8581 require_presentation: true,
8582 ..Default::default()
8583 });
8584 let entries = collector.collect_unseen().entries;
8585 let signal_index = entries
8586 .iter()
8587 .position(|entry| {
8588 matches!(
8589 entry,
8590 ForegroundJournalEntry::PlatformSignal(signal)
8591 if signal.window_id == window.window_id()
8592 && signal.signal_at == signal_at
8593 && signal.source == source
8594 && signal_at.is_none_or(|at| signal.handled_at >= at)
8595 )
8596 })
8597 .expect("frame request metadata");
8598 let skip_index = entries
8599 .iter()
8600 .position(|entry| {
8601 matches!(
8602 entry,
8603 ForegroundJournalEntry::Boundary(IntervalBoundary::FrameSkipped(skipped))
8604 if skipped.window_id == window.window_id()
8605 && skipped.reason == FrameSkipReason::NoRenderNeeded
8606 )
8607 })
8608 .expect("completed no-render boundary");
8609 assert!(signal_index < skip_index);
8610 }
8611 }
8612
8613 #[cfg(feature = "profiler")]
8614 #[gpui::test]
8615 fn test_frame_demand_survives_throttling(cx: &mut TestAppContext) {
8616 use crate::profiler::journal::{
8617 self, ForegroundJournalEntry, FrameSkipReason, IntervalBoundary,
8618 };
8619 use scheduler::Instant;
8620
8621 cx.app.borrow_mut().mode = crate::app::GpuiMode::Production;
8623 let (journal, _guard) = journal::install_test_foreground_journal(256, 4);
8624 let mut collector = journal.collector();
8625 for initially_dirty in [true, false] {
8626 let window = cx
8628 .app
8629 .borrow_mut()
8630 .open_window(
8631 WindowOptions {
8632 inactive_frame_interval: Some(Duration::from_secs(3600)),
8633 ..Default::default()
8634 },
8635 |_, cx| cx.new(|_| EmptyView),
8636 )
8637 .expect("open inactive test window");
8638 let platform_window = cx.test_window(window.into());
8639 platform_window.simulate_active_status_change(false);
8640 platform_window.simulate_frame_request(RequestFrameOptions::default());
8641 let callback_ran = Rc::new(Cell::new(false));
8642 let draws_before = window
8643 .update(cx, |_, window, _| {
8644 let draws_before = window
8645 .frame_duration_snapshot()
8646 .draw_duration_histogram
8647 .len();
8648 if initially_dirty {
8649 window.refresh();
8650 }
8651 window.on_next_frame({
8652 let callback_ran = callback_ran.clone();
8653 move |window, _| {
8654 callback_ran.set(true);
8655 window.refresh();
8656 }
8657 });
8658 draws_before
8659 })
8660 .expect("schedule throttled frame");
8661 let input_at = Instant::now();
8663 journal::record_input(journal::InputTiming {
8664 kind: "test",
8665 start: input_at,
8666 end: input_at,
8667 caused_invalidation: false,
8668 });
8669 collector.collect_unseen();
8670 platform_window.simulate_frame_request(RequestFrameOptions {
8671 signal_at: Some(Instant::now()),
8672 ..Default::default()
8673 });
8674 window
8675 .update(cx, |_, window, _| {
8676 assert_eq!(
8677 window
8678 .frame_duration_snapshot()
8679 .draw_duration_histogram
8680 .len(),
8681 draws_before,
8682 );
8683 })
8684 .expect("inspect deferred frame");
8685 assert!(!callback_ran.get());
8686 assert!(collector.collect_unseen().entries.iter().any(|entry| {
8687 matches!(
8688 entry,
8689 ForegroundJournalEntry::Boundary(IntervalBoundary::FrameSkipped(skipped))
8690 if skipped.window_id == window.window_id()
8691 && skipped.reason == FrameSkipReason::InactiveFrameRateLimit
8692 )
8693 }));
8694 platform_window.simulate_frame_request(RequestFrameOptions {
8695 signal_at: Some(Instant::now()),
8696 require_presentation: true,
8697 ..Default::default()
8698 });
8699 window
8700 .update(cx, |_, window, _| {
8701 assert_eq!(
8702 window
8703 .frame_duration_snapshot()
8704 .draw_duration_histogram
8705 .len(),
8706 draws_before + 1,
8707 );
8708 })
8709 .expect("inspect presented frame");
8710 assert!(callback_ran.get());
8711 }
8712 }
8713
8714 #[cfg(feature = "profiler")]
8718 #[gpui::test]
8719 fn test_notify_redraw_bypasses_inactive_throttle(cx: &mut TestAppContext) {
8720 cx.app.borrow_mut().mode = crate::app::GpuiMode::Production;
8722 let window = cx
8723 .app
8724 .borrow_mut()
8725 .open_window(
8726 WindowOptions {
8727 inactive_frame_interval: Some(Duration::from_secs(3600)),
8728 ..Default::default()
8729 },
8730 |_, cx| cx.new(|_| EmptyView),
8731 )
8732 .expect("open inactive test window");
8733 let platform_window = cx.test_window(window.into());
8734 platform_window.simulate_active_status_change(false);
8735 platform_window.simulate_frame_request(RequestFrameOptions::default());
8737 let draws = |cx: &mut TestAppContext| {
8738 window
8739 .update(cx, |_, window, _| {
8740 window
8741 .frame_duration_snapshot()
8742 .draw_duration_histogram
8743 .len()
8744 })
8745 .expect("read draw count")
8746 };
8747 for _ in 0..3 {
8748 let before = draws(cx);
8749 window
8750 .update(cx, |_, _, cx| cx.notify())
8751 .expect("notify root view");
8752 platform_window.simulate_frame_request(RequestFrameOptions::default());
8753 assert_eq!(draws(cx), before + 1, "notify redraw was throttled");
8754 }
8755 }
8756
8757 #[gpui::test]
8761 fn test_window_visibility(cx: &mut TestAppContext) {
8762 use crate::WindowVisibility;
8763
8764 let window = cx.add_window(|_, _| EmptyView);
8765 let observed = Rc::new(RefCell::new(Vec::new()));
8766 let _subscription = window
8767 .update(cx, {
8768 let observed = observed.clone();
8769 move |_, window, _| {
8770 assert_eq!(window.visibility(), WindowVisibility::Visible);
8771 assert!(window.is_visible());
8772 window.observe_window_visibility(move |visibility, window, _| {
8773 assert_eq!(window.visibility(), visibility);
8774 observed.borrow_mut().push(visibility);
8775 })
8776 }
8777 })
8778 .unwrap();
8779 let test_window = cx.test_window(window.into());
8780 let frame_wake_count = test_window.frame_wake_count();
8781
8782 test_window.simulate_visibility_change(WindowVisibility::Hidden);
8783 assert_eq!(*observed.borrow(), [WindowVisibility::Hidden]);
8784 window
8785 .update(cx, |_, window, _| assert!(!window.is_visible()))
8786 .unwrap();
8787
8788 test_window.simulate_visibility_change(WindowVisibility::Hidden);
8790 assert_eq!(observed.borrow().len(), 1);
8791
8792 test_window.simulate_visibility_change(WindowVisibility::Visible);
8793 assert_eq!(
8794 *observed.borrow(),
8795 [WindowVisibility::Hidden, WindowVisibility::Visible]
8796 );
8797 window
8798 .update(cx, |_, window, _| assert!(window.is_visible()))
8799 .unwrap();
8800 assert_eq!(test_window.frame_wake_count(), frame_wake_count);
8801 }
8802
8803 #[gpui::test]
8806 fn test_display_tracking(cx: &mut TestAppContext) {
8807 use crate::{DisplayChanges, DisplayEvent, DisplayId, Subscription};
8808
8809 let sixty_hertz = Duration::from_secs(1) / 60;
8810 let one_hundred_twenty_hertz = Duration::from_secs(1) / 120;
8811 let one_hundred_forty_four_hertz = Duration::from_secs(1) / 144;
8812
8813 let display_events = Rc::new(RefCell::new(Vec::new()));
8814 let _display_subscription = cx.update({
8815 let display_events = display_events.clone();
8816 move |cx| cx.observe_displays(move |event, _| display_events.borrow_mut().push(event))
8817 });
8818
8819 let window = cx.add_window(|window, cx| DisplayTracker {
8820 refresh_intervals: Vec::new(),
8821 _subscription: cx.observe_window_display(
8822 window,
8823 |tracker: &mut DisplayTracker, window, cx| {
8824 tracker.refresh_intervals.push(window.refresh_interval(cx));
8825 },
8826 ),
8827 });
8828 let window_notifications = Rc::new(RefCell::new(Vec::new()));
8829 let _window_subscription = window
8830 .update(cx, {
8831 let window_notifications = window_notifications.clone();
8832 move |_, window, cx| {
8833 assert_eq!(window.refresh_interval(cx), Some(sixty_hertz));
8834 window.observe_window_display(move |window, cx| {
8835 window_notifications
8836 .borrow_mut()
8837 .push((window.display_id, window.refresh_interval(cx)));
8838 })
8839 }
8840 })
8841 .unwrap();
8842 let test_window = cx.test_window(window.into());
8843
8844 cx.test_platform()
8849 .connect_display(DisplayId(2), Some(one_hundred_twenty_hertz));
8850 test_window.simulate_move_to_display(DisplayId(2));
8851 assert_eq!(
8852 window_notifications.take(),
8853 [(Some(DisplayId(2)), Some(one_hundred_twenty_hertz))]
8854 );
8855 assert_eq!(display_events.take(), [DisplayEvent::Added(DisplayId(2))]);
8856 cx.simulate_display_refresh_interval_change(DisplayId(2), Some(one_hundred_twenty_hertz));
8857 assert!(window_notifications.borrow().is_empty());
8858
8859 test_window.simulate_move_to_display(DisplayId(2));
8861 assert!(window_notifications.borrow().is_empty());
8862
8863 cx.simulate_display_refresh_interval_change(
8864 DisplayId(2),
8865 Some(one_hundred_forty_four_hertz),
8866 );
8867 assert_eq!(
8868 window_notifications.take(),
8869 [(Some(DisplayId(2)), Some(one_hundred_forty_four_hertz))]
8870 );
8871
8872 cx.simulate_display_refresh_interval_change(DisplayId(1), Some(one_hundred_twenty_hertz));
8874 cx.simulate_display_removed(DisplayId(1));
8875 assert!(window_notifications.borrow().is_empty());
8876
8877 assert_eq!(
8878 display_events.take(),
8879 [
8880 DisplayEvent::Changed {
8881 id: DisplayId(2),
8882 changes: DisplayChanges::REFRESH_INTERVAL,
8883 },
8884 DisplayEvent::Changed {
8885 id: DisplayId(1),
8886 changes: DisplayChanges::REFRESH_INTERVAL,
8887 },
8888 DisplayEvent::Removed(DisplayId(1)),
8889 ]
8890 );
8891 window
8892 .update(cx, |tracker, _, _| {
8893 assert_eq!(
8894 tracker.refresh_intervals,
8895 [
8896 Some(one_hundred_twenty_hertz),
8897 Some(one_hundred_forty_four_hertz)
8898 ]
8899 );
8900 })
8901 .unwrap();
8902
8903 struct DisplayTracker {
8904 refresh_intervals: Vec<Option<Duration>>,
8905 _subscription: Subscription,
8906 }
8907
8908 impl Render for DisplayTracker {
8909 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
8910 Empty
8911 }
8912 }
8913 }
8914
8915 #[gpui::test]
8916 fn test_fully_visible_bounds_preserve_layout_viewport(cx: &mut TestAppContext) {
8917 let window = cx.add_window(|_, _| EmptyView);
8918 let mut platform_window = cx.test_window(window.into());
8919 platform_window.simulate_resize(size(px(400.), px(800.)));
8920 window
8921 .update(cx, |_, window, _| {
8922 assert_eq!(
8923 window.visual_viewport_bounds(),
8924 Bounds::new(Point::default(), size(px(400.), px(800.)))
8925 );
8926 assert_eq!(
8927 window.fully_visible_bounds(),
8928 window.visual_viewport_bounds()
8929 );
8930 })
8931 .unwrap();
8932
8933 platform_window.simulate_frame_request(RequestFrameOptions::default());
8934 let wakes = platform_window.frame_wake_count();
8935 let visual_bounds = Bounds::new(point(px(10.), px(40.)), size(px(380.), px(460.)));
8936 platform_window.simulate_visual_viewport_change(visual_bounds);
8937 assert!(platform_window.frame_wake_count() > wakes);
8938 platform_window.simulate_frame_request(RequestFrameOptions::default());
8939 let wakes = platform_window.frame_wake_count();
8940 let insets = crate::WindowInsets {
8941 safe_area: crate::Edges {
8942 top: px(60.),
8943 right: px(20.),
8944 bottom: px(30.),
8945 left: px(-10.),
8946 },
8947 ime: crate::Edges {
8948 bottom: px(350.),
8949 ..Default::default()
8950 },
8951 ..Default::default()
8952 };
8953 platform_window.simulate_insets_change(insets.clone());
8954 assert!(platform_window.frame_wake_count() > wakes);
8955 window
8956 .update(cx, |_, window, cx| {
8957 let focus = cx.focus_handle();
8958 window.focus(&focus, cx);
8959 assert_eq!(window.insets(), insets);
8960 assert_eq!(window.viewport_size(), size(px(400.), px(800.)));
8961 assert_eq!(window.visual_viewport_bounds(), visual_bounds);
8962 assert_eq!(
8963 window.fully_visible_bounds(),
8964 Bounds::new(point(px(10.), px(60.)), size(px(370.), px(390.)))
8965 );
8966 window.request_virtual_keyboard();
8967 window.dismiss_virtual_keyboard();
8968 assert!(focus.is_focused(window));
8969 assert_eq!(window.viewport_size(), size(px(400.), px(800.)));
8970 })
8971 .unwrap();
8972 assert_eq!(platform_window.virtual_keyboard_requests(), 1);
8973 assert_eq!(platform_window.virtual_keyboard_dismissals(), 1);
8974
8975 platform_window.simulate_insets_change(crate::WindowInsets {
8976 safe_area: crate::Edges {
8977 top: px(900.),
8978 left: px(500.),
8979 ..Default::default()
8980 },
8981 ..Default::default()
8982 });
8983 window
8984 .update(cx, |_, window, _| {
8985 assert_eq!(
8986 window.fully_visible_bounds().size,
8987 size(Pixels::ZERO, Pixels::ZERO)
8988 );
8989 })
8990 .unwrap();
8991
8992 use crate::PlatformWindow as _;
8994 platform_window.on_insets_changed(Box::new(|_| {}));
8995 platform_window.simulate_insets_change(crate::WindowInsets::default());
8996 window
8997 .update(cx, |_, window, _| {
8998 assert_eq!(window.insets(), crate::WindowInsets::default());
8999 })
9000 .unwrap();
9001 }
9002
9003 #[test]
9004 fn frame_throttle_accepts_display_link_rounding() {
9005 assert!(!super::frame_request_arrived_too_early(
9006 Duration::from_micros(16_666),
9007 super::SIXTY_FPS_INTERVAL,
9008 ));
9009 assert!(!super::frame_request_arrived_too_early(
9010 Duration::from_micros(33_332),
9011 super::THIRTY_FPS_INTERVAL,
9012 ));
9013 }
9014
9015 #[test]
9016 fn frame_throttle_rejects_an_extra_display_refresh() {
9017 assert!(super::frame_request_arrived_too_early(
9018 Duration::from_micros(8_333),
9019 super::SIXTY_FPS_INTERVAL,
9020 ));
9021 assert!(super::frame_request_arrived_too_early(
9022 Duration::from_micros(16_666),
9023 super::THIRTY_FPS_INTERVAL,
9024 ));
9025 }
9026
9027 #[test]
9028 fn power_modes_preserve_frame_limits() {
9029 for (power_mode, active_interval, thermal_interval) in [
9030 (
9031 crate::PowerMode::LowPower,
9032 Some(super::THIRTY_FPS_INTERVAL),
9033 super::THIRTY_FPS_INTERVAL,
9034 ),
9035 (crate::PowerMode::Unknown, None, super::SIXTY_FPS_INTERVAL),
9036 (crate::PowerMode::Balanced, None, super::SIXTY_FPS_INTERVAL),
9037 (crate::PowerMode::HighPower, None, super::SIXTY_FPS_INTERVAL),
9038 ] {
9039 assert_eq!(
9040 super::minimum_frame_interval(true, false, None, ThermalState::Nominal, power_mode),
9041 active_interval,
9042 );
9043 assert_eq!(
9044 super::minimum_frame_interval(true, false, None, ThermalState::Serious, power_mode),
9045 Some(thermal_interval),
9046 );
9047 let inactive_interval = Duration::from_millis(100);
9048 assert_eq!(
9049 super::minimum_frame_interval(
9050 true,
9051 true,
9052 Some(inactive_interval),
9053 ThermalState::Nominal,
9054 power_mode,
9055 ),
9056 Some(inactive_interval),
9057 );
9058 }
9059 }
9060
9061 struct EmptyView;
9062
9063 impl Render for EmptyView {
9064 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
9065 div()
9066 }
9067 }
9068
9069 struct OpensWindowOnPaint {
9070 opened: Rc<Cell<bool>>,
9071 }
9072
9073 impl Render for OpensWindowOnPaint {
9074 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
9075 let opened = self.opened.clone();
9076 div()
9077 .size_full()
9078 .child(canvas(
9079 |_, _, _| {},
9080 move |_, _, _window, cx| {
9081 if !opened.replace(true) {
9082 cx.open_window(WindowOptions::default(), |_, cx| cx.new(|_| EmptyView))
9083 .unwrap();
9084 }
9085 },
9086 ))
9087 .child(div().child("after"))
9091 }
9092 }
9093
9094 #[test]
9100 fn test_window_opened_during_draw_defers_arena_clear() {
9101 let mut cx = TestAppContext::single();
9102
9103 let opened = Rc::new(Cell::new(false));
9104 let window = cx.add_window({
9106 let opened = opened.clone();
9107 move |_, _| OpensWindowOnPaint { opened }
9108 });
9109
9110 assert!(opened.get());
9111 assert_eq!(cx.windows().len(), 2);
9112
9113 cx.update_window(window.into(), |_, window, cx| window.draw(cx).clear(cx))
9116 .unwrap();
9117 }
9118
9119 #[test]
9120 fn test_scale_factor_change_preserves_bounds_and_survives_resize() {
9121 let mut cx = TestAppContext::single();
9122 let window = cx.add_window(|_, _| EmptyView);
9123 let handle: AnyWindowHandle = window.into();
9124 let window_state = |cx: &mut TestAppContext| {
9125 cx.update_window(handle, |_, window, _| {
9126 (
9127 window.scale_factor(),
9128 window.bounds(),
9129 window.viewport_size(),
9130 )
9131 })
9132 .unwrap()
9133 };
9134
9135 let (scale_factor, mut expected_bounds, _) = window_state(&mut cx);
9136 assert_eq!(scale_factor, 2.0);
9137
9138 for (scale_factor, resized_size) in [
9139 (1.0, size(px(800.), px(600.))),
9140 (1.25, size(px(640.), px(480.))),
9141 (2.0, size(px(1024.), px(768.))),
9142 ] {
9143 cx.simulate_window_scale_factor_change(handle, scale_factor);
9144 assert_eq!(
9145 window_state(&mut cx),
9146 (scale_factor, expected_bounds, expected_bounds.size)
9147 );
9148
9149 cx.simulate_window_resize(handle, resized_size);
9150 expected_bounds.size = resized_size;
9151 assert_eq!(
9152 window_state(&mut cx),
9153 (scale_factor, expected_bounds, resized_size)
9154 );
9155 }
9156 }
9157
9158 #[gpui::test]
9163 fn test_frame_waker_fires_on_frame_demand(cx: &mut TestAppContext) {
9164 let window = cx.add_window(|_, _| EmptyView);
9165 let test_window = cx.test_window(window.into());
9166
9167 assert!(
9171 test_window.frame_wake_count() >= 1,
9172 "opening a window must wake the frame source for the initial frame"
9173 );
9174
9175 test_window.simulate_frame_request(RequestFrameOptions::default());
9177
9178 let baseline = test_window.frame_wake_count();
9181 test_window.simulate_frame_request(RequestFrameOptions::default());
9182 window.update(cx, |_, _, _| {}).unwrap();
9183 assert_eq!(
9184 test_window.frame_wake_count(),
9185 baseline,
9186 "clean frames and non-notifying updates must not wake the frame source"
9187 );
9188
9189 window.update(cx, |_, _, cx| cx.notify()).unwrap();
9191 assert!(
9192 test_window.frame_wake_count() > baseline,
9193 "notifying a view in an idle window must wake the frame source"
9194 );
9195
9196 test_window.simulate_frame_request(RequestFrameOptions::default());
9198 let baseline = test_window.frame_wake_count();
9199 test_window.simulate_frame_request(RequestFrameOptions::default());
9200 assert_eq!(
9201 test_window.frame_wake_count(),
9202 baseline,
9203 "serving demand must return the window to idle"
9204 );
9205
9206 window
9208 .update(cx, |_, window, _| window.on_next_frame(|_, _| {}))
9209 .unwrap();
9210 assert!(
9211 test_window.frame_wake_count() > baseline,
9212 "scheduling a next-frame callback in an idle window must wake the frame source"
9213 );
9214 }
9215
9216 #[gpui::test]
9221 fn test_pending_next_frame_callbacks_are_not_stranded(cx: &mut TestAppContext) {
9222 let window = cx.add_window(|_, _| EmptyView);
9223 let test_window = cx.test_window(window.into());
9224 test_window.simulate_frame_request(RequestFrameOptions::default());
9227
9228 let callback_ran = Rc::new(Cell::new(false));
9229 window
9230 .update(cx, {
9231 let callback_ran = callback_ran.clone();
9232 move |_, window, _| {
9233 window.on_next_frame(move |_, _| callback_ran.set(true));
9234 }
9235 })
9236 .unwrap();
9237
9238 let baseline = test_window.frame_wake_count();
9239 test_window.simulate_frame_request(RequestFrameOptions::default());
9240 assert!(
9247 test_window.frame_wake_count() > baseline || callback_ran.get(),
9248 "a frame request with pending next-frame callbacks must either run them or re-arm the frame source"
9249 );
9250 }
9251
9252 #[gpui::test]
9253 fn color_mode_default_platform_is_compatible_and_rejects_drawing(cx: &mut TestAppContext) {
9254 let handle = cx.add_window(|_, _| EmptyView);
9255 handle
9256 .update(cx, |_, window, _| {
9257 let original = window.color_status();
9258 assert_eq!(original, crate::WindowColorStatus::default());
9259 assert_eq!(
9260 window
9261 .set_color_mode(crate::WindowColorMode::Default)
9262 .unwrap(),
9263 original
9264 );
9265 assert!(
9266 window
9267 .set_color_mode(crate::WindowColorMode::WideGamutSdr)
9268 .is_err()
9269 );
9270 assert_eq!(window.color_status(), original);
9271 for phase in [
9272 super::DrawPhase::Prepaint,
9273 super::DrawPhase::Paint,
9274 super::DrawPhase::Focus,
9275 ] {
9276 window.invalidator.set_phase(phase);
9277 assert!(
9278 window
9279 .set_color_mode(crate::WindowColorMode::Default)
9280 .is_err()
9281 );
9282 assert_eq!(window.color_status(), original);
9283 }
9284 window.invalidator.set_phase(super::DrawPhase::None);
9285 })
9286 .unwrap();
9287 }
9288
9289 #[gpui::test]
9290 fn test_window_reports_no_raw_handle_instead_of_panicking(cx: &mut TestAppContext) {
9291 use raw_window_handle::{HandleError, HasDisplayHandle as _, HasWindowHandle as _};
9292
9293 let window = cx.add_window(|_, _| EmptyView);
9294 window
9295 .update(cx, |_, window, _| {
9296 assert!(matches!(
9297 window.window_handle(),
9298 Err(HandleError::NotSupported)
9299 ));
9300 assert!(matches!(
9301 window.display_handle(),
9302 Err(HandleError::NotSupported)
9303 ));
9304 })
9305 .unwrap();
9306 }
9307
9308 #[gpui::test]
9309 fn test_appearance_change_runs_after_app_update(cx: &mut TestAppContext) {
9310 let window = cx.add_window(|_, _| EmptyView);
9311 let observed_appearance = Rc::new(Cell::new(None));
9312 let _subscription = window
9313 .update(cx, {
9314 let observed_appearance = observed_appearance.clone();
9315 move |_, window, _| {
9316 window.observe_window_appearance(move |window, _| {
9317 observed_appearance.set(Some(window.appearance()));
9318 })
9319 }
9320 })
9321 .unwrap();
9322 let test_window = cx.test_window(window.into());
9323
9324 cx.update(|_| {
9325 test_window.simulate_appearance_change(WindowAppearance::Dark);
9326 assert_eq!(observed_appearance.get(), None);
9327 });
9328 cx.run_until_parked();
9329
9330 assert_eq!(observed_appearance.get(), Some(WindowAppearance::Dark));
9331 }
9332
9333 #[gpui::test]
9334 fn queued_frame_callback_wakes_a_parked_render_loop(cx: &mut TestAppContext) {
9335 let window = cx.add_window(|_, _| Empty);
9336 let test_window = cx.test_window(window.into());
9337
9338 assert!(test_window.simulate_scheduled_frame());
9339 assert!(test_window.simulate_scheduled_frame());
9340 assert!(!test_window.frame_scheduled());
9341
9342 cx.update_window(window.into(), |_, window, _| {
9343 window.active.set(true);
9344 window.on_next_frame(|_, _| {});
9345 })
9346 .unwrap();
9347 assert!(
9348 test_window.frame_scheduled(),
9349 "queuing work on a parked window must wake the render loop"
9350 );
9351
9352 assert!(test_window.simulate_scheduled_frame());
9353 assert!(
9354 test_window.frame_scheduled(),
9355 "presenting the frame must await one compositor callback"
9356 );
9357 assert!(test_window.simulate_scheduled_frame());
9358 assert!(!test_window.frame_scheduled());
9359 }
9360
9361 #[gpui::test]
9362 fn pending_presentation_wakes_a_parked_render_loop(cx: &mut TestAppContext) {
9363 let window = cx.add_window(|_, _| Empty);
9364 let test_window = cx.test_window(window.into());
9365
9366 assert!(test_window.simulate_scheduled_frame());
9367 assert!(test_window.simulate_scheduled_frame());
9368 assert!(!test_window.frame_scheduled());
9369
9370 cx.update_window(window.into(), |_, window, cx| window.draw(cx).clear(cx))
9371 .unwrap();
9372
9373 assert!(
9374 test_window.frame_scheduled(),
9375 "a rendered scene awaiting presentation must wake the render loop"
9376 );
9377 }
9378
9379 #[gpui::test]
9380 fn callback_queued_during_a_frame_requests_a_follow_up(cx: &mut TestAppContext) {
9381 let window = cx.add_window(|_, _| Empty);
9382 let test_window = cx.test_window(window.into());
9383
9384 let callback_ran = Rc::new(Cell::new(false));
9385 cx.update_window(window.into(), |_, window, _| {
9386 window.active.set(true);
9389 let callback_ran = callback_ran.clone();
9390 window.on_next_frame(move |window, _| {
9391 window.on_next_frame(move |_, _| callback_ran.set(true));
9392 });
9393 })
9394 .unwrap();
9395
9396 assert!(test_window.simulate_scheduled_frame());
9397 assert!(!callback_ran.get());
9398 assert!(
9399 test_window.frame_scheduled(),
9400 "a callback queued mid-frame must schedule a follow-up before the loop parks"
9401 );
9402
9403 assert!(test_window.simulate_scheduled_frame());
9404 assert!(callback_ran.get());
9405 }
9406
9407 struct RootView {
9408 explicit_size: bool,
9409 child_bounds: Rc<Cell<Bounds<Pixels>>>,
9410 }
9411
9412 impl Render for RootView {
9413 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
9414 let child_bounds = self.child_bounds.clone();
9415 let root = div().flex().flex_col().child(
9416 canvas(
9417 move |bounds, _, _| child_bounds.set(bounds),
9418 |_, _, _, _| {},
9419 )
9420 .size_full(),
9421 );
9422 if self.explicit_size {
9423 root.w(px(300.)).h(px(200.))
9424 } else {
9425 root
9426 }
9427 }
9428 }
9429
9430 #[test]
9431 fn auto_sized_window_root_fills_the_window() {
9432 let mut cx = TestAppContext::single();
9433 let child_bounds = Rc::new(Cell::new(Bounds::default()));
9434 let window = cx.add_window({
9435 let child_bounds = child_bounds.clone();
9436 move |_, _| RootView {
9437 explicit_size: false,
9438 child_bounds,
9439 }
9440 });
9441
9442 let viewport_size = cx
9443 .update_window(window.into(), |_, window, cx| {
9444 window.draw(cx).clear(cx);
9445 window.viewport_size()
9446 })
9447 .unwrap();
9448
9449 assert_eq!(child_bounds.get().size, viewport_size);
9450 }
9451
9452 #[test]
9453 fn explicitly_sized_window_root_keeps_its_size() {
9454 let mut cx = TestAppContext::single();
9455 let child_bounds = Rc::new(Cell::new(Bounds::default()));
9456 let window = cx.add_window({
9457 let child_bounds = child_bounds.clone();
9458 move |_, _| RootView {
9459 explicit_size: true,
9460 child_bounds,
9461 }
9462 });
9463
9464 cx.update_window(window.into(), |_, window, cx| {
9465 window.draw(cx).clear(cx);
9466 })
9467 .unwrap();
9468
9469 assert_eq!(child_bounds.get().size, size(px(300.), px(200.)));
9470 }
9471
9472 struct FileDragView {
9473 path: PathBuf,
9474 observed_drag_moves: Rc<RefCell<Vec<Point<Pixels>>>>,
9475 observed_drops: Rc<RefCell<Vec<PathBuf>>>,
9476 }
9477
9478 struct FileDropExitView(Rc<Cell<usize>>);
9479
9480 impl Render for FileDropExitView {
9481 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
9482 div().size_full().on_file_drop_exit({
9483 let observed_file_drop_exit = self.0.clone();
9484 move |_, _, _| observed_file_drop_exit.set(observed_file_drop_exit.get() + 1)
9485 })
9486 }
9487 }
9488
9489 impl Render for FileDragView {
9490 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
9491 div()
9492 .id("file-drag")
9493 .size_full()
9494 .on_drag(self.path.clone(), |_, _, _, cx| cx.new(|_| Empty))
9495 .external_drag_payload(|path: &PathBuf, _, _| {
9496 Some(ExternalDragPayload::Files(FileDragPaths::new([(
9497 path.clone(),
9498 true,
9499 )])))
9500 })
9501 .on_drag_move({
9502 let observed_drag_moves = self.observed_drag_moves.clone();
9503 move |event: &DragMoveEvent<PathBuf>, _, _| {
9504 observed_drag_moves.borrow_mut().push(event.event.position);
9505 }
9506 })
9507 .on_drop({
9508 let observed_drops = self.observed_drops.clone();
9509 move |path: &PathBuf, _, _| observed_drops.borrow_mut().push(path.clone())
9510 })
9511 }
9512 }
9513
9514 #[gpui::test]
9515 fn file_drag_is_promoted_once_and_restored_in_source_window(cx: &mut TestAppContext) {
9516 struct Drag {
9517 window: AnyWindowHandle,
9518 observed_drag_moves: Rc<RefCell<Vec<Point<Pixels>>>>,
9519 observed_drops: Rc<RefCell<Vec<PathBuf>>>,
9520 }
9521
9522 fn start_drag(cx: &mut TestAppContext, path: PathBuf, platform_result: bool) -> Drag {
9523 let observed_drag_moves = Rc::new(RefCell::new(Vec::new()));
9524 let observed_drops = Rc::new(RefCell::new(Vec::new()));
9525 let window: AnyWindowHandle = cx
9526 .add_window({
9527 let observed_drag_moves = observed_drag_moves.clone();
9528 let observed_drops = observed_drops.clone();
9529 move |_, _| FileDragView {
9530 path,
9531 observed_drag_moves,
9532 observed_drops,
9533 }
9534 })
9535 .into();
9536 cx.test_window(window)
9537 .set_start_external_drag_result(platform_result);
9538
9539 let update_result = cx.update_window(window, |_, window, cx| {
9540 window.draw(cx).clear(cx);
9541 window.dispatch_event(
9542 MouseDownEvent {
9543 position: point(px(10.), px(10.)),
9544 button: MouseButton::Left,
9545 modifiers: Default::default(),
9546 click_count: 1,
9547 first_mouse: false,
9548 }
9549 .to_platform_input(),
9550 cx,
9551 );
9552 window.dispatch_event(
9553 MouseMoveEvent {
9554 position: point(px(20.), px(20.)),
9555 pressed_button: Some(MouseButton::Left),
9556 modifiers: Default::default(),
9557 }
9558 .to_platform_input(),
9559 cx,
9560 );
9561 assert!(cx.active_drag.is_some());
9562 });
9563 assert!(
9564 update_result.is_ok(),
9565 "failed to start drag: {update_result:?}"
9566 );
9567
9568 assert!(cx.test_window(window).external_drag_files().is_empty());
9569 Drag {
9570 window,
9571 observed_drag_moves,
9572 observed_drops,
9573 }
9574 }
9575
9576 let successful_path = PathBuf::from("/tmp/successful-drag");
9577 let successful = start_drag(cx, successful_path.clone(), true);
9578 let outside_position = point(px(-1.), px(20.));
9579 let update_result = cx.update_window(successful.window, |_, window, cx| {
9580 window.dispatch_event(
9581 MouseMoveEvent {
9582 position: outside_position,
9583 pressed_button: Some(MouseButton::Left),
9584 modifiers: Default::default(),
9585 }
9586 .to_platform_input(),
9587 cx,
9588 );
9589 assert!(cx.active_drag.is_none());
9590 });
9591 assert!(
9592 update_result.is_ok(),
9593 "failed to promote drag: {update_result:?}"
9594 );
9595 assert_eq!(
9596 cx.test_window(successful.window).external_drag_files(),
9597 [(successful_path.clone(), true)]
9598 );
9599 assert_eq!(
9602 successful.observed_drag_moves.borrow().last(),
9603 Some(&outside_position)
9604 );
9605
9606 let first_destination_exit_count = Rc::new(Cell::new(0));
9607 let first_destination: AnyWindowHandle = cx
9608 .add_window({
9609 let first_destination_exit_count = first_destination_exit_count.clone();
9610 move |_, _| FileDropExitView(first_destination_exit_count)
9611 })
9612 .into();
9613 let second_destination_exit_count = Rc::new(Cell::new(0));
9614 let second_destination: AnyWindowHandle = cx
9615 .add_window({
9616 let second_destination_exit_count = second_destination_exit_count.clone();
9617 move |_, _| FileDropExitView(second_destination_exit_count)
9618 })
9619 .into();
9620 let reentry_position = point(px(30.), px(30.));
9621 let external_paths = || ExternalPaths([successful_path.clone()].into_iter().collect());
9622 let update_result = cx.update_window(first_destination, |_, window, cx| {
9623 window.draw(cx).clear(cx);
9624 window.dispatch_event(
9625 FileDropEvent::Entered {
9626 position: reentry_position,
9627 paths: external_paths(),
9628 }
9629 .to_platform_input(),
9630 cx,
9631 );
9632 assert!(
9633 cx.active_drag
9634 .as_ref()
9635 .is_some_and(|drag| drag.value.downcast_ref::<ExternalPaths>().is_some())
9636 );
9637 window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx);
9638 assert!(cx.active_drag.is_none());
9639 assert_eq!(first_destination_exit_count.get(), 1);
9640 assert_eq!(second_destination_exit_count.get(), 0);
9641 });
9642 assert!(
9643 update_result.is_ok(),
9644 "failed to handle drag in first destination window: {update_result:?}"
9645 );
9646
9647 let update_result = cx.update_window(second_destination, |_, window, cx| {
9648 window.draw(cx).clear(cx);
9649 window.dispatch_event(
9650 PlatformInput::KeyDown(KeyDownEvent {
9651 keystroke: Keystroke::parse("down").expect("valid keystroke"),
9652 is_held: false,
9653 prefer_character_input: false,
9654 }),
9655 cx,
9656 );
9657 window.dispatch_event(
9658 FileDropEvent::Entered {
9659 position: reentry_position,
9660 paths: external_paths(),
9661 }
9662 .to_platform_input(),
9663 cx,
9664 );
9665 assert!(
9666 cx.active_drag
9667 .as_ref()
9668 .is_some_and(|drag| drag.value.downcast_ref::<ExternalPaths>().is_some())
9669 );
9670 assert_eq!(first_destination_exit_count.get(), 1);
9671 assert_eq!(second_destination_exit_count.get(), 0);
9672
9673 window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx);
9674 assert!(cx.active_drag.is_none());
9675 assert_eq!(first_destination_exit_count.get(), 1);
9676 assert_eq!(second_destination_exit_count.get(), 1);
9677 });
9678 assert!(
9679 update_result.is_ok(),
9680 "failed to handle drag in second destination window: {update_result:?}"
9681 );
9682
9683 let update_result = cx.update_window(successful.window, |_, window, cx| {
9684 window.dispatch_event(
9685 FileDropEvent::Entered {
9686 position: reentry_position,
9687 paths: external_paths(),
9688 }
9689 .to_platform_input(),
9690 cx,
9691 );
9692 assert!(
9693 cx.active_drag
9694 .as_ref()
9695 .is_some_and(|drag| drag.value.downcast_ref::<PathBuf>().is_some())
9696 );
9697 assert_eq!(
9698 successful.observed_drag_moves.borrow().last(),
9699 Some(&reentry_position)
9700 );
9701
9702 window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx);
9703 assert!(cx.active_drag.is_none());
9704
9705 window.dispatch_event(
9706 FileDropEvent::Entered {
9707 position: reentry_position,
9708 paths: external_paths(),
9709 }
9710 .to_platform_input(),
9711 cx,
9712 );
9713 assert!(
9714 cx.active_drag
9715 .as_ref()
9716 .is_some_and(|drag| drag.value.downcast_ref::<PathBuf>().is_some())
9717 );
9718
9719 window.dispatch_event(
9720 FileDropEvent::Submit {
9721 position: reentry_position,
9722 }
9723 .to_platform_input(),
9724 cx,
9725 );
9726 assert_eq!(
9727 successful.observed_drops.borrow().as_slice(),
9728 std::slice::from_ref(&successful_path)
9729 );
9730 assert!(cx.active_drag.is_none());
9731
9732 window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx);
9733 assert!(cx.active_drag.is_none());
9734 window.dispatch_event(FileDropEvent::Ended.to_platform_input(), cx);
9735 assert!(cx.active_drag.is_none());
9736
9737 window.dispatch_event(
9738 FileDropEvent::Entered {
9739 position: reentry_position,
9740 paths: external_paths(),
9741 }
9742 .to_platform_input(),
9743 cx,
9744 );
9745 assert!(
9746 cx.active_drag
9747 .as_ref()
9748 .is_some_and(|drag| drag.value.downcast_ref::<ExternalPaths>().is_some())
9749 );
9750 window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx);
9751 });
9752 assert!(
9753 update_result.is_ok(),
9754 "failed to restore drag in source window: {update_result:?}"
9755 );
9756
9757 let cancelled_path = PathBuf::from("/tmp/cancelled-drag");
9758 let cancelled = start_drag(cx, cancelled_path.clone(), true);
9759 let update_result = cx.update_window(cancelled.window, |_, window, cx| {
9760 window.dispatch_event(
9761 MouseMoveEvent {
9762 position: outside_position,
9763 pressed_button: Some(MouseButton::Left),
9764 modifiers: Default::default(),
9765 }
9766 .to_platform_input(),
9767 cx,
9768 );
9769 assert!(cx.active_drag.is_none());
9770
9771 window.dispatch_event(
9772 FileDropEvent::Entered {
9773 position: reentry_position,
9774 paths: ExternalPaths([cancelled_path].into_iter().collect()),
9775 }
9776 .to_platform_input(),
9777 cx,
9778 );
9779 assert!(
9780 cx.active_drag
9781 .as_ref()
9782 .is_some_and(|drag| drag.value.downcast_ref::<PathBuf>().is_some())
9783 );
9784 assert!(cx.stop_active_drag(window));
9785 assert!(cx.active_drag.is_none());
9786 });
9787 assert!(
9788 update_result.is_ok(),
9789 "failed to cancel restored drag: {update_result:?}"
9790 );
9791 assert!(!cx.update(|cx| cx.end_platform_drag(cancelled.window.window_id())));
9792
9793 let removed_path = PathBuf::from("/tmp/removed-window-drag");
9794 let removed = start_drag(cx, removed_path, true);
9795 let removed_window_id = removed.window.window_id();
9796 let update_result = cx.update_window(removed.window, |_, window, cx| {
9797 window.dispatch_event(
9798 MouseMoveEvent {
9799 position: outside_position,
9800 pressed_button: Some(MouseButton::Left),
9801 modifiers: Default::default(),
9802 }
9803 .to_platform_input(),
9804 cx,
9805 );
9806 assert!(cx.active_drag.is_none());
9807 window.remove_window();
9808 });
9809 assert!(
9810 update_result.is_ok(),
9811 "failed to remove drag source window: {update_result:?}"
9812 );
9813 assert!(!cx.update(|cx| cx.end_platform_drag(removed_window_id)));
9814
9815 let failed_path = PathBuf::from("/tmp/failed-drag");
9816 let failed = start_drag(cx, failed_path.clone(), false);
9817 let update_result = cx.update_window(failed.window, |_, window, cx| {
9818 for x_position in [-1., -2.] {
9819 window.dispatch_event(
9820 MouseMoveEvent {
9821 position: point(px(x_position), px(20.)),
9822 pressed_button: Some(MouseButton::Left),
9823 modifiers: Default::default(),
9824 }
9825 .to_platform_input(),
9826 cx,
9827 );
9828 }
9829 assert!(cx.active_drag.is_some());
9830 });
9831 assert!(
9832 update_result.is_ok(),
9833 "failed to retain drag after platform failure: {update_result:?}"
9834 );
9835 assert_eq!(
9836 cx.test_window(failed.window).external_drag_files(),
9837 [(failed_path, true)]
9838 );
9839 }
9840
9841 struct FocusForwarder {
9842 a: FocusHandle,
9843 b: FocusHandle,
9844 }
9845
9846 impl Render for FocusForwarder {
9847 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
9848 div()
9849 .size_full()
9850 .child(div().w(px(50.)).h(px(50.)).track_focus(&self.a))
9851 .child(div().w(px(50.)).h(px(50.)).track_focus(&self.b))
9852 }
9853 }
9854
9855 #[gpui::test]
9859 fn test_focus_moved_by_focus_listener_is_dispatched(cx: &mut TestAppContext) {
9860 let b_focus_count = Rc::new(Cell::new(0));
9861 let window = cx.add_window({
9862 let b_focus_count = b_focus_count.clone();
9863 move |window, cx| {
9864 let a = cx.focus_handle();
9865 let b = cx.focus_handle();
9866 cx.on_focus(&a, window, |this: &mut FocusForwarder, window, cx| {
9867 let b = this.b.clone();
9868 window.focus(&b, cx);
9869 })
9870 .detach();
9871 cx.on_focus(&b, window, move |_, _, _| {
9872 b_focus_count.set(b_focus_count.get() + 1);
9873 })
9874 .detach();
9875 FocusForwarder { a, b }
9876 }
9877 });
9878
9879 window
9880 .update(cx, |_, window, _| window.activate_window())
9881 .unwrap();
9882 cx.executor().run_until_parked();
9883
9884 window
9885 .update(cx, |this, window, cx| {
9886 let a = this.a.clone();
9887 window.focus(&a, cx);
9888 })
9889 .unwrap();
9890 cx.executor().run_until_parked();
9891
9892 window
9893 .update(cx, |this, window, _| {
9894 assert!(this.b.is_focused(window));
9895 })
9896 .unwrap();
9897 assert_eq!(b_focus_count.get(), 1);
9898 }
9899
9900 #[gpui::test]
9901 fn claimed_touch_drag_receives_movement_and_release(cx: &mut TestAppContext) {
9902 let events = Rc::new(RefCell::new(Vec::new()));
9903 let window = cx.add_window({
9904 let events = events.clone();
9905 move |_, _| TouchDragListener { events }
9906 });
9907 let touch = TouchId(1);
9908
9909 dispatch_touch(window, cx, touch, TouchPhase::Started, 10.);
9910 dispatch_touch(window, cx, touch, TouchPhase::Moved, 30.);
9911 dispatch_touch(window, cx, touch, TouchPhase::Ended, 40.);
9912
9913 assert_eq!(
9914 events.borrow().as_slice(),
9915 [
9916 (TouchPhase::Started, px(10.)),
9917 (TouchPhase::Moved, px(30.)),
9918 (TouchPhase::Ended, px(40.)),
9919 ]
9920 );
9921 }
9922
9923 #[gpui::test]
9924 fn back_gesture_is_claimed_by_the_innermost_listener(cx: &mut TestAppContext) {
9925 let claims = Rc::new(RefCell::new(Vec::new()));
9926 let window = cx.add_window({
9927 let claims = claims.clone();
9928 move |_, _| BackListeners { claims }
9929 });
9930 let dispatch_back = |cx: &mut TestAppContext, x: f32| {
9931 window
9932 .update(cx, |_, window, cx| {
9933 window
9934 .dispatch_event(
9935 BackGestureEvent {
9936 phase: BackGesturePhase::Started,
9937 start_position: point(px(x), px(10.)),
9938 progress: 0.,
9939 }
9940 .to_platform_input(),
9941 cx,
9942 )
9943 .default_prevented
9944 })
9945 .unwrap()
9946 };
9947 assert!(dispatch_back(cx, 10.));
9948 assert!(dispatch_back(cx, 80.));
9949 assert!(!dispatch_back(cx, 150.));
9950 assert_eq!(claims.borrow().as_slice(), ["inner", "outer"]);
9951 }
9952
9953 struct BackListeners {
9954 claims: Rc<RefCell<Vec<&'static str>>>,
9955 }
9956
9957 impl Render for BackListeners {
9958 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
9959 let claim = |name: &'static str| {
9960 let claims = self.claims.clone();
9961 move |_: &BackGestureEvent, _: &mut Window, _: &mut crate::App| {
9962 claims.borrow_mut().push(name)
9963 }
9964 };
9965 div().size_full().child(
9966 div()
9967 .w(px(100.))
9968 .h_full()
9969 .on_back_gesture(claim("outer"))
9970 .child(div().w(px(50.)).h_full().on_back_gesture(claim("inner"))),
9971 )
9972 }
9973 }
9974
9975 struct TouchDragListener {
9976 events: Rc<RefCell<Vec<(TouchPhase, Pixels)>>>,
9977 }
9978
9979 impl Render for TouchDragListener {
9980 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
9981 let events = self.events.clone();
9982 canvas(
9983 |_, _, _| {},
9984 move |_, _, window, _| {
9985 window.on_mouse_event(move |event: &TouchDragEvent, phase, window, _cx| {
9986 if phase != DispatchPhase::Bubble {
9987 return;
9988 }
9989 events.borrow_mut().push((event.phase, event.position.x));
9990 if event.phase == TouchPhase::Started {
9991 window.prevent_default();
9992 }
9993 });
9994 },
9995 )
9996 }
9997 }
9998
9999 #[gpui::test]
10000 fn long_press_is_claimed_only_when_started_prevents_default(cx: &mut TestAppContext) {
10001 for response in [
10002 LongPressResponse::PreventDefault,
10003 LongPressResponse::StopPropagation,
10004 LongPressResponse::None,
10005 ] {
10006 let phases = Rc::new(RefCell::new(Vec::new()));
10007 let window = cx.add_window({
10008 let phases = phases.clone();
10009 move |_, _| LongPressListener { phases, response }
10010 });
10011 dispatch_touch(window, cx, TouchId(1), TouchPhase::Started, 0.);
10012 cx.executor().advance_clock(Duration::from_millis(501));
10013 cx.executor().run_until_parked();
10014 window
10015 .update(cx, |_, window, _| {
10016 assert_eq!(
10017 window.long_press_capture.is_some(),
10018 response == LongPressResponse::PreventDefault
10019 );
10020 })
10021 .unwrap();
10022 dispatch_touch(window, cx, TouchId(1), TouchPhase::Moved, 2.);
10023 dispatch_touch(window, cx, TouchId(1), TouchPhase::Ended, 2.);
10024 window
10025 .update(cx, |_, window, _| {
10026 assert!(window.long_press_capture.is_none());
10027 })
10028 .unwrap();
10029
10030 let phases = phases.borrow();
10031 if response == LongPressResponse::PreventDefault {
10032 assert_eq!(
10033 phases.as_slice(),
10034 [TouchPhase::Started, TouchPhase::Moved, TouchPhase::Ended]
10035 );
10036 } else {
10037 assert_eq!(phases.as_slice(), [TouchPhase::Started]);
10038 }
10039 }
10040 }
10041
10042 #[gpui::test]
10043 fn stale_default_prevention_does_not_claim_long_press(cx: &mut TestAppContext) {
10044 let phases = Rc::new(RefCell::new(Vec::new()));
10045 let window = cx.add_window({
10046 let phases = phases.clone();
10047 move |_, _| LongPressListener {
10048 phases,
10049 response: LongPressResponse::None,
10050 }
10051 });
10052 window
10053 .update(cx, |_, window, _| {
10054 window.prevent_default();
10055 })
10056 .unwrap();
10057
10058 dispatch_touch(window, cx, TouchId(1), TouchPhase::Started, 0.);
10059 cx.executor().advance_clock(Duration::from_millis(501));
10060 cx.executor().run_until_parked();
10061 dispatch_touch(window, cx, TouchId(1), TouchPhase::Moved, 2.);
10062
10063 assert_eq!(phases.borrow().as_slice(), [TouchPhase::Started]);
10064 }
10065
10066 #[gpui::test]
10067 fn resolved_touch_cancels_scheduled_long_press(cx: &mut TestAppContext) {
10068 for (phase, position) in [
10069 (TouchPhase::Ended, 0.),
10070 (TouchPhase::Cancelled, 0.),
10071 (TouchPhase::Moved, 20.),
10072 ] {
10073 let phases = Rc::new(RefCell::new(Vec::new()));
10074 let window = cx.add_window({
10075 let phases = phases.clone();
10076 move |_, _| LongPressListener {
10077 phases,
10078 response: LongPressResponse::PreventDefault,
10079 }
10080 });
10081 dispatch_touch(window, cx, TouchId(1), TouchPhase::Started, 0.);
10082 dispatch_touch(window, cx, TouchId(1), phase, position);
10083 cx.executor().advance_clock(Duration::from_millis(501));
10084 cx.executor().run_until_parked();
10085
10086 assert!(phases.borrow().is_empty(), "{phase:?} allowed long press");
10087 }
10088 }
10089
10090 #[gpui::test]
10091 fn stale_long_press_timer_cannot_affect_replacement_touch(cx: &mut TestAppContext) {
10092 let phases = Rc::new(RefCell::new(Vec::new()));
10093 let window = cx.add_window({
10094 let phases = phases.clone();
10095 move |_, _| LongPressListener {
10096 phases,
10097 response: LongPressResponse::PreventDefault,
10098 }
10099 });
10100 let first_touch = TouchId(1);
10101 dispatch_touch(window, cx, first_touch, TouchPhase::Started, 0.);
10102 cx.executor().advance_clock(Duration::from_millis(250));
10103 dispatch_touch(window, cx, first_touch, TouchPhase::Cancelled, 0.);
10104 dispatch_touch(window, cx, TouchId(2), TouchPhase::Started, 10.);
10105
10106 cx.executor().advance_clock(Duration::from_millis(251));
10107 cx.executor().run_until_parked();
10108 assert!(phases.borrow().is_empty());
10109
10110 cx.executor().advance_clock(Duration::from_millis(250));
10111 cx.executor().run_until_parked();
10112 assert_eq!(phases.borrow().as_slice(), [TouchPhase::Started]);
10113 }
10114
10115 #[gpui::test]
10116 fn test_underline_exclusions_preserve_device_geometry(cx: &mut TestAppContext) {
10117 test_underline_paint_at_scales(cx, |window| {
10118 let (numerator, denominator) = match window.scale_factor() {
10119 1.0 => (1, 1),
10120 1.25 => (5, 4),
10121 1.5 => (3, 2),
10122 2.0 => (2, 1),
10123 3.0 => (3, 1),
10124 scale => panic!("unexpected scale {scale}"),
10125 };
10126 let snap = |quarters: i32| {
10127 let scaled = quarters * numerator;
10128 let divisor = 4 * denominator;
10129 let rounded =
10130 scaled.abs() / divisor + i32::from(2 * (scaled.abs() % divisor) > divisor);
10131 ScaledPixels((scaled.signum() * rounded) as f32)
10132 };
10133 let stroke = |quarters| {
10134 if quarters == 0 {
10135 ScaledPixels(0.)
10136 } else {
10137 snap(quarters).max(ScaledPixels(1.))
10138 }
10139 };
10140
10141 for origin in [-101, -3, -2, -1, 0, 1, 2, 3, 101] {
10142 for width in [0, 1, 3, 84] {
10143 for thickness in [0, 1, 4, 7] {
10144 for wavy in [false, true] {
10145 let style = UnderlineStyle {
10146 thickness: px(thickness as f32 / 4.),
10147 color: Some(hsla(0.25, 0.5, 0.75, 0.5)),
10148 wavy,
10149 };
10150 let bounds = Bounds::new(
10151 point(snap(origin), snap(-origin)),
10152 size(
10153 stroke(width),
10154 stroke(thickness) * if wavy { 3. } else { 1. },
10155 ),
10156 );
10157 let origin = point(px(origin as f32 / 4.), px(-origin as f32 / 4.));
10158 let width = px(width as f32 / 4.);
10159 assert_eq!(window.underline_bounds(origin, width, &style), bounds);
10160 let original = paint_test_underlines(window, |window| {
10161 window.paint_underline(origin, width, &style);
10162 })
10163 .to_vec();
10164 assert_eq!(original.len(), usize::from(!bounds.is_empty()));
10165 if let Some(underline) = original.first() {
10166 assert_eq!(underline.bounds, bounds);
10167 assert_eq!(underline.thickness, stroke(thickness));
10168 assert_eq!(underline.wavy, wavy.into());
10169 assert_eq!(underline.color, hsla(0.25, 0.5, 0.75, 0.5));
10170 }
10171
10172 let underlines = paint_test_underlines(window, |window| {
10173 window.paint_underline_with_exclusions(origin, width, &style, &[]);
10174 });
10175 assert_eq!(underlines.len(), original.len());
10176 for (actual, original) in underlines.iter().zip(&original) {
10177 assert_same_underline_geometry(actual, original);
10178 assert_eq!(actual.content_mask, original.content_mask);
10179 }
10180
10181 let left = bounds.left().0 as i32;
10182 let right = bounds.right().0 as i32;
10183 let exclusions = [
10184 (15, 25),
10185 (3, 7),
10186 (5, 10),
10187 (10, 12),
10188 (0, 0),
10189 (4, 2),
10190 (-50, -1),
10191 (70, 100),
10192 ]
10193 .map(|(start, end)| {
10194 ScaledPixels((left + start) as f32)
10195 ..ScaledPixels((left + end) as f32)
10196 });
10197 let underlines = paint_test_underlines(window, |window| {
10198 window.paint_underline_with_exclusions(
10199 origin,
10200 width,
10201 &style,
10202 &exclusions,
10203 );
10204 });
10205 let expected = (left..right)
10206 .filter(|column| {
10207 !bounds.is_empty()
10208 && !exclusions.iter().any(|span| {
10209 span.contains(&ScaledPixels(*column as f32))
10210 })
10211 })
10212 .collect::<Vec<_>>();
10213 assert_eq!(underline_device_columns(underlines), expected);
10214 for underline in underlines {
10215 assert_same_underline_geometry(underline, &original[0]);
10216 }
10217 }
10218 }
10219 }
10220 }
10221 });
10222 }
10223
10224 #[gpui::test]
10225 fn test_underline_exclusions_preserve_helvetica_warning_edge(cx: &mut TestAppContext) {
10226 test_underline_paint_at_scales(cx, |window| {
10227 if window.scale_factor() != 2.0 {
10228 return;
10229 }
10230 let style = UnderlineStyle {
10231 thickness: px(1.),
10232 color: None,
10233 wavy: true,
10234 };
10235 let origin = point(px(2.888_183_8), px(15.357_917));
10236 let width = px(23.848_145) - origin.x;
10237 let original = paint_test_underlines(window, |window| {
10238 window.paint_underline(origin, width, &style);
10239 })[0];
10240 assert_eq!(original.bounds.left(), ScaledPixels(6.));
10241 assert_eq!(original.bounds.right(), ScaledPixels(48.));
10242 let point_bounds = window.underline_bounds(origin, px(10.829_102), &style);
10243 assert_eq!(point_bounds.left(), ScaledPixels(6.));
10244 assert_eq!(point_bounds.right(), ScaledPixels(28.));
10245
10246 let underlines = paint_test_underlines(window, |window| {
10247 window.paint_underline_with_exclusions(
10248 origin,
10249 width,
10250 &style,
10251 &[point_bounds.left()..point_bounds.right()],
10252 );
10253 });
10254 assert_eq!(underlines.len(), 1);
10255 assert_same_underline_geometry(&underlines[0], &original);
10256 assert_eq!(
10257 underline_device_columns(underlines),
10258 (28..48).collect::<Vec<_>>()
10259 );
10260 });
10261 }
10262
10263 #[gpui::test]
10264 fn test_underline_exclusions_preserve_content_mask_and_opacity(cx: &mut TestAppContext) {
10265 test_underline_paint_at_scales(cx, |window| {
10266 let scale = window.scale_factor();
10267 let original_mask = window.content_mask();
10268 let original_opacity = window.element_opacity();
10269 let mask = ContentMask {
10270 bounds: Bounds::from_corners(point(px(4.), px(10.)), point(px(16.), px(12.))),
10271 };
10272 let style = UnderlineStyle {
10273 thickness: px(2.),
10274 color: Some(hsla(0.25, 0.5, 0.75, 0.5)),
10275 wavy: true,
10276 };
10277 let origin = point(px(-1.25), px(9.25));
10278 let width = px(24.);
10279 window.with_content_mask(Some(mask), |window| {
10280 window.with_element_opacity(Some(0.5), |window| {
10281 window.with_element_opacity(Some(0.5), |window| {
10282 let original = paint_test_underlines(window, |window| {
10283 window.paint_underline(origin, width, &style);
10284 })[0];
10285 assert_eq!(original.color, hsla(0.25, 0.5, 0.75, 0.125));
10286 let underlines = paint_test_underlines(window, |window| {
10287 window.paint_underline_with_exclusions(
10288 origin,
10289 width,
10290 &style,
10291 &[ScaledPixels(6. * scale)..ScaledPixels(8. * scale)],
10292 );
10293 });
10294 assert_eq!(underlines.len(), 2);
10295 let expected = [(4. * scale, 6. * scale), (8. * scale, 16. * scale)].map(
10296 |(left, right)| {
10297 Bounds::from_corners(
10298 point(ScaledPixels(left), ScaledPixels((10. * scale).floor())),
10299 point(ScaledPixels(right), ScaledPixels((12. * scale).ceil())),
10300 )
10301 },
10302 );
10303 assert_eq!(
10304 underlines
10305 .iter()
10306 .map(|underline| underline.content_mask.bounds)
10307 .collect::<Vec<_>>(),
10308 expected
10309 );
10310 for underline in underlines {
10311 assert_same_underline_geometry(underline, &original);
10312 }
10313 });
10314 });
10315 });
10316 assert_eq!(window.content_mask(), original_mask);
10317 assert_eq!(window.element_opacity(), original_opacity);
10318 });
10319 }
10320
10321 #[gpui::test]
10322 fn test_underline_exclusions_adversarial_permutations(cx: &mut TestAppContext) {
10323 test_underline_paint_at_scales(cx, |window| {
10324 let style = UnderlineStyle {
10325 thickness: px(1.),
10326 color: None,
10327 wavy: true,
10328 };
10329 let origin = point(px(-4.), px(-1.));
10330 let width = px(8.);
10331 let left = (-4. * window.scale_factor()) as i32;
10332 let right = (4. * window.scale_factor()) as i32;
10333 let endpoints = [-16, -8, -1, 0, 1, 8, 16];
10334 for first_start in endpoints {
10335 for first_end in endpoints {
10336 for second_start in endpoints {
10337 for second_end in endpoints {
10338 let exclusions = [first_start..first_end, second_start..second_end];
10339 let expected = (left..right)
10340 .filter(|column| {
10341 !exclusions.iter().any(|span| span.contains(column))
10342 })
10343 .collect::<Vec<_>>();
10344 let exclusions = exclusions.map(|span| {
10345 ScaledPixels(span.start as f32)..ScaledPixels(span.end as f32)
10346 });
10347 let underlines = paint_test_underlines(window, |window| {
10348 window.paint_underline_with_exclusions(
10349 origin,
10350 width,
10351 &style,
10352 &exclusions,
10353 );
10354 });
10355 assert_eq!(
10356 underline_device_columns(underlines),
10357 expected,
10358 "{exclusions:?}"
10359 );
10360 }
10361 }
10362 }
10363 }
10364 });
10365 }
10366
10367 #[derive(Clone, Copy, PartialEq)]
10368 enum LongPressResponse {
10369 PreventDefault,
10370 StopPropagation,
10371 None,
10372 }
10373
10374 struct LongPressListener {
10375 phases: Rc<RefCell<Vec<TouchPhase>>>,
10376 response: LongPressResponse,
10377 }
10378
10379 impl Render for LongPressListener {
10380 fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
10381 let entity = cx.entity();
10382 let phases = self.phases.clone();
10383 let response = self.response;
10384 canvas(
10385 |_, _, _| {},
10386 move |_, _, window, _| {
10387 window.on_mouse_event(move |event: &LongPressEvent, phase, window, cx| {
10388 if phase != DispatchPhase::Bubble {
10389 return;
10390 }
10391 phases.borrow_mut().push(event.phase);
10392 match response {
10393 LongPressResponse::PreventDefault => {
10394 window.capture_long_press(&entity);
10395 window.prevent_default();
10396 }
10397 LongPressResponse::StopPropagation => cx.stop_propagation(),
10398 LongPressResponse::None => {}
10399 }
10400 });
10401 },
10402 )
10403 }
10404 }
10405
10406 struct UnderlineTestView(Rc<dyn Fn(&mut Window)>);
10407
10408 #[gpui::test]
10409 fn preparation_window_geometry_survives_scaling_and_clipping(cx: &mut TestAppContext) {
10410 #[derive(Debug)]
10411 struct Probe;
10412 impl crate::CustomContent for Probe {
10413 fn prepare(
10414 &self,
10415 _: &dyn std::any::Any,
10416 _: crate::Size<crate::DevicePixels>,
10417 ) -> anyhow::Result<Box<dyn std::any::Any>> {
10418 panic!("request-aware preparation expected")
10419 }
10420
10421 fn prepare_with_request(
10422 &self,
10423 _: &dyn std::any::Any,
10424 request: &crate::PreparationRequest,
10425 ) -> anyhow::Result<Box<dyn std::any::Any>> {
10426 Ok(Box::new(*request))
10427 }
10428 }
10429 test_underline_paint_at_scales(cx, |window| {
10430 let bounds = Bounds::new(point(px(10.3), px(20.7)), size(px(100.3), px(50.7)));
10431 let content: std::sync::Arc<dyn crate::CustomContent> = std::sync::Arc::new(Probe);
10432 let mut path = crate::PathBuilder::fill();
10433 path.move_to(bounds.origin);
10434 path.line_to(bounds.top_right());
10435 path.line_to(bounds.bottom_right());
10436 path.line_to(bounds.bottom_left());
10437 path.close();
10438 let path = path.build().unwrap();
10439 let mut full_requests = Vec::new();
10440 for clipped in [false, true] {
10441 window.next_frame.scene.clear();
10442 if clipped {
10443 window.content_mask_stack.push(ContentMask {
10444 bounds: Bounds::new(point(px(30.), px(30.)), size(px(20.), px(15.))),
10445 });
10446 }
10447 window.paint_custom(
10448 crate::CustomGeometry::RoundedRect {
10449 bounds,
10450 corner_radii: crate::Corners::default(),
10451 },
10452 content.clone(),
10453 );
10454 window.paint_custom(
10455 crate::CustomGeometry::Border(crate::quad(
10456 bounds,
10457 crate::Corners::default(),
10458 crate::transparent_black(),
10459 crate::Edges::all(px(2.)),
10460 crate::transparent_black(),
10461 crate::BorderStyle::Solid,
10462 )),
10463 content.clone(),
10464 );
10465 window.paint_custom(crate::CustomGeometry::Path(path.clone()), content.clone());
10466 if clipped {
10467 window.content_mask_stack.pop();
10468 }
10469 let draws = &window.next_frame.scene.custom_draws;
10470 assert_eq!(draws.len(), 3);
10471 for (index, draw) in draws.iter().enumerate() {
10472 let raster = draw
10473 .bounds
10474 .size
10475 .map(|v| crate::DevicePixels(v.0.ceil() as i32));
10476 let default_request = crate::PreparationRequest::new(
10477 draw.bounds.size.map(|v| px(v.0)),
10478 raster,
10479 1.,
10480 )
10481 .unwrap();
10482 let output = draw
10483 .content
10484 .prepare_with_request(&(), &default_request)
10485 .unwrap();
10486 let request = *output.downcast::<crate::PreparationRequest>().unwrap();
10487 let logical = if index == 2 {
10488 path.bounds.size
10489 } else {
10490 bounds.size
10491 };
10492 assert_eq!(request.logical_size(), logical);
10493 assert_eq!(request.scale_factor(), window.scale_factor());
10494 assert_eq!(request.raster_size(), raster);
10495 if clipped {
10496 assert_eq!(request, full_requests[index]);
10497 } else {
10498 full_requests.push(request);
10499 }
10500 }
10501 }
10502 });
10503 }
10504
10505 impl Render for UnderlineTestView {
10506 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
10507 let paint = self.0.clone();
10508 canvas(
10509 |_, _, _| {},
10510 move |_, _, window, _| {
10511 window.content_mask_stack.push(ContentMask {
10512 bounds: Bounds::from_corners(
10513 point(px(-1000.), px(-1000.)),
10514 point(px(1000.), px(1000.)),
10515 ),
10516 });
10517 paint(window);
10518 window.content_mask_stack.pop();
10519 },
10520 )
10521 .size_full()
10522 }
10523 }
10524
10525 fn test_underline_paint_at_scales(
10526 cx: &mut TestAppContext,
10527 paint: impl Fn(&mut Window) + 'static,
10528 ) {
10529 let window = cx.add_window(move |_, _| UnderlineTestView(Rc::new(paint)));
10530 for scale in [1., 1.25, 1.5, 2., 3.] {
10531 cx.simulate_window_scale_factor_change(window.into(), scale);
10532 cx.update_window(window.into(), |_, window, cx| window.draw(cx).clear(cx))
10533 .unwrap();
10534 }
10535 }
10536
10537 fn paint_test_underlines(window: &mut Window, paint: impl FnOnce(&mut Window)) -> &[Underline] {
10538 window.next_frame.scene.clear();
10539 paint(window);
10540 &window.next_frame.scene.underlines
10541 }
10542
10543 fn assert_same_underline_geometry(actual: &Underline, expected: &Underline) {
10544 assert_eq!(actual.bounds, expected.bounds);
10545 assert_eq!(actual.thickness, expected.thickness);
10546 assert_eq!(actual.wavy, expected.wavy);
10547 assert_eq!(actual.color, expected.color);
10548 assert_eq!(actual.pad, expected.pad);
10549 }
10550
10551 fn underline_device_columns(underlines: &[Underline]) -> Vec<i32> {
10552 underlines
10553 .iter()
10554 .flat_map(|underline| {
10555 let bounds = underline.bounds.intersect(&underline.content_mask.bounds);
10556 bounds.left().0 as i32..bounds.right().0 as i32
10557 })
10558 .collect()
10559 }
10560
10561 fn dispatch_touch<T: 'static>(
10562 window: crate::WindowHandle<T>,
10563 cx: &mut TestAppContext,
10564 id: TouchId,
10565 phase: TouchPhase,
10566 x: f32,
10567 ) {
10568 window
10569 .update(cx, |_, window, cx| {
10570 window.dispatch_event(
10571 TouchEvent {
10572 id,
10573 phase,
10574 position: point(px(x), px(0.)),
10575 predicted_position: None,
10576 force: None,
10577 }
10578 .to_platform_input(),
10579 cx,
10580 );
10581 })
10582 .unwrap();
10583 }
10584}
10585
10586#[cfg(all(test, debug_assertions))]
10587mod inspector_tests {
10588 use super::*;
10589 use crate::{
10590 DivInspectorState, InspectorElementId, MouseDownEvent, StyleRefinement, TestAppContext, div,
10591 };
10592
10593 #[gpui::test]
10594 fn inspector_only_tracks_its_open_window(cx: &mut TestAppContext) {
10595 let windows = [
10596 cx.add_window(|_, cx| InspectorTestRoot {
10597 child: cx.new(|_| InspectorTestView::default()),
10598 }),
10599 cx.add_window(|_, cx| InspectorTestRoot {
10600 child: cx.new(|_| InspectorTestView::default()),
10601 }),
10602 ];
10603 for window in windows {
10604 assert_closed_inspector(window.into(), cx);
10605 }
10606 for _ in 0..2 {
10607 cx.update_window(windows[0].into(), |root, window, cx| {
10608 let root = root.downcast::<InspectorTestRoot>().expect("test root");
10609 let child_widths = root.read(cx).child.read(cx).child_widths.clone();
10610 window.toggle_inspector(cx);
10611 window.draw(cx).clear(cx);
10612 assert_eq!(child_widths.borrow().as_slice(), &[px(10.); 3]);
10613 let path = window
10614 .rendered_frame
10615 .next_inspector_instance_ids
10616 .iter()
10617 .find_map(|(path, count)| (*count == 3).then(|| path.clone()))
10618 .expect("anonymous siblings share an inspector path");
10619 let selected_id = InspectorElementId {
10620 path,
10621 instance_id: 1,
10622 };
10623 let position = window
10624 .rendered_frame
10625 .hitboxes
10626 .iter()
10627 .find(|hitbox| {
10628 window.rendered_frame.inspector_hitboxes.get(&hitbox.id)
10629 == Some(&selected_id)
10630 })
10631 .expect("middle sibling is pickable")
10632 .bounds
10633 .center();
10634 window.simulate_mouse_move(position, cx);
10635 window.dispatch_event(
10636 PlatformInput::MouseDown(MouseDownEvent {
10637 position,
10638 button: MouseButton::Left,
10639 modifiers: Modifiers::default(),
10640 click_count: 1,
10641 first_mouse: false,
10642 }),
10643 cx,
10644 );
10645 window.dispatch_event(
10646 PlatformInput::MouseUp(MouseUpEvent {
10647 position,
10648 button: MouseButton::Left,
10649 modifiers: Modifiers::default(),
10650 click_count: 1,
10651 }),
10652 cx,
10653 );
10654 assert!(!window.is_inspector_picking(cx));
10655 assert_eq!(
10656 window
10657 .inspector
10658 .as_ref()
10659 .expect("open inspector")
10660 .read(cx)
10661 .active_element_id(),
10662 Some(&selected_id)
10663 );
10664 window.draw(cx).clear(cx);
10665 window.with_inspector_state::<DivInspectorState, _>(
10666 Some(&selected_id),
10667 cx,
10668 |state, _| {
10669 let state = state.as_mut().expect("selected div has style state");
10670 assert_eq!(
10671 state.base_style.size.width,
10672 Some(crate::Length::from(px(10.)))
10673 );
10674 state.base_style.size.width = Some(crate::Length::from(px(25.)));
10675 },
10676 );
10677 window.refresh();
10678 window.draw(cx).clear(cx);
10679 assert_eq!(
10680 child_widths.borrow().as_slice(),
10681 &[px(10.), px(25.), px(10.)]
10682 );
10683 })
10684 .expect("pick and edit a cached child");
10685 assert_closed_inspector(windows[1].into(), cx);
10686 windows[0]
10687 .update(cx, |_, window, cx| window.toggle_inspector(cx))
10688 .expect("close inspector");
10689 assert_closed_inspector(windows[0].into(), cx);
10690 }
10691 }
10692
10693 struct InspectorTestRoot {
10694 child: Entity<InspectorTestView>,
10695 }
10696
10697 impl Render for InspectorTestRoot {
10698 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
10699 self.child
10700 .clone()
10701 .cached(StyleRefinement::default().size(px(100.)))
10702 }
10703 }
10704
10705 #[derive(Default)]
10706 struct InspectorTestView {
10707 child_widths: Rc<RefCell<Vec<Pixels>>>,
10708 }
10709
10710 impl Render for InspectorTestView {
10711 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
10712 let child_widths = self.child_widths.clone();
10713 div()
10714 .on_children_prepainted(move |bounds, _, _| {
10715 *child_widths.borrow_mut() =
10716 bounds.iter().map(|bounds| bounds.size.width).collect();
10717 })
10718 .with_dynamic_prepaint_order(|_, _| SmallVec::from_iter([2, 0, 1]))
10719 .id("inspector-root")
10720 .flex()
10721 .children((0..3).map(|_| div().size(px(10.)).flex_shrink_0()))
10722 }
10723 }
10724
10725 fn assert_closed_inspector(window: AnyWindowHandle, cx: &mut TestAppContext) {
10726 cx.update_window(window, |root, window, cx| {
10727 window.draw(cx).clear(cx);
10728 window.draw(cx).clear(cx);
10729 let root = root.downcast::<InspectorTestRoot>().expect("test root");
10730 assert_eq!(
10731 root.read(cx)
10732 .child
10733 .read(cx)
10734 .child_widths
10735 .borrow()
10736 .as_slice(),
10737 &[px(10.); 3]
10738 );
10739 for frame in [&window.rendered_frame, &window.next_frame] {
10740 assert_eq!(frame.next_inspector_instance_ids.capacity(), 0);
10741 assert_eq!(frame.inspector_hitboxes.capacity(), 0);
10742 }
10743 })
10744 .expect("closed inspector has no bookkeeping and no style overrides");
10745 }
10746}