1#[cfg(feature = "profiler")]
2use crate::DebugFrameOverlayMode;
3#[cfg(any(feature = "inspector", debug_assertions))]
4use crate::Inspector;
5#[cfg(feature = "profiler")]
6use crate::profiler;
7use crate::{
8 Action, AnyDrag, AnyElement, AnyImageCache, AnyTooltip, AnyView, App, AppContext, Arena, Asset,
9 AsyncWindowContext, AtlasTile, AvailableSpace, Background, BorderStyle, Bounds, BoxShadow,
10 Capslock, ContentMask, Context, Corners, CursorHideMode, CursorStyle, CustomRenderPrimitive,
11 DEFAULT_WINDOW_SIZE,
12 Decorations, DevicePixels, DispatchActionListener, DispatchEventResult, DispatchNodeId,
13 DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, FontId,
14 Global, GlobalElementId, GlyphId, GpuSpecs, Hsla, ImportedTextureHandle,
15 InputHandler, InputPreference, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeyEvent, Keystroke,
16 KeystrokeEvent, LayoutId, LineLayoutIndex, MeasureContext, MeasureHandles, Modifiers,
17 ModifiersChangedEvent, MonochromeSprite, MouseButton, MouseEvent, MouseMoveEvent, MouseUpEvent,
18 Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler,
19 PlatformWindow, Point, PolychromeSprite, Priority, PromptButton, PromptLevel, Quad, Render,
20 RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, Replay, ResizeEdge,
21 SMOOTH_SVG_SCALE_FACTOR, SUBPIXEL_VARIANTS_X, SUBPIXEL_VARIANTS_Y, ScaledPixels, Scene, Shadow,
22 SharedString, Size, StrikethroughStyle, Style, SubpixelSprite, SubscriberSet, Subscription,
23 SystemWindowTab, SystemWindowTabController, TabStopMap, Task,
24 TextInputConfiguration, TextInputStateChange, TextRenderingMode, TextStyle, TextStyleRefinement,
25 ThermalState, TransformationMatrix, Underline, UnderlineStyle, WindowAppearance,
26 WindowBackgroundAppearance, WindowBounds, WindowControlArea, WindowControls, WindowDecorations,
27 WindowId, WindowMetrics, WindowOptions, WindowParams, WindowTextSystem, WindowVisibility,
28 new_platform_input_handler, point, prelude::*, px, rems, size, transparent_black,
29};
30
31use crate::TouchEvent;
32
33#[cfg(any(test, feature = "test-support"))]
35use crate::PaintSurface;
36use crate::gestures::{GestureTuning, RecognizedTouchGesture, TouchGestureRecognizer};
37use anyhow::{Context as _, Result, anyhow};
38use arc_swap::ArcSwap;
39use collections::{FxHashMap, FxHashSet};
40use derive_more::{Deref, DerefMut};
41use futures::channel::oneshot;
42use gpui_engine::FrameSession;
43use gpui_util::post_inc;
44use gpui_util::{ResultExt, measure};
45use itertools::FoldWhile::{Continue, Done};
46use itertools::Itertools;
47use parking_lot::RwLock;
48use raw_window_handle::{HandleError, HasDisplayHandle, HasWindowHandle};
49use refineable::Refineable;
50use scheduler::Instant;
51use slotmap::SlotMap;
52use smallvec::SmallVec;
53use std::{
54 any::{Any, TypeId},
55 borrow::Cow,
56 cell::{Cell, RefCell},
57 cmp,
58 fmt::{Debug, Display},
59 hash::{Hash, Hasher},
60 marker::PhantomData,
61 mem,
62 ops::{DerefMut, Range},
63 rc::Rc,
64 sync::{
65 Arc, Weak,
66 atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst},
67 },
68 thread::AccessError,
69 time::Duration,
70};
71use uuid::Uuid;
72
73pub(crate) mod a11y;
74mod frame_pipeline;
75mod prompts;
76
77pub use a11y::A11ySubtreeBuilder;
78pub use frame_pipeline::{FramePipeline, StandardImmediatePipeline};
79
80use self::a11y::A11y;
81#[cfg(not(target_family = "wasm"))]
82use self::a11y::ROOT_NODE_ID;
83use crate::engine_layout::to_engine_layout_style;
84use crate::util::{
85 atomic_incr_if_not_zero, ceil_to_device_pixel, floor_to_device_pixel, round_half_toward_zero,
86 round_half_toward_zero_f64, round_stroke_to_device_pixel, round_to_device_pixel,
87};
88pub use prompts::*;
89
90pub const DEFAULT_ADDITIONAL_WINDOW_SIZE: Size<Pixels> = Size {
93 width: Pixels(900.),
94 height: Pixels(750.),
95};
96
97struct WindowMeasureContext<'a> {
104 core: &'a mut WindowHostCore,
105 frame_state: &'a mut WindowFrameState,
106 cx: &'a mut App,
107}
108
109impl MeasureContext for WindowMeasureContext<'_> {
110 fn handles(&mut self) -> MeasureHandles<'_> {
111 MeasureHandles {
112 window: (self.core, self.frame_state),
113 app: self.cx,
114 }
115 }
116}
117
118#[derive(Default, Copy, Clone, Debug, Eq, PartialEq)]
120pub enum DispatchPhase {
121 #[default]
126 Bubble,
127 Capture,
133}
134
135impl DispatchPhase {
136 #[inline]
138 pub fn bubble(self) -> bool {
139 self == DispatchPhase::Bubble
140 }
141
142 #[inline]
144 pub fn capture(self) -> bool {
145 self == DispatchPhase::Capture
146 }
147}
148
149struct WindowInvalidatorInner {
150 #[cfg(feature = "profiler")]
151 pub window_id: WindowId,
152 pub dirty: bool,
153 pub draw_phase: DrawPhase,
154 pub dirty_views: FxHashSet<EntityId>,
155 pub update_count: usize,
156 #[cfg(feature = "profiler")]
157 pub frame_dirty: FrameDirtyAccumulator,
158 pub platform_waker: Option<Rc<dyn Fn()>>,
159}
160
161#[cfg(feature = "profiler")]
167#[derive(Default)]
168struct FrameDirtyAccumulator {
169 dirty_at: Option<Instant>,
170 invalidations: u64,
171}
172
173#[derive(Clone)]
174pub(crate) struct WindowInvalidator {
175 inner: Rc<RefCell<WindowInvalidatorInner>>,
176}
177
178impl WindowInvalidator {
179 pub fn new(#[allow(unused_variables)] window_id: WindowId) -> Self {
180 WindowInvalidator {
181 inner: Rc::new(RefCell::new(WindowInvalidatorInner {
182 #[cfg(feature = "profiler")]
183 window_id,
184 dirty: true,
185 draw_phase: DrawPhase::None,
186 dirty_views: FxHashSet::default(),
187 update_count: 0,
188 #[cfg(feature = "profiler")]
189 frame_dirty: FrameDirtyAccumulator::default(),
190 platform_waker: None,
191 })),
192 }
193 }
194
195 pub fn invalidate_view(&self, entity: EntityId, cx: &mut App) -> bool {
196 let mut inner = self.inner.borrow_mut();
197 inner.update_count += 1;
198 inner.dirty_views.insert(entity);
199 if inner.draw_phase == DrawPhase::None {
200 #[cfg(feature = "profiler")]
201 let dirty_at = Self::record_frame_dirty(&mut inner);
202 let became_dirty = !inner.dirty;
203 inner.dirty = true;
204 let waker = became_dirty.then(|| inner.platform_waker.clone()).flatten();
205 #[cfg(feature = "profiler")]
206 let window_id = inner.window_id;
207 drop(inner);
208 #[cfg(feature = "profiler")]
209 if became_dirty {
210 profiler::journal::record_frame_pending(window_id, dirty_at);
211 }
212 cx.push_effect(Effect::Notify { emitter: entity });
213 if let Some(waker) = waker {
214 waker();
215 }
216 true
217 } else {
218 false
219 }
220 }
221
222 pub fn is_dirty(&self) -> bool {
223 self.inner.borrow().dirty
224 }
225
226 pub fn set_dirty(&self, dirty: bool) {
227 let mut inner = self.inner.borrow_mut();
228 let became_dirty = dirty && !inner.dirty;
229 inner.dirty = dirty;
230 if dirty {
231 inner.update_count += 1;
232 }
233 #[cfg(feature = "profiler")]
234 let dirty_at = dirty.then(|| Self::record_frame_dirty(&mut inner));
235 let waker = became_dirty.then(|| inner.platform_waker.clone()).flatten();
236 #[cfg(feature = "profiler")]
237 let window_id = inner.window_id;
238 drop(inner);
239 #[cfg(feature = "profiler")]
240 if became_dirty && let Some(dirty_at) = dirty_at {
241 profiler::journal::record_frame_pending(window_id, dirty_at);
242 }
243 if let Some(waker) = waker {
244 waker();
245 }
246 }
247
248 pub fn set_platform_waker(&self, waker: Option<Rc<dyn Fn()>>) {
249 let mut inner = self.inner.borrow_mut();
250 inner.platform_waker = waker;
251 let waker = inner.dirty.then(|| inner.platform_waker.clone()).flatten();
252 drop(inner);
253 if let Some(waker) = waker {
254 waker();
255 }
256 }
257
258 pub fn wake_platform(&self) {
262 let waker = self.inner.borrow().platform_waker.clone();
263 if let Some(waker) = waker {
264 waker();
265 }
266 }
267
268 pub fn set_phase(&self, phase: DrawPhase) {
269 self.inner.borrow_mut().draw_phase = phase
270 }
271
272 pub fn update_count(&self) -> usize {
273 self.inner.borrow().update_count
274 }
275
276 #[cfg(feature = "profiler")]
277 fn record_frame_dirty(inner: &mut WindowInvalidatorInner) -> Instant {
278 let dirty_at = *inner.frame_dirty.dirty_at.get_or_insert_with(Instant::now);
279 inner.frame_dirty.invalidations += 1;
280 dirty_at
281 }
282
283 #[cfg(feature = "profiler")]
284 fn take_frame_dirty(&self) -> FrameDirtyAccumulator {
285 mem::take(&mut self.inner.borrow_mut().frame_dirty)
286 }
287
288 pub fn take_views(&self) -> FxHashSet<EntityId> {
289 mem::take(&mut self.inner.borrow_mut().dirty_views)
290 }
291
292 pub fn replace_views(&self, views: FxHashSet<EntityId>) {
293 self.inner.borrow_mut().dirty_views = views;
294 }
295
296 pub fn not_drawing(&self) -> bool {
297 self.inner.borrow().draw_phase == DrawPhase::None
298 }
299
300 #[track_caller]
301 pub fn debug_assert_paint(&self) {
302 debug_assert!(
303 matches!(self.inner.borrow().draw_phase, DrawPhase::Paint),
304 "this method can only be called during paint"
305 );
306 }
307
308 #[track_caller]
309 pub fn debug_assert_prepaint(&self) {
310 debug_assert!(
311 matches!(self.inner.borrow().draw_phase, DrawPhase::Prepaint),
312 "this method can only be called during request_layout, or prepaint"
313 );
314 }
315
316 #[track_caller]
317 pub fn debug_assert_paint_or_prepaint(&self) {
318 debug_assert!(
319 matches!(
320 self.inner.borrow().draw_phase,
321 DrawPhase::Paint | DrawPhase::Prepaint
322 ),
323 "this method can only be called during request_layout, prepaint, or paint"
324 );
325 }
326}
327
328type AnyObserver = Box<dyn FnMut(&mut Window, &mut App) -> bool + 'static>;
329
330pub(crate) type AnyWindowFocusListener =
331 Box<dyn FnMut(&WindowFocusEvent, &mut Window, &mut App) -> bool + 'static>;
332
333pub(crate) struct WindowFocusEvent {
334 pub(crate) previous_focus_path: SmallVec<[FocusId; 8]>,
335 pub(crate) current_focus_path: SmallVec<[FocusId; 8]>,
336}
337
338impl WindowFocusEvent {
339 pub fn is_focus_in(&self, focus_id: FocusId) -> bool {
340 !self.previous_focus_path.contains(&focus_id) && self.current_focus_path.contains(&focus_id)
341 }
342
343 pub fn is_focus_out(&self, focus_id: FocusId) -> bool {
344 self.previous_focus_path.contains(&focus_id) && !self.current_focus_path.contains(&focus_id)
345 }
346}
347
348pub struct FocusOutEvent {
350 pub blurred: WeakFocusHandle,
352}
353
354slotmap::new_key_type! {
355 pub struct FocusId;
357}
358
359thread_local! {
360 pub(crate) static ELEMENT_ARENA: RefCell<Arena> = RefCell::new(Arena::new(1024 * 1024));
363
364 static CURRENT_ELEMENT_ARENA: Cell<Option<*const RefCell<Arena>>> = const { Cell::new(None) };
368}
369
370fn draw_in_progress() -> bool {
381 CURRENT_ELEMENT_ARENA.with(|current| current.get().is_some())
382}
383
384#[inline(always)]
387pub(crate) fn with_element_arena<R>(callback: impl FnOnce(&mut Arena) -> R) -> R {
388 let mut callback = Some(callback);
389 let mut result = None;
390 let access = with_element_arena_erased(&mut |arena| {
391 result = Some(callback.take().expect("arena callback runs once")(arena));
392 });
393 drop(callback);
394 access.expect("cannot access a Thread Local Storage value during or after destruction");
395 result.expect("arena callback produces a result")
396}
397
398pub(crate) struct ElementArenaScope {
413 entered: *const RefCell<Arena>,
416 previous: Option<*const RefCell<Arena>>,
417 exited: bool,
418}
419
420impl ElementArenaScope {
421 pub(crate) fn enter(arena: &RefCell<Arena>) -> Self {
423 arena.borrow_mut().begin_scope();
424 let previous = CURRENT_ELEMENT_ARENA.with(|current| {
425 let prev = current.get();
426 current.set(Some(arena as *const RefCell<Arena>));
427 prev
428 });
429 Self {
430 entered: arena as *const RefCell<Arena>,
431 previous,
432 exited: false,
433 }
434 }
435
436 pub(crate) fn exit(mut self, arena: &RefCell<Arena>) -> ArenaClearNeeded {
445 assert!(
446 std::ptr::eq(self.entered, arena),
447 "ElementArenaScope::exit called with a different arena than was entered"
448 );
449 self.exited = true;
450 ArenaClearNeeded::new(arena)
455 }
456}
457
458impl Drop for ElementArenaScope {
459 fn drop(&mut self) {
460 CURRENT_ELEMENT_ARENA.with(|current| {
467 current.set(self.previous);
468 });
469 unsafe { &*self.entered }.borrow_mut().end_scope();
473 if !self.exited && !std::thread::panicking() {
474 debug_assert!(false, "ElementArenaScope dropped without calling exit()");
475 log::error!(
476 "ElementArenaScope dropped without calling exit(); \
477 the arena clear for this draw was never requested"
478 );
479 }
480 }
481}
482
483#[must_use]
485pub struct ArenaClearNeeded {
486 arena: *const RefCell<Arena>,
489}
490
491impl ArenaClearNeeded {
492 fn new(arena: &RefCell<Arena>) -> Self {
495 Self {
496 arena: arena as *const RefCell<Arena>,
497 }
498 }
499
500 pub fn clear(self, cx: &mut App) {
509 assert!(
510 std::ptr::eq(self.arena, &cx.element_arena),
511 "ArenaClearNeeded::clear called with a different App than the draw ran against"
512 );
513 cx.element_arena.borrow_mut().clear();
514 }
515}
516
517pub(crate) type FocusMap = RwLock<SlotMap<FocusId, FocusRef>>;
518pub(crate) struct FocusRef {
519 pub(crate) ref_count: AtomicUsize,
520 pub(crate) tab_index: isize,
521 pub(crate) tab_stop: bool,
522}
523
524impl FocusId {
525 pub fn is_focused(&self, window: &Window) -> bool {
527 window.core.focus == Some(*self)
528 }
529
530 pub fn contains_focused(&self, window: &Window, cx: &App) -> bool {
533 window
534 .focused(cx)
535 .is_some_and(|focused| self.contains(focused.id, window))
536 }
537
538 pub fn within_focused(&self, window: &Window, cx: &App) -> bool {
541 let focused = window.focused(cx);
542 focused.is_some_and(|focused| focused.id.contains(*self, window))
543 }
544
545 pub(crate) fn contains(&self, other: Self, window: &Window) -> bool {
547 window
548 .frame_state
549 .rendered_frame
550 .dispatch_tree
551 .focus_contains(*self, other)
552 }
553}
554
555pub struct FocusHandle {
557 pub(crate) id: FocusId,
558 handles: Arc<FocusMap>,
559 pub tab_index: isize,
561 pub tab_stop: bool,
563}
564
565impl std::fmt::Debug for FocusHandle {
566 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
567 f.write_fmt(format_args!("FocusHandle({:?})", self.id))
568 }
569}
570
571impl FocusHandle {
572 pub(crate) fn new(handles: &Arc<FocusMap>) -> Self {
573 let id = handles.write().insert(FocusRef {
574 ref_count: AtomicUsize::new(1),
575 tab_index: 0,
576 tab_stop: false,
577 });
578
579 Self {
580 id,
581 tab_index: 0,
582 tab_stop: false,
583 handles: handles.clone(),
584 }
585 }
586
587 pub(crate) fn for_id(id: FocusId, handles: &Arc<FocusMap>) -> Option<Self> {
588 let lock = handles.read();
589 let focus = lock.get(id)?;
590 if atomic_incr_if_not_zero(&focus.ref_count) == 0 {
591 return None;
592 }
593 Some(Self {
594 id,
595 tab_index: focus.tab_index,
596 tab_stop: focus.tab_stop,
597 handles: handles.clone(),
598 })
599 }
600
601 pub fn tab_index(mut self, index: isize) -> Self {
603 self.tab_index = index;
604 if let Some(focus) = self.handles.write().get_mut(self.id) {
605 focus.tab_index = index;
606 }
607 self
608 }
609
610 pub fn tab_stop(mut self, tab_stop: bool) -> Self {
614 self.tab_stop = tab_stop;
615 if let Some(focus) = self.handles.write().get_mut(self.id) {
616 focus.tab_stop = tab_stop;
617 }
618 self
619 }
620
621 pub fn downgrade(&self) -> WeakFocusHandle {
623 WeakFocusHandle {
624 id: self.id,
625 handles: Arc::downgrade(&self.handles),
626 }
627 }
628
629 pub fn focus(&self, window: &mut Window, cx: &mut App) {
631 window.focus(self, cx)
632 }
633
634 pub fn is_focused(&self, window: &Window) -> bool {
636 self.id.is_focused(window)
637 }
638
639 pub fn contains_focused(&self, window: &Window, cx: &App) -> bool {
642 self.id.contains_focused(window, cx)
643 }
644
645 pub fn within_focused(&self, window: &Window, cx: &mut App) -> bool {
648 self.id.within_focused(window, cx)
649 }
650
651 pub fn contains(&self, other: &Self, window: &Window) -> bool {
653 self.id.contains(other.id, window)
654 }
655
656 pub fn dispatch_action(&self, action: &dyn Action, window: &mut Window, cx: &mut App) {
658 if let Some(node_id) = window
659 .frame_state
660 .rendered_frame
661 .dispatch_tree
662 .focusable_node_id(self.id)
663 {
664 window.dispatch_action_on_node(node_id, action, cx)
665 }
666 }
667}
668
669impl Clone for FocusHandle {
670 fn clone(&self) -> Self {
671 Self::for_id(self.id, &self.handles).unwrap()
672 }
673}
674
675impl PartialEq for FocusHandle {
676 fn eq(&self, other: &Self) -> bool {
677 self.id == other.id
678 }
679}
680
681impl Eq for FocusHandle {}
682
683impl Drop for FocusHandle {
684 fn drop(&mut self) {
685 self.handles
686 .read()
687 .get(self.id)
688 .unwrap()
689 .ref_count
690 .fetch_sub(1, SeqCst);
691 }
692}
693
694#[derive(Clone, Debug)]
696pub struct WeakFocusHandle {
697 pub(crate) id: FocusId,
698 pub(crate) handles: Weak<FocusMap>,
699}
700
701impl WeakFocusHandle {
702 pub fn upgrade(&self) -> Option<FocusHandle> {
704 let handles = self.handles.upgrade()?;
705 FocusHandle::for_id(self.id, &handles)
706 }
707}
708
709impl PartialEq for WeakFocusHandle {
710 fn eq(&self, other: &WeakFocusHandle) -> bool {
711 self.id == other.id
712 }
713}
714
715impl Eq for WeakFocusHandle {}
716
717impl PartialEq<FocusHandle> for WeakFocusHandle {
718 fn eq(&self, other: &FocusHandle) -> bool {
719 self.id == other.id
720 }
721}
722
723impl PartialEq<WeakFocusHandle> for FocusHandle {
724 fn eq(&self, other: &WeakFocusHandle) -> bool {
725 self.id == other.id
726 }
727}
728
729pub trait Focusable: 'static {
732 fn focus_handle(&self, cx: &App) -> FocusHandle;
734}
735
736impl<V: Focusable> Focusable for Entity<V> {
737 fn focus_handle(&self, cx: &App) -> FocusHandle {
738 self.read(cx).focus_handle(cx)
739 }
740}
741
742pub trait ManagedView: Focusable + EventEmitter<DismissEvent> + Render {}
745
746impl<M: Focusable + EventEmitter<DismissEvent> + Render> ManagedView for M {}
747
748pub struct DismissEvent;
750
751type FrameCallback = Box<dyn FnOnce(&mut Window, &mut App)>;
752
753pub(crate) type AnyMouseListener =
754 Box<dyn FnMut(&dyn Any, DispatchPhase, &mut Window, &mut App) + 'static>;
755
756#[derive(Clone)]
757pub(crate) struct CursorStyleRequest {
758 pub(crate) hitbox_id: Option<HitboxId>,
759 pub(crate) style: CursorStyle,
760}
761
762#[derive(Default, Eq, PartialEq)]
763pub(crate) struct HitTest {
764 pub(crate) ids: SmallVec<[HitboxId; 8]>,
765 pub(crate) hover_hitbox_count: usize,
766}
767
768#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
770pub struct HitboxId(u64);
771
772#[cfg(feature = "test-support")]
773impl HitboxId {
774 pub const fn placeholder() -> Self {
779 Self(0)
780 }
781}
782
783impl HitboxId {
784 pub fn is_hovered(self, window: &Window) -> bool {
791 if window.core.captured_hitbox == Some(self) {
793 return true;
794 }
795 if window.last_input_was_keyboard() {
796 return false;
797 }
798 self.hit_test(window)
799 }
800
801 pub(crate) fn is_hovered_ignoring_last_input(self, window: &Window) -> bool {
806 if window.core.captured_hitbox == Some(self) {
808 return true;
809 }
810 self.hit_test(window)
811 }
812
813 fn hit_test(self, window: &Window) -> bool {
814 let hit_test = &window.core.mouse_hit_test;
815 for id in hit_test.ids.iter().take(hit_test.hover_hitbox_count) {
816 if self == *id {
817 return true;
818 }
819 }
820 false
821 }
822
823 pub fn should_handle_scroll(self, window: &Window) -> bool {
828 window.core.mouse_hit_test.ids.contains(&self)
829 }
830
831 fn next(mut self) -> HitboxId {
832 HitboxId(self.0.wrapping_add(1))
833 }
834}
835
836#[derive(Clone)]
845pub struct HitboxFilter(Rc<dyn Fn(Point<Pixels>) -> bool>);
846
847impl HitboxFilter {
848 pub fn new(allows: impl Fn(Point<Pixels>) -> bool + 'static) -> Self {
851 Self(Rc::new(allows))
852 }
853
854 fn allows(&self, position: Point<Pixels>) -> bool {
855 (self.0)(position)
856 }
857}
858
859impl Debug for HitboxFilter {
860 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
861 f.write_str("HitboxFilter(..)")
862 }
863}
864
865#[derive(Clone, Debug, Deref)]
868pub struct Hitbox {
869 pub id: HitboxId,
871 #[deref]
873 pub bounds: Bounds<Pixels>,
874 pub content_mask: ContentMask<Pixels>,
876 pub behavior: HitboxBehavior,
878}
879
880const _: fn() = || {
884 fn assert_send_sync<T: Send + Sync>() {}
885 assert_send_sync::<Hitbox>();
886};
887
888impl Hitbox {
889 pub fn is_hovered(&self, window: &Window) -> bool {
906 self.id.is_hovered(window)
907 }
908
909 pub fn is_hovered_at(&self, position: Point<Pixels>, window: &Window) -> bool {
912 let hit_test = window.frame_state.rendered_frame.hit_test(position);
913 hit_test
914 .ids
915 .iter()
916 .take(hit_test.hover_hitbox_count)
917 .any(|id| self.id == *id)
918 }
919
920 pub fn should_handle_scroll(&self, window: &Window) -> bool {
927 self.id.should_handle_scroll(window)
928 }
929}
930
931#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
933pub enum HitboxBehavior {
934 #[default]
936 Normal,
937
938 BlockMouse,
960
961 BlockMouseExceptScroll,
988}
989
990#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
992pub struct TooltipId(usize);
993
994impl TooltipId {
995 pub fn is_hovered(&self, window: &Window) -> bool {
997 window
998 .frame_state
999 .tooltip_bounds
1000 .as_ref()
1001 .is_some_and(|tooltip_bounds| {
1002 tooltip_bounds.id == *self
1003 && tooltip_bounds.bounds.contains(&window.mouse_position())
1004 })
1005 }
1006}
1007
1008pub(crate) struct TooltipBounds {
1009 id: TooltipId,
1010 bounds: Bounds<Pixels>,
1011}
1012
1013#[derive(Clone)]
1014pub(crate) struct TooltipRequest {
1015 id: TooltipId,
1016 tooltip: AnyTooltip,
1017}
1018
1019pub(crate) struct DeferredDraw {
1020 current_view: EntityId,
1021 priority: usize,
1022 parent_node: DispatchNodeId,
1023 element_id_stack: SmallVec<[ElementId; 32]>,
1024 text_style_stack: Vec<TextStyleRefinement>,
1025 content_mask: Option<ContentMask<Pixels>>,
1026 rem_size: Pixels,
1027 element: Option<AnyElement>,
1028 absolute_offset: Point<Pixels>,
1029 prepaint_range: Range<PrepaintStateIndex>,
1030 paint_range: Range<PaintIndex>,
1031}
1032
1033pub(crate) struct Frame {
1034 pub(crate) focus: Option<FocusId>,
1035 pub(crate) window_active: bool,
1036 pub(crate) element_states: FxHashMap<(GlobalElementId, TypeId), ElementStateBox>,
1037 accessed_element_states: Vec<(GlobalElementId, TypeId)>,
1038 pub(crate) mouse_listeners: Vec<Option<AnyMouseListener>>,
1039 pub(crate) dispatch_tree: DispatchTree,
1040 pub(crate) scene: Scene,
1041 pub(crate) hitboxes: Vec<Hitbox>,
1042 pub(crate) hitbox_filters: Vec<Option<HitboxFilter>>,
1047 pub(crate) window_control_hitboxes: Vec<(WindowControlArea, Hitbox)>,
1048 pub(crate) deferred_draws: Vec<DeferredDraw>,
1049 pub(crate) input_handlers: Vec<Option<PlatformInputHandler>>,
1050 pub(crate) tooltip_requests: Vec<Option<TooltipRequest>>,
1051 pub(crate) cursor_styles: Vec<CursorStyleRequest>,
1052 #[cfg(any(test, feature = "test-support"))]
1053 pub(crate) debug_bounds: FxHashMap<String, Bounds<Pixels>>,
1054 #[cfg(any(test, feature = "test-support"))]
1055 debug_bounds_records: Vec<(String, Bounds<Pixels>)>,
1056 #[cfg(any(feature = "inspector", debug_assertions))]
1057 pub(crate) next_inspector_instance_ids: FxHashMap<Rc<crate::InspectorElementPath>, usize>,
1058 #[cfg(any(feature = "inspector", debug_assertions))]
1059 pub(crate) inspector_hitboxes: FxHashMap<HitboxId, crate::InspectorElementId>,
1060 pub(crate) tab_stops: TabStopMap,
1061}
1062
1063#[derive(Clone, Default)]
1064pub(crate) struct PrepaintStateIndex {
1065 hitboxes_index: usize,
1066 tooltips_index: usize,
1067 deferred_draws_index: usize,
1068 dispatch_tree_index: usize,
1069 accessed_element_states_index: usize,
1070 line_layout_index: LineLayoutIndex,
1071}
1072
1073#[derive(Clone, Default)]
1074pub(crate) struct PaintIndex {
1075 scene_index: usize,
1076 #[cfg(any(test, feature = "test-support"))]
1077 debug_bounds_index: usize,
1078 mouse_listeners_index: usize,
1079 input_handlers_index: usize,
1080 cursor_styles_index: usize,
1081 accessed_element_states_index: usize,
1082 tab_handle_index: usize,
1083 line_layout_index: LineLayoutIndex,
1084}
1085
1086impl Frame {
1087 #[cfg(any(test, feature = "test-support"))]
1088 pub(crate) fn record_debug_bounds(&mut self, selector: String, bounds: Bounds<Pixels>) {
1089 self.debug_bounds.insert(selector.clone(), bounds);
1090 self.debug_bounds_records.push((selector, bounds));
1091 }
1092
1093 pub(crate) fn new(dispatch_tree: DispatchTree) -> Self {
1094 Frame {
1095 focus: None,
1096 window_active: false,
1097 element_states: FxHashMap::default(),
1098 accessed_element_states: Vec::new(),
1099 mouse_listeners: Vec::new(),
1100 dispatch_tree,
1101 scene: Scene::default(),
1102 hitboxes: Vec::new(),
1103 hitbox_filters: Vec::new(),
1104 window_control_hitboxes: Vec::new(),
1105 deferred_draws: Vec::new(),
1106 input_handlers: Vec::new(),
1107 tooltip_requests: Vec::new(),
1108 cursor_styles: Vec::new(),
1109
1110 #[cfg(any(test, feature = "test-support"))]
1111 debug_bounds: FxHashMap::default(),
1112 #[cfg(any(test, feature = "test-support"))]
1113 debug_bounds_records: Vec::new(),
1114
1115 #[cfg(any(feature = "inspector", debug_assertions))]
1116 next_inspector_instance_ids: FxHashMap::default(),
1117
1118 #[cfg(any(feature = "inspector", debug_assertions))]
1119 inspector_hitboxes: FxHashMap::default(),
1120 tab_stops: TabStopMap::default(),
1121 }
1122 }
1123
1124 pub(crate) fn clear(&mut self) {
1125 self.element_states.clear();
1126 self.accessed_element_states.clear();
1127 self.mouse_listeners.clear();
1128 self.dispatch_tree.clear();
1129 self.scene.clear();
1130 self.input_handlers.clear();
1131 self.tooltip_requests.clear();
1132 self.cursor_styles.clear();
1133 self.hitboxes.clear();
1134 self.hitbox_filters.clear();
1135 self.window_control_hitboxes.clear();
1136 self.deferred_draws.clear();
1137 self.tab_stops.clear();
1138 self.focus = None;
1139
1140 #[cfg(any(test, feature = "test-support"))]
1141 {
1142 self.debug_bounds.clear();
1143 self.debug_bounds_records.clear();
1144 }
1145
1146 #[cfg(any(feature = "inspector", debug_assertions))]
1147 {
1148 self.next_inspector_instance_ids.clear();
1149 self.inspector_hitboxes.clear();
1150 }
1151 }
1152
1153 pub(crate) fn cursor_style(&self, window: &Window) -> Option<CursorStyle> {
1154 self.cursor_styles
1155 .iter()
1156 .rev()
1157 .fold_while(None, |style, request| match request.hitbox_id {
1158 None => Done(Some(request.style)),
1159 Some(hitbox_id) => Continue(style.or_else(|| {
1160 hitbox_id
1161 .is_hovered_ignoring_last_input(window)
1162 .then_some(request.style)
1163 })),
1164 })
1165 .into_inner()
1166 }
1167
1168 pub(crate) fn hit_test(&self, position: Point<Pixels>) -> HitTest {
1169 let mut set_hover_hitbox_count = false;
1170 let mut hit_test = HitTest::default();
1171 for (index, hitbox) in self.hitboxes.iter().enumerate().rev() {
1172 let bounds = hitbox.bounds.intersect(&hitbox.content_mask.bounds);
1173 if bounds.contains(&position) {
1174 if let Some(filter) = self.hitbox_filters.get(index).and_then(Option::as_ref)
1175 && !filter.allows(position)
1176 {
1177 continue;
1180 }
1181 hit_test.ids.push(hitbox.id);
1182 if !set_hover_hitbox_count
1183 && hitbox.behavior == HitboxBehavior::BlockMouseExceptScroll
1184 {
1185 hit_test.hover_hitbox_count = hit_test.ids.len();
1186 set_hover_hitbox_count = true;
1187 }
1188 if hitbox.behavior == HitboxBehavior::BlockMouse {
1189 break;
1190 }
1191 }
1192 }
1193 if !set_hover_hitbox_count {
1194 hit_test.hover_hitbox_count = hit_test.ids.len();
1195 }
1196 hit_test
1197 }
1198
1199 pub(crate) fn focus_path(&self) -> SmallVec<[FocusId; 8]> {
1200 self.focus
1201 .map(|focus_id| self.dispatch_tree.focus_path(focus_id))
1202 .unwrap_or_default()
1203 }
1204
1205 pub(crate) fn finish(&mut self, prev_frame: &mut Self) {
1206 for element_state_key in &self.accessed_element_states {
1207 if let Some((element_state_key, element_state)) =
1208 prev_frame.element_states.remove_entry(element_state_key)
1209 {
1210 self.element_states.insert(element_state_key, element_state);
1211 }
1212 }
1213
1214 self.scene.finish();
1215 }
1216}
1217
1218#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
1219enum InputModality {
1220 Mouse,
1221 Keyboard,
1222 Touch,
1223}
1224
1225#[cfg(debug_assertions)]
1235#[derive(Copy, Clone, PartialEq, Eq, Debug)]
1236pub(crate) enum FramePhase {
1237 Idle,
1239 Begun,
1241 Evaluated,
1243 LaidOut,
1245 Painted,
1247 Finished,
1249 Completed,
1251}
1252
1253#[cfg(debug_assertions)]
1254impl FramePhase {
1255 #[track_caller]
1257 fn enter(&mut self, from: &[FramePhase], next: FramePhase, method: &'static str) {
1258 debug_assert!(
1259 from.contains(self),
1260 "{method} ran while the frame was {self:?}, but it must follow one of {from:?}"
1261 );
1262 *self = next;
1263 }
1264}
1265
1266pub(crate) struct WindowFrameState {
1267 rem_size_override_stack: SmallVec<[Pixels; 8]>,
1272 layout_session: Rc<FrameSession>,
1273 pub(crate) inspector_element_id: Option<crate::InspectorElementId>,
1278 pub(crate) dirty_views: FxHashSet<EntityId>,
1280 focus_lost_path: SmallVec<[FocusId; 8]>,
1283 pub(crate) element_id_stack: SmallVec<[ElementId; 32]>,
1284 pub(crate) text_style_stack: Vec<TextStyleRefinement>,
1285 pub(crate) rendered_entity_stack: Vec<EntityId>,
1286 pub(crate) element_offset_stack: Vec<Point<Pixels>>,
1287 pub(crate) element_opacity: f32,
1288 pub(crate) content_mask_stack: Vec<ContentMask<Pixels>>,
1289 pub(crate) requested_autoscroll: Option<Bounds<Pixels>>,
1290 pub(crate) image_cache_stack: Vec<AnyImageCache>,
1291 pub(crate) rendered_frame: Frame,
1292 pub(crate) next_frame: Frame,
1293 next_hitbox_id: HitboxId,
1294 pub(crate) next_tooltip_id: TooltipId,
1295 pub(crate) tooltip_bounds: Option<TooltipBounds>,
1296 #[cfg(debug_assertions)]
1298 frame_phase: FramePhase,
1299}
1300
1301pub(crate) struct WindowHostCore {
1307 pub(crate) handle: AnyWindowHandle,
1308 pub(crate) invalidator: WindowInvalidator,
1309 frame_pipeline: Rc<RefCell<Box<dyn FramePipeline>>>,
1314 pub(crate) removed: bool,
1315 metrics: Arc<ArcSwap<WindowMetrics>>,
1317 pub(crate) platform_window: Box<dyn PlatformWindow>,
1318 display_id: Option<DisplayId>,
1319 is_resizable: bool,
1320 is_minimizable: bool,
1321 sprite_atlas: Arc<dyn PlatformAtlas>,
1322 text_system: Arc<WindowTextSystem>,
1323 text_rendering_mode: Rc<Cell<TextRenderingMode>>,
1324 rem_size: Pixels,
1325 pub(crate) viewport_size: Size<Pixels>,
1326 pub(crate) root: Option<AnyView>,
1327 last_text_input_configuration: Option<TextInputConfiguration>,
1331 focused_text_input_active: bool,
1332 pub(crate) next_frame_callbacks: Rc<RefCell<Vec<FrameCallback>>>,
1333 focus_listeners: SubscriberSet<(), AnyWindowFocusListener>,
1334 pub(crate) focus_lost_listeners: SubscriberSet<(), AnyObserver>,
1335 default_prevented: bool,
1336 mouse_position: Point<Pixels>,
1337 mouse_hit_test: HitTest,
1338 modifiers: Modifiers,
1339 capslock: Capslock,
1340 scale_factor: f32,
1341 pub(crate) bounds_observers: SubscriberSet<(), AnyObserver>,
1342 appearance: WindowAppearance,
1343 pub(crate) appearance_observers: SubscriberSet<(), AnyObserver>,
1344 pub(crate) button_layout_observers: SubscriberSet<(), AnyObserver>,
1345 active: Rc<Cell<bool>>,
1346 visibility: WindowVisibility,
1347 pub(crate) visibility_observers:
1348 SubscriberSet<(), Box<dyn FnMut(WindowVisibility, &mut Window, &mut App) -> bool>>,
1349 hovered: Rc<Cell<bool>>,
1350 pub(crate) needs_present: Rc<Cell<bool>>,
1351 pub(crate) input_rate_tracker: Rc<RefCell<InputRateTracker>>,
1354 #[cfg(feature = "profiler")]
1355 window_profiler: profiler::WindowProfiler,
1356 last_input_modality: InputModality,
1357 touch_gestures: TouchGestureRecognizer,
1358 touch_prediction_enabled: bool,
1359 long_press_timer: Option<Task<()>>,
1360 long_press_capture: Option<EntityId>,
1361 pub(crate) refreshing: bool,
1362 pub(crate) activation_observers: SubscriberSet<(), AnyObserver>,
1363 pub(crate) focus: Option<FocusId>,
1364 focus_enabled: bool,
1365 pub(crate) focus_generation: u64,
1368 pending_input: Option<PendingInput>,
1369 pending_modifier: ModifierState,
1370 pub(crate) pending_input_observers: SubscriberSet<(), AnyObserver>,
1371 prompt: Option<RenderablePromptHandle>,
1372 pub(crate) client_inset: Option<Pixels>,
1373 captured_hitbox: Option<HitboxId>,
1376 #[cfg(any(feature = "inspector", debug_assertions))]
1377 inspector: Option<Entity<Inspector>>,
1378 #[cfg(feature = "profiler")]
1379 debug_frame_overlay: crate::debug_overlay::DebugFrameOverlay,
1380 pub(crate) a11y: A11y,
1381}
1382
1383impl WindowHostCore {
1384 fn metrics_handle(&self) -> WindowMetricsHandle {
1385 WindowMetricsHandle {
1386 id: self.handle.window_id(),
1387 metrics: self.metrics.clone(),
1388 }
1389 }
1390
1391 pub(crate) fn should_render_frame(&self) -> bool {
1394 let is_dirty = self.invalidator.is_dirty();
1395 let metrics = self.metrics.load();
1396 self.frame_pipeline
1397 .borrow_mut()
1398 .should_render(is_dirty, &metrics)
1399 }
1400}
1401
1402pub struct WindowMetricsHandle {
1408 id: WindowId,
1409 metrics: Arc<ArcSwap<WindowMetrics>>,
1410}
1411
1412impl WindowMetricsHandle {
1413 pub fn id(&self) -> WindowId {
1415 self.id
1416 }
1417
1418 pub fn snapshot(&self) -> Arc<WindowMetrics> {
1423 self.metrics.load_full()
1424 }
1425
1426 pub fn bounds(&self) -> Bounds<Pixels> {
1428 self.metrics.load().bounds
1429 }
1430
1431 pub fn viewport(&self) -> Bounds<Pixels> {
1436 self.metrics.load().viewport
1437 }
1438
1439 pub fn content_size(&self) -> Size<Pixels> {
1441 self.metrics.load().content_size
1442 }
1443
1444 pub fn scale_factor(&self) -> f32 {
1446 self.metrics.load().scale_factor
1447 }
1448
1449 pub fn display_id(&self) -> Option<DisplayId> {
1451 self.metrics.load().display_id
1452 }
1453
1454 pub fn is_active(&self) -> bool {
1456 self.metrics.load().is_active
1457 }
1458
1459 pub fn is_fullscreen(&self) -> bool {
1461 self.metrics.load().is_fullscreen
1462 }
1463}
1464
1465pub struct Window<'frame> {
1481 pub(crate) core: &'frame mut WindowHostCore,
1484 pub(crate) frame_state: &'frame mut WindowFrameState,
1486}
1487
1488pub(crate) struct WindowHost {
1494 pub(crate) core: WindowHostCore,
1495 pub(crate) frame_state: WindowFrameState,
1496}
1497
1498impl WindowHost {
1499 pub(crate) fn with_window<R>(&mut self, f: impl FnOnce(&mut Window<'_>) -> R) -> R {
1501 let core = &mut self.core;
1502 let frame_state = &mut self.frame_state;
1503 f(&mut Window { core, frame_state })
1504 }
1505}
1506
1507#[derive(Clone, Debug, Default)]
1508struct ModifierState {
1509 modifiers: Modifiers,
1510 saw_other_input: bool,
1511}
1512
1513#[derive(Clone, Debug)]
1516pub(crate) struct InputRateTracker {
1517 timestamps: Vec<Instant>,
1518 window: Duration,
1519 inputs_per_second: u32,
1520 sustain_until: Instant,
1521 sustain_duration: Duration,
1522}
1523
1524impl Default for InputRateTracker {
1525 fn default() -> Self {
1526 Self {
1527 timestamps: Vec::new(),
1528 window: Duration::from_millis(100),
1529 inputs_per_second: 60,
1530 sustain_until: Instant::now(),
1531 sustain_duration: Duration::from_secs(1),
1532 }
1533 }
1534}
1535
1536impl InputRateTracker {
1537 pub fn record_input(&mut self) {
1538 let now = Instant::now();
1539 self.timestamps.push(now);
1540 self.prune_old_timestamps(now);
1541
1542 let min_events = self.inputs_per_second as u128 * self.window.as_millis() / 1000;
1543 if self.timestamps.len() as u128 >= min_events {
1544 self.sustain_until = now + self.sustain_duration;
1545 }
1546 }
1547
1548 pub fn is_high_rate(&self) -> bool {
1549 Instant::now() < self.sustain_until
1550 }
1551
1552 fn prune_old_timestamps(&mut self, now: Instant) {
1553 self.timestamps
1554 .retain(|&t| now.duration_since(t) <= self.window);
1555 }
1556}
1557
1558#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1559pub(crate) enum DrawPhase {
1560 None,
1561 Prepaint,
1562 Paint,
1563 Focus,
1564}
1565
1566pub(crate) const PENDING_INPUT_TIMEOUT: Duration = Duration::from_secs(1);
1567
1568pub struct PendingInputStatus<'a> {
1570 keystrokes: &'a [Keystroke],
1571 timeout: Option<PendingInputTimeoutStatus>,
1572}
1573
1574impl<'a> PendingInputStatus<'a> {
1575 pub fn keystrokes(&self) -> &'a [Keystroke] {
1577 self.keystrokes
1578 }
1579
1580 pub fn timeout(&self) -> Option<PendingInputTimeoutStatus> {
1582 self.timeout
1583 }
1584}
1585
1586#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1588pub struct PendingInputTimeoutStatus {
1589 duration: Duration,
1590 remaining: Duration,
1591 started_at: Option<Instant>,
1592 paused: bool,
1593}
1594
1595impl PendingInputTimeoutStatus {
1596 pub fn duration(&self) -> Duration {
1598 self.duration
1599 }
1600
1601 pub fn remaining(&self, cx: &App) -> Duration {
1603 self.started_at
1604 .map(|started_at| {
1605 self.remaining
1606 .saturating_sub(cx.background_executor().now() - started_at)
1607 })
1608 .unwrap_or(self.remaining)
1609 }
1610
1611 pub fn is_paused(&self) -> bool {
1613 self.paused
1614 }
1615}
1616
1617#[derive(Debug)]
1618struct PendingInputTimeout {
1619 duration: Duration,
1620 remaining: Duration,
1621 state: PendingInputTimeoutState,
1622}
1623
1624#[derive(Debug)]
1625enum PendingInputTimeoutState {
1626 Running { started_at: Instant, task: Task<()> },
1627 Paused { pause: PendingInputTimeoutPause },
1628}
1629
1630#[derive(Debug)]
1631struct PendingInputTimeoutPause {
1632 owner_id: EntityId,
1633 _release_subscription: Subscription,
1634}
1635
1636impl PendingInputTimeout {
1637 fn is_paused(&self) -> bool {
1638 matches!(&self.state, PendingInputTimeoutState::Paused { .. })
1639 }
1640
1641 fn pause(&mut self, pause: PendingInputTimeoutPause, now: Instant) -> bool {
1642 match std::mem::replace(&mut self.state, PendingInputTimeoutState::Paused { pause }) {
1643 PendingInputTimeoutState::Running { started_at, task } => {
1644 self.remaining = self.remaining.saturating_sub(now - started_at);
1645 drop(task);
1646 true
1647 }
1648 previous_state @ PendingInputTimeoutState::Paused { .. } => {
1649 self.state = previous_state;
1650 false
1651 }
1652 }
1653 }
1654
1655 fn pause_owner_id(&self) -> Option<EntityId> {
1656 match &self.state {
1657 PendingInputTimeoutState::Running { .. } => None,
1658 PendingInputTimeoutState::Paused { pause } => Some(pause.owner_id),
1659 }
1660 }
1661
1662 fn resume(&mut self, owner_id: EntityId, started_at: Instant, task: Task<()>) -> bool {
1663 match std::mem::replace(
1664 &mut self.state,
1665 PendingInputTimeoutState::Running { started_at, task },
1666 ) {
1667 PendingInputTimeoutState::Paused { pause } if pause.owner_id == owner_id => true,
1668 previous_state => {
1669 self.state = previous_state;
1670 false
1671 }
1672 }
1673 }
1674
1675 fn reset_duration(&mut self, duration: Duration) {
1676 self.duration = duration;
1677 self.remaining = duration;
1678 }
1679
1680 fn status(&self) -> PendingInputTimeoutStatus {
1681 let (started_at, paused) = match &self.state {
1682 PendingInputTimeoutState::Running { started_at, .. } => (Some(*started_at), false),
1683 PendingInputTimeoutState::Paused { .. } => (None, true),
1684 };
1685 PendingInputTimeoutStatus {
1686 duration: self.duration,
1687 remaining: self.remaining,
1688 started_at,
1689 paused,
1690 }
1691 }
1692}
1693
1694#[derive(Default, Debug)]
1695struct PendingInput {
1696 keystrokes: SmallVec<[Keystroke; 1]>,
1697 focus: Option<FocusId>,
1698 timeout: Option<PendingInputTimeout>,
1699}
1700
1701pub(crate) struct ElementStateBox {
1702 pub(crate) inner: Box<dyn Any>,
1703 #[cfg(debug_assertions)]
1704 pub(crate) type_name: &'static str,
1705}
1706
1707fn default_bounds(display_id: Option<DisplayId>, cx: &mut App) -> WindowBounds {
1708 let active_window_bounds = cx
1713 .active_window()
1714 .and_then(|w| w.update(cx, |_, window, _| window.window_bounds()).ok());
1715
1716 const CASCADE_OFFSET: f32 = 25.0;
1717
1718 let display = display_id
1719 .map(|id| cx.find_display(id))
1720 .unwrap_or_else(|| cx.primary_display());
1721
1722 let default_placement = || Bounds::new(point(px(0.), px(0.)), DEFAULT_WINDOW_SIZE);
1723
1724 let display_bounds = display
1726 .as_ref()
1727 .map(|d| d.visible_bounds())
1728 .unwrap_or_else(default_placement);
1729
1730 let (
1731 Bounds {
1732 origin: base_origin,
1733 size: base_size,
1734 },
1735 window_bounds_ctor,
1736 ): (_, fn(Bounds<Pixels>) -> WindowBounds) = match active_window_bounds {
1737 Some(bounds) => match bounds {
1738 WindowBounds::Windowed(bounds) => (bounds, WindowBounds::Windowed),
1739 WindowBounds::Maximized(bounds) => (bounds, WindowBounds::Maximized),
1740 WindowBounds::Fullscreen(bounds) => (bounds, WindowBounds::Fullscreen),
1741 },
1742 None => (
1743 display
1744 .as_ref()
1745 .map(|d| d.default_bounds())
1746 .unwrap_or_else(default_placement),
1747 WindowBounds::Windowed,
1748 ),
1749 };
1750
1751 let cascade_offset = point(px(CASCADE_OFFSET), px(CASCADE_OFFSET));
1752 let proposed_origin = base_origin + cascade_offset;
1753 let proposed_bounds = Bounds::new(proposed_origin, base_size);
1754
1755 let display_right = display_bounds.origin.x + display_bounds.size.width;
1756 let display_bottom = display_bounds.origin.y + display_bounds.size.height;
1757 let window_right = proposed_bounds.origin.x + proposed_bounds.size.width;
1758 let window_bottom = proposed_bounds.origin.y + proposed_bounds.size.height;
1759
1760 let fits_horizontally = window_right <= display_right;
1761 let fits_vertically = window_bottom <= display_bottom;
1762
1763 let final_origin = match (fits_horizontally, fits_vertically) {
1764 (true, true) => proposed_origin,
1765 (false, true) => point(display_bounds.origin.x, base_origin.y),
1766 (true, false) => point(base_origin.x, display_bounds.origin.y),
1767 (false, false) => display_bounds.origin,
1768 };
1769 window_bounds_ctor(Bounds::new(final_origin, base_size))
1770}
1771
1772impl WindowHost {
1773 pub(crate) fn new(
1774 handle: AnyWindowHandle,
1775 options: WindowOptions,
1776 cx: &mut App,
1777 ) -> Result<Self> {
1778 let WindowOptions {
1779 window_bounds,
1780 titlebar,
1781 focus,
1782 show,
1783 kind,
1784 is_movable,
1785 app_owns_titlebar_drag,
1786 inactive_frame_interval,
1787 is_resizable,
1788 is_minimizable,
1789 display_id,
1790 window_background,
1791 app_id,
1792 window_min_size,
1793 window_decorations,
1794 #[cfg_attr(
1795 not(any(target_os = "linux", target_os = "freebsd")),
1796 allow(unused_variables)
1797 )]
1798 icon,
1799 #[cfg_attr(not(target_os = "macos"), allow(unused_variables))]
1800 tabbing_identifier,
1801 renderer_factory,
1802 } = options;
1803
1804 let initial_window_title = titlebar
1805 .as_ref()
1806 .and_then(|titlebar| titlebar.title.clone());
1807
1808 let window_bounds = window_bounds.unwrap_or_else(|| default_bounds(display_id, cx));
1809 let mut platform_window = cx.platform.open_window(
1810 handle.window_id(),
1811 WindowParams {
1812 bounds: window_bounds.get_bounds(),
1813 titlebar,
1814 kind,
1815 is_movable,
1816 app_owns_titlebar_drag,
1817 is_resizable,
1818 is_minimizable,
1819 focus,
1820 show,
1821 display_id,
1822 window_min_size,
1823 app_id: app_id.clone(),
1824 icon,
1825 #[cfg(target_os = "macos")]
1826 tabbing_identifier,
1827 renderer_factory,
1828 },
1829 )?;
1830
1831 let tab_bar_visible = platform_window.tab_bar_visible();
1832 SystemWindowTabController::init_visible(cx, tab_bar_visible);
1833 if let Some(tabs) = platform_window.tabbed_windows() {
1834 SystemWindowTabController::add_tab(cx, handle.window_id(), tabs);
1835 }
1836
1837 let display_id = platform_window.display().map(|display| display.id());
1838 let mut sprite_atlas = None;
1839 platform_window.with_renderer(&mut |renderer| {
1840 sprite_atlas = Some(renderer.sprite_atlas());
1841 });
1842 let sprite_atlas = sprite_atlas
1843 .ok_or_else(|| anyhow!("platform window did not provide a scene renderer"))?;
1844 let mouse_position = platform_window.mouse_position();
1845 let modifiers = platform_window.modifiers();
1846 let capslock = platform_window.capslock();
1847 let content_size = platform_window.content_size();
1848 let scale_factor = platform_window.scale_factor();
1849 let appearance = platform_window.appearance();
1850 let text_system = Arc::new(WindowTextSystem::new(cx.text_system().clone()));
1851 let invalidator = WindowInvalidator::new(handle.window_id());
1852 let active = Rc::new(Cell::new(platform_window.is_active()));
1853 let visibility = platform_window.visibility();
1854 #[cfg(feature = "profiler")]
1855 profiler::journal::record_window_visibility(handle.window_id(), visibility);
1856 let metrics = Arc::new(ArcSwap::from_pointee(WindowMetrics {
1857 bounds: platform_window.bounds(),
1858 viewport: platform_window.visual_viewport_bounds(),
1859 content_size,
1860 scale_factor,
1861 display_id,
1862 is_active: active.get(),
1863 is_fullscreen: platform_window.is_fullscreen(),
1864 }));
1865 let hovered = Rc::new(Cell::new(platform_window.is_hovered()));
1866 let needs_present = Rc::new(Cell::new(false));
1867 let next_frame_callbacks: Rc<RefCell<Vec<FrameCallback>>> = Default::default();
1868 let input_rate_tracker = Rc::new(RefCell::new(InputRateTracker::default()));
1869 let last_frame_time = Rc::new(Cell::new(None));
1870
1871 platform_window
1872 .request_decorations(window_decorations.unwrap_or(WindowDecorations::Server));
1873 platform_window.set_background_appearance(window_background);
1874
1875 match window_bounds {
1876 WindowBounds::Fullscreen(_) => platform_window.toggle_fullscreen(),
1877 WindowBounds::Maximized(_) => platform_window.zoom(),
1878 WindowBounds::Windowed(_) => {}
1879 }
1880
1881 let accessibility_force_disabled = cx.accessibility_force_disabled;
1882 let a11y_active_flag = Arc::new(AtomicBool::new(false));
1883
1884 #[cfg(not(target_family = "wasm"))]
1885 if !accessibility_force_disabled {
1886 let mut initial_root_node = accesskit::Node::new(accesskit::Role::Window);
1887 if let Some(title) = &initial_window_title {
1888 initial_root_node.set_label(title.to_string());
1889 }
1890 let initial_tree = accesskit::TreeUpdate {
1891 nodes: vec![(ROOT_NODE_ID, initial_root_node)],
1892 tree: Some(accesskit::Tree::new(ROOT_NODE_ID)),
1893 tree_id: accesskit::TreeId::ROOT,
1894 focus: ROOT_NODE_ID,
1895 };
1896 let (activation_sender, activation_receiver) = async_channel::unbounded::<()>();
1897 let (deactivation_sender, deactivation_receiver) = async_channel::unbounded::<()>();
1898 let (action_sender, action_receiver) =
1899 async_channel::unbounded::<accesskit::ActionRequest>();
1900
1901 platform_window.a11y_init(crate::A11yCallbacks {
1902 activation: {
1903 let active_flag = a11y_active_flag.clone();
1904 Box::new(move || {
1905 log::info!("Accessibility activated");
1906 active_flag.store(true, SeqCst);
1907 activation_sender.send_blocking(()).log_err();
1908 Some(initial_tree.clone())
1909 })
1910 },
1911 action: Box::new(move |request| {
1912 action_sender.send_blocking(request).log_err();
1913 }),
1914 deactivation: {
1915 let active_flag = a11y_active_flag.clone();
1916 Box::new(move || {
1917 log::info!("Accessibility deactivated");
1918 active_flag.store(false, SeqCst);
1919 deactivation_sender.send_blocking(()).log_err();
1920 })
1921 },
1922 });
1923
1924 let mut async_cx = cx.to_async();
1930 cx.foreground_executor()
1931 .spawn(async move {
1932 while activation_receiver.recv().await.is_ok() {
1933 handle
1934 .update(&mut async_cx, |_, window, _| window.refresh())
1935 .log_err();
1936 }
1937 })
1938 .detach();
1939
1940 let mut async_cx = cx.to_async();
1941 cx.foreground_executor()
1942 .spawn(async move {
1943 while deactivation_receiver.recv().await.is_ok() {
1944 handle
1945 .update(&mut async_cx, |_, window, _| window.refresh())
1946 .log_err();
1947 }
1948 })
1949 .detach();
1950
1951 let mut async_cx = cx.to_async();
1952 cx.foreground_executor()
1953 .spawn(async move {
1954 while let Ok(request) = action_receiver.recv().await {
1955 handle
1956 .update(&mut async_cx, |_, window, cx| {
1957 window.handle_a11y_action(request, cx);
1958 })
1959 .log_err();
1960 }
1961 })
1962 .detach();
1963 }
1964
1965 platform_window.on_close(Box::new({
1966 let window_id = handle.window_id();
1967 let mut cx = cx.to_async();
1968 move || {
1969 let _ = handle.update(&mut cx, |_, window, _| window.remove_window());
1970 let _ = cx.update(|cx| {
1971 SystemWindowTabController::remove_tab(cx, window_id);
1972 });
1973 }
1974 }));
1975 platform_window.on_request_frame(Box::new({
1976 let mut cx = cx.to_async();
1977 let invalidator = invalidator.clone();
1978 let active = active.clone();
1979 let needs_present = needs_present.clone();
1980 let next_frame_callbacks = next_frame_callbacks.clone();
1981 let input_rate_tracker = input_rate_tracker.clone();
1982 let mut deferred_force_render = false;
1983 move |request_frame_options| {
1984 #[cfg(feature = "profiler")]
1985 let _foreground_turn = profiler::journal::foreground_turn();
1986 if draw_in_progress() {
2002 log::debug!("deferring re-entrant window draw request");
2003 deferred_force_render |= request_frame_options.force_render;
2004 return;
2005 }
2006 let force_render =
2010 mem::take(&mut deferred_force_render) || request_frame_options.force_render;
2011
2012 let thermal_state = handle
2013 .update(&mut cx, |_, _, cx| cx.thermal_state())
2014 .log_err();
2015
2016 let min_frame_interval = if request_frame_options.require_presentation
2020 || (!request_frame_options.force_render
2021 && next_frame_callbacks.borrow().is_empty())
2022 {
2023 None
2024 } else if !active.get() && !input_rate_tracker.borrow_mut().is_high_rate() {
2025 inactive_frame_interval
2026 } else if let Some(ThermalState::Critical | ThermalState::Serious) = thermal_state {
2027 Some(Duration::from_micros(16667))
2028 } else {
2029 None
2030 };
2031
2032 let now = Instant::now();
2033 if let Some(min_interval) = min_frame_interval {
2034 if let Some(last_frame) = last_frame_time.get()
2035 && now.duration_since(last_frame) < min_interval
2036 {
2037 deferred_force_render |= force_render;
2039 handle
2041 .update(&mut cx, |_, window, _| {
2042 window.core.platform_window.schedule_frame();
2043 })
2044 .log_err();
2045 invalidator.wake_platform();
2050 return;
2051 }
2052 }
2053 last_frame_time.set(Some(now));
2054
2055 let pending_next_frame_callbacks = next_frame_callbacks.take();
2056 if !pending_next_frame_callbacks.is_empty() {
2057 handle
2058 .update(&mut cx, |_, window, cx| {
2059 for callback in pending_next_frame_callbacks {
2060 callback(window, cx);
2061 }
2062 })
2063 .log_err();
2064 }
2065
2066 let needs_present = request_frame_options.require_presentation
2070 || needs_present.get()
2071 || input_rate_tracker.borrow_mut().is_high_rate();
2072
2073 if invalidator.is_dirty() || force_render {
2074 measure("frame duration", || {
2075 handle
2076 .update(&mut cx, |_, window, cx| {
2077 if force_render {
2078 window.refresh();
2081 }
2082 if force_render || window.core.should_render_frame() {
2085 let arena_clear_needed = window.draw(cx);
2086 window.present();
2087 arena_clear_needed.clear(cx);
2088 }
2089 })
2090 .log_err();
2091 })
2092 } else if needs_present {
2093 handle
2094 .update(&mut cx, |_, window, _| window.present())
2095 .log_err();
2096 }
2097
2098 handle
2099 .update(&mut cx, |_, window, _| {
2100 if window.core.invalidator.is_dirty()
2101 || !window.core.next_frame_callbacks.borrow().is_empty()
2102 {
2103 window.core.platform_window.schedule_frame();
2104 }
2105 })
2106 .log_err();
2107
2108 if invalidator.is_dirty() || !next_frame_callbacks.borrow().is_empty() {
2114 invalidator.wake_platform();
2115 }
2116 }
2117 }));
2118 invalidator.set_platform_waker(platform_window.frame_waker());
2119 platform_window.on_visual_viewport_changed(Box::new({
2120 let mut cx = cx.to_async();
2121 move || {
2122 handle
2123 .update(&mut cx, |_, window, _| window.refresh())
2124 .log_err();
2125 }
2126 }));
2127 platform_window.on_insets_changed(Box::new({
2128 let mut cx = cx.to_async();
2129 move |_| {
2130 handle
2131 .update(&mut cx, |_, window, _| window.refresh())
2132 .log_err();
2133 }
2134 }));
2135 platform_window.on_resize(Box::new({
2136 let mut cx = cx.to_async();
2137 move |_, _| {
2138 handle
2139 .update(&mut cx, |_, window, cx| window.bounds_changed(cx))
2140 .log_err();
2141 }
2142 }));
2143 platform_window.on_moved(Box::new({
2144 let mut cx = cx.to_async();
2145 move || {
2146 handle
2147 .update(&mut cx, |_, window, cx| window.bounds_changed(cx))
2148 .log_err();
2149 }
2150 }));
2151 platform_window.on_appearance_changed(Box::new({
2152 let cx = cx.to_async();
2153 let foreground_executor = cx.foreground_executor().clone();
2154 move || {
2155 let mut cx = cx.clone();
2156 foreground_executor
2159 .spawn(async move {
2160 handle
2161 .update(&mut cx, |_, window, cx| window.appearance_changed(cx))
2162 .log_err();
2163 })
2164 .detach();
2165 }
2166 }));
2167 platform_window.on_button_layout_changed(Box::new({
2168 let mut cx = cx.to_async();
2169 move || {
2170 handle
2171 .update(&mut cx, |_, window, cx| window.button_layout_changed(cx))
2172 .log_err();
2173 }
2174 }));
2175 platform_window.on_active_status_change(Box::new({
2176 let mut cx = cx.to_async();
2177 move |active| {
2178 handle
2179 .update(&mut cx, |_, window, cx| {
2180 window.core.active.set(active);
2181 window.core.modifiers = window.core.platform_window.modifiers();
2182 window.core.capslock = window.core.platform_window.capslock();
2183 window
2184 .core
2185 .activation_observers
2186 .clone()
2187 .retain(&(), |callback| callback(window, cx));
2188
2189 window.bounds_changed(cx);
2190 window.refresh();
2191
2192 SystemWindowTabController::update_last_active(cx, window.core.handle.id);
2193 })
2194 .log_err();
2195 }
2196 }));
2197 platform_window.on_visibility_change(Box::new({
2198 let mut cx = cx.to_async();
2199 move |_| {
2200 handle
2201 .update(&mut cx, |_, window, cx| {
2202 window.refresh_visibility(cx);
2203 })
2204 .log_err();
2205 }
2206 }));
2207 platform_window.on_hover_status_change(Box::new({
2208 let mut cx = cx.to_async();
2209 move |active| {
2210 handle
2211 .update(&mut cx, |_, window, _| {
2212 window.core.hovered.set(active);
2213 window.refresh();
2214 })
2215 .log_err();
2216 }
2217 }));
2218 platform_window.on_input({
2219 let mut cx = cx.to_async();
2220 Box::new(move |event| {
2221 handle
2222 .update(&mut cx, |_, window, cx| window.dispatch_event(event, cx))
2223 .log_err()
2224 .unwrap_or(DispatchEventResult::default())
2225 })
2226 });
2227 platform_window.on_hit_test_window_control({
2228 let mut cx = cx.to_async();
2229 Box::new(move || {
2230 handle
2231 .update(&mut cx, |_, window, _cx| {
2232 for (area, hitbox) in
2233 &window.frame_state.rendered_frame.window_control_hitboxes
2234 {
2235 if window.core.mouse_hit_test.ids.contains(&hitbox.id) {
2236 return Some(*area);
2237 }
2238 }
2239 None
2240 })
2241 .log_err()
2242 .unwrap_or(None)
2243 })
2244 });
2245 platform_window.on_move_tab_to_new_window({
2246 let mut cx = cx.to_async();
2247 Box::new(move || {
2248 handle
2249 .update(&mut cx, |_, _window, cx| {
2250 SystemWindowTabController::move_tab_to_new_window(cx, handle.window_id());
2251 })
2252 .log_err();
2253 })
2254 });
2255 platform_window.on_merge_all_windows({
2256 let mut cx = cx.to_async();
2257 Box::new(move || {
2258 handle
2259 .update(&mut cx, |_, _window, cx| {
2260 SystemWindowTabController::merge_all_windows(cx, handle.window_id());
2261 })
2262 .log_err();
2263 })
2264 });
2265 platform_window.on_select_next_tab({
2266 let mut cx = cx.to_async();
2267 Box::new(move || {
2268 handle
2269 .update(&mut cx, |_, _window, cx| {
2270 SystemWindowTabController::select_next_tab(cx, handle.window_id());
2271 })
2272 .log_err();
2273 })
2274 });
2275 platform_window.on_select_previous_tab({
2276 let mut cx = cx.to_async();
2277 Box::new(move || {
2278 handle
2279 .update(&mut cx, |_, _window, cx| {
2280 SystemWindowTabController::select_previous_tab(cx, handle.window_id())
2281 })
2282 .log_err();
2283 })
2284 });
2285 platform_window.on_toggle_tab_bar({
2286 let mut cx = cx.to_async();
2287 Box::new(move || {
2288 handle
2289 .update(&mut cx, |_, window, cx| {
2290 let tab_bar_visible = window.core.platform_window.tab_bar_visible();
2291 SystemWindowTabController::set_visible(cx, tab_bar_visible);
2292 })
2293 .log_err();
2294 })
2295 });
2296
2297 if let Some(app_id) = app_id {
2298 platform_window.set_app_id(&app_id);
2299 }
2300
2301 platform_window.map_window().unwrap();
2302
2303 Ok(WindowHost {
2304 core: WindowHostCore {
2305 handle,
2306 invalidator,
2307 frame_pipeline: Rc::new(RefCell::new(cx.new_frame_pipeline(handle.window_id()))),
2308 removed: false,
2309 metrics,
2310 platform_window,
2311 display_id,
2312 is_resizable,
2313 is_minimizable,
2314 sprite_atlas,
2315 text_system,
2316 text_rendering_mode: cx.text_rendering_mode.clone(),
2317 rem_size: px(16.),
2318 viewport_size: content_size,
2319 root: None,
2320 last_text_input_configuration: None,
2321 focused_text_input_active: false,
2322 next_frame_callbacks,
2323 focus_listeners: SubscriberSet::new(),
2324 focus_lost_listeners: SubscriberSet::new(),
2325 default_prevented: true,
2326 mouse_position,
2327 mouse_hit_test: HitTest::default(),
2328 modifiers,
2329 capslock,
2330 scale_factor,
2331 bounds_observers: SubscriberSet::new(),
2332 appearance,
2333 appearance_observers: SubscriberSet::new(),
2334 button_layout_observers: SubscriberSet::new(),
2335 active,
2336 visibility,
2337 visibility_observers: SubscriberSet::new(),
2338 hovered,
2339 needs_present,
2340 input_rate_tracker,
2341 #[cfg(feature = "profiler")]
2342 window_profiler: profiler::WindowProfiler::new(handle.window_id())?,
2343 last_input_modality: InputModality::Mouse,
2344 touch_gestures: TouchGestureRecognizer::new(
2345 cx.platform
2346 .gestures()
2347 .map_or_else(GestureTuning::default, |gestures| gestures.tuning()),
2348 ),
2349 touch_prediction_enabled: true,
2350 long_press_timer: None,
2351 long_press_capture: None,
2352 refreshing: false,
2353 activation_observers: SubscriberSet::new(),
2354 focus: None,
2355 focus_enabled: true,
2356 focus_generation: 0,
2357 pending_input: None,
2358 pending_modifier: ModifierState::default(),
2359 pending_input_observers: SubscriberSet::new(),
2360 prompt: None,
2361 client_inset: None,
2362 captured_hitbox: None,
2363 #[cfg(any(feature = "inspector", debug_assertions))]
2364 inspector: None,
2365 #[cfg(feature = "profiler")]
2366 debug_frame_overlay: crate::debug_overlay::DebugFrameOverlay::new(),
2367 a11y: A11y::new(
2368 a11y_active_flag,
2369 accessibility_force_disabled,
2370 initial_window_title,
2371 ),
2372 },
2373 frame_state: WindowFrameState {
2374 rem_size_override_stack: SmallVec::new(),
2375 layout_session: Rc::new(FrameSession::new(cx.new_layout_engine())),
2376 inspector_element_id: None,
2377 dirty_views: FxHashSet::default(),
2378 focus_lost_path: SmallVec::new(),
2379 element_id_stack: SmallVec::default(),
2380 text_style_stack: Vec::new(),
2381 rendered_entity_stack: Vec::new(),
2382 element_offset_stack: Vec::new(),
2383 element_opacity: 1.0,
2384 content_mask_stack: Vec::new(),
2385 requested_autoscroll: None,
2386 image_cache_stack: Vec::new(),
2387 rendered_frame: Frame::new(DispatchTree::new(
2388 cx.keymap.clone(),
2389 cx.actions.clone(),
2390 )),
2391 next_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())),
2392 next_hitbox_id: HitboxId(0),
2393 next_tooltip_id: TooltipId::default(),
2394 tooltip_bounds: None,
2395 #[cfg(debug_assertions)]
2396 frame_phase: FramePhase::Idle,
2397 },
2398 })
2399 }
2400}
2401
2402pub struct PreparedRoots {
2414 pub root: AnyElement,
2416 pub prompt: Option<AnyElement>,
2418 pub tooltip: Option<AnyElement>,
2420 pub drag: Option<(AnyElement, Point<Pixels>)>,
2423 pub inspector: Option<AnyElement>,
2425}
2426
2427impl Window<'_> {
2428 pub(crate) fn new_focus_listener(
2429 &self,
2430 value: AnyWindowFocusListener,
2431 ) -> (Subscription, impl FnOnce() + use<>) {
2432 self.core.focus_listeners.insert((), value)
2433 }
2434
2435 fn mark_view_dirty(&mut self, view_id: EntityId) {
2436 for view_id in self
2439 .frame_state
2440 .rendered_frame
2441 .dispatch_tree
2442 .view_path_reversed(view_id)
2443 {
2444 if !self.frame_state.dirty_views.insert(view_id) {
2445 break;
2446 }
2447 }
2448 }
2449
2450 pub(crate) fn refresh_visibility(&mut self, cx: &mut App) {
2451 let visibility = self.core.platform_window.visibility();
2452 if self.core.visibility != visibility {
2453 self.core.visibility = visibility;
2454 #[cfg(feature = "profiler")]
2455 profiler::journal::record_window_visibility(self.core.handle.window_id(), visibility);
2456 self.core
2457 .visibility_observers
2458 .clone()
2459 .retain(&(), |callback| callback(visibility, self, cx));
2460 }
2461 #[cfg(feature = "profiler")]
2462 if self.core.invalidator.is_dirty() || self.core.needs_present.get() {
2463 profiler::journal::record_frame_pending(self.core.handle.window_id(), Instant::now());
2464 }
2465 }
2466
2467 pub fn visibility(&self) -> WindowVisibility {
2470 self.core.visibility
2471 }
2472
2473 pub fn is_visible(&self) -> bool {
2479 self.core.visibility.is_visible()
2480 }
2481
2482 pub fn observe_window_visibility(
2484 &self,
2485 mut callback: impl FnMut(WindowVisibility, &mut Window, &mut App) + 'static,
2486 ) -> Subscription {
2487 let (subscription, activate) = self.core.visibility_observers.insert(
2488 (),
2489 Box::new(move |visibility, window, cx| {
2490 callback(visibility, window, cx);
2491 true
2492 }),
2493 );
2494 activate();
2495 subscription
2496 }
2497
2498 pub fn observe_window_appearance(
2500 &self,
2501 mut callback: impl FnMut(&mut Window, &mut App) + 'static,
2502 ) -> Subscription {
2503 let (subscription, activate) = self.core.appearance_observers.insert(
2504 (),
2505 Box::new(move |window, cx| {
2506 callback(window, cx);
2507 true
2508 }),
2509 );
2510 activate();
2511 subscription
2512 }
2513
2514 pub fn observe_button_layout_changed(
2516 &self,
2517 mut callback: impl FnMut(&mut Window, &mut App) + 'static,
2518 ) -> Subscription {
2519 let (subscription, activate) = self.core.button_layout_observers.insert(
2520 (),
2521 Box::new(move |window, cx| {
2522 callback(window, cx);
2523 true
2524 }),
2525 );
2526 activate();
2527 subscription
2528 }
2529
2530 pub fn replace_root<E>(
2532 &mut self,
2533 cx: &mut App,
2534 build_view: impl FnOnce(&mut Window, &mut Context<E>) -> E,
2535 ) -> Entity<E>
2536 where
2537 E: 'static + Render,
2538 {
2539 let view = cx.new(|cx| build_view(self, cx));
2540 self.core.root = Some(view.clone().into());
2541 self.refresh();
2542 view
2543 }
2544
2545 pub fn root<E>(&self) -> Option<Option<Entity<E>>>
2547 where
2548 E: 'static + Render,
2549 {
2550 self.core
2551 .root
2552 .as_ref()
2553 .map(|view| view.clone().downcast::<E>().ok())
2554 }
2555
2556 pub fn window_handle(&self) -> AnyWindowHandle {
2558 self.core.handle
2559 }
2560
2561 pub fn metrics_handle(&self) -> WindowMetricsHandle {
2567 self.core.metrics_handle()
2568 }
2569
2570 pub fn refresh(&mut self) {
2572 if self.core.invalidator.not_drawing() {
2573 self.core.refreshing = true;
2574 self.core.invalidator.set_dirty(true);
2575 }
2576 }
2577
2578 pub fn remove_window(&mut self) {
2580 self.core.removed = true;
2581 }
2582
2583 pub fn focused(&self, cx: &App) -> Option<FocusHandle> {
2585 self.core
2586 .focus
2587 .and_then(|id| FocusHandle::for_id(id, &cx.focus_handles))
2588 }
2589
2590 pub fn focus_lost_restore_target(&self, cx: &App) -> Option<FocusHandle> {
2594 let (_leaf, ancestors) = self.frame_state.focus_lost_path.split_last()?;
2595 ancestors.iter().rev().find_map(|id| {
2596 self.frame_state
2597 .rendered_frame
2598 .dispatch_tree
2599 .focusable_node_id(*id)?;
2600 FocusHandle::for_id(*id, &cx.focus_handles)
2601 })
2602 }
2603
2604 pub fn focus(&mut self, handle: &FocusHandle, cx: &mut App) {
2606 if !self.core.focus_enabled || self.core.focus == Some(handle.id) {
2607 return;
2608 }
2609
2610 self.core.focus = Some(handle.id);
2611 self.core.focus_generation = self.core.focus_generation.wrapping_add(1);
2612 self.clear_pending_keystrokes(cx);
2613
2614 self.refresh();
2615 }
2616
2617 pub fn blur(&mut self, cx: &mut App) {
2619 self.clear_pending_keystrokes(cx);
2620
2621 if !self.core.focus_enabled {
2622 return;
2623 }
2624
2625 if self.core.focus.is_some() {
2626 self.core.focus_generation = self.core.focus_generation.wrapping_add(1);
2627 }
2628 self.core.focus = None;
2629 self.refresh();
2630 }
2631
2632 pub fn disable_focus(&mut self, cx: &mut App) {
2634 self.blur(cx);
2635 self.core.focus_enabled = false;
2636 }
2637
2638 pub fn focus_next(&mut self, cx: &mut App) {
2640 if !self.core.focus_enabled {
2641 return;
2642 }
2643
2644 if let Some(handle) = self
2645 .frame_state
2646 .rendered_frame
2647 .tab_stops
2648 .next(self.core.focus.as_ref())
2649 {
2650 self.focus(&handle, cx)
2651 }
2652 }
2653
2654 pub fn focus_prev(&mut self, cx: &mut App) {
2656 if !self.core.focus_enabled {
2657 return;
2658 }
2659
2660 if let Some(handle) = self
2661 .frame_state
2662 .rendered_frame
2663 .tab_stops
2664 .prev(self.core.focus.as_ref())
2665 {
2666 self.focus(&handle, cx)
2667 }
2668 }
2669
2670 pub fn text_system(&self) -> &Arc<WindowTextSystem> {
2672 &self.core.text_system
2673 }
2674
2675 pub fn text_style(&self) -> TextStyle {
2677 let mut style = TextStyle::default();
2678 for refinement in &self.frame_state.text_style_stack {
2679 style.refine(refinement);
2680 }
2681 style
2682 }
2683
2684 pub fn is_maximized(&self) -> bool {
2688 self.core.platform_window.is_maximized()
2689 }
2690
2691 pub fn request_decorations(&self, decorations: WindowDecorations) {
2693 self.core.platform_window.request_decorations(decorations);
2694 }
2695
2696 pub fn set_exclusive_zone(&self, zone: Pixels) {
2702 self.core.platform_window.set_exclusive_zone(zone);
2703 }
2704
2705 #[cfg(all(target_os = "linux", feature = "wayland"))]
2710 pub fn set_exclusive_edge(&self, edge: crate::layer_shell::Anchor) {
2711 self.core.platform_window.set_exclusive_edge(edge);
2712 }
2713
2714 pub fn start_window_resize(&self, edge: ResizeEdge) {
2716 if self.core.is_resizable {
2717 self.core.platform_window.start_window_resize(edge);
2718 }
2719 }
2720
2721 pub fn set_input_region(&self, region: Option<&[Bounds<Pixels>]>) {
2728 self.core.platform_window.set_input_region(region);
2729 }
2730
2731 pub fn window_bounds(&self) -> WindowBounds {
2734 self.core.platform_window.window_bounds()
2735 }
2736
2737 pub fn inner_window_bounds(&self) -> WindowBounds {
2739 self.core.platform_window.inner_window_bounds()
2740 }
2741
2742 pub fn native_window_state(&self) -> Option<Vec<u8>> {
2745 self.core.platform_window.native_window_state()
2746 }
2747
2748 pub fn restore_native_window_state(&self, state: &[u8]) {
2751 self.core.platform_window.restore_native_window_state(state);
2752 }
2753
2754 pub fn dispatch_action(&mut self, action: Box<dyn Action>, cx: &mut App) {
2756 let focus_id = self.focused(cx).map(|handle| handle.id);
2757
2758 let window = self.core.handle;
2759 cx.defer(move |cx| {
2760 window
2761 .update(cx, |_, window, cx| {
2762 let node_id = window.focus_node_id_in_rendered_frame(focus_id);
2763 window.dispatch_action_on_node(node_id, action.as_ref(), cx);
2764 })
2765 .log_err();
2766 })
2767 }
2768
2769 pub(crate) fn dispatch_keystroke_observers(
2770 &mut self,
2771 keystroke: &Keystroke,
2772 input_preference: InputPreference,
2773 action: Option<&dyn Action>,
2774 context_stack: Vec<KeyContext>,
2775 cx: &mut App,
2776 ) {
2777 cx.keystroke_observers.clone().retain(&(), move |callback| {
2778 (callback)(
2779 &KeystrokeEvent {
2780 keystroke: keystroke.clone(),
2781 input_preference,
2782 action: action.map(|action| action.boxed_clone()),
2783 context_stack: context_stack.clone(),
2784 },
2785 self,
2786 cx,
2787 )
2788 });
2789 }
2790
2791 pub(crate) fn dispatch_keystroke_interceptors(
2792 &mut self,
2793 keystroke: &Keystroke,
2794 input_preference: InputPreference,
2795 context_stack: Vec<KeyContext>,
2796 cx: &mut App,
2797 ) {
2798 cx.keystroke_interceptors
2799 .clone()
2800 .retain(&(), move |callback| {
2801 (callback)(
2802 &KeystrokeEvent {
2803 keystroke: keystroke.clone(),
2804 input_preference,
2805 action: None,
2806 context_stack: context_stack.clone(),
2807 },
2808 self,
2809 cx,
2810 )
2811 });
2812 }
2813
2814 pub fn defer(&self, cx: &mut App, f: impl FnOnce(&mut Window, &mut App) + 'static) {
2817 let handle = self.core.handle;
2818 cx.defer(move |cx| {
2819 handle.update(cx, |_, window, cx| f(window, cx)).ok();
2820 });
2821 }
2822
2823 pub fn observe<T: 'static>(
2827 &mut self,
2828 observed: &Entity<T>,
2829 cx: &mut App,
2830 mut on_notify: impl FnMut(Entity<T>, &mut Window, &mut App) + 'static,
2831 ) -> Subscription {
2832 let entity_id = observed.entity_id();
2833 let observed = observed.downgrade();
2834 let window_handle = self.core.handle;
2835 cx.new_observer(
2836 entity_id,
2837 Box::new(move |cx| {
2838 window_handle
2839 .update(cx, |_, window, cx| {
2840 if let Some(handle) = observed.upgrade() {
2841 on_notify(handle, window, cx);
2842 true
2843 } else {
2844 false
2845 }
2846 })
2847 .unwrap_or(false)
2848 }),
2849 )
2850 }
2851
2852 pub fn subscribe<Emitter, Evt>(
2856 &mut self,
2857 entity: &Entity<Emitter>,
2858 cx: &mut App,
2859 mut on_event: impl FnMut(Entity<Emitter>, &Evt, &mut Window, &mut App) + 'static,
2860 ) -> Subscription
2861 where
2862 Emitter: EventEmitter<Evt>,
2863 Evt: 'static,
2864 {
2865 let entity_id = entity.entity_id();
2866 let handle = entity.downgrade();
2867 let window_handle = self.core.handle;
2868 cx.new_subscription(
2869 entity_id,
2870 (
2871 TypeId::of::<Evt>(),
2872 Box::new(move |event, cx| {
2873 window_handle
2874 .update(cx, |_, window, cx| {
2875 if let Some(entity) = handle.upgrade() {
2876 let event = event.downcast_ref().expect("invalid event type");
2877 on_event(entity, event, window, cx);
2878 true
2879 } else {
2880 false
2881 }
2882 })
2883 .unwrap_or(false)
2884 }),
2885 ),
2886 )
2887 }
2888
2889 pub fn observe_release<T>(
2891 &self,
2892 entity: &Entity<T>,
2893 cx: &mut App,
2894 mut on_release: impl FnOnce(&mut T, &mut Window, &mut App) + 'static,
2895 ) -> Subscription
2896 where
2897 T: 'static,
2898 {
2899 let entity_id = entity.entity_id();
2900 let window_handle = self.core.handle;
2901 let (subscription, activate) = cx.release_listeners.insert(
2902 entity_id,
2903 Box::new(move |entity, cx| {
2904 let entity = entity.downcast_mut().expect("invalid entity type");
2905 let _ = window_handle.update(cx, |_, window, cx| on_release(entity, window, cx));
2906 }),
2907 );
2908 activate();
2909 subscription
2910 }
2911
2912 pub fn to_async(&self, cx: &App) -> AsyncWindowContext {
2915 AsyncWindowContext::new_context(cx.to_async(), self.core.handle)
2916 }
2917
2918 pub fn on_next_frame(&self, callback: impl FnOnce(&mut Window, &mut App) + 'static) {
2920 RefCell::borrow_mut(&self.core.next_frame_callbacks).push(Box::new(callback));
2921 self.core.platform_window.schedule_frame();
2922 self.core.invalidator.wake_platform();
2925 }
2926
2927 pub fn request_animation_frame(&self) {
2940 let entity = self.current_view();
2941 self.on_next_frame(move |_, cx| cx.notify(entity));
2942 }
2943
2944 #[cfg(any(test, feature = "test-support"))]
2949 pub fn simulate_next_frame(&mut self, cx: &mut App) -> usize {
2950 let callbacks = self.core.next_frame_callbacks.take();
2951 let count = callbacks.len();
2952 for callback in callbacks {
2953 callback(self, cx);
2954 }
2955 count
2956 }
2957
2958 #[track_caller]
2962 pub fn spawn<AsyncFn, R>(&self, cx: &App, f: AsyncFn) -> Task<R>
2963 where
2964 R: 'static,
2965 AsyncFn: AsyncFnOnce(&mut AsyncWindowContext) -> R + 'static,
2966 {
2967 let handle = self.core.handle;
2968 cx.spawn(async move |app| {
2969 let mut async_window_cx = AsyncWindowContext::new_context(app.clone(), handle);
2970 f(&mut async_window_cx).await
2971 })
2972 }
2973
2974 #[track_caller]
2978 pub fn spawn_with_priority<AsyncFn, R>(
2979 &self,
2980 priority: Priority,
2981 cx: &App,
2982 f: AsyncFn,
2983 ) -> Task<R>
2984 where
2985 R: 'static,
2986 AsyncFn: AsyncFnOnce(&mut AsyncWindowContext) -> R + 'static,
2987 {
2988 let handle = self.core.handle;
2989 cx.spawn_with_priority(priority, async move |app| {
2990 let mut async_window_cx = AsyncWindowContext::new_context(app.clone(), handle);
2991 f(&mut async_window_cx).await
2992 })
2993 }
2994
2995 fn sync_metrics(&self) {
3001 self.core.metrics.store(Arc::new(WindowMetrics {
3002 bounds: self.core.platform_window.bounds(),
3003 viewport: self.core.platform_window.visual_viewport_bounds(),
3004 content_size: self.core.viewport_size,
3005 scale_factor: self.core.scale_factor,
3006 display_id: self.core.display_id,
3007 is_active: self.core.active.get(),
3008 is_fullscreen: self.core.platform_window.is_fullscreen(),
3009 }));
3010 }
3011
3012 pub fn bounds_changed(&mut self, cx: &mut App) {
3018 self.core.scale_factor = self.core.platform_window.scale_factor();
3019 self.core.viewport_size = self.core.platform_window.content_size();
3020 self.core.display_id = self
3021 .core
3022 .platform_window
3023 .display()
3024 .map(|display| display.id());
3025 self.core.mouse_position = self.core.platform_window.mouse_position();
3026
3027 self.sync_metrics();
3028 self.refresh();
3029
3030 self.core
3031 .bounds_observers
3032 .clone()
3033 .retain(&(), |callback| callback(self, cx));
3034 }
3035
3036 pub fn bounds(&self) -> Bounds<Pixels> {
3038 self.core.platform_window.bounds()
3039 }
3040
3041 #[cfg(any(test, feature = "test-support"))]
3045 pub fn render_to_image(&mut self) -> anyhow::Result<image::RgbaImage> {
3046 let size = self.bounds().size.to_device_pixels(self.scale_factor());
3047 let scene = &self.frame_state.rendered_frame.scene;
3048 let mut result = None;
3049 self.core.platform_window.with_renderer(&mut |renderer| {
3050 result = Some(renderer.render_scene_to_image(scene, size));
3051 });
3052 let pixels = result
3053 .unwrap_or_else(|| anyhow::bail!("platform window does not support image capture"))?;
3054 let (width, height) = (pixels.width(), pixels.height());
3055 image::RgbaImage::from_raw(width, height, pixels.into_data()).ok_or_else(|| {
3056 anyhow::anyhow!("the renderer returned a {width}x{height} pixel buffer of the wrong length")
3057 })
3058 }
3059
3060 #[cfg(any(test, feature = "test-support"))]
3065 pub fn painted_quads(&self) -> Vec<Quad> {
3066 self.frame_state.rendered_frame.scene.quads.clone()
3067 }
3068
3069 #[cfg(any(test, feature = "test-support"))]
3071 pub fn painted_underlines(&self) -> Vec<Underline> {
3072 self.frame_state.rendered_frame.scene.underlines.clone()
3073 }
3074
3075 #[cfg(any(test, feature = "test-support"))]
3079 pub fn painted_surfaces(&self) -> Vec<PaintSurface> {
3080 self.frame_state.rendered_frame.scene.surfaces.clone()
3081 }
3082
3083 #[cfg(any(test, feature = "test-support"))]
3088 pub fn painted_imported_textures(&self) -> Vec<CustomRenderPrimitive> {
3089 self.frame_state.rendered_frame.scene.custom.clone()
3090 }
3091
3092 pub fn resize(&mut self, size: Size<Pixels>) {
3094 self.core.platform_window.resize(size);
3095 }
3096
3097 pub fn is_fullscreen(&self) -> bool {
3099 self.core.platform_window.is_fullscreen()
3100 }
3101
3102 pub fn is_simple_fullscreen(&self) -> bool {
3106 self.core.platform_window.is_simple_fullscreen()
3107 }
3108
3109 pub(crate) fn appearance_changed(&mut self, cx: &mut App) {
3110 self.core.appearance = self.core.platform_window.appearance();
3111
3112 self.core
3113 .appearance_observers
3114 .clone()
3115 .retain(&(), |callback| callback(self, cx));
3116 }
3117
3118 pub(crate) fn button_layout_changed(&mut self, cx: &mut App) {
3119 self.core
3120 .button_layout_observers
3121 .clone()
3122 .retain(&(), |callback| callback(self, cx));
3123 }
3124
3125 pub fn appearance(&self) -> WindowAppearance {
3127 self.core.appearance
3128 }
3129
3130 pub fn viewport_size(&self) -> Size<Pixels> {
3132 self.core.viewport_size
3133 }
3134
3135 pub fn visual_viewport_bounds(&self) -> Bounds<Pixels> {
3141 self.core.platform_window.visual_viewport_bounds()
3142 }
3143
3144 pub fn fully_visible_bounds(&self) -> Bounds<Pixels> {
3149 let insets = self.core.platform_window.insets().effective();
3150 let viewport = self.viewport_size();
3151 let left = insets.left.max(Pixels::ZERO).min(viewport.width);
3152 let top = insets.top.max(Pixels::ZERO).min(viewport.height);
3153 let right = (viewport.width - insets.right.max(Pixels::ZERO)).max(left);
3154 let bottom = (viewport.height - insets.bottom.max(Pixels::ZERO)).max(top);
3155 let safe_bounds = Bounds::from_corners(point(left, top), point(right, bottom));
3156 let mut visible = safe_bounds.intersect(&self.visual_viewport_bounds());
3157 visible.size.width = visible.size.width.max(Pixels::ZERO);
3158 visible.size.height = visible.size.height.max(Pixels::ZERO);
3159 visible
3160 }
3161
3162 pub fn request_virtual_keyboard(&self) {
3167 self.core.platform_window.show_soft_keyboard();
3168 }
3169
3170 pub fn dismiss_virtual_keyboard(&self) {
3172 self.core.platform_window.hide_soft_keyboard();
3173 }
3174
3175 pub fn is_window_active(&self) -> bool {
3177 self.core.active.get()
3178 }
3179
3180 pub fn is_window_hovered(&self) -> bool {
3184 if cfg!(any(
3185 target_os = "windows",
3186 target_os = "linux",
3187 target_os = "freebsd"
3188 )) {
3189 self.core.hovered.get()
3190 } else {
3191 self.is_window_active()
3192 }
3193 }
3194
3195 pub fn zoom_window(&self) {
3197 self.core.platform_window.zoom();
3198 }
3199
3200 pub fn show_window_menu(&self, position: Point<Pixels>) {
3202 self.core.platform_window.show_window_menu(position)
3203 }
3204
3205 pub fn start_window_move(&self) {
3210 self.core.platform_window.start_window_move()
3211 }
3212
3213 pub fn set_client_inset(&mut self, inset: Pixels) {
3215 self.core.client_inset = Some(inset);
3216 self.core.platform_window.set_client_inset(inset);
3217 }
3218
3219 pub fn client_inset(&self) -> Option<Pixels> {
3221 self.core.client_inset
3222 }
3223
3224 pub fn window_decorations(&self) -> Decorations {
3226 self.core.platform_window.window_decorations()
3227 }
3228
3229 pub fn is_resizable(&self) -> bool {
3231 self.core.is_resizable
3232 }
3233
3234 pub fn is_minimizable(&self) -> bool {
3236 self.core.is_minimizable
3237 }
3238
3239 pub fn window_controls(&self) -> WindowControls {
3241 self.core.platform_window.window_controls()
3242 }
3243
3244 pub fn set_window_title(&mut self, title: &str) {
3246 self.core.platform_window.set_title(title);
3247 self.core.a11y.set_window_title(title.to_string());
3248 }
3249
3250 #[cfg(target_os = "macos")]
3252 pub fn set_traffic_light_position(&self, position: Point<Pixels>) {
3253 self.core
3254 .platform_window
3255 .set_traffic_light_position(position);
3256 }
3257
3258 pub fn set_app_id(&mut self, app_id: &str) {
3260 self.core.platform_window.set_app_id(app_id);
3261 }
3262
3263 pub fn set_background_appearance(&self, background_appearance: WindowBackgroundAppearance) {
3265 self.core
3266 .platform_window
3267 .set_background_appearance(background_appearance);
3268 }
3269
3270 pub fn set_window_edited(&mut self, edited: bool) {
3272 self.core.platform_window.set_edited(edited);
3273 }
3274
3275 pub fn set_document_path(&self, path: Option<&std::path::Path>) {
3278 self.core.platform_window.set_document_path(path);
3279 }
3280
3281 pub fn display(&self, cx: &App) -> Option<Rc<dyn PlatformDisplay>> {
3283 cx.platform
3284 .displays()
3285 .into_iter()
3286 .find(|display| Some(display.id()) == self.core.display_id)
3287 }
3288
3289 pub fn show_character_palette(&self) {
3291 self.core.platform_window.show_character_palette();
3292 }
3293
3294 pub fn scale_factor(&self) -> f32 {
3298 self.core.scale_factor
3299 }
3300
3301 #[cfg(any(test, feature = "test-support"))]
3303 pub fn set_scale_factor(&mut self, scale_factor: f32) {
3304 self.core.scale_factor = scale_factor;
3305 self.sync_metrics();
3306 self.refresh();
3307 }
3308
3309 pub fn rem_size(&self) -> Pixels {
3312 self.frame_state
3313 .rem_size_override_stack
3314 .last()
3315 .copied()
3316 .unwrap_or(self.core.rem_size)
3317 }
3318
3319 pub fn set_rem_size(&mut self, rem_size: impl Into<Pixels>) {
3322 self.core.rem_size = rem_size.into();
3323 }
3324
3325 pub fn with_global_id<R>(
3328 &mut self,
3329 element_id: ElementId,
3330 f: impl FnOnce(&GlobalElementId, &mut Self) -> R,
3331 ) -> R {
3332 self.with_id(element_id, |this| {
3333 let global_id = GlobalElementId(Arc::from(&*this.frame_state.element_id_stack));
3334
3335 f(&global_id, this)
3336 })
3337 }
3338
3339 #[inline]
3341 pub fn with_id<R>(
3342 &mut self,
3343 element_id: impl Into<ElementId>,
3344 f: impl FnOnce(&mut Self) -> R,
3345 ) -> R {
3346 self.frame_state.element_id_stack.push(element_id.into());
3347 let result = f(self);
3348 self.frame_state.element_id_stack.pop();
3349 result
3350 }
3351
3352 #[inline]
3358 pub fn with_rem_size<F, R>(&mut self, rem_size: Option<impl Into<Pixels>>, f: F) -> R
3359 where
3360 F: FnOnce(&mut Self) -> R,
3361 {
3362 self.core.invalidator.debug_assert_paint_or_prepaint();
3363
3364 if let Some(rem_size) = rem_size {
3365 self.frame_state
3366 .rem_size_override_stack
3367 .push(rem_size.into());
3368 let result = f(self);
3369 self.frame_state.rem_size_override_stack.pop();
3370 result
3371 } else {
3372 f(self)
3373 }
3374 }
3375
3376 pub fn line_height(&self) -> Pixels {
3378 self.text_style().line_height_in_pixels(self.rem_size())
3379 }
3380
3381 #[inline]
3383 pub fn pixel_snap(&self, value: Pixels) -> Pixels {
3384 px(round_to_device_pixel(value.0, self.scale_factor()) / self.scale_factor())
3385 }
3386
3387 #[inline]
3389 pub fn pixel_snap_f64(&self, value: f64) -> f64 {
3390 let scale_factor = f64::from(self.scale_factor());
3391 round_half_toward_zero_f64(value * scale_factor) / scale_factor
3392 }
3393
3394 #[inline]
3396 pub fn pixel_snap_bounds(&self, bounds: Bounds<Pixels>) -> Bounds<Pixels> {
3397 bounds.map(|c| self.pixel_snap(c))
3398 }
3399
3400 #[inline]
3402 pub fn pixel_snap_point(&self, position: Point<Pixels>) -> Point<Pixels> {
3403 position.map(|c| self.pixel_snap(c))
3404 }
3405
3406 pub fn underline_bounds(
3408 &self,
3409 origin: Point<Pixels>,
3410 width: Pixels,
3411 style: &UnderlineStyle,
3412 ) -> Bounds<ScaledPixels> {
3413 let scale_factor = self.scale_factor();
3414 let thickness = self.snap_stroke(style.thickness);
3415 let height = if style.wavy {
3416 ScaledPixels(thickness.0 * 3.)
3417 } else {
3418 thickness
3419 };
3420 Bounds {
3421 origin: origin.map(|c| ScaledPixels(round_to_device_pixel(c.0, scale_factor))),
3422 size: size(self.snap_stroke(width), height),
3423 }
3424 }
3425
3426 pub fn paint_underline_with_exclusions(
3428 &mut self,
3429 origin: Point<Pixels>,
3430 width: Pixels,
3431 style: &UnderlineStyle,
3432 exclusions: &[Range<ScaledPixels>],
3433 ) {
3434 self.core.invalidator.debug_assert_paint();
3435 let underline = self.underline(origin, width, style);
3436 if exclusions.is_empty() {
3437 self.frame_state
3438 .next_frame
3439 .scene
3440 .insert_primitive(underline);
3441 return;
3442 }
3443
3444 let bounds = underline.bounds.intersect(&underline.content_mask.bounds);
3445 if bounds.is_empty() {
3446 return;
3447 }
3448
3449 let mut exclusions = exclusions
3450 .iter()
3451 .filter_map(|span| {
3452 let start = span.start.max(bounds.left());
3453 let end = span.end.min(bounds.right());
3454 (start < end).then_some(start..end)
3455 })
3456 .collect::<SmallVec<[_; 4]>>();
3457 if exclusions.is_empty() {
3458 self.frame_state
3459 .next_frame
3460 .scene
3461 .insert_primitive(underline);
3462 return;
3463 }
3464 exclusions.sort_unstable_by_key(|span| span.start);
3465
3466 let mut paint_span = |start, end| {
3467 self.frame_state
3468 .next_frame
3469 .scene
3470 .insert_primitive(Underline {
3471 content_mask: ContentMask {
3472 bounds: Bounds::from_corners(
3473 point(start, bounds.top()),
3474 point(end, bounds.bottom()),
3475 ),
3476 },
3477 ..underline
3478 });
3479 };
3480 let mut start = bounds.left();
3481 for exclusion in exclusions {
3482 if start < exclusion.start {
3483 paint_span(start, exclusion.start);
3484 }
3485 start = start.max(exclusion.end);
3486 }
3487 if start < bounds.right() {
3488 paint_span(start, bounds.right());
3489 }
3490 }
3491
3492 fn underline(&self, origin: Point<Pixels>, width: Pixels, style: &UnderlineStyle) -> Underline {
3493 Underline {
3494 order: 0,
3495 pad: 0,
3496 bounds: self.underline_bounds(origin, width, style),
3497 content_mask: self.snapped_content_mask(),
3498 color: style
3499 .color
3500 .unwrap_or_default()
3501 .opacity(self.element_opacity()),
3502 thickness: self.snap_stroke(style.thickness),
3503 wavy: style.wavy.into(),
3504 }
3505 }
3506
3507 #[inline]
3508 pub(crate) fn snap_bounds(&self, bounds: Bounds<Pixels>) -> Bounds<ScaledPixels> {
3509 let scale_factor = self.scale_factor();
3510 let left = round_to_device_pixel(bounds.left().0, scale_factor);
3511 let top = round_to_device_pixel(bounds.top().0, scale_factor);
3512 let right = round_to_device_pixel(bounds.right().0, scale_factor).max(left);
3513 let bottom = round_to_device_pixel(bounds.bottom().0, scale_factor).max(top);
3514 Bounds::from_corners(
3515 point(ScaledPixels(left), ScaledPixels(top)),
3516 point(ScaledPixels(right), ScaledPixels(bottom)),
3517 )
3518 }
3519
3520 #[inline]
3522 fn snap_stroke(&self, value: Pixels) -> ScaledPixels {
3523 ScaledPixels(round_stroke_to_device_pixel(value.0, self.scale_factor()))
3524 }
3525
3526 #[inline]
3527 fn snap_border_widths(&self, edges: Edges<Pixels>) -> Edges<ScaledPixels> {
3528 edges.map(|e| self.snap_stroke(*e))
3529 }
3530
3531 #[inline]
3533 fn cover_bounds(&self, bounds: Bounds<Pixels>) -> Bounds<ScaledPixels> {
3534 let scale_factor = self.scale_factor();
3535 let left = floor_to_device_pixel(bounds.left().0, scale_factor);
3536 let top = floor_to_device_pixel(bounds.top().0, scale_factor);
3537 let right = ceil_to_device_pixel(bounds.right().0, scale_factor).max(left);
3538 let bottom = ceil_to_device_pixel(bounds.bottom().0, scale_factor).max(top);
3539 Bounds::from_corners(
3540 point(ScaledPixels(left), ScaledPixels(top)),
3541 point(ScaledPixels(right), ScaledPixels(bottom)),
3542 )
3543 }
3544
3545 #[inline]
3546 pub(crate) fn snapped_content_mask(&self) -> ContentMask<ScaledPixels> {
3547 ContentMask {
3548 bounds: self.cover_bounds(self.content_mask().bounds),
3549 }
3550 }
3551
3552 pub fn prevent_default(&mut self) {
3555 self.core.default_prevented = true;
3556 }
3557
3558 pub fn default_prevented(&self) -> bool {
3560 self.core.default_prevented
3561 }
3562
3563 pub fn is_action_available(&self, action: &dyn Action, cx: &App) -> bool {
3565 let node_id =
3566 self.focus_node_id_in_rendered_frame(self.focused(cx).map(|handle| handle.id));
3567 self.frame_state
3568 .rendered_frame
3569 .dispatch_tree
3570 .is_action_available(action, node_id)
3571 }
3572
3573 pub fn is_action_available_in(&self, action: &dyn Action, focus_handle: &FocusHandle) -> bool {
3575 let node_id = self.focus_node_id_in_rendered_frame(Some(focus_handle.id));
3576 self.frame_state
3577 .rendered_frame
3578 .dispatch_tree
3579 .is_action_available(action, node_id)
3580 }
3581
3582 pub fn mouse_position(&self) -> Point<Pixels> {
3584 self.core.mouse_position
3585 }
3586
3587 pub fn capture_pointer(&mut self, hitbox_id: HitboxId) {
3594 self.core.captured_hitbox = Some(hitbox_id);
3595 }
3596
3597 pub fn release_pointer(&mut self) {
3599 self.core.captured_hitbox = None;
3600 }
3601
3602 pub fn captured_hitbox(&self) -> Option<HitboxId> {
3604 self.core.captured_hitbox
3605 }
3606
3607 pub fn capture_long_press<T: 'static>(&mut self, entity: &Entity<T>) {
3613 self.core.long_press_capture = Some(entity.entity_id());
3614 }
3615
3616 pub fn has_long_press_capture<T: 'static>(&self, entity: &Entity<T>) -> bool {
3618 self.core.long_press_capture == Some(entity.entity_id())
3619 }
3620
3621 pub fn modifiers(&self) -> Modifiers {
3623 self.core.modifiers
3624 }
3625
3626 pub fn last_input_was_keyboard(&self) -> bool {
3629 self.core.last_input_modality == InputModality::Keyboard
3630 }
3631
3632 pub(crate) fn last_input_was_touch(&self) -> bool {
3633 self.core.last_input_modality == InputModality::Touch
3634 }
3635
3636 pub fn capslock(&self) -> Capslock {
3638 self.core.capslock
3639 }
3640
3641 #[doc(hidden)]
3648 #[profiling::function]
3649 pub fn draw(&mut self, cx: &mut App) -> ArenaClearNeeded {
3650 let pipeline = self.core.frame_pipeline.clone();
3651 pipeline.borrow_mut().draw(self, cx)
3652 }
3653
3654 pub fn begin_frame(&mut self, cx: &mut App) {
3659 #[cfg(debug_assertions)]
3660 self.frame_state.frame_phase.enter(
3661 &[FramePhase::Idle, FramePhase::Completed],
3662 FramePhase::Begun,
3663 "begin_frame",
3664 );
3665 if self.core.platform_window.prepare_frame() {
3666 self.refresh();
3667 }
3668 self.sync_metrics();
3669 self.invalidate_entities();
3670 cx.entities.clear_accessed();
3671 debug_assert!(self.frame_state.rendered_entity_stack.is_empty());
3672 self.core.invalidator.set_dirty(false);
3673 self.frame_state.requested_autoscroll = None;
3674 }
3675
3676 fn restore_input_handler(&mut self) {
3681 if let Some(input_handler) = self.core.platform_window.take_input_handler() {
3682 if let Some(slot) = self
3683 .frame_state
3684 .rendered_frame
3685 .input_handlers
3686 .iter_mut()
3687 .rev()
3688 .find(|h| h.is_none())
3689 {
3690 *slot = Some(input_handler);
3691 } else {
3692 self.frame_state
3693 .rendered_frame
3694 .input_handlers
3695 .push(Some(input_handler));
3696 }
3697 }
3698 }
3699
3700 pub fn finish_frame(&mut self, cx: &mut App) {
3710 #[cfg(debug_assertions)]
3711 self.frame_state.frame_phase.enter(
3712 &[FramePhase::Begun, FramePhase::Painted],
3713 FramePhase::Finished,
3714 "finish_frame",
3715 );
3716 self.frame_state.dirty_views.clear();
3717 self.frame_state.next_frame.window_active = self.core.active.get();
3718
3719 let focused_text_input_active = if let Some(mut input_handler) = self
3724 .frame_state
3725 .next_frame
3726 .input_handlers
3727 .iter_mut()
3728 .rev()
3729 .find_map(|h| h.take())
3730 {
3731 let accepts_text_input = input_handler.accepts_text_input(self, cx);
3732 self.core.platform_window.set_input_handler(input_handler);
3733 accepts_text_input
3734 } else {
3735 false
3736 };
3737 self.apply_text_input_configuration(cx);
3738 if focused_text_input_active != self.core.focused_text_input_active {
3739 self.core.focused_text_input_active = focused_text_input_active;
3740 self.core
3741 .platform_window
3742 .text_input_state_changed(if focused_text_input_active {
3743 TextInputStateChange::FocusGained
3744 } else {
3745 TextInputStateChange::FocusLost
3746 });
3747 }
3748
3749 self.frame_state.layout_session.clear();
3750 self.text_system().finish_frame();
3751 self.frame_state
3752 .next_frame
3753 .finish(&mut self.frame_state.rendered_frame);
3754 }
3755
3756 pub fn complete_frame(&mut self, cx: &mut App) -> Option<FocusId> {
3764 #[cfg(debug_assertions)]
3765 self.frame_state.frame_phase.enter(
3766 &[FramePhase::Finished],
3767 FramePhase::Completed,
3768 "complete_frame",
3769 );
3770 self.core.invalidator.set_phase(DrawPhase::Focus);
3771 let previous_focus_path = self.frame_state.rendered_frame.focus_path();
3772 let previous_window_active = self.frame_state.rendered_frame.window_active;
3773 mem::swap(
3774 &mut self.frame_state.rendered_frame,
3775 &mut self.frame_state.next_frame,
3776 );
3777 self.frame_state.next_frame.clear();
3778 let current_focus_path = self.frame_state.rendered_frame.focus_path();
3779 let current_window_active = self.frame_state.rendered_frame.window_active;
3780 let mut focus_before_listeners = self.core.focus;
3781
3782 if previous_focus_path != current_focus_path
3783 || previous_window_active != current_window_active
3784 {
3785 if !previous_focus_path.is_empty() && current_focus_path.is_empty() {
3786 self.frame_state.focus_lost_path = previous_focus_path.clone();
3787 self.core
3788 .focus_lost_listeners
3789 .clone()
3790 .retain(&(), |listener| listener(self, cx));
3791 self.frame_state.focus_lost_path = SmallVec::new();
3792 focus_before_listeners = self.core.focus;
3797 }
3798
3799 let event = WindowFocusEvent {
3800 previous_focus_path: if previous_window_active {
3801 previous_focus_path
3802 } else {
3803 Default::default()
3804 },
3805 current_focus_path: if current_window_active {
3806 current_focus_path
3807 } else {
3808 Default::default()
3809 },
3810 };
3811 self.core
3812 .focus_listeners
3813 .clone()
3814 .retain(&(), |listener| listener(&event, self, cx));
3815 }
3816
3817 focus_before_listeners
3818 }
3819
3820 pub fn end_frame(&mut self, cx: &mut App, focus_before_listeners: Option<FocusId>) {
3824 #[cfg(debug_assertions)]
3825 self.frame_state
3826 .frame_phase
3827 .enter(&[FramePhase::Completed], FramePhase::Idle, "end_frame");
3828 debug_assert!(self.frame_state.rendered_entity_stack.is_empty());
3829 self.record_entities_accessed(cx);
3830 self.reset_cursor_style(cx);
3831 self.core.refreshing = false;
3832 self.core.invalidator.set_phase(DrawPhase::None);
3833 if self.core.focus != focus_before_listeners {
3838 self.refresh();
3839 }
3840 self.core.needs_present.set(true);
3841 }
3842 fn record_entities_accessed(&mut self, cx: &mut App) {
3843 let mut entities_ref = cx.entities.accessed_entities.get_mut();
3844 let mut entities = mem::take(entities_ref.deref_mut());
3845 let handle = self.core.handle;
3846 cx.record_entities_accessed(
3847 handle,
3848 self.core.invalidator.clone(),
3850 &entities,
3851 );
3852 let mut entities_ref = cx.entities.accessed_entities.get_mut();
3853 mem::swap(&mut entities, entities_ref.deref_mut());
3854 }
3855
3856 fn invalidate_entities(&mut self) {
3857 let mut views = self.core.invalidator.take_views();
3858 for entity in views.drain() {
3859 self.mark_view_dirty(entity);
3860 }
3861 self.core.invalidator.replace_views(views);
3862 }
3863
3864 #[profiling::function]
3865 fn present(&mut self) {
3866 #[cfg(feature = "profiler")]
3867 let _foreground_turn = profiler::journal::foreground_turn();
3868 #[cfg(feature = "profiler")]
3869 let present_start = Instant::now();
3870 self.core
3871 .platform_window
3872 .present(&mut |renderer| renderer.draw(&self.frame_state.rendered_frame.scene));
3873 #[cfg(feature = "profiler")]
3874 self.core.window_profiler.record_present(
3875 present_start,
3876 Instant::now(),
3877 self.core.active.get(),
3878 !self.core.next_frame_callbacks.borrow().is_empty(),
3879 );
3880 self.core.needs_present.set(false);
3881 profiling::finish_frame!();
3882 }
3883
3884 #[cfg(all(test, feature = "profiler"))]
3886 pub(crate) fn present_if_needed(&mut self) {
3887 if self.core.needs_present.get() {
3888 self.present();
3889 }
3890 }
3891
3892 #[cfg(feature = "profiler")]
3894 pub fn input_latency_snapshot(&self) -> profiler::InputLatencySnapshot {
3895 self.core.window_profiler.input_latency_snapshot()
3896 }
3897
3898 #[cfg(feature = "profiler")]
3900 pub fn frame_duration_snapshot(&self) -> profiler::FrameDurationSnapshot {
3901 self.core.window_profiler.frame_duration_snapshot()
3902 }
3903
3904 #[cfg(feature = "profiler")]
3906 pub fn debug_frame_overlay_mode(&self) -> DebugFrameOverlayMode {
3907 self.core.debug_frame_overlay.mode()
3908 }
3909
3910 #[cfg(feature = "profiler")]
3912 pub fn set_debug_frame_overlay_mode(&mut self, mode: DebugFrameOverlayMode) {
3913 self.core.debug_frame_overlay.set_mode(mode);
3914 self.refresh();
3915 }
3916
3917 #[cfg(feature = "profiler")]
3920 pub fn cycle_debug_frame_overlay_mode(&mut self) {
3921 self.set_debug_frame_overlay_mode(self.core.debug_frame_overlay.mode().next());
3922 }
3923
3924 #[cfg(feature = "profiler")]
3927 pub fn reset_debug_frame_overlay_stats(&mut self) {
3928 self.core.debug_frame_overlay.reset_stats();
3929 self.refresh();
3930 }
3931
3932 pub fn evaluate_roots(&mut self, cx: &mut App) -> PreparedRoots {
3938 #[cfg(debug_assertions)]
3939 self.frame_state.frame_phase.enter(
3940 &[FramePhase::Begun],
3941 FramePhase::Evaluated,
3942 "evaluate_roots",
3943 );
3944 let root = self.core.root.as_ref().unwrap().clone().into_any_element();
3945
3946 let mut prompt = None;
3949 let mut drag = None;
3950 if let Some(handle) = self.core.prompt.take() {
3951 prompt = Some(handle.view.any_view().into_any_element());
3952 self.core.prompt = Some(handle);
3953 } else if let Some(active_drag) = cx.active_drag.take() {
3954 let origin = self.mouse_position() - active_drag.cursor_offset;
3955 drag = Some((active_drag.view.clone().into_any_element(), origin));
3956 cx.active_drag = Some(active_drag);
3957 }
3958
3959 PreparedRoots {
3960 root,
3961 prompt,
3962 tooltip: None,
3963 drag,
3964 inspector: None,
3965 }
3966 }
3967
3968 pub fn layout_roots(&mut self, roots: &mut PreparedRoots, cx: &mut App) {
3971 #[cfg(debug_assertions)]
3972 self.frame_state.frame_phase.enter(
3973 &[FramePhase::Evaluated],
3974 FramePhase::LaidOut,
3975 "layout_roots",
3976 );
3977 self.core.invalidator.set_phase(DrawPhase::Prepaint);
3978 self.frame_state.tooltip_bounds.take();
3979
3980 self.core.a11y.sync_active_flag();
3981 if self.core.a11y.is_active() {
3982 self.core.a11y.begin_frame();
3983 }
3984
3985 #[cfg(any(feature = "inspector", debug_assertions))]
3986 let inspector_width: Pixels = rems(30.0).to_pixels(self.rem_size());
3987 let root_size = {
3988 #[cfg(any(feature = "inspector", debug_assertions))]
3989 {
3990 if self.core.inspector.is_some() {
3991 let mut size = self.core.viewport_size;
3992 size.width = (size.width - inspector_width).max(px(0.0));
3993 size
3994 } else {
3995 self.core.viewport_size
3996 }
3997 }
3998 #[cfg(not(any(feature = "inspector", debug_assertions)))]
3999 {
4000 self.core.viewport_size
4001 }
4002 };
4003
4004 let scale_factor = self.scale_factor();
4008 let root_layout_id = roots.root.request_layout(self, cx);
4009 self.frame_state.layout_session.stretch_auto_size_to_fill(
4010 root_layout_id,
4011 root_size,
4012 scale_factor,
4013 );
4014 roots
4015 .root
4016 .prepaint_as_root(Point::default(), root_size.into(), self, cx);
4017
4018 #[cfg(any(feature = "inspector", debug_assertions))]
4019 {
4020 roots.inspector = self.prepaint_inspector(inspector_width, cx);
4021 }
4022
4023 self.prepaint_deferred_draws(cx);
4024
4025 if let Some(prompt) = roots.prompt.as_mut() {
4026 let prompt_layout_id = prompt.request_layout(self, cx);
4027 self.frame_state.layout_session.stretch_auto_size_to_fill(
4028 prompt_layout_id,
4029 root_size,
4030 scale_factor,
4031 );
4032 prompt.prepaint_as_root(Point::default(), root_size.into(), self, cx);
4033 } else if let Some((drag, origin)) = roots.drag.as_mut() {
4034 drag.prepaint_as_root(*origin, AvailableSpace::min_size(), self, cx);
4035 } else {
4036 roots.tooltip = self.prepaint_tooltip(cx);
4037 }
4038
4039 self.core.mouse_hit_test = self
4040 .frame_state
4041 .next_frame
4042 .hit_test(self.core.mouse_position);
4043 }
4044
4045 pub fn paint_roots(&mut self, mut roots: PreparedRoots, cx: &mut App) {
4047 #[cfg(debug_assertions)]
4048 self.frame_state.frame_phase.enter(
4049 &[FramePhase::LaidOut],
4050 FramePhase::Painted,
4051 "paint_roots",
4052 );
4053 self.core.invalidator.set_phase(DrawPhase::Paint);
4054 roots.root.paint(self, cx);
4055
4056 #[cfg(any(feature = "inspector", debug_assertions))]
4057 self.paint_inspector(roots.inspector.take(), cx);
4058
4059 self.paint_deferred_draws(cx);
4060
4061 if let Some(mut prompt) = roots.prompt.take() {
4062 prompt.paint(self, cx);
4063 } else if let Some((mut drag, _)) = roots.drag.take() {
4064 drag.paint(self, cx);
4065 } else if let Some(mut tooltip) = roots.tooltip.take() {
4066 tooltip.paint(self, cx);
4067 }
4068
4069 #[cfg(any(feature = "inspector", debug_assertions))]
4070 self.paint_inspector_hitbox(cx);
4071
4072 let a11y_active_start_of_frame = self.core.a11y.is_active();
4074 self.core.a11y.sync_active_flag();
4075 let a11y_active_end_of_frame = self.core.a11y.is_active();
4076
4077 let should_send_a11y_update = a11y_active_start_of_frame && a11y_active_end_of_frame;
4078
4079 if a11y_active_start_of_frame {
4080 let frame_info = crate::window::a11y::debug::FrameDebugInfo {
4083 viewport_size: self.core.viewport_size,
4084 scale_factor: self.core.scale_factor,
4085 tab_stop_count: self.frame_state.next_frame.tab_stops.tab_stop_count(),
4086 };
4087 let tree_update = self.core.a11y.end_frame(frame_info);
4089
4090 if should_send_a11y_update {
4091 log::debug!(
4092 "Sending a11y tree update: {} nodes",
4093 tree_update.nodes.len()
4094 );
4095 self.core.platform_window.a11y_tree_update(tree_update);
4096 }
4097 }
4098 }
4099
4100 pub fn draw_roots(&mut self, cx: &mut App) {
4106 let mut roots = self.evaluate_roots(cx);
4107 self.layout_roots(&mut roots, cx);
4108 self.paint_roots(roots, cx);
4109 }
4110
4111 fn prepaint_tooltip(&mut self, cx: &mut App) -> Option<AnyElement> {
4112 for tooltip_request_index in (0..self.frame_state.next_frame.tooltip_requests.len()).rev() {
4114 let Some(Some(tooltip_request)) = self
4115 .frame_state
4116 .next_frame
4117 .tooltip_requests
4118 .get(tooltip_request_index)
4119 .cloned()
4120 else {
4121 log::error!("Unexpectedly absent TooltipRequest");
4122 continue;
4123 };
4124 let mut element = tooltip_request.tooltip.view.clone().into_any_element();
4125 let mouse_position = tooltip_request.tooltip.mouse_position;
4126 let tooltip_size = element.layout_as_root(AvailableSpace::min_size(), self, cx);
4127
4128 let mut tooltip_bounds =
4129 Bounds::new(mouse_position + point(px(1.), px(1.)), tooltip_size);
4130 let window_bounds = Bounds {
4131 origin: Point::default(),
4132 size: self.viewport_size(),
4133 };
4134
4135 if tooltip_bounds.right() > window_bounds.right() {
4136 let new_x = mouse_position.x - tooltip_bounds.size.width - px(1.);
4137 if new_x >= Pixels::ZERO {
4138 tooltip_bounds.origin.x = new_x;
4139 } else {
4140 tooltip_bounds.origin.x = cmp::max(
4141 Pixels::ZERO,
4142 tooltip_bounds.origin.x - tooltip_bounds.right() - window_bounds.right(),
4143 );
4144 }
4145 }
4146
4147 if tooltip_bounds.bottom() > window_bounds.bottom() {
4148 let new_y = mouse_position.y - tooltip_bounds.size.height - px(1.);
4149 if new_y >= Pixels::ZERO {
4150 tooltip_bounds.origin.y = new_y;
4151 } else {
4152 tooltip_bounds.origin.y = cmp::max(
4153 Pixels::ZERO,
4154 tooltip_bounds.origin.y - tooltip_bounds.bottom() - window_bounds.bottom(),
4155 );
4156 }
4157 }
4158
4159 let is_visible =
4163 (tooltip_request.tooltip.check_visible_and_update)(tooltip_bounds, self, cx);
4164 if !is_visible {
4165 continue;
4166 }
4167
4168 self.with_absolute_element_offset(tooltip_bounds.origin, |window| {
4169 element.prepaint(window, cx)
4170 });
4171
4172 self.frame_state.tooltip_bounds = Some(TooltipBounds {
4173 id: tooltip_request.id,
4174 bounds: tooltip_bounds,
4175 });
4176 return Some(element);
4177 }
4178 None
4179 }
4180
4181 fn prepaint_deferred_draws(&mut self, cx: &mut App) {
4182 assert_eq!(self.frame_state.element_id_stack.len(), 0);
4183
4184 let mut round_start = 0;
4195 let mut depth = 0;
4196 loop {
4197 let round_end = self.frame_state.next_frame.deferred_draws.len();
4198 if round_start == round_end {
4199 break;
4200 }
4201 assert!(depth < 10, "Exceeded maximum (10) deferred depth");
4203 depth += 1;
4204
4205 let mut traversal_order = (round_start..round_end).collect::<SmallVec<[usize; 8]>>();
4207 traversal_order
4208 .sort_by_key(|ix| self.frame_state.next_frame.deferred_draws[*ix].priority);
4209
4210 for deferred_draw_ix in traversal_order {
4211 let (element, parent_node, current_view, rem_size, absolute_offset, prepaint_range) = {
4212 let deferred_draw =
4213 &mut self.frame_state.next_frame.deferred_draws[deferred_draw_ix];
4214 self.frame_state
4215 .element_id_stack
4216 .clone_from(&deferred_draw.element_id_stack);
4217 self.frame_state
4218 .text_style_stack
4219 .clone_from(&deferred_draw.text_style_stack);
4220 (
4221 deferred_draw.element.take(),
4222 deferred_draw.parent_node,
4223 deferred_draw.current_view,
4224 deferred_draw.rem_size,
4225 deferred_draw.absolute_offset,
4226 deferred_draw.prepaint_range.clone(),
4227 )
4228 };
4229 self.frame_state
4230 .next_frame
4231 .dispatch_tree
4232 .set_active_node(parent_node);
4233
4234 let prepaint_start = self.prepaint_index();
4235 if let Some(mut element) = element {
4236 self.with_rendered_view(current_view, |window| {
4237 window.with_rem_size(Some(rem_size), |window| {
4238 window.with_absolute_element_offset(absolute_offset, |window| {
4239 element.prepaint(window, cx);
4240 });
4241 });
4242 });
4243 self.frame_state.next_frame.deferred_draws[deferred_draw_ix].element =
4244 Some(element);
4245 } else {
4246 self.reuse_prepaint(prepaint_range);
4247 }
4248 let prepaint_end = self.prepaint_index();
4249 self.frame_state.next_frame.deferred_draws[deferred_draw_ix].prepaint_range =
4250 prepaint_start..prepaint_end;
4251 }
4252
4253 self.frame_state.element_id_stack.clear();
4254 self.frame_state.text_style_stack.clear();
4255 round_start = round_end;
4256 }
4257 }
4258
4259 fn paint_deferred_draws(&mut self, cx: &mut App) {
4260 assert_eq!(self.frame_state.element_id_stack.len(), 0);
4261
4262 if self.frame_state.next_frame.deferred_draws.len() == 0 {
4265 return;
4266 }
4267
4268 let traversal_order = self.deferred_draw_traversal_order();
4269 let mut deferred_draws = mem::take(&mut self.frame_state.next_frame.deferred_draws);
4270 for deferred_draw_ix in traversal_order {
4271 let mut deferred_draw = &mut deferred_draws[deferred_draw_ix];
4272 self.frame_state
4273 .element_id_stack
4274 .clone_from(&deferred_draw.element_id_stack);
4275 self.frame_state
4276 .next_frame
4277 .dispatch_tree
4278 .set_active_node(deferred_draw.parent_node);
4279
4280 let paint_start = self.paint_index();
4281 let content_mask = deferred_draw.content_mask;
4282 if let Some(element) = deferred_draw.element.as_mut() {
4283 self.with_rendered_view(deferred_draw.current_view, |window| {
4284 window.with_content_mask(content_mask, |window| {
4285 window.with_rem_size(Some(deferred_draw.rem_size), |window| {
4286 element.paint(window, cx);
4287 });
4288 })
4289 })
4290 } else {
4291 self.reuse_paint(deferred_draw.paint_range.clone());
4292 }
4293 let paint_end = self.paint_index();
4294 deferred_draw.paint_range = paint_start..paint_end;
4295 }
4296 self.frame_state.next_frame.deferred_draws = deferred_draws;
4297 self.frame_state.element_id_stack.clear();
4298 }
4299
4300 fn deferred_draw_traversal_order(&mut self) -> SmallVec<[usize; 8]> {
4301 let deferred_count = self.frame_state.next_frame.deferred_draws.len();
4302 let mut sorted_indices = (0..deferred_count).collect::<SmallVec<[_; 8]>>();
4303 sorted_indices.sort_by_key(|ix| self.frame_state.next_frame.deferred_draws[*ix].priority);
4304 sorted_indices
4305 }
4306
4307 pub(crate) fn prepaint_index(&self) -> PrepaintStateIndex {
4308 PrepaintStateIndex {
4309 hitboxes_index: self.frame_state.next_frame.hitboxes.len(),
4310 tooltips_index: self.frame_state.next_frame.tooltip_requests.len(),
4311 deferred_draws_index: self.frame_state.next_frame.deferred_draws.len(),
4312 dispatch_tree_index: self.frame_state.next_frame.dispatch_tree.len(),
4313 accessed_element_states_index: self
4314 .frame_state
4315 .next_frame
4316 .accessed_element_states
4317 .len(),
4318 line_layout_index: self.core.text_system.layout_index(),
4319 }
4320 }
4321
4322 pub(crate) fn reuse_prepaint(&mut self, range: Range<PrepaintStateIndex>) {
4323 self.frame_state.next_frame.hitboxes.extend(
4324 self.frame_state.rendered_frame.hitboxes
4325 [range.start.hitboxes_index..range.end.hitboxes_index]
4326 .iter()
4327 .cloned(),
4328 );
4329 self.frame_state.next_frame.hitbox_filters.extend(
4330 self.frame_state.rendered_frame.hitbox_filters
4331 [range.start.hitboxes_index..range.end.hitboxes_index]
4332 .iter()
4333 .cloned(),
4334 );
4335 self.frame_state.next_frame.tooltip_requests.extend(
4336 self.frame_state.rendered_frame.tooltip_requests
4337 [range.start.tooltips_index..range.end.tooltips_index]
4338 .iter_mut()
4339 .map(|request| request.take()),
4340 );
4341 self.frame_state.next_frame.accessed_element_states.extend(
4342 self.frame_state.rendered_frame.accessed_element_states[range
4343 .start
4344 .accessed_element_states_index
4345 ..range.end.accessed_element_states_index]
4346 .iter()
4347 .map(|(id, type_id)| (id.clone(), *type_id)),
4348 );
4349 self.core
4350 .text_system
4351 .reuse_layouts(range.start.line_layout_index..range.end.line_layout_index);
4352
4353 let reused_subtree = self.frame_state.next_frame.dispatch_tree.reuse_subtree(
4354 range.start.dispatch_tree_index..range.end.dispatch_tree_index,
4355 &mut self.frame_state.rendered_frame.dispatch_tree,
4356 self.core.focus,
4357 );
4358
4359 if reused_subtree.contains_focus() {
4360 self.frame_state.next_frame.focus = self.core.focus;
4361 }
4362
4363 self.frame_state.next_frame.deferred_draws.extend(
4364 self.frame_state.rendered_frame.deferred_draws
4365 [range.start.deferred_draws_index..range.end.deferred_draws_index]
4366 .iter()
4367 .map(|deferred_draw| DeferredDraw {
4368 current_view: deferred_draw.current_view,
4369 parent_node: reused_subtree.refresh_node_id(deferred_draw.parent_node),
4370 element_id_stack: deferred_draw.element_id_stack.clone(),
4371 text_style_stack: deferred_draw.text_style_stack.clone(),
4372 content_mask: deferred_draw.content_mask,
4373 rem_size: deferred_draw.rem_size,
4374 priority: deferred_draw.priority,
4375 element: None,
4376 absolute_offset: deferred_draw.absolute_offset,
4377 prepaint_range: deferred_draw.prepaint_range.clone(),
4378 paint_range: deferred_draw.paint_range.clone(),
4379 }),
4380 );
4381 }
4382
4383 pub(crate) fn paint_index(&self) -> PaintIndex {
4384 PaintIndex {
4385 scene_index: self.frame_state.next_frame.scene.len(),
4386 #[cfg(any(test, feature = "test-support"))]
4387 debug_bounds_index: self.frame_state.next_frame.debug_bounds_records.len(),
4388 mouse_listeners_index: self.frame_state.next_frame.mouse_listeners.len(),
4389 input_handlers_index: self.frame_state.next_frame.input_handlers.len(),
4390 cursor_styles_index: self.frame_state.next_frame.cursor_styles.len(),
4391 accessed_element_states_index: self
4392 .frame_state
4393 .next_frame
4394 .accessed_element_states
4395 .len(),
4396 tab_handle_index: self.frame_state.next_frame.tab_stops.paint_index(),
4397 line_layout_index: self.core.text_system.layout_index(),
4398 }
4399 }
4400
4401 pub(crate) fn reuse_paint(&mut self, range: Range<PaintIndex>) {
4402 #[cfg(any(test, feature = "test-support"))]
4404 for (selector, bounds) in &self.frame_state.rendered_frame.debug_bounds_records
4405 [range.start.debug_bounds_index..range.end.debug_bounds_index]
4406 {
4407 self.frame_state
4408 .next_frame
4409 .record_debug_bounds(selector.clone(), *bounds);
4410 }
4411 self.frame_state.next_frame.cursor_styles.extend(
4412 self.frame_state.rendered_frame.cursor_styles
4413 [range.start.cursor_styles_index..range.end.cursor_styles_index]
4414 .iter()
4415 .cloned(),
4416 );
4417 self.frame_state.next_frame.input_handlers.extend(
4418 self.frame_state.rendered_frame.input_handlers
4419 [range.start.input_handlers_index..range.end.input_handlers_index]
4420 .iter_mut()
4421 .map(|handler| handler.take()),
4422 );
4423 self.frame_state.next_frame.mouse_listeners.extend(
4424 self.frame_state.rendered_frame.mouse_listeners
4425 [range.start.mouse_listeners_index..range.end.mouse_listeners_index]
4426 .iter_mut()
4427 .map(|listener| listener.take()),
4428 );
4429 self.frame_state.next_frame.accessed_element_states.extend(
4430 self.frame_state.rendered_frame.accessed_element_states[range
4431 .start
4432 .accessed_element_states_index
4433 ..range.end.accessed_element_states_index]
4434 .iter()
4435 .map(|(id, type_id)| (id.clone(), *type_id)),
4436 );
4437 self.frame_state.next_frame.tab_stops.replay(
4438 &self.frame_state.rendered_frame.tab_stops.insertion_history
4439 [range.start.tab_handle_index..range.end.tab_handle_index],
4440 );
4441
4442 self.core
4443 .text_system
4444 .reuse_layouts(range.start.line_layout_index..range.end.line_layout_index);
4445 self.frame_state.next_frame.scene.replay(
4446 range.start.scene_index..range.end.scene_index,
4447 &self.frame_state.rendered_frame.scene,
4448 );
4449 }
4450
4451 pub fn with_text_style<F, R>(&mut self, style: Option<TextStyleRefinement>, f: F) -> R
4455 where
4456 F: FnOnce(&mut Self) -> R,
4457 {
4458 self.core.invalidator.debug_assert_paint_or_prepaint();
4459 if let Some(style) = style {
4460 self.frame_state.text_style_stack.push(style);
4461 let result = f(self);
4462 self.frame_state.text_style_stack.pop();
4463 result
4464 } else {
4465 f(self)
4466 }
4467 }
4468
4469 pub fn set_cursor_style(&mut self, style: CursorStyle, hitbox: &Hitbox) {
4472 self.core.invalidator.debug_assert_paint();
4473 self.frame_state
4474 .next_frame
4475 .cursor_styles
4476 .push(CursorStyleRequest {
4477 hitbox_id: Some(hitbox.id),
4478 style,
4479 });
4480 }
4481
4482 pub fn set_window_cursor_style(&mut self, style: CursorStyle) {
4487 self.core.invalidator.debug_assert_paint();
4488 self.frame_state
4489 .next_frame
4490 .cursor_styles
4491 .push(CursorStyleRequest {
4492 hitbox_id: None,
4493 style,
4494 })
4495 }
4496
4497 pub fn set_tooltip(&mut self, tooltip: AnyTooltip) -> TooltipId {
4500 self.core.invalidator.debug_assert_prepaint();
4501 let id = TooltipId(post_inc(&mut self.frame_state.next_tooltip_id.0));
4502 self.frame_state
4503 .next_frame
4504 .tooltip_requests
4505 .push(Some(TooltipRequest { id, tooltip }));
4506 id
4507 }
4508
4509 #[inline]
4514 pub fn with_content_mask<R>(
4515 &mut self,
4516 mask: Option<ContentMask<Pixels>>,
4517 f: impl FnOnce(&mut Self) -> R,
4518 ) -> R {
4519 self.core.invalidator.debug_assert_paint_or_prepaint();
4520 if let Some(mask) = mask {
4521 let mask = mask.intersect(&self.content_mask());
4522 self.frame_state.content_mask_stack.push(mask);
4523 let result = f(self);
4524 self.frame_state.content_mask_stack.pop();
4525 result
4526 } else {
4527 f(self)
4528 }
4529 }
4530
4531 pub fn with_element_offset<R>(
4534 &mut self,
4535 offset: Point<Pixels>,
4536 f: impl FnOnce(&mut Self) -> R,
4537 ) -> R {
4538 self.core.invalidator.debug_assert_prepaint();
4539
4540 if offset.is_zero() {
4541 return f(self);
4542 };
4543
4544 let abs_offset = self.element_offset() + offset;
4545 self.with_absolute_element_offset(abs_offset, f)
4546 }
4547
4548 pub fn with_absolute_element_offset<R>(
4552 &mut self,
4553 offset: Point<Pixels>,
4554 f: impl FnOnce(&mut Self) -> R,
4555 ) -> R {
4556 self.core.invalidator.debug_assert_prepaint();
4557 self.frame_state.element_offset_stack.push(offset);
4558 let result = f(self);
4559 self.frame_state.element_offset_stack.pop();
4560 result
4561 }
4562
4563 pub(crate) fn with_element_opacity<R>(
4564 &mut self,
4565 opacity: Option<f32>,
4566 f: impl FnOnce(&mut Self) -> R,
4567 ) -> R {
4568 self.core.invalidator.debug_assert_paint_or_prepaint();
4569
4570 let Some(opacity) = opacity else {
4571 return f(self);
4572 };
4573
4574 let previous_opacity = self.frame_state.element_opacity;
4575 self.frame_state.element_opacity = previous_opacity * opacity;
4576 let result = f(self);
4577 self.frame_state.element_opacity = previous_opacity;
4578 result
4579 }
4580
4581 pub fn transact<T, U>(&mut self, f: impl FnOnce(&mut Self) -> Result<T, U>) -> Result<T, U> {
4587 self.core.invalidator.debug_assert_prepaint();
4588 let index = self.prepaint_index();
4589 let result = f(self);
4590 if result.is_err() {
4591 self.frame_state
4592 .next_frame
4593 .hitboxes
4594 .truncate(index.hitboxes_index);
4595 self.frame_state
4596 .next_frame
4597 .tooltip_requests
4598 .truncate(index.tooltips_index);
4599 self.frame_state
4600 .next_frame
4601 .deferred_draws
4602 .truncate(index.deferred_draws_index);
4603 self.frame_state
4604 .next_frame
4605 .dispatch_tree
4606 .truncate(index.dispatch_tree_index);
4607 self.frame_state
4608 .next_frame
4609 .accessed_element_states
4610 .truncate(index.accessed_element_states_index);
4611 self.core
4612 .text_system
4613 .truncate_layouts(index.line_layout_index);
4614 }
4615 result
4616 }
4617
4618 pub fn request_autoscroll(&mut self, bounds: Bounds<Pixels>) {
4624 self.core.invalidator.debug_assert_prepaint();
4625 self.frame_state.requested_autoscroll = Some(bounds);
4626 }
4627
4628 pub fn take_autoscroll(&mut self) -> Option<Bounds<Pixels>> {
4631 self.core.invalidator.debug_assert_prepaint();
4632 self.frame_state.requested_autoscroll.take()
4633 }
4634
4635 pub fn use_asset<A: Asset>(&mut self, source: &A::Source, cx: &mut App) -> Option<A::Output> {
4641 cx.asset_entry::<A>(source).use_by(self.current_view())
4642 }
4643
4644 pub fn get_asset<A: Asset>(&mut self, source: &A::Source, cx: &mut App) -> Option<A::Output> {
4650 cx.fetch_asset::<A>(source)
4651 }
4652 pub fn element_offset(&self) -> Point<Pixels> {
4655 self.core.invalidator.debug_assert_prepaint();
4656 self.frame_state
4657 .element_offset_stack
4658 .last()
4659 .copied()
4660 .unwrap_or_default()
4661 }
4662
4663 #[inline]
4666 pub(crate) fn element_opacity(&self) -> f32 {
4667 self.core.invalidator.debug_assert_paint_or_prepaint();
4668 self.frame_state.element_opacity
4669 }
4670
4671 pub fn content_mask(&self) -> ContentMask<Pixels> {
4673 self.core.invalidator.debug_assert_paint_or_prepaint();
4674 self.frame_state
4675 .content_mask_stack
4676 .last()
4677 .cloned()
4678 .unwrap_or_else(|| ContentMask {
4679 bounds: Bounds {
4680 origin: Point::default(),
4681 size: self.core.viewport_size,
4682 },
4683 })
4684 }
4685
4686 pub fn with_element_namespace<R>(
4689 &mut self,
4690 element_id: impl Into<ElementId>,
4691 f: impl FnOnce(&mut Self) -> R,
4692 ) -> R {
4693 self.frame_state.element_id_stack.push(element_id.into());
4694 let result = f(self);
4695 self.frame_state.element_id_stack.pop();
4696 result
4697 }
4698
4699 pub fn use_keyed_state<S: 'static>(
4701 &mut self,
4702 key: impl Into<ElementId>,
4703 cx: &mut App,
4704 init: impl FnOnce(&mut Self, &mut Context<S>) -> S,
4705 ) -> Entity<S> {
4706 let current_view = self.current_view();
4707 self.with_global_id(key.into(), |global_id, window| {
4708 window.with_element_state(global_id, |state: Option<Entity<S>>, window| {
4709 if let Some(state) = state {
4710 (state.clone(), state)
4711 } else {
4712 let new_state = cx.new(|cx| init(window, cx));
4713 Self::observe_keyed_state(&new_state, current_view, cx);
4714 (new_state.clone(), new_state)
4715 }
4716 })
4717 })
4718 }
4719
4720 #[track_caller]
4726 pub fn use_state<S: 'static>(
4727 &mut self,
4728 cx: &mut App,
4729 init: impl FnOnce(&mut Self, &mut Context<S>) -> S,
4730 ) -> Entity<S> {
4731 self.use_keyed_state(
4732 ElementId::CodeLocation(*core::panic::Location::caller()),
4733 cx,
4734 init,
4735 )
4736 }
4737
4738 #[inline(always)]
4743 pub fn with_element_state<S, R>(
4744 &mut self,
4745 global_id: &GlobalElementId,
4746 f: impl FnOnce(Option<S>, &mut Self) -> (R, S),
4747 ) -> R
4748 where
4749 S: 'static,
4750 {
4751 self.core.invalidator.debug_assert_paint_or_prepaint();
4752
4753 let (key, state) = self.take_element_state(global_id, TypeId::of::<S>());
4754
4755 if let Some(any) = state {
4756 let ElementStateBox {
4757 inner,
4758 #[cfg(debug_assertions)]
4759 type_name,
4760 } = any;
4761 let mut state_box = inner
4763 .downcast::<Option<S>>()
4764 .map_err(|_| {
4765 #[cfg(debug_assertions)]
4766 {
4767 anyhow::anyhow!(
4768 "invalid element state type for id, requested {:?}, actual: {:?}",
4769 std::any::type_name::<S>(),
4770 type_name
4771 )
4772 }
4773
4774 #[cfg(not(debug_assertions))]
4775 {
4776 anyhow::anyhow!(
4777 "invalid element state type for id, requested {:?}",
4778 std::any::type_name::<S>(),
4779 )
4780 }
4781 })
4782 .unwrap();
4783
4784 let state = state_box.take().expect(
4785 "reentrant call to with_element_state for the same state type and element id",
4786 );
4787 let (result, state) = f(Some(state), self);
4788 state_box.replace(state);
4789 self.insert_element_state(
4790 key,
4791 ElementStateBox {
4792 inner: state_box,
4793 #[cfg(debug_assertions)]
4794 type_name,
4795 },
4796 );
4797 result
4798 } else {
4799 let (result, state) = f(None, self);
4800 self.insert_element_state(
4801 key,
4802 ElementStateBox {
4803 inner: Box::new(Some(state)),
4804 #[cfg(debug_assertions)]
4805 type_name: std::any::type_name::<S>(),
4806 },
4807 );
4808 result
4809 }
4810 }
4811
4812 pub fn with_optional_element_state<S, R>(
4819 &mut self,
4820 global_id: Option<&GlobalElementId>,
4821 f: impl FnOnce(Option<Option<S>>, &mut Self) -> (R, Option<S>),
4822 ) -> R
4823 where
4824 S: 'static,
4825 {
4826 self.core.invalidator.debug_assert_paint_or_prepaint();
4827
4828 if let Some(global_id) = global_id {
4829 self.with_element_state(global_id, |state, cx| {
4830 let (result, state) = f(Some(state), cx);
4831 let state =
4832 state.expect("you must return some state when you pass some element id");
4833 (result, state)
4834 })
4835 } else {
4836 let (result, state) = f(None, self);
4837 debug_assert!(
4838 state.is_none(),
4839 "you must not return an element state when passing None for the global id"
4840 );
4841 result
4842 }
4843 }
4844
4845 #[inline]
4847 pub fn with_tab_group<R>(&mut self, index: Option<isize>, f: impl FnOnce(&mut Self) -> R) -> R {
4848 if let Some(index) = index {
4849 self.frame_state.next_frame.tab_stops.begin_group(index);
4850 let result = f(self);
4851 self.frame_state.next_frame.tab_stops.end_group();
4852 result
4853 } else {
4854 f(self)
4855 }
4856 }
4857
4858 pub fn defer_draw(
4867 &mut self,
4868 element: AnyElement,
4869 absolute_offset: Point<Pixels>,
4870 priority: usize,
4871 content_mask: Option<ContentMask<Pixels>>,
4872 ) {
4873 self.core.invalidator.debug_assert_prepaint();
4874 let parent_node = self
4875 .frame_state
4876 .next_frame
4877 .dispatch_tree
4878 .active_node_id()
4879 .unwrap();
4880 self.frame_state
4881 .next_frame
4882 .deferred_draws
4883 .push(DeferredDraw {
4884 current_view: self.current_view(),
4885 parent_node,
4886 element_id_stack: self.frame_state.element_id_stack.clone(),
4887 text_style_stack: self.frame_state.text_style_stack.clone(),
4888 content_mask,
4889 rem_size: self.rem_size(),
4890 priority,
4891 element: Some(element),
4892 absolute_offset,
4893 prepaint_range: PrepaintStateIndex::default()..PrepaintStateIndex::default(),
4894 paint_range: PaintIndex::default()..PaintIndex::default(),
4895 });
4896 }
4897
4898 pub fn paint_layer<R>(&mut self, bounds: Bounds<Pixels>, f: impl FnOnce(&mut Self) -> R) -> R {
4904 self.core.invalidator.debug_assert_paint();
4905
4906 let content_mask = self.content_mask();
4907 let clipped_bounds = bounds.intersect(&content_mask.bounds);
4908 if !clipped_bounds.is_empty() {
4909 self.frame_state
4910 .next_frame
4911 .scene
4912 .push_layer(self.cover_bounds(clipped_bounds));
4913 }
4914
4915 let result = f(self);
4916
4917 if !clipped_bounds.is_empty() {
4918 self.frame_state.next_frame.scene.pop_layer();
4919 }
4920
4921 result
4922 }
4923
4924 pub fn paint_drop_shadows(
4931 &mut self,
4932 bounds: Bounds<Pixels>,
4933 corner_radii: Corners<Pixels>,
4934 shadows: &[BoxShadow],
4935 ) {
4936 self.core.invalidator.debug_assert_paint();
4937
4938 let scale_factor = self.scale_factor();
4939 let content_mask = self.snapped_content_mask();
4940 let opacity = self.element_opacity();
4941 let element_bounds = self.cover_bounds(bounds);
4942 let element_corner_radii = corner_radii.scale(scale_factor);
4943 for shadow in shadows {
4944 if shadow.inset {
4945 continue;
4946 }
4947 let shadow_bounds = (bounds + shadow.offset).dilate(shadow.spread_radius);
4948 self.frame_state.next_frame.scene.insert_primitive(Shadow {
4949 order: 0,
4950 blur_radius: shadow.blur_radius.scale(scale_factor),
4951 bounds: self.cover_bounds(shadow_bounds),
4952 content_mask,
4953 corner_radii: corner_radii.scale(scale_factor),
4954 color: shadow.color.opacity(opacity),
4955 element_bounds,
4956 element_corner_radii,
4957 inset: 0,
4958 pad: 0,
4959 });
4960 }
4961 }
4962
4963 pub fn paint_inset_shadows(
4968 &mut self,
4969 bounds: Bounds<Pixels>,
4970 corner_radii: Corners<Pixels>,
4971 shadows: &[BoxShadow],
4972 ) {
4973 self.core.invalidator.debug_assert_paint();
4974
4975 let scale_factor = self.scale_factor();
4976 let content_mask = self.snapped_content_mask();
4977 let opacity = self.element_opacity();
4978 let element_bounds = self.cover_bounds(bounds);
4979 let element_corner_radii = corner_radii.scale(scale_factor);
4980 for shadow in shadows {
4981 if !shadow.inset {
4982 continue;
4983 }
4984 let hole = (bounds + shadow.offset).dilate(-shadow.spread_radius);
4985 let zero = Pixels::ZERO;
4988 let hole_corner_radii = Corners {
4989 top_left: (corner_radii.top_left - shadow.spread_radius).max(zero),
4990 top_right: (corner_radii.top_right - shadow.spread_radius).max(zero),
4991 bottom_right: (corner_radii.bottom_right - shadow.spread_radius).max(zero),
4992 bottom_left: (corner_radii.bottom_left - shadow.spread_radius).max(zero),
4993 };
4994 self.frame_state.next_frame.scene.insert_primitive(Shadow {
4995 order: 0,
4996 blur_radius: shadow.blur_radius.scale(scale_factor),
4997 bounds: self.cover_bounds(hole),
4998 content_mask,
4999 corner_radii: hole_corner_radii.scale(scale_factor),
5000 color: shadow.color.opacity(opacity),
5001 element_bounds,
5002 element_corner_radii,
5003 inset: 1,
5004 pad: 0,
5005 });
5006 }
5007 }
5008
5009 fn largest_border_interior(quad: &Quad) -> Bounds<ScaledPixels> {
5010 let radii = &quad.corner_radii;
5011 let widths = &quad.border_widths;
5012 let edge_radii = Edges {
5013 top: radii.top_left.max(radii.top_right),
5014 right: radii.top_right.max(radii.bottom_right),
5015 bottom: radii.bottom_left.max(radii.bottom_right),
5016 left: radii.top_left.max(radii.bottom_left),
5017 };
5018
5019 let antialias_inset = point(ScaledPixels(1.0), ScaledPixels(1.0));
5020 let inset_bounds = |top_left_inset, bottom_right_inset| {
5021 Bounds::from_corners(
5022 quad.bounds.origin + top_left_inset + antialias_inset,
5023 quad.bounds.bottom_right() - bottom_right_inset - antialias_inset,
5024 )
5025 };
5026
5027 let horizontal_band = inset_bounds(
5030 point(widths.left, widths.top.max(edge_radii.top)),
5031 point(widths.right, widths.bottom.max(edge_radii.bottom)),
5032 );
5033 let vertical_band = inset_bounds(
5034 point(widths.left.max(edge_radii.left), widths.top),
5035 point(widths.right.max(edge_radii.right), widths.bottom),
5036 );
5037
5038 let area = |bounds: &Bounds<ScaledPixels>| {
5039 bounds.size.width.0.max(0.) * bounds.size.height.0.max(0.)
5040 };
5041 if area(&horizontal_band) >= area(&vertical_band) {
5042 horizontal_band
5043 } else {
5044 vertical_band
5045 }
5046 }
5047
5048 pub(crate) fn paint_imported_texture(
5055 &mut self,
5056 handle: ImportedTextureHandle,
5057 bounds: Bounds<Pixels>,
5058 corner_radii: Corners<Pixels>,
5059 opacity: f32,
5060 flip_v: bool,
5061 ) {
5062 self.core.invalidator.debug_assert_paint();
5063
5064 let primitive = CustomRenderPrimitive::Texture {
5065 order: 0,
5066 handle,
5067 bounds: self.snap_bounds(bounds),
5068 content_mask: self.snapped_content_mask(),
5069 radii: corner_radii.scale(self.scale_factor()),
5070 opacity: opacity * self.element_opacity(),
5071 flip_v,
5072 };
5073 self.frame_state
5074 .next_frame
5075 .scene
5076 .insert_primitive(primitive);
5077 }
5078
5079 pub fn paint_quad(&mut self, quad: PaintQuad) {
5090 self.core.invalidator.debug_assert_paint();
5091
5092 let opacity = self.element_opacity();
5093 let snapped_bounds = self.snap_bounds(quad.bounds);
5094 let snapped_border_widths = self.snap_border_widths(quad.border_widths);
5095 let quad = Quad {
5096 order: 0,
5097 bounds: snapped_bounds,
5098 content_mask: self.snapped_content_mask(),
5099 background: quad.background.opacity(opacity),
5100 border_color: quad.border_color.opacity(opacity),
5101 corner_radii: quad.corner_radii.scale(self.scale_factor()),
5102 border_widths: snapped_border_widths,
5103 border_style: quad.border_style,
5104 };
5105
5106 if !quad.background.is_transparent() {
5107 self.frame_state.next_frame.scene.insert_primitive(quad);
5108 return;
5109 }
5110
5111 let outer_bounds = quad.bounds;
5114 let inner_bounds = Self::largest_border_interior(&quad);
5115
5116 if inner_bounds.is_empty() {
5117 self.frame_state.next_frame.scene.insert_primitive(quad);
5118 return;
5119 }
5120
5121 let strips = [
5122 Bounds::from_corners(
5124 outer_bounds.origin,
5125 point(outer_bounds.right(), inner_bounds.top()),
5126 ),
5127 Bounds::from_corners(
5129 point(outer_bounds.left(), inner_bounds.bottom()),
5130 outer_bounds.bottom_right(),
5131 ),
5132 Bounds::from_corners(
5134 point(outer_bounds.left(), inner_bounds.top()),
5135 inner_bounds.bottom_left(),
5136 ),
5137 Bounds::from_corners(
5139 inner_bounds.top_right(),
5140 point(outer_bounds.right(), inner_bounds.bottom()),
5141 ),
5142 ];
5143
5144 for strip in strips {
5145 let content_mask_bounds = quad.content_mask.bounds.intersect(&strip);
5146 if !content_mask_bounds.is_empty() {
5147 self.frame_state.next_frame.scene.insert_primitive(Quad {
5148 content_mask: ContentMask {
5149 bounds: content_mask_bounds,
5150 },
5151 ..quad
5152 });
5153 }
5154 }
5155 }
5156
5157 pub fn paint_path(&mut self, mut path: Path<Pixels>, color: impl Into<Background>) {
5162 self.core.invalidator.debug_assert_paint();
5163
5164 let scale_factor = self.scale_factor();
5165 let content_mask = self.content_mask();
5166 let opacity = self.element_opacity();
5167 path.content_mask = content_mask;
5168 let color: Background = color.into();
5169 path.color = color.opacity(opacity);
5170 self.frame_state
5171 .next_frame
5172 .scene
5173 .insert_primitive(path.scale(scale_factor));
5174 }
5175
5176 pub fn paint_underline(
5180 &mut self,
5181 origin: Point<Pixels>,
5182 width: Pixels,
5183 style: &UnderlineStyle,
5184 ) {
5185 self.core.invalidator.debug_assert_paint();
5186 let underline = self.underline(origin, width, style);
5187 self.frame_state
5188 .next_frame
5189 .scene
5190 .insert_primitive(underline);
5191 }
5192
5193 pub fn paint_strikethrough(
5197 &mut self,
5198 origin: Point<Pixels>,
5199 width: Pixels,
5200 style: &StrikethroughStyle,
5201 ) {
5202 self.core.invalidator.debug_assert_paint();
5203
5204 let scale_factor = self.scale_factor();
5205 let height = style.thickness;
5206 let bounds = Bounds {
5207 origin: origin.map(|c| ScaledPixels(round_to_device_pixel(c.0, scale_factor))),
5208 size: size(self.snap_stroke(width), self.snap_stroke(height)),
5209 };
5210 let opacity = self.element_opacity();
5211
5212 self.frame_state
5213 .next_frame
5214 .scene
5215 .insert_primitive(Underline {
5216 order: 0,
5217 pad: 0,
5218 bounds,
5219 content_mask: self.snapped_content_mask(),
5220 thickness: self.snap_stroke(style.thickness),
5221 color: style.color.unwrap_or_default().opacity(opacity),
5222 wavy: false.into(),
5223 });
5224 }
5225
5226 pub fn paint_glyph(
5235 &mut self,
5236 origin: Point<Pixels>,
5237 font_id: FontId,
5238 glyph_id: GlyphId,
5239 font_size: Pixels,
5240 color: Hsla,
5241 ) -> Result<()> {
5242 self.core.invalidator.debug_assert_paint();
5243
5244 let element_opacity = self.element_opacity();
5245 let scale_factor = self.scale_factor();
5246 let glyph_origin = origin.scale(scale_factor);
5247
5248 let quantized_origin = Point::new(
5249 round_half_toward_zero(glyph_origin.x.0 * SUBPIXEL_VARIANTS_X as f32)
5250 / SUBPIXEL_VARIANTS_X as f32,
5251 round_half_toward_zero(glyph_origin.y.0 * SUBPIXEL_VARIANTS_Y as f32)
5252 / SUBPIXEL_VARIANTS_Y as f32,
5253 );
5254 let subpixel_variant = Point::new(
5255 (quantized_origin.x.fract() * SUBPIXEL_VARIANTS_X as f32) as u8,
5256 (quantized_origin.y.fract() * SUBPIXEL_VARIANTS_Y as f32) as u8,
5257 );
5258 let integer_origin = quantized_origin.map(|c| ScaledPixels(c.trunc()));
5259 let subpixel_rendering = self.should_use_subpixel_rendering(font_id, font_size);
5260 let dilation = self.text_system().glyph_dilation_for_color(color);
5261 let params = RenderGlyphParams {
5262 font_id,
5263 glyph_id,
5264 font_size,
5265 subpixel_variant,
5266 scale_factor,
5267 is_emoji: false,
5268 subpixel_rendering,
5269 dilation,
5270 };
5271
5272 let raster_bounds = self.text_system().raster_bounds(¶ms)?;
5273 if !raster_bounds.is_zero() {
5274 let tile = self
5275 .core
5276 .sprite_atlas
5277 .get_or_insert_with(params.clone().into(), &mut || {
5278 let (size, bytes) = self.text_system().rasterize_glyph(¶ms)?;
5279 Ok(Some((size, Cow::Owned(bytes))))
5280 })?
5281 .expect("Callback above only errors or returns Some");
5282 let bounds = Bounds {
5283 origin: integer_origin + raster_bounds.origin.map(Into::into),
5284 size: tile.bounds.size.map(Into::into),
5285 };
5286 let content_mask = self.snapped_content_mask();
5287
5288 if subpixel_rendering {
5289 self.frame_state
5290 .next_frame
5291 .scene
5292 .insert_primitive(SubpixelSprite {
5293 order: 0,
5294 pad: 0,
5295 bounds,
5296 content_mask,
5297 color: color.opacity(element_opacity),
5298 tile,
5299 transformation: TransformationMatrix::unit(),
5300 });
5301 } else {
5302 self.frame_state
5303 .next_frame
5304 .scene
5305 .insert_primitive(MonochromeSprite {
5306 order: 0,
5307 pad: 0,
5308 bounds,
5309 content_mask,
5310 color: color.opacity(element_opacity),
5311 tile,
5312 transformation: TransformationMatrix::unit(),
5313 });
5314 }
5315 }
5316 Ok(())
5317 }
5318
5319 fn should_use_subpixel_rendering(&self, font_id: FontId, font_size: Pixels) -> bool {
5320 if self.core.platform_window.background_appearance() != WindowBackgroundAppearance::Opaque {
5321 return false;
5322 }
5323
5324 if !self.core.platform_window.is_subpixel_rendering_supported() {
5325 return false;
5326 }
5327
5328 let mode = match self.core.text_rendering_mode.get() {
5329 TextRenderingMode::PlatformDefault => self
5330 .text_system()
5331 .recommended_rendering_mode(font_id, font_size),
5332 mode => mode,
5333 };
5334
5335 mode == TextRenderingMode::Subpixel
5336 }
5337
5338 pub fn paint_emoji(
5347 &mut self,
5348 origin: Point<Pixels>,
5349 font_id: FontId,
5350 glyph_id: GlyphId,
5351 font_size: Pixels,
5352 ) -> Result<()> {
5353 self.core.invalidator.debug_assert_paint();
5354
5355 let scale_factor = self.scale_factor();
5356 let glyph_origin = origin.scale(scale_factor);
5357 let integer_origin = glyph_origin.map(|c| ScaledPixels(round_half_toward_zero(c.0)));
5358 let params = RenderGlyphParams {
5359 font_id,
5360 glyph_id,
5361 font_size,
5362 subpixel_variant: Default::default(),
5363 scale_factor,
5364 is_emoji: true,
5365 subpixel_rendering: false,
5366 dilation: 0,
5367 };
5368
5369 let raster_bounds = self.text_system().raster_bounds(¶ms)?;
5370 if !raster_bounds.is_zero() {
5371 let tile = self
5372 .core
5373 .sprite_atlas
5374 .get_or_insert_with(params.clone().into(), &mut || {
5375 let (size, bytes) = self.text_system().rasterize_glyph(¶ms)?;
5376 Ok(Some((size, Cow::Owned(bytes))))
5377 })?
5378 .expect("Callback above only errors or returns Some");
5379
5380 let bounds = Bounds {
5381 origin: integer_origin + raster_bounds.origin.map(Into::into),
5382 size: tile.bounds.size.map(Into::into),
5383 };
5384 let content_mask = self.snapped_content_mask();
5385 let opacity = self.element_opacity();
5386
5387 self.frame_state
5388 .next_frame
5389 .scene
5390 .insert_primitive(PolychromeSprite {
5391 order: 0,
5392 pad: 0,
5393 grayscale: false.into(),
5394 bounds,
5395 corner_radii: Default::default(),
5396 content_mask,
5397 tile,
5398 opacity,
5399 });
5400 }
5401 Ok(())
5402 }
5403
5404 pub fn paint_svg(
5408 &mut self,
5409 bounds: Bounds<Pixels>,
5410 path: SharedString,
5411 mut data: Option<&[u8]>,
5412 transformation: TransformationMatrix,
5413 color: Hsla,
5414 cx: &App,
5415 ) -> Result<()> {
5416 self.core.invalidator.debug_assert_paint();
5417
5418 let element_opacity = self.element_opacity();
5419 let bounds = self.snap_bounds(bounds);
5420
5421 let params = RenderSvgParams {
5422 path,
5423 size: bounds.size.map(|pixels| {
5424 DevicePixels::from((pixels.0 * SMOOTH_SVG_SCALE_FACTOR).ceil() as i32)
5425 }),
5426 };
5427
5428 let Some(tile) =
5429 self.core
5430 .sprite_atlas
5431 .get_or_insert_with(params.clone().into(), &mut || {
5432 let Some((size, bytes)) = cx.svg_renderer.render_alpha_mask(¶ms, data)?
5433 else {
5434 return Ok(None);
5435 };
5436 Ok(Some((size, Cow::Owned(bytes))))
5437 })?
5438 else {
5439 return Ok(());
5440 };
5441 let content_mask = self.snapped_content_mask();
5442 let svg_bounds = Bounds {
5443 origin: bounds.center()
5444 - Point::new(
5445 ScaledPixels(tile.bounds.size.width.0 as f32 / SMOOTH_SVG_SCALE_FACTOR / 2.),
5446 ScaledPixels(tile.bounds.size.height.0 as f32 / SMOOTH_SVG_SCALE_FACTOR / 2.),
5447 ),
5448 size: tile
5449 .bounds
5450 .size
5451 .map(|value| ScaledPixels(value.0 as f32 / SMOOTH_SVG_SCALE_FACTOR)),
5452 };
5453 let final_bounds = svg_bounds
5454 .map_origin(|value| ScaledPixels(round_half_toward_zero(value.0)))
5455 .map_size(|size| size.ceil());
5456
5457 self.frame_state
5458 .next_frame
5459 .scene
5460 .insert_primitive(MonochromeSprite {
5461 order: 0,
5462 pad: 0,
5463 bounds: final_bounds,
5464 content_mask,
5465 color: color.opacity(element_opacity),
5466 tile,
5467 transformation,
5468 });
5469
5470 Ok(())
5471 }
5472
5473 pub fn paint_image(
5483 &mut self,
5484 bounds: Bounds<Pixels>,
5485 image_bounds: Bounds<Pixels>,
5486 corner_radii: Corners<Pixels>,
5487 data: Arc<RenderImage>,
5488 frame_index: usize,
5489 grayscale: bool,
5490 ) -> Result<()> {
5491 self.core.invalidator.debug_assert_paint();
5492
5493 let visible_bounds = bounds.intersect(&image_bounds);
5494 if visible_bounds.size.width <= Pixels::ZERO || visible_bounds.size.height <= Pixels::ZERO {
5495 return Ok(());
5496 }
5497 if image_bounds.size.width <= Pixels::ZERO || image_bounds.size.height <= Pixels::ZERO {
5498 return Ok(());
5499 }
5500
5501 let params = RenderImageParams {
5502 image_id: data.id,
5503 frame_index,
5504 };
5505
5506 let tile = self
5507 .core
5508 .sprite_atlas
5509 .get_or_insert_with(params.into(), &mut || {
5510 Ok(Some((
5511 data.size(frame_index),
5512 Cow::Borrowed(
5513 data.as_bytes(frame_index)
5514 .expect("It's the caller's job to pass a valid frame index"),
5515 ),
5516 )))
5517 })?
5518 .expect("Callback above only returns Some");
5519
5520 let visible_bounds_snapped = self.snap_bounds(visible_bounds);
5521
5522 let sub_tile = if visible_bounds == image_bounds {
5523 tile
5524 } else {
5525 let x_offset_ratio =
5526 (visible_bounds.origin.x - image_bounds.origin.x) / image_bounds.size.width;
5527 let y_offset_ratio =
5528 (visible_bounds.origin.y - image_bounds.origin.y) / image_bounds.size.height;
5529 let width_ratio = visible_bounds.size.width / image_bounds.size.width;
5530 let height_ratio = visible_bounds.size.height / image_bounds.size.height;
5531
5532 let tile_origin_x = tile.bounds.origin.x.0;
5533 let tile_origin_y = tile.bounds.origin.y.0;
5534 let tile_width = tile.bounds.size.width.0;
5535 let tile_height = tile.bounds.size.height.0;
5536
5537 let sub_origin_x = tile_origin_x + (x_offset_ratio * tile_width as f32).round() as i32;
5538 let sub_origin_y = tile_origin_y + (y_offset_ratio * tile_height as f32).round() as i32;
5539 let sub_width = (width_ratio * tile_width as f32).round() as i32;
5540 let sub_height = (height_ratio * tile_height as f32).round() as i32;
5541
5542 let max_x = tile_origin_x + tile_width;
5543 let max_y = tile_origin_y + tile_height;
5544
5545 let clamped_origin_x = sub_origin_x.clamp(tile_origin_x, max_x);
5546 let clamped_origin_y = sub_origin_y.clamp(tile_origin_y, max_y);
5547 let clamped_width = sub_width.min(max_x - clamped_origin_x).max(0);
5548 let clamped_height = sub_height.min(max_y - clamped_origin_y).max(0);
5549
5550 AtlasTile {
5551 bounds: Bounds {
5552 origin: point(
5553 DevicePixels(clamped_origin_x),
5554 DevicePixels(clamped_origin_y),
5555 ),
5556 size: size(DevicePixels(clamped_width), DevicePixels(clamped_height)),
5557 },
5558 ..tile
5559 }
5560 };
5561
5562 let content_mask = self.snapped_content_mask();
5563 let corner_radii = corner_radii
5564 .clamp_radii_for_quad_size(visible_bounds.size)
5565 .scale(self.scale_factor());
5566 let opacity = self.element_opacity();
5567
5568 self.frame_state
5569 .next_frame
5570 .scene
5571 .insert_primitive(PolychromeSprite {
5572 order: 0,
5573 pad: 0,
5574 grayscale: grayscale.into(),
5575 bounds: visible_bounds_snapped,
5576 content_mask,
5577 corner_radii,
5578 tile: sub_tile,
5579 opacity,
5580 });
5581 Ok(())
5582 }
5583
5584 pub fn drop_image(&mut self, data: Arc<RenderImage>) -> Result<()> {
5586 for frame_index in 0..data.frame_count() {
5587 let params = RenderImageParams {
5588 image_id: data.id,
5589 frame_index,
5590 };
5591
5592 self.core.sprite_atlas.remove(¶ms.clone().into());
5593 }
5594
5595 Ok(())
5596 }
5597
5598 #[cfg(any(test, feature = "test-support"))]
5600 pub fn has_image_atlas_entry(&self, data: &RenderImage) -> bool {
5601 data.frame_count() > 0
5602 && (0..data.frame_count()).all(|frame_index| {
5603 self.core.sprite_atlas.contains(
5604 &RenderImageParams {
5605 image_id: data.id,
5606 frame_index,
5607 }
5608 .into(),
5609 )
5610 })
5611 }
5612
5613 #[must_use]
5620 pub fn request_layout(
5621 &mut self,
5622 style: Style,
5623 children: impl IntoIterator<Item = LayoutId>,
5624 cx: &mut App,
5625 ) -> LayoutId {
5626 self.core.invalidator.debug_assert_prepaint();
5627
5628 cx.layout_id_buffer.clear();
5629 cx.layout_id_buffer.extend(children);
5630 let rem_size = self.rem_size();
5631 let scale_factor = self.scale_factor();
5632 let engine_style = to_engine_layout_style(&style);
5633
5634 self.frame_state.layout_session.request_layout(
5635 &engine_style,
5636 rem_size,
5637 scale_factor,
5638 &cx.layout_id_buffer,
5639 )
5640 }
5641
5642 pub fn request_measured_layout<F>(&mut self, style: Style, measure: F) -> LayoutId
5652 where
5653 F: Fn(Size<Option<Pixels>>, Size<AvailableSpace>, &mut Window, &mut App) -> Size<Pixels>
5654 + 'static,
5655 {
5656 self.core.invalidator.debug_assert_prepaint();
5657
5658 let rem_size = self.rem_size();
5659 let scale_factor = self.scale_factor();
5660 let engine_style = to_engine_layout_style(&style);
5661 let measure = move |known_dimensions, available_space, context: &mut dyn MeasureContext| {
5662 let MeasureHandles { window, app } = context.handles();
5663 let (core, frame_state) = window;
5664 let core = core
5665 .downcast_mut::<WindowHostCore>()
5666 .expect("measure context window half should be a window's core");
5667 let frame_state = frame_state
5668 .downcast_mut::<WindowFrameState>()
5669 .expect("measure context window half should be a window's frame state");
5670 let cx = app
5671 .downcast_mut::<App>()
5672 .expect("measure context app should be an App");
5673 measure(
5674 known_dimensions,
5675 available_space,
5676 &mut Window { core, frame_state },
5677 cx,
5678 )
5679 };
5680 self.frame_state.layout_session.request_measured_layout(
5681 &engine_style,
5682 rem_size,
5683 scale_factor,
5684 Box::new(measure),
5685 )
5686 }
5687
5688 pub(crate) fn compute_layout(
5694 &mut self,
5695 layout_id: LayoutId,
5696 available_space: Size<AvailableSpace>,
5697 cx: &mut App,
5698 ) {
5699 self.core.invalidator.debug_assert_prepaint();
5700
5701 let scale_factor = self.scale_factor();
5702 let layout_session = self.frame_state.layout_session.clone();
5703 let core = &mut *self.core;
5704 let frame_state = &mut *self.frame_state;
5705 layout_session.compute_layout(
5706 layout_id,
5707 available_space,
5708 scale_factor,
5709 &mut WindowMeasureContext {
5710 core,
5711 frame_state,
5712 cx,
5713 },
5714 );
5715 }
5716
5717 pub fn layout_bounds(&mut self, layout_id: LayoutId) -> Bounds<Pixels> {
5723 self.core.invalidator.debug_assert_prepaint();
5724
5725 let scale_factor = self.scale_factor();
5726 let mut bounds = self
5727 .frame_state
5728 .layout_session
5729 .layout_bounds(layout_id, scale_factor)
5730 .map(Into::into);
5731 let snapped_offset = self.pixel_snap_point(self.element_offset());
5732 bounds.origin += snapped_offset;
5733 bounds
5734 }
5735
5736 pub fn insert_hitbox(&mut self, bounds: Bounds<Pixels>, behavior: HitboxBehavior) -> Hitbox {
5743 self.insert_hitbox_with_filter(bounds, behavior, None)
5744 }
5745
5746 pub fn insert_hitbox_with_filter(
5751 &mut self,
5752 bounds: Bounds<Pixels>,
5753 behavior: HitboxBehavior,
5754 filter: Option<HitboxFilter>,
5755 ) -> Hitbox {
5756 self.core.invalidator.debug_assert_prepaint();
5757
5758 let content_mask = self.content_mask();
5759 let mut id = self.frame_state.next_hitbox_id;
5760 self.frame_state.next_hitbox_id = self.frame_state.next_hitbox_id.next();
5761 let hitbox = Hitbox {
5762 id,
5763 bounds,
5764 content_mask,
5765 behavior,
5766 };
5767 self.frame_state.next_frame.hitboxes.push(hitbox.clone());
5768 self.frame_state.next_frame.hitbox_filters.push(filter);
5769 hitbox
5770 }
5771
5772 pub fn insert_window_control_hitbox(&mut self, area: WindowControlArea, hitbox: Hitbox) {
5776 self.core.invalidator.debug_assert_paint();
5777 self.frame_state
5778 .next_frame
5779 .window_control_hitboxes
5780 .push((area, hitbox));
5781 }
5782
5783 pub fn set_key_context(&mut self, context: KeyContext) {
5788 self.core.invalidator.debug_assert_paint();
5789 self.frame_state
5790 .next_frame
5791 .dispatch_tree
5792 .set_key_context(context);
5793 }
5794
5795 pub fn set_focus_handle(&mut self, focus_handle: &FocusHandle, _: &App) {
5800 self.core.invalidator.debug_assert_prepaint();
5801 if focus_handle.is_focused(self) {
5802 self.frame_state.next_frame.focus = Some(focus_handle.id);
5803 }
5804 self.frame_state
5805 .next_frame
5806 .dispatch_tree
5807 .set_focus_id(focus_handle.id);
5808 }
5809
5810 pub fn set_view_id(&mut self, view_id: EntityId) {
5816 self.core.invalidator.debug_assert_prepaint();
5817 self.frame_state
5818 .next_frame
5819 .dispatch_tree
5820 .set_view_id(view_id);
5821 }
5822
5823 pub fn current_view(&self) -> EntityId {
5825 self.core.invalidator.debug_assert_paint_or_prepaint();
5826 self.frame_state
5827 .rendered_entity_stack
5828 .last()
5829 .copied()
5830 .unwrap()
5831 }
5832
5833 #[inline]
5834 pub(crate) fn with_rendered_view<R>(
5835 &mut self,
5836 id: EntityId,
5837 f: impl FnOnce(&mut Self) -> R,
5838 ) -> R {
5839 self.frame_state.rendered_entity_stack.push(id);
5840 let result = f(self);
5841 self.frame_state.rendered_entity_stack.pop();
5842 result
5843 }
5844
5845 pub fn with_image_cache<F, R>(&mut self, image_cache: Option<AnyImageCache>, f: F) -> R
5847 where
5848 F: FnOnce(&mut Self) -> R,
5849 {
5850 if let Some(image_cache) = image_cache {
5851 self.frame_state.image_cache_stack.push(image_cache);
5852 let result = f(self);
5853 self.frame_state.image_cache_stack.pop();
5854 result
5855 } else {
5856 f(self)
5857 }
5858 }
5859
5860 pub fn handle_input(
5869 &mut self,
5870 focus_handle: &FocusHandle,
5871 input_handler: impl InputHandler,
5872 cx: &App,
5873 ) {
5874 self.core.invalidator.debug_assert_paint();
5875
5876 if focus_handle.is_focused(self) {
5877 let cx = self.to_async(cx);
5878 self.frame_state
5879 .next_frame
5880 .input_handlers
5881 .push(Some(new_platform_input_handler(
5882 cx,
5883 Box::new(input_handler),
5884 )));
5885 }
5886 }
5887
5888 fn apply_text_input_configuration(&mut self, cx: &mut App) {
5893 let configuration = match self.core.platform_window.take_input_handler() {
5894 Some(mut input_handler) => {
5895 let configuration = input_handler.text_input_configuration(self, cx);
5896 self.core.platform_window.set_input_handler(input_handler);
5897 configuration
5898 }
5899 None => TextInputConfiguration::default(),
5900 };
5901 if self.core.last_text_input_configuration.as_ref() != Some(&configuration) {
5902 self.core
5903 .platform_window
5904 .set_text_input_configuration(configuration.clone());
5905 self.core.last_text_input_configuration = Some(configuration);
5906 }
5907 }
5908
5909 pub fn on_mouse_event<Event: MouseEvent>(
5915 &mut self,
5916 mut listener: impl FnMut(&Event, DispatchPhase, &mut Window, &mut App) + 'static,
5917 ) {
5918 self.core.invalidator.debug_assert_paint();
5919
5920 self.frame_state
5921 .next_frame
5922 .mouse_listeners
5923 .push(Some(Box::new(
5924 move |event: &dyn Any, phase: DispatchPhase, window: &mut Window, cx: &mut App| {
5925 if let Some(event) = event.downcast_ref() {
5926 listener(event, phase, window, cx)
5927 }
5928 },
5929 )));
5930 }
5931
5932 pub fn on_key_event<Event: KeyEvent>(
5941 &mut self,
5942 listener: impl Fn(&Event, DispatchPhase, &mut Window, &mut App) + 'static,
5943 ) {
5944 self.core.invalidator.debug_assert_paint();
5945
5946 self.frame_state
5947 .next_frame
5948 .dispatch_tree
5949 .on_key_event(Rc::new(
5950 move |event: &dyn Any, phase, window: &mut Window, cx: &mut App| {
5951 if let Some(event) = event.downcast_ref::<Event>() {
5952 listener(event, phase, window, cx)
5953 }
5954 },
5955 ));
5956 }
5957
5958 pub fn on_modifiers_changed(
5965 &mut self,
5966 listener: impl Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static,
5967 ) {
5968 self.core.invalidator.debug_assert_paint();
5969
5970 self.frame_state
5971 .next_frame
5972 .dispatch_tree
5973 .on_modifiers_changed(Rc::new(
5974 move |event: &ModifiersChangedEvent, window: &mut Window, cx: &mut App| {
5975 listener(event, window, cx)
5976 },
5977 ));
5978 }
5979
5980 pub fn on_focus_in(
5984 &mut self,
5985 handle: &FocusHandle,
5986 cx: &mut App,
5987 mut listener: impl FnMut(&mut Window, &mut App) + 'static,
5988 ) -> Subscription {
5989 let focus_id = handle.id;
5990 let (subscription, activate) =
5991 self.new_focus_listener(Box::new(move |event, window, cx| {
5992 if event.is_focus_in(focus_id) {
5993 listener(window, cx);
5994 }
5995 true
5996 }));
5997 cx.defer(move |_| activate());
5998 subscription
5999 }
6000
6001 pub fn on_focus_out(
6004 &mut self,
6005 handle: &FocusHandle,
6006 cx: &mut App,
6007 mut listener: impl FnMut(FocusOutEvent, &mut Window, &mut App) + 'static,
6008 ) -> Subscription {
6009 let focus_id = handle.id;
6010 let (subscription, activate) =
6011 self.new_focus_listener(Box::new(move |event, window, cx| {
6012 if let Some(blurred_id) = event.previous_focus_path.last().copied()
6013 && event.is_focus_out(focus_id)
6014 {
6015 let event = FocusOutEvent {
6016 blurred: WeakFocusHandle {
6017 id: blurred_id,
6018 handles: Arc::downgrade(&cx.focus_handles),
6019 },
6020 };
6021 listener(event, window, cx)
6022 }
6023 true
6024 }));
6025 cx.defer(move |_| activate());
6026 subscription
6027 }
6028
6029 fn reset_cursor_style(&self, cx: &mut App) {
6030 if self.is_window_hovered() {
6032 let style = self
6033 .frame_state
6034 .rendered_frame
6035 .cursor_style(self)
6036 .unwrap_or(CursorStyle::Arrow);
6037 cx.platform.set_cursor_style(style);
6038 }
6039 }
6040
6041 pub fn dispatch_keystroke(&mut self, keystroke: Keystroke, cx: &mut App) -> bool {
6044 let keystroke = keystroke.with_simulated_ime();
6045 let result = self.dispatch_event(
6046 PlatformInput::KeyDown(KeyDownEvent {
6047 keystroke: keystroke.clone(),
6048 is_held: false,
6049 prefer_character_input: false,
6050 }),
6051 cx,
6052 );
6053 if !result.propagate {
6054 return true;
6055 }
6056
6057 if let Some(input) = keystroke.key_char
6058 && let Some(mut input_handler) = self.core.platform_window.take_input_handler()
6059 {
6060 input_handler.dispatch_input(&input, self, cx);
6061 self.core.platform_window.set_input_handler(input_handler);
6062 return true;
6063 }
6064
6065 false
6066 }
6067
6068 pub fn keystroke_text_for(&self, action: &dyn Action) -> String {
6071 self.highest_precedence_binding_for_action(action)
6072 .map(|binding| {
6073 binding
6074 .keystrokes()
6075 .iter()
6076 .map(ToString::to_string)
6077 .collect::<Vec<_>>()
6078 .join(" ")
6079 })
6080 .unwrap_or_else(|| action.name().to_string())
6081 }
6082
6083 #[profiling::function]
6085 pub fn dispatch_event(&mut self, event: PlatformInput, cx: &mut App) -> DispatchEventResult {
6086 #[cfg(feature = "profiler")]
6087 self.core.window_profiler.begin_input(event.kind_name());
6088 let update_count_before = self.core.invalidator.update_count();
6089 let old_modality = self.core.last_input_modality;
6093 self.core.last_input_modality = match &event {
6094 PlatformInput::KeyDown(_) => InputModality::Keyboard,
6095 PlatformInput::MouseMove(_) | PlatformInput::MouseDown(_) => InputModality::Mouse,
6096 PlatformInput::Touch(_) => InputModality::Touch,
6097 _ => self.core.last_input_modality,
6098 };
6099 if self.core.last_input_modality != old_modality {
6100 self.refresh();
6101 }
6102
6103 cx.propagate_event = true;
6105 self.core.default_prevented = false;
6107
6108 let event = match event {
6109 PlatformInput::MouseMove(mouse_move) => {
6112 self.core.mouse_position = mouse_move.position;
6113 self.core.modifiers = mouse_move.modifiers;
6114 PlatformInput::MouseMove(mouse_move)
6115 }
6116 PlatformInput::MouseDown(mouse_down) => {
6117 self.core.mouse_position = mouse_down.position;
6118 self.core.modifiers = mouse_down.modifiers;
6119 PlatformInput::MouseDown(mouse_down)
6120 }
6121 PlatformInput::MouseUp(mouse_up) => {
6122 self.core.mouse_position = mouse_up.position;
6123 self.core.modifiers = mouse_up.modifiers;
6124 PlatformInput::MouseUp(mouse_up)
6125 }
6126 PlatformInput::MousePressure(mouse_pressure) => {
6127 PlatformInput::MousePressure(mouse_pressure)
6128 }
6129 PlatformInput::MouseExited(mouse_exited) => {
6130 self.core.modifiers = mouse_exited.modifiers;
6131 PlatformInput::MouseExited(mouse_exited)
6132 }
6133 PlatformInput::ModifiersChanged(modifiers_changed) => {
6134 self.core.modifiers = modifiers_changed.modifiers;
6135 self.core.capslock = modifiers_changed.capslock;
6136 PlatformInput::ModifiersChanged(modifiers_changed)
6137 }
6138 PlatformInput::ScrollWheel(scroll_wheel) => {
6139 self.core.mouse_position = scroll_wheel.position;
6140 self.core.modifiers = scroll_wheel.modifiers;
6141 PlatformInput::ScrollWheel(scroll_wheel)
6142 }
6143 PlatformInput::Pinch(pinch) => {
6144 self.core.mouse_position = pinch.position;
6145 self.core.modifiers = pinch.modifiers;
6146 PlatformInput::Pinch(pinch)
6147 }
6148 PlatformInput::FileDrop(file_drop) => match file_drop {
6151 FileDropEvent::Entered { position, paths } => {
6152 self.core.mouse_position = position;
6153 let source_window = self.core.handle.window_id();
6154 if !cx.restore_platform_drag(source_window) && cx.active_drag.is_none() {
6155 cx.active_drag = Some(AnyDrag {
6156 value: Arc::new(paths.clone()),
6157 view: cx.new(|_| paths).into(),
6158 cursor_offset: position,
6159 cursor_style: None,
6160 external_payload_source: None,
6161 });
6162 }
6163 PlatformInput::MouseMove(MouseMoveEvent {
6164 position,
6165 pressed_button: Some(MouseButton::Left),
6166 modifiers: Modifiers::default(),
6167 })
6168 }
6169 FileDropEvent::Pending { position } => {
6170 self.core.mouse_position = position;
6171 PlatformInput::MouseMove(MouseMoveEvent {
6172 position,
6173 pressed_button: Some(MouseButton::Left),
6174 modifiers: Modifiers::default(),
6175 })
6176 }
6177 FileDropEvent::Submit { position } => {
6178 cx.activate(true);
6179 self.core.mouse_position = position;
6180 PlatformInput::MouseUp(MouseUpEvent {
6181 button: MouseButton::Left,
6182 position,
6183 modifiers: Modifiers::default(),
6184 click_count: 1,
6185 })
6186 }
6187 FileDropEvent::Exited => {
6188 if !cx.hand_restored_drag_to_platform(self.core.handle.window_id()) {
6189 cx.active_drag.take();
6190 }
6191 self.refresh();
6192 PlatformInput::FileDrop(FileDropEvent::Exited)
6193 }
6194 FileDropEvent::Ended => {
6195 cx.end_platform_drag(self.core.handle.window_id());
6196 self.refresh();
6197 PlatformInput::FileDrop(FileDropEvent::Ended)
6198 }
6199 },
6200 PlatformInput::Touch(touch) => PlatformInput::Touch(touch),
6201 PlatformInput::LongPress(long_press) => {
6202 self.core.mouse_position = if long_press.phase == crate::TouchPhase::Started {
6203 long_press.start_position
6204 } else {
6205 long_press.position
6206 };
6207 if long_press.phase == crate::TouchPhase::Started {
6208 self.core.long_press_capture = None;
6209 }
6210 PlatformInput::LongPress(long_press)
6211 }
6212 PlatformInput::TouchDrag(touch_drag) => {
6213 self.core.mouse_position = touch_drag.start_position;
6214 PlatformInput::TouchDrag(touch_drag)
6215 }
6216 PlatformInput::KeyDown(_) | PlatformInput::KeyUp(_) => event,
6217 };
6218
6219 if let Some(any_mouse_event) = event.mouse_event() {
6220 self.dispatch_mouse_event(any_mouse_event, cx);
6221 } else if let Some(any_key_event) = event.keyboard_event() {
6222 self.dispatch_key_event(any_key_event, cx);
6223 } else if let Some(touch_event) = event.touch_event() {
6224 self.dispatch_touch_event(touch_event, cx);
6225 }
6226 if let PlatformInput::LongPress(long_press) = &event {
6227 match long_press.phase {
6228 crate::TouchPhase::Started if !self.core.default_prevented => {
6229 self.core.long_press_capture = None;
6230 }
6231 crate::TouchPhase::Ended | crate::TouchPhase::Cancelled => {
6232 self.core.long_press_capture = None;
6233 }
6234 crate::TouchPhase::Started | crate::TouchPhase::Moved => {}
6235 }
6236 }
6237
6238 self.promote_external_drag_to_platform(&event, cx);
6241
6242 let caused_invalidation = self.core.invalidator.update_count() > update_count_before;
6243 if caused_invalidation {
6244 self.core.input_rate_tracker.borrow_mut().record_input();
6245 }
6246 #[cfg(feature = "profiler")]
6247 self.core.window_profiler.end_input(caused_invalidation);
6248
6249 DispatchEventResult {
6250 propagate: cx.propagate_event,
6251 default_prevented: self.core.default_prevented,
6252 }
6253 }
6254
6255 fn promote_external_drag_to_platform(&mut self, event: &PlatformInput, cx: &mut App) {
6256 let PlatformInput::MouseMove(mouse_move) = event else {
6257 return;
6258 };
6259 if mouse_move.pressed_button != Some(MouseButton::Left) {
6260 return;
6261 }
6262 if Bounds::new(Point::default(), self.core.viewport_size).contains(&mouse_move.position) {
6263 return;
6264 }
6265 if !self.core.platform_window.can_start_external_drag() {
6266 return;
6267 }
6268 let Some(payload_source) = cx
6269 .active_drag
6270 .as_mut()
6271 .and_then(|drag| drag.external_payload_source.take())
6272 else {
6273 return;
6274 };
6275 let Some(payload) = payload_source(self, cx) else {
6276 return;
6277 };
6278 if self.core.platform_window.start_external_drag(&payload)
6279 && cx.hand_active_drag_to_platform(self.core.handle.window_id())
6280 {
6281 self.refresh();
6282 }
6283 }
6284
6285 pub fn touch_prediction_enabled(&self) -> bool {
6289 self.core.touch_prediction_enabled
6290 }
6291
6292 pub fn set_touch_prediction_enabled(&mut self, enabled: bool) {
6296 self.core.touch_prediction_enabled = enabled;
6297 }
6298
6299 fn dispatch_touch_event(&mut self, event: &TouchEvent, cx: &mut App) {
6303 let mut event = event.clone();
6304 if !self.core.touch_prediction_enabled {
6305 event.predicted_position = None;
6306 }
6307 let recognized_gestures = self.core.touch_gestures.handle_event(&event);
6308 if event.phase == crate::TouchPhase::Started
6309 && let Some(touch_drag) = self.core.touch_gestures.offer_touch_drag(event.id)
6310 {
6311 self.dispatch_recognized_touch_gesture(touch_drag, cx);
6312 }
6313 if event.phase == crate::TouchPhase::Started
6314 && self.core.touch_gestures.pending_long_press().is_some()
6315 {
6316 self.core.long_press_capture = None;
6317 }
6318 let mut tapped = false;
6319 for gesture in recognized_gestures {
6320 tapped |= matches!(gesture, RecognizedTouchGesture::Tap { .. });
6321 self.dispatch_recognized_touch_gesture(gesture, cx);
6322 }
6323 if event.phase == crate::TouchPhase::Started {
6324 self.schedule_long_press_timer(cx);
6325 } else if self.core.touch_gestures.pending_long_press().is_none() {
6326 self.core.long_press_timer.take();
6327 }
6328 if tapped && self.core.invalidator.is_dirty() {
6334 self.draw(cx).clear(cx);
6335 }
6336 if self.core.touch_gestures.has_momentum() {
6337 self.schedule_touch_momentum_tick();
6338 }
6339 }
6340
6341 fn dispatch_recognized_touch_gesture(&mut self, gesture: RecognizedTouchGesture, cx: &mut App) {
6342 match gesture {
6343 RecognizedTouchGesture::Scroll(scroll_wheel) => {
6344 self.core.mouse_position = scroll_wheel.position;
6345 cx.propagate_event = true;
6346 self.dispatch_mouse_event(&scroll_wheel, cx);
6347 }
6348 RecognizedTouchGesture::Tap { down, up } => {
6349 self.core.mouse_position = up.position;
6350 cx.propagate_event = true;
6351 self.dispatch_mouse_event(&down, cx);
6352 cx.propagate_event = true;
6353 self.dispatch_mouse_event(&up, cx);
6354 }
6355 RecognizedTouchGesture::TouchDrag(touch_drag) => {
6356 self.core.mouse_position = touch_drag.start_position;
6357 cx.propagate_event = true;
6358 self.core.default_prevented = false;
6359 let started = touch_drag.phase == crate::TouchPhase::Started;
6360 self.dispatch_mouse_event(&touch_drag, cx);
6361 if started {
6362 self.core
6363 .touch_gestures
6364 .resolve_touch_drag(self.core.default_prevented);
6365 }
6366 }
6367 RecognizedTouchGesture::LongPress(long_press) => {
6368 self.core.mouse_position = if long_press.phase == crate::TouchPhase::Started {
6369 long_press.start_position
6370 } else {
6371 long_press.position
6372 };
6373 cx.propagate_event = true;
6374 self.core.default_prevented = false;
6375 let started = long_press.phase == crate::TouchPhase::Started;
6376 let ended = matches!(
6377 long_press.phase,
6378 crate::TouchPhase::Ended | crate::TouchPhase::Cancelled
6379 );
6380 self.dispatch_mouse_event(&long_press, cx);
6381 if started {
6382 let claimed = self.core.default_prevented;
6383 self.core.touch_gestures.resolve_long_press(claimed);
6384 if !claimed {
6385 self.core.long_press_capture = None;
6386 }
6387 }
6388 if ended {
6389 self.core.long_press_capture = None;
6390 }
6391 }
6392 }
6393 }
6394
6395 fn schedule_long_press_timer(&mut self, cx: &mut App) {
6396 self.core.long_press_timer.take();
6397 let Some((touch_id, duration)) = self.core.touch_gestures.pending_long_press() else {
6398 return;
6399 };
6400 self.core.long_press_timer = Some(self.spawn(cx, async move |cx| {
6401 cx.background_executor.timer(duration).await;
6402 cx.update(move |window, cx| {
6403 window.core.long_press_timer.take();
6404 if let Some(gesture) = window.core.touch_gestures.offer_long_press(touch_id) {
6405 window.dispatch_recognized_touch_gesture(gesture, cx);
6406 }
6407 })
6408 .log_err();
6409 }));
6410 }
6411
6412 fn schedule_touch_momentum_tick(&mut self) {
6413 self.on_next_frame(|window, cx| {
6414 if let Some(gesture) = window.core.touch_gestures.tick_momentum() {
6415 window.dispatch_recognized_touch_gesture(gesture, cx);
6416 }
6417 if window.core.touch_gestures.has_momentum() {
6418 window.schedule_touch_momentum_tick();
6419 }
6420 });
6421 }
6422
6423 fn dispatch_mouse_event(&mut self, event: &dyn Any, cx: &mut App) {
6424 let hit_test = self
6425 .frame_state
6426 .rendered_frame
6427 .hit_test(self.mouse_position());
6428 if hit_test != self.core.mouse_hit_test {
6429 self.core.mouse_hit_test = hit_test;
6430 self.reset_cursor_style(cx);
6431 }
6432
6433 #[cfg(any(feature = "inspector", debug_assertions))]
6434 if self.is_inspector_picking(cx) {
6435 self.handle_inspector_mouse_event(event, cx);
6436 return;
6438 }
6439
6440 let mut mouse_listeners = mem::take(&mut self.frame_state.rendered_frame.mouse_listeners);
6441
6442 for listener in &mut mouse_listeners {
6445 let listener = listener.as_mut().unwrap();
6446 listener(event, DispatchPhase::Capture, self, cx);
6447 if !cx.propagate_event {
6448 break;
6449 }
6450 }
6451
6452 if cx.propagate_event {
6454 for listener in mouse_listeners.iter_mut().rev() {
6455 let listener = listener.as_mut().unwrap();
6456 listener(event, DispatchPhase::Bubble, self, cx);
6457 if !cx.propagate_event {
6458 break;
6459 }
6460 }
6461 }
6462
6463 self.frame_state.rendered_frame.mouse_listeners = mouse_listeners;
6464
6465 if cx.has_active_drag() {
6466 if event.is::<MouseMoveEvent>() {
6467 self.refresh();
6470 } else if event.is::<MouseUpEvent>() {
6471 cx.active_drag = None;
6474 self.refresh();
6475 }
6476 }
6477
6478 if event.is::<MouseUpEvent>() && self.core.captured_hitbox.is_some() {
6480 self.core.captured_hitbox = None;
6481 }
6482 }
6483
6484 fn input_preference(&mut self, event: &dyn Any, cx: &mut App) -> InputPreference {
6487 let prefer_character_input = event
6488 .downcast_ref::<KeyDownEvent>()
6489 .is_some_and(|key_down_event| key_down_event.prefer_character_input);
6490 if !prefer_character_input {
6491 return InputPreference::KeyBindings;
6492 }
6493 let Some(mut input_handler) = self.core.platform_window.take_input_handler() else {
6494 return InputPreference::KeyBindings;
6495 };
6496 let accepts_text_input = input_handler.accepts_text_input(self, cx);
6497 self.core.platform_window.set_input_handler(input_handler);
6498 if accepts_text_input {
6499 InputPreference::CharacterInput
6500 } else {
6501 InputPreference::KeyBindings
6502 }
6503 }
6504
6505 fn dispatch_key_event(&mut self, event: &dyn Any, cx: &mut App) {
6506 if self.core.invalidator.is_dirty() {
6507 self.draw(cx).clear(cx);
6508 }
6509
6510 let node_id = self.focus_node_id_in_rendered_frame(self.core.focus);
6511 let dispatch_path = self
6512 .frame_state
6513 .rendered_frame
6514 .dispatch_tree
6515 .dispatch_path(node_id);
6516
6517 let mut keystroke: Option<Keystroke> = None;
6518
6519 if let Some(event) = event.downcast_ref::<ModifiersChangedEvent>() {
6520 if event.modifiers.number_of_modifiers() == 0
6521 && self.core.pending_modifier.modifiers.number_of_modifiers() == 1
6522 && !self.core.pending_modifier.saw_other_input
6523 {
6524 let key = match self.core.pending_modifier.modifiers {
6525 modifiers if modifiers.shift => Some("shift"),
6526 modifiers if modifiers.control => Some("control"),
6527 modifiers if modifiers.alt => Some("alt"),
6528 modifiers if modifiers.platform => Some("platform"),
6529 modifiers if modifiers.function => Some("function"),
6530 _ => None,
6531 };
6532 if let Some(key) = key {
6533 keystroke = Some(Keystroke {
6534 key: key.to_string(),
6535 key_char: None,
6536 modifiers: Modifiers::default(),
6537 });
6538 }
6539 }
6540
6541 if self.core.pending_modifier.modifiers.number_of_modifiers() == 0
6542 && event.modifiers.number_of_modifiers() == 1
6543 {
6544 self.core.pending_modifier.saw_other_input = false
6545 } else if event.modifiers.number_of_modifiers() > 1 {
6546 self.core.pending_modifier.saw_other_input = true
6547 }
6548 self.core.pending_modifier.modifiers = event.modifiers
6549 } else if let Some(key_down_event) = event.downcast_ref::<KeyDownEvent>() {
6550 self.core.pending_modifier.saw_other_input = true;
6551 keystroke = Some(key_down_event.keystroke.clone());
6552 if key_down_event.keystroke.key_char.is_some()
6553 && matches!(
6554 cx.cursor_hide_mode,
6555 CursorHideMode::OnTyping | CursorHideMode::OnTypingAndAction
6556 )
6557 {
6558 cx.platform.hide_cursor_until_mouse_moves();
6559 }
6560 }
6561
6562 let Some(keystroke) = keystroke else {
6563 self.finish_dispatch_key_event(
6564 event,
6565 None,
6566 InputPreference::KeyBindings,
6567 dispatch_path,
6568 self.context_stack(),
6569 cx,
6570 );
6571 return;
6572 };
6573
6574 let input_preference = self.input_preference(event, cx);
6575
6576 cx.propagate_event = true;
6577 self.dispatch_keystroke_interceptors(
6578 &keystroke,
6579 input_preference,
6580 self.context_stack(),
6581 cx,
6582 );
6583 if !cx.propagate_event {
6584 self.finish_dispatch_key_event(
6585 event,
6586 Some(&keystroke),
6587 input_preference,
6588 dispatch_path,
6589 self.context_stack(),
6590 cx,
6591 );
6592 return;
6593 }
6594
6595 let mut currently_pending = self.core.pending_input.take().unwrap_or_default();
6596 if currently_pending.focus.is_some() && currently_pending.focus != self.core.focus {
6597 currently_pending = PendingInput::default();
6598 }
6599
6600 let match_result = self.frame_state.rendered_frame.dispatch_tree.dispatch_key(
6601 currently_pending.keystrokes,
6602 keystroke.clone(),
6603 &dispatch_path,
6604 );
6605
6606 if !match_result.to_replay.is_empty() {
6607 self.replay_pending_input(match_result.to_replay, cx);
6608 cx.propagate_event = true;
6609 }
6610
6611 if !match_result.pending.is_empty() {
6612 let previous_timeout = currently_pending.timeout.take();
6613 currently_pending.keystrokes = match_result.pending;
6614 currently_pending.focus = self.core.focus;
6615
6616 let text_input_requires_timeout = event
6617 .downcast_ref::<KeyDownEvent>()
6618 .filter(|key_down| key_down.keystroke.key_char.is_some())
6619 .and_then(|_| self.core.platform_window.take_input_handler())
6620 .map_or(false, |mut input_handler| {
6621 let accepts = input_handler.accepts_text_input(self, cx);
6622 self.core.platform_window.set_input_handler(input_handler);
6623 accepts
6624 });
6625
6626 let needs_timeout = previous_timeout.is_some()
6627 || match_result.pending_has_binding
6628 || text_input_requires_timeout;
6629 currently_pending.timeout = if needs_timeout {
6630 match previous_timeout {
6631 Some(mut timeout) if timeout.is_paused() => {
6632 timeout.reset_duration(PENDING_INPUT_TIMEOUT);
6633 Some(timeout)
6634 }
6635 previous_timeout => {
6636 drop(previous_timeout);
6637 Some(self.new_pending_input_timeout(PENDING_INPUT_TIMEOUT, cx))
6638 }
6639 }
6640 } else {
6641 None
6642 };
6643 self.core.pending_input = Some(currently_pending);
6644 self.pending_input_changed(cx);
6645 cx.propagate_event = false;
6646 return;
6647 }
6648
6649 let input_preference = self.input_preference(event, cx);
6651 if input_preference == InputPreference::KeyBindings {
6652 for binding in match_result.bindings {
6653 self.dispatch_action_on_node(node_id, binding.action.as_ref(), cx);
6654 if !cx.propagate_event {
6655 self.dispatch_keystroke_observers(
6656 &keystroke,
6657 input_preference,
6658 Some(binding.action.as_ref()),
6659 match_result.context_stack,
6660 cx,
6661 );
6662 self.pending_input_changed(cx);
6663 return;
6664 }
6665 }
6666 }
6667
6668 self.finish_dispatch_key_event(
6669 event,
6670 Some(&keystroke),
6671 input_preference,
6672 dispatch_path,
6673 match_result.context_stack,
6674 cx,
6675 );
6676 self.pending_input_changed(cx);
6677 }
6678
6679 fn new_pending_input_timeout(&self, duration: Duration, cx: &App) -> PendingInputTimeout {
6680 let (started_at, task) = self.start_pending_input_timeout(duration, cx);
6681 PendingInputTimeout {
6682 duration,
6683 remaining: duration,
6684 state: PendingInputTimeoutState::Running { started_at, task },
6685 }
6686 }
6687
6688 fn start_pending_input_timeout(&self, remaining: Duration, cx: &App) -> (Instant, Task<()>) {
6689 let started_at = cx.background_executor().now();
6690 let task = self.spawn(cx, async move |cx| {
6691 cx.background_executor.timer(remaining).await;
6692 cx.update(move |window, cx| {
6693 let Some(currently_pending) = window
6694 .core
6695 .pending_input
6696 .take()
6697 .filter(|pending| pending.focus == window.core.focus)
6698 else {
6699 return;
6700 };
6701
6702 let node_id = window.focus_node_id_in_rendered_frame(window.core.focus);
6703 let dispatch_path = window
6704 .frame_state
6705 .rendered_frame
6706 .dispatch_tree
6707 .dispatch_path(node_id);
6708
6709 let to_replay = window
6710 .frame_state
6711 .rendered_frame
6712 .dispatch_tree
6713 .flush_dispatch(currently_pending.keystrokes, &dispatch_path);
6714
6715 window.pending_input_changed(cx);
6716 window.replay_pending_input(to_replay, cx)
6717 })
6718 .log_err();
6719 });
6720 (started_at, task)
6721 }
6722
6723 fn finish_dispatch_key_event(
6724 &mut self,
6725 event: &dyn Any,
6726 recognized_keystroke: Option<&Keystroke>,
6727 input_preference: InputPreference,
6728 dispatch_path: SmallVec<[DispatchNodeId; 32]>,
6729 context_stack: Vec<KeyContext>,
6730 cx: &mut App,
6731 ) {
6732 self.dispatch_key_down_up_event(event, &dispatch_path, cx);
6733 if !cx.propagate_event {
6734 return;
6735 }
6736
6737 self.dispatch_modifiers_changed_event(event, &dispatch_path, cx);
6738 if !cx.propagate_event {
6739 return;
6740 }
6741
6742 if let Some(keystroke) = recognized_keystroke {
6743 self.dispatch_keystroke_observers(keystroke, input_preference, None, context_stack, cx);
6744 }
6745 }
6746
6747 pub(crate) fn pending_input_changed(&mut self, cx: &mut App) {
6748 self.core
6749 .pending_input_observers
6750 .clone()
6751 .retain(&(), |callback| callback(self, cx));
6752 }
6753
6754 fn defer_pending_input_changed(&self, cx: &mut App) {
6755 let window_handle = self.core.handle;
6758 cx.defer(move |cx| {
6759 window_handle
6760 .update(cx, |_, window, cx| {
6761 window.pending_input_changed(cx);
6762 })
6763 .ok();
6764 });
6765 }
6766
6767 fn dispatch_key_down_up_event(
6768 &mut self,
6769 event: &dyn Any,
6770 dispatch_path: &SmallVec<[DispatchNodeId; 32]>,
6771 cx: &mut App,
6772 ) {
6773 for node_id in dispatch_path {
6775 let node = self.frame_state.rendered_frame.dispatch_tree.node(*node_id);
6776
6777 for key_listener in node.key_listeners.clone() {
6778 key_listener(event, DispatchPhase::Capture, self, cx);
6779 if !cx.propagate_event {
6780 return;
6781 }
6782 }
6783 }
6784
6785 for node_id in dispatch_path.iter().rev() {
6787 let node = self.frame_state.rendered_frame.dispatch_tree.node(*node_id);
6789 for key_listener in node.key_listeners.clone() {
6790 key_listener(event, DispatchPhase::Bubble, self, cx);
6791 if !cx.propagate_event {
6792 return;
6793 }
6794 }
6795 }
6796 }
6797
6798 fn dispatch_modifiers_changed_event(
6799 &mut self,
6800 event: &dyn Any,
6801 dispatch_path: &SmallVec<[DispatchNodeId; 32]>,
6802 cx: &mut App,
6803 ) {
6804 let Some(event) = event.downcast_ref::<ModifiersChangedEvent>() else {
6805 return;
6806 };
6807 for node_id in dispatch_path.iter().rev() {
6808 let node = self.frame_state.rendered_frame.dispatch_tree.node(*node_id);
6809 for listener in node.modifiers_changed_listeners.clone() {
6810 listener(event, self, cx);
6811 if !cx.propagate_event {
6812 return;
6813 }
6814 }
6815 }
6816 }
6817
6818 pub fn has_pending_keystrokes(&self) -> bool {
6820 self.pending_input().is_some()
6821 }
6822
6823 #[cfg(test)]
6824 pub(crate) fn pending_input_is_none(&self) -> bool {
6825 self.core.pending_input.is_none()
6826 }
6827
6828 pub(crate) fn clear_pending_keystrokes(&mut self, cx: &mut App) {
6829 if self.core.pending_input.take().is_some() {
6830 self.defer_pending_input_changed(cx);
6831 }
6832 }
6833
6834 pub fn pending_input(&self) -> Option<PendingInputStatus<'_>> {
6837 self.core
6838 .pending_input
6839 .as_ref()
6840 .filter(|pending_input| pending_input.focus == self.core.focus)
6841 .map(|pending_input| PendingInputStatus {
6842 keystrokes: pending_input.keystrokes.as_slice(),
6843 timeout: pending_input
6844 .timeout
6845 .as_ref()
6846 .map(PendingInputTimeout::status),
6847 })
6848 }
6849
6850 pub fn set_pending_input_timeout_paused<T: 'static>(
6855 &mut self,
6856 owner: &Entity<T>,
6857 paused: bool,
6858 cx: &mut App,
6859 ) -> bool {
6860 let owner_id = owner.entity_id();
6861 if !paused {
6862 return self.resume_pending_input_timeout(owner_id, cx);
6863 }
6864
6865 let timeout = self
6866 .core
6867 .pending_input
6868 .as_ref()
6869 .filter(|pending_input| pending_input.focus == self.core.focus)
6870 .and_then(|pending_input| pending_input.timeout.as_ref());
6871 let Some(timeout) = timeout else {
6872 return false;
6873 };
6874 if timeout.is_paused() {
6875 return false;
6876 }
6877
6878 let release_subscription = self.observe_release(owner, cx, move |_, window, cx| {
6879 window.resume_pending_input_timeout(owner_id, cx);
6880 });
6881 let now = cx.background_executor().now();
6882 let changed = self
6883 .core
6884 .pending_input
6885 .as_mut()
6886 .filter(|pending_input| pending_input.focus == self.core.focus)
6887 .and_then(|pending_input| pending_input.timeout.as_mut())
6888 .is_some_and(|timeout| {
6889 timeout.pause(
6890 PendingInputTimeoutPause {
6891 owner_id,
6892 _release_subscription: release_subscription,
6893 },
6894 now,
6895 )
6896 });
6897
6898 if changed {
6899 self.defer_pending_input_changed(cx);
6900 }
6901 changed
6902 }
6903
6904 fn resume_pending_input_timeout(&mut self, owner_id: EntityId, cx: &mut App) -> bool {
6905 let Some(remaining) = self
6906 .core
6907 .pending_input
6908 .as_ref()
6909 .and_then(|pending_input| pending_input.timeout.as_ref())
6910 .filter(|timeout| timeout.pause_owner_id() == Some(owner_id))
6911 .map(|timeout| timeout.remaining)
6912 else {
6913 return false;
6914 };
6915
6916 let (started_at, task) = self.start_pending_input_timeout(remaining, cx);
6917 let changed = self
6918 .core
6919 .pending_input
6920 .as_mut()
6921 .and_then(|pending_input| pending_input.timeout.as_mut())
6922 .is_some_and(|timeout| timeout.resume(owner_id, started_at, task));
6923
6924 if changed {
6925 self.defer_pending_input_changed(cx);
6926 }
6927 changed
6928 }
6929
6930 pub fn pending_input_keystrokes(&self) -> Option<&[Keystroke]> {
6932 self.pending_input()
6933 .map(|pending_input| pending_input.keystrokes())
6934 }
6935
6936 fn replay_pending_input(&mut self, replays: SmallVec<[Replay; 1]>, cx: &mut App) {
6937 let node_id = self.focus_node_id_in_rendered_frame(self.core.focus);
6938 let dispatch_path = self
6939 .frame_state
6940 .rendered_frame
6941 .dispatch_tree
6942 .dispatch_path(node_id);
6943
6944 'replay: for replay in replays {
6945 let event = KeyDownEvent {
6946 keystroke: replay.keystroke.clone(),
6947 is_held: false,
6948 prefer_character_input: true,
6949 };
6950
6951 cx.propagate_event = true;
6952 for binding in replay.bindings {
6953 self.dispatch_action_on_node(node_id, binding.action.as_ref(), cx);
6954 if !cx.propagate_event {
6955 self.dispatch_keystroke_observers(
6956 &replay.keystroke,
6957 InputPreference::KeyBindings,
6958 Some(binding.action.as_ref()),
6959 Vec::default(),
6960 cx,
6961 );
6962 continue 'replay;
6963 }
6964 }
6965
6966 self.dispatch_key_down_up_event(&event, &dispatch_path, cx);
6967 if !cx.propagate_event {
6968 continue 'replay;
6969 }
6970 if let Some(input) = replay.keystroke.key_char.as_ref().cloned()
6971 && let Some(mut input_handler) = self.core.platform_window.take_input_handler()
6972 {
6973 input_handler.dispatch_input(&input, self, cx);
6974 self.core.platform_window.set_input_handler(input_handler)
6975 }
6976 }
6977 }
6978
6979 fn focus_node_id_in_rendered_frame(&self, focus_id: Option<FocusId>) -> DispatchNodeId {
6980 focus_id
6981 .and_then(|focus_id| {
6982 self.frame_state
6983 .rendered_frame
6984 .dispatch_tree
6985 .focusable_node_id(focus_id)
6986 })
6987 .unwrap_or_else(|| self.frame_state.rendered_frame.dispatch_tree.root_node_id())
6988 }
6989
6990 fn dispatch_action_on_node(
6991 &mut self,
6992 node_id: DispatchNodeId,
6993 action: &dyn Action,
6994 cx: &mut App,
6995 ) {
6996 self.dispatch_action_on_node_inner(node_id, action, cx);
6997
6998 if !cx.propagate_event
6999 && cx.cursor_hide_mode == CursorHideMode::OnTypingAndAction
7000 && self.last_input_was_keyboard()
7001 {
7002 cx.platform.hide_cursor_until_mouse_moves();
7003 }
7004 }
7005
7006 fn dispatch_action_on_node_inner(
7007 &mut self,
7008 node_id: DispatchNodeId,
7009 action: &dyn Action,
7010 cx: &mut App,
7011 ) {
7012 let dispatch_path = self
7013 .frame_state
7014 .rendered_frame
7015 .dispatch_tree
7016 .dispatch_path(node_id);
7017
7018 cx.propagate_event = true;
7020 if let Some(mut global_listeners) = cx
7021 .global_action_listeners
7022 .remove(&action.as_any().type_id())
7023 {
7024 for listener in &global_listeners {
7025 #[cfg(feature = "profiler")]
7026 self.core.window_profiler.begin_action_handler(action, cx);
7027 listener(action.as_any(), DispatchPhase::Capture, cx);
7028 #[cfg(feature = "profiler")]
7029 self.core.window_profiler.end_action_handler();
7030 if !cx.propagate_event {
7031 break;
7032 }
7033 }
7034
7035 global_listeners.extend(
7036 cx.global_action_listeners
7037 .remove(&action.as_any().type_id())
7038 .unwrap_or_default(),
7039 );
7040
7041 cx.global_action_listeners
7042 .insert(action.as_any().type_id(), global_listeners);
7043 }
7044
7045 if !cx.propagate_event {
7046 return;
7047 }
7048
7049 for node_id in &dispatch_path {
7051 let node = self.frame_state.rendered_frame.dispatch_tree.node(*node_id);
7052 for DispatchActionListener {
7053 action_type,
7054 listener,
7055 } in node.action_listeners.clone()
7056 {
7057 let any_action = action.as_any();
7058 if action_type == any_action.type_id() {
7059 #[cfg(feature = "profiler")]
7060 self.core.window_profiler.begin_action_handler(action, cx);
7061 listener(any_action, DispatchPhase::Capture, self, cx);
7062 #[cfg(feature = "profiler")]
7063 self.core.window_profiler.end_action_handler();
7064
7065 if !cx.propagate_event {
7066 return;
7067 }
7068 }
7069 }
7070 }
7071
7072 for node_id in dispatch_path.iter().rev() {
7074 let node = self.frame_state.rendered_frame.dispatch_tree.node(*node_id);
7075 for DispatchActionListener {
7076 action_type,
7077 listener,
7078 } in node.action_listeners.clone()
7079 {
7080 let any_action = action.as_any();
7081 if action_type == any_action.type_id() {
7082 cx.propagate_event = false; #[cfg(feature = "profiler")]
7084 self.core.window_profiler.begin_action_handler(action, cx);
7085 listener(any_action, DispatchPhase::Bubble, self, cx);
7086 #[cfg(feature = "profiler")]
7087 self.core.window_profiler.end_action_handler();
7088
7089 if !cx.propagate_event {
7090 return;
7091 }
7092 }
7093 }
7094 }
7095
7096 if let Some(mut global_listeners) = cx
7098 .global_action_listeners
7099 .remove(&action.as_any().type_id())
7100 {
7101 for listener in global_listeners.iter().rev() {
7102 cx.propagate_event = false; #[cfg(feature = "profiler")]
7105 self.core.window_profiler.begin_action_handler(action, cx);
7106 listener(action.as_any(), DispatchPhase::Bubble, cx);
7107 #[cfg(feature = "profiler")]
7108 self.core.window_profiler.end_action_handler();
7109 if !cx.propagate_event {
7110 break;
7111 }
7112 }
7113
7114 global_listeners.extend(
7115 cx.global_action_listeners
7116 .remove(&action.as_any().type_id())
7117 .unwrap_or_default(),
7118 );
7119
7120 cx.global_action_listeners
7121 .insert(action.as_any().type_id(), global_listeners);
7122 }
7123 }
7124
7125 pub fn observe_global<G: Global>(
7128 &mut self,
7129 cx: &mut App,
7130 f: impl Fn(&mut Window, &mut App) + 'static,
7131 ) -> Subscription {
7132 let window_handle = self.core.handle;
7133 let (subscription, activate) = cx.global_observers.insert(
7134 TypeId::of::<G>(),
7135 Box::new(move |cx| {
7136 window_handle
7137 .update(cx, |_, window, cx| f(window, cx))
7138 .is_ok()
7139 }),
7140 );
7141 cx.defer(move |_| activate());
7142 subscription
7143 }
7144
7145 pub fn activate_window(&self) {
7147 self.core.platform_window.activate();
7148 }
7149
7150 pub fn request_attention(&self) {
7152 self.core.platform_window.request_attention();
7153 }
7154
7155 pub fn minimize_window(&self) {
7157 self.core.platform_window.minimize();
7158 }
7159
7160 pub fn toggle_fullscreen(&self) {
7162 self.core.platform_window.toggle_fullscreen();
7163 }
7164
7165 pub fn toggle_simple_fullscreen(&self) {
7170 self.core.platform_window.toggle_simple_fullscreen();
7171 }
7172
7173 pub fn invalidate_character_coordinates(&self) {
7175 self.on_next_frame(|window, cx| {
7176 if let Some(mut input_handler) = window.core.platform_window.take_input_handler() {
7177 if let Some(bounds) = input_handler.selected_bounds(window, cx) {
7178 window.core.platform_window.update_ime_position(bounds);
7179 }
7180 window.core.platform_window.set_input_handler(input_handler);
7181 }
7182 });
7183 }
7184
7185 pub fn prompt<T>(
7189 &mut self,
7190 level: PromptLevel,
7191 message: &str,
7192 detail: Option<&str>,
7193 answers: &[T],
7194 cx: &mut App,
7195 ) -> oneshot::Receiver<usize>
7196 where
7197 T: Clone + Into<PromptButton>,
7198 {
7199 let prompt_builder = cx.prompt_builder.take();
7200 let Some(prompt_builder) = prompt_builder else {
7201 unreachable!("Re-entrant window prompting is not supported by GPUI");
7202 };
7203
7204 let answers = answers
7205 .iter()
7206 .map(|answer| answer.clone().into())
7207 .collect::<Vec<_>>();
7208
7209 let receiver = match &prompt_builder {
7210 PromptBuilder::Default => self
7211 .core
7212 .platform_window
7213 .prompt(level, message, detail, &answers)
7214 .unwrap_or_else(|| {
7215 self.build_custom_prompt(&prompt_builder, level, message, detail, &answers, cx)
7216 }),
7217 PromptBuilder::Custom(_) => {
7218 self.build_custom_prompt(&prompt_builder, level, message, detail, &answers, cx)
7219 }
7220 };
7221
7222 cx.prompt_builder = Some(prompt_builder);
7223
7224 receiver
7225 }
7226
7227 fn build_custom_prompt(
7228 &mut self,
7229 prompt_builder: &PromptBuilder,
7230 level: PromptLevel,
7231 message: &str,
7232 detail: Option<&str>,
7233 answers: &[PromptButton],
7234 cx: &mut App,
7235 ) -> oneshot::Receiver<usize> {
7236 let (sender, receiver) = oneshot::channel();
7237 let handle = PromptHandle::new(sender);
7238 let handle = (prompt_builder)(level, message, detail, answers, handle, self, cx);
7239 self.core.prompt = Some(handle);
7240 receiver
7241 }
7242
7243 pub fn has_active_prompt(&self) -> bool {
7248 self.core.prompt.is_some()
7249 }
7250
7251 pub fn context_stack(&self) -> Vec<KeyContext> {
7253 let node_id = self.focus_node_id_in_rendered_frame(self.core.focus);
7254 let dispatch_tree = &self.frame_state.rendered_frame.dispatch_tree;
7255 dispatch_tree
7256 .dispatch_path(node_id)
7257 .iter()
7258 .filter_map(move |&node_id| dispatch_tree.node(node_id).context.clone())
7259 .collect()
7260 }
7261
7262 pub fn available_actions(&self, cx: &App) -> Vec<Box<dyn Action>> {
7264 let node_id = self.focus_node_id_in_rendered_frame(self.core.focus);
7265 let mut actions = self
7266 .frame_state
7267 .rendered_frame
7268 .dispatch_tree
7269 .available_actions(node_id);
7270 for action_type in cx.global_action_listeners.keys() {
7271 if let Err(ix) = actions.binary_search_by_key(action_type, |a| a.as_any().type_id()) {
7272 let action = cx.actions.build_action_type(action_type).ok();
7273 if let Some(action) = action {
7274 actions.insert(ix, action);
7275 }
7276 }
7277 }
7278 actions
7279 }
7280
7281 pub fn bindings_for_action(&self, action: &dyn Action) -> Vec<KeyBinding> {
7284 self.frame_state
7285 .rendered_frame
7286 .dispatch_tree
7287 .bindings_for_action(
7288 action,
7289 &self.frame_state.rendered_frame.dispatch_tree.context_stack,
7290 )
7291 }
7292
7293 pub fn highest_precedence_binding_for_action(&self, action: &dyn Action) -> Option<KeyBinding> {
7296 self.frame_state
7297 .rendered_frame
7298 .dispatch_tree
7299 .highest_precedence_binding_for_action(
7300 action,
7301 &self.frame_state.rendered_frame.dispatch_tree.context_stack,
7302 )
7303 }
7304
7305 pub fn bindings_for_action_in_context(
7307 &self,
7308 action: &dyn Action,
7309 context: KeyContext,
7310 ) -> Vec<KeyBinding> {
7311 let dispatch_tree = &self.frame_state.rendered_frame.dispatch_tree;
7312 dispatch_tree.bindings_for_action(action, &[context])
7313 }
7314
7315 pub fn highest_precedence_binding_for_action_in_context(
7318 &self,
7319 action: &dyn Action,
7320 context: KeyContext,
7321 ) -> Option<KeyBinding> {
7322 let dispatch_tree = &self.frame_state.rendered_frame.dispatch_tree;
7323 dispatch_tree.highest_precedence_binding_for_action(action, &[context])
7324 }
7325
7326 pub fn bindings_for_action_in(
7330 &self,
7331 action: &dyn Action,
7332 focus_handle: &FocusHandle,
7333 ) -> Vec<KeyBinding> {
7334 let dispatch_tree = &self.frame_state.rendered_frame.dispatch_tree;
7335 let Some(context_stack) = self.context_stack_for_focus_handle(focus_handle) else {
7336 return vec![];
7337 };
7338 dispatch_tree.bindings_for_action(action, &context_stack)
7339 }
7340
7341 pub fn highest_precedence_binding_for_action_in(
7345 &self,
7346 action: &dyn Action,
7347 focus_handle: &FocusHandle,
7348 ) -> Option<KeyBinding> {
7349 let dispatch_tree = &self.frame_state.rendered_frame.dispatch_tree;
7350 let context_stack = self.context_stack_for_focus_handle(focus_handle)?;
7351 dispatch_tree.highest_precedence_binding_for_action(action, &context_stack)
7352 }
7353
7354 pub fn possible_bindings_for_input(&self, input: &[Keystroke]) -> Vec<KeyBinding> {
7356 self.frame_state
7357 .rendered_frame
7358 .dispatch_tree
7359 .possible_next_bindings_for_input(input, &self.context_stack())
7360 }
7361
7362 fn context_stack_for_focus_handle(
7363 &self,
7364 focus_handle: &FocusHandle,
7365 ) -> Option<Vec<KeyContext>> {
7366 let dispatch_tree = &self.frame_state.rendered_frame.dispatch_tree;
7367 let node_id = dispatch_tree.focusable_node_id(focus_handle.id)?;
7368 let context_stack: Vec<_> = dispatch_tree
7369 .dispatch_path(node_id)
7370 .into_iter()
7371 .filter_map(|node_id| dispatch_tree.node(node_id).context.clone())
7372 .collect();
7373 Some(context_stack)
7374 }
7375
7376 pub fn listener_for<T: 'static, E>(
7378 &self,
7379 view: &Entity<T>,
7380 f: impl Fn(&mut T, &E, &mut Window, &mut Context<T>) + 'static,
7381 ) -> impl Fn(&E, &mut Window, &mut App) + 'static {
7382 let view = view.downgrade();
7383 move |e: &E, window: &mut Window, cx: &mut App| {
7384 view.update(cx, |view, cx| f(view, e, window, cx)).ok();
7385 }
7386 }
7387
7388 pub fn handler_for<E: 'static, Callback: Fn(&mut E, &mut Window, &mut Context<E>) + 'static>(
7390 &self,
7391 entity: &Entity<E>,
7392 f: Callback,
7393 ) -> impl Fn(&mut Window, &mut App) + 'static {
7394 let entity = entity.downgrade();
7395 move |window: &mut Window, cx: &mut App| {
7396 entity.update(cx, |entity, cx| f(entity, window, cx)).ok();
7397 }
7398 }
7399
7400 pub fn on_window_should_close(
7403 &self,
7404 cx: &App,
7405 f: impl Fn(&mut Window, &mut App) -> bool + 'static,
7406 ) {
7407 let mut cx = self.to_async(cx);
7408 self.core.platform_window.on_should_close(Box::new(move || {
7409 cx.update(|window, cx| f(window, cx)).unwrap_or(true)
7410 }))
7411 }
7412
7413 pub fn on_action(
7422 &mut self,
7423 action_type: TypeId,
7424 listener: impl Fn(&dyn Any, DispatchPhase, &mut Window, &mut App) + 'static,
7425 ) {
7426 self.core.invalidator.debug_assert_paint();
7427
7428 self.frame_state
7429 .next_frame
7430 .dispatch_tree
7431 .on_action(action_type, Rc::new(listener));
7432 }
7433
7434 pub fn on_action_when(
7443 &mut self,
7444 condition: bool,
7445 action_type: TypeId,
7446 listener: impl Fn(&dyn Any, DispatchPhase, &mut Window, &mut App) + 'static,
7447 ) {
7448 self.core.invalidator.debug_assert_paint();
7449
7450 if condition {
7451 self.frame_state
7452 .next_frame
7453 .dispatch_tree
7454 .on_action(action_type, Rc::new(listener));
7455 }
7456 }
7457
7458 pub fn gpu_specs(&self) -> Option<GpuSpecs> {
7461 self.core.platform_window.gpu_specs()
7462 }
7463
7464 pub fn titlebar_double_click(&self) {
7467 self.core
7468 .platform_window
7469 .titlebar_double_click(self.core.is_resizable, self.core.is_minimizable);
7470 }
7471
7472 pub fn window_title(&self) -> String {
7475 self.core.platform_window.get_title()
7476 }
7477
7478 pub fn tabbed_windows(&self) -> Option<Vec<SystemWindowTab>> {
7481 self.core.platform_window.tabbed_windows()
7482 }
7483
7484 pub fn tab_bar_visible(&self) -> bool {
7487 self.core.platform_window.tab_bar_visible()
7488 }
7489
7490 pub fn merge_all_windows(&self) {
7493 self.core.platform_window.merge_all_windows()
7494 }
7495
7496 pub fn move_tab_to_new_window(&self) {
7499 self.core.platform_window.move_tab_to_new_window()
7500 }
7501
7502 pub fn toggle_window_tab_overview(&self) {
7505 self.core.platform_window.toggle_window_tab_overview()
7506 }
7507
7508 pub fn set_tabbing_identifier(&self, tabbing_identifier: Option<String>) {
7511 self.core
7512 .platform_window
7513 .set_tabbing_identifier(tabbing_identifier)
7514 }
7515
7516 pub fn play_system_bell(&self) {
7519 self.core.platform_window.play_system_bell()
7520 }
7521
7522 pub fn is_a11y_enabled(&self) -> bool {
7529 self.core.a11y.is_enabled()
7530 }
7531
7532 pub fn is_a11y_active(&self) -> bool {
7543 self.core.a11y.is_active()
7544 }
7545
7546 pub fn debug_a11y_tree_json(&self) -> Option<String> {
7548 self.core.a11y.debug_tree_json()
7549 }
7550
7551 pub fn on_a11y_action(
7557 &mut self,
7558 node_id: accesskit::NodeId,
7559 action: accesskit::Action,
7560 listener: impl FnMut(Option<&accesskit::ActionData>, &mut Window, &mut App) + 'static,
7561 ) {
7562 self.core
7563 .a11y
7564 .action_listeners
7565 .entry(node_id)
7566 .or_default()
7567 .push((action, Box::new(listener)));
7568 }
7569
7570 #[cfg(not(target_family = "wasm"))]
7571 pub(crate) fn handle_a11y_action(&mut self, request: accesskit::ActionRequest, cx: &mut App) {
7572 if let Some(mut listeners) = self.core.a11y.action_listeners.remove(&request.target_node) {
7575 let extra_data = request.data.as_ref();
7576 let mut matched = false;
7577 for (action, listener) in &mut listeners {
7578 if *action == request.action {
7579 listener(extra_data, self, cx);
7580 matched = true;
7581 }
7582 }
7583 self.core
7584 .a11y
7585 .action_listeners
7586 .insert(request.target_node, listeners);
7587 if matched {
7588 return;
7589 }
7590 }
7591
7592 match request.action {
7594 accesskit::Action::Click => {
7595 if let Some(bounds) = self
7596 .core
7597 .a11y
7598 .node_bounds
7599 .get(&request.target_node)
7600 .copied()
7601 {
7602 let center = bounds.center();
7603 let mouse_down = PlatformInput::MouseDown(crate::MouseDownEvent {
7604 button: MouseButton::Left,
7605 position: center,
7606 modifiers: Modifiers::default(),
7607 click_count: 1,
7608 first_mouse: false,
7609 });
7610 let mouse_up = PlatformInput::MouseUp(MouseUpEvent {
7611 button: MouseButton::Left,
7612 position: center,
7613 modifiers: Modifiers::default(),
7614 click_count: 1,
7615 });
7616 self.dispatch_event(mouse_down, cx);
7617 self.dispatch_event(mouse_up, cx);
7618 }
7619 }
7620 accesskit::Action::Focus => {
7621 if let Some(focus_id) = self.core.a11y.focus_ids.get(&request.target_node).copied()
7622 && let Some(handle) = FocusHandle::for_id(focus_id, &cx.focus_handles)
7623 {
7624 self.focus(&handle, cx);
7625 }
7626 }
7627 accesskit::Action::Blur => {
7628 self.blur(cx);
7629 }
7630 _ => {
7631 log::debug!(
7632 "Unhandled a11y action: {:?} on {:?}",
7633 request.action,
7634 request.target_node
7635 );
7636 }
7637 }
7638 }
7639
7640 #[cfg(any(feature = "inspector", debug_assertions))]
7642 pub fn toggle_inspector(&mut self, cx: &mut App) {
7643 self.core.inspector = match self.core.inspector {
7644 None => Some(cx.new(|_| Inspector::new())),
7645 Some(_) => {
7646 self.frame_state.rendered_frame.next_inspector_instance_ids = FxHashMap::default();
7647 self.frame_state.rendered_frame.inspector_hitboxes = FxHashMap::default();
7648 self.frame_state.next_frame.next_inspector_instance_ids = FxHashMap::default();
7649 self.frame_state.next_frame.inspector_hitboxes = FxHashMap::default();
7650 None
7651 }
7652 };
7653 self.refresh();
7654 }
7655
7656 pub fn is_inspector_picking(&self, _cx: &App) -> bool {
7658 #[cfg(any(feature = "inspector", debug_assertions))]
7659 {
7660 if let Some(inspector) = &self.core.inspector {
7661 return inspector.read(_cx).is_picking();
7662 }
7663 }
7664 false
7665 }
7666
7667 pub(crate) fn set_inspector_element_id(
7671 &mut self,
7672 inspector_element_id: Option<crate::InspectorElementId>,
7673 ) -> Option<crate::InspectorElementId> {
7674 std::mem::replace(
7675 &mut self.frame_state.inspector_element_id,
7676 inspector_element_id,
7677 )
7678 }
7679
7680 #[cfg(any(feature = "inspector", debug_assertions))]
7685 pub fn with_inspector_state<T: 'static, R>(
7686 &mut self,
7687 inspector_id: Option<&crate::InspectorElementId>,
7688 cx: &mut App,
7689 f: impl FnOnce(&mut Option<T>, &mut Self) -> R,
7690 ) -> Option<R> {
7691 let inspector_id = inspector_id?;
7692 let inspector = self.core.inspector.as_ref()?;
7693 if inspector.read(cx).active_element_id() != Some(inspector_id) {
7694 return None;
7695 }
7696 let inspector = inspector.clone();
7697 Some(inspector.update(cx, |inspector, _cx| {
7698 inspector.with_active_element_state(self, f)
7699 }))
7700 }
7701
7702 #[cfg(any(feature = "inspector", debug_assertions))]
7703 pub(crate) fn inspector_enabled(&self) -> bool {
7704 self.core.inspector.is_some()
7705 }
7706
7707 #[cfg(any(feature = "inspector", debug_assertions))]
7713 pub fn with_current_inspector_state<T: 'static, R>(
7714 &mut self,
7715 cx: &mut App,
7716 f: impl FnOnce(&mut Option<T>, &mut Self) -> R,
7717 ) -> Option<R> {
7718 let inspector_id = self.frame_state.inspector_element_id.clone();
7719 self.with_inspector_state(inspector_id.as_ref(), cx, f)
7720 }
7721
7722 #[cfg(any(feature = "inspector", debug_assertions))]
7723 pub(crate) fn build_inspector_element_id(
7724 &mut self,
7725 path: crate::InspectorElementPath,
7726 ) -> crate::InspectorElementId {
7727 self.core.invalidator.debug_assert_paint_or_prepaint();
7728 let path = Rc::new(path);
7729 let next_instance_id = self
7730 .frame_state
7731 .next_frame
7732 .next_inspector_instance_ids
7733 .entry(path.clone())
7734 .or_insert(0);
7735 let instance_id = *next_instance_id;
7736 *next_instance_id += 1;
7737 crate::InspectorElementId { path, instance_id }
7738 }
7739
7740 #[cfg(any(feature = "inspector", debug_assertions))]
7741 fn prepaint_inspector(&mut self, inspector_width: Pixels, cx: &mut App) -> Option<AnyElement> {
7742 if let Some(inspector) = self.core.inspector.take() {
7743 let mut inspector_element = AnyView::from(inspector.clone()).into_any_element();
7744 inspector_element.prepaint_as_root(
7745 point(self.core.viewport_size.width - inspector_width, px(0.0)),
7746 size(inspector_width, self.core.viewport_size.height).into(),
7747 self,
7748 cx,
7749 );
7750 self.core.inspector = Some(inspector);
7751 Some(inspector_element)
7752 } else {
7753 None
7754 }
7755 }
7756
7757 #[cfg(any(feature = "inspector", debug_assertions))]
7758 fn paint_inspector(&mut self, mut inspector_element: Option<AnyElement>, cx: &mut App) {
7759 if let Some(mut inspector_element) = inspector_element {
7760 inspector_element.paint(self, cx);
7761 };
7762 }
7763
7764 #[cfg(any(feature = "inspector", debug_assertions))]
7770 pub(crate) fn insert_inspector_hitbox(&mut self, hitbox_id: HitboxId, cx: &App) {
7771 self.core.invalidator.debug_assert_paint_or_prepaint();
7772 if !self.is_inspector_picking(cx) {
7773 return;
7774 }
7775 if let Some(inspector_id) = self.frame_state.inspector_element_id.clone() {
7776 self.frame_state
7777 .next_frame
7778 .inspector_hitboxes
7779 .insert(hitbox_id, inspector_id);
7780 }
7781 }
7782
7783 #[cfg(any(feature = "inspector", debug_assertions))]
7784 fn paint_inspector_hitbox(&mut self, cx: &App) {
7785 if let Some(inspector) = self.core.inspector.as_ref() {
7786 let inspector = inspector.read(cx);
7787 if let Some((hitbox_id, _)) =
7788 self.hovered_inspector_hitbox(inspector, &self.frame_state.next_frame)
7789 && let Some(hitbox) = self
7790 .frame_state
7791 .next_frame
7792 .hitboxes
7793 .iter()
7794 .find(|hitbox| hitbox.id == hitbox_id)
7795 {
7796 self.paint_quad(crate::fill(hitbox.bounds, crate::rgba(0x61afef4d)));
7797 }
7798 }
7799 }
7800
7801 #[cfg(any(feature = "inspector", debug_assertions))]
7802 fn handle_inspector_mouse_event(&mut self, event: &dyn Any, cx: &mut App) {
7803 let Some(inspector) = self.core.inspector.clone() else {
7804 return;
7805 };
7806 if event.downcast_ref::<MouseMoveEvent>().is_some() {
7807 inspector.update(cx, |inspector, _cx| {
7808 if let Some((_, inspector_id)) =
7809 self.hovered_inspector_hitbox(inspector, &self.frame_state.rendered_frame)
7810 {
7811 inspector.hover(inspector_id, self);
7812 }
7813 });
7814 } else if event.downcast_ref::<crate::MouseDownEvent>().is_some() {
7815 inspector.update(cx, |inspector, _cx| {
7816 if let Some((_, inspector_id)) =
7817 self.hovered_inspector_hitbox(inspector, &self.frame_state.rendered_frame)
7818 {
7819 inspector.select(inspector_id, self);
7820 }
7821 });
7822 } else if let Some(event) = event.downcast_ref::<crate::ScrollWheelEvent>() {
7823 const SCROLL_LINES: f32 = 3.0;
7825 const SCROLL_PIXELS_PER_LAYER: f32 = 36.0;
7826 let delta_y = event
7827 .delta
7828 .pixel_delta(px(SCROLL_PIXELS_PER_LAYER / SCROLL_LINES))
7829 .y;
7830 if let Some(inspector) = self.core.inspector.clone() {
7831 inspector.update(cx, |inspector, _cx| {
7832 if let Some(depth) = inspector.pick_depth.as_mut() {
7833 *depth += f32::from(delta_y) / SCROLL_PIXELS_PER_LAYER;
7834 let max_depth = self.core.mouse_hit_test.ids.len() as f32 - 0.5;
7835 if *depth < 0.0 {
7836 *depth = 0.0;
7837 } else if *depth > max_depth {
7838 *depth = max_depth;
7839 }
7840 if let Some((_, inspector_id)) = self
7841 .hovered_inspector_hitbox(inspector, &self.frame_state.rendered_frame)
7842 {
7843 inspector.set_active_element_id(inspector_id, self);
7844 }
7845 }
7846 });
7847 }
7848 }
7849 }
7850
7851 #[cfg(any(feature = "inspector", debug_assertions))]
7852 fn hovered_inspector_hitbox(
7853 &self,
7854 inspector: &Inspector,
7855 frame: &Frame,
7856 ) -> Option<(HitboxId, crate::InspectorElementId)> {
7857 if let Some(pick_depth) = inspector.pick_depth {
7858 let depth = (pick_depth as i64).try_into().unwrap_or(0);
7859 let max_skipped = self.core.mouse_hit_test.ids.len().saturating_sub(1);
7860 let skip_count = (depth as usize).min(max_skipped);
7861 for hitbox_id in self.core.mouse_hit_test.ids.iter().skip(skip_count) {
7862 if let Some(inspector_id) = frame.inspector_hitboxes.get(hitbox_id) {
7863 return Some((*hitbox_id, inspector_id.clone()));
7864 }
7865 }
7866 }
7867 None
7868 }
7869
7870 #[cfg(any(test, feature = "test-support"))]
7873 pub fn set_modifiers(&mut self, modifiers: Modifiers) {
7874 self.core.modifiers = modifiers;
7875 }
7876
7877 #[cfg(any(test, feature = "test-support"))]
7881 pub fn simulate_mouse_move(&mut self, position: Point<Pixels>, cx: &mut App) {
7882 let event = PlatformInput::MouseMove(MouseMoveEvent {
7883 position,
7884 modifiers: self.core.modifiers,
7885 pressed_button: None,
7886 });
7887 let _ = self.dispatch_event(event, cx);
7888 }
7889
7890 #[inline(never)]
7891 fn take_element_state(
7892 &mut self,
7893 global_id: &GlobalElementId,
7894 state_type: TypeId,
7895 ) -> ((GlobalElementId, TypeId), Option<ElementStateBox>) {
7896 let key = (global_id.clone(), state_type);
7897 self.frame_state
7898 .next_frame
7899 .accessed_element_states
7900 .push(key.clone());
7901 let state = self
7902 .frame_state
7903 .next_frame
7904 .element_states
7905 .remove(&key)
7906 .or_else(|| self.frame_state.rendered_frame.element_states.remove(&key));
7907 (key, state)
7908 }
7909
7910 #[inline(never)]
7911 fn insert_element_state(
7912 &mut self,
7913 key: (GlobalElementId, TypeId),
7914 state: ElementStateBox,
7915 ) -> Option<ElementStateBox> {
7916 self.frame_state
7917 .next_frame
7918 .element_states
7919 .insert(key, state)
7920 }
7921
7922 #[inline(never)]
7923 fn observe_keyed_state<S: 'static>(state: &Entity<S>, current_view: EntityId, cx: &mut App) {
7924 cx.observe(state, move |_, cx| {
7925 cx.notify(current_view);
7926 })
7927 .detach();
7928 }
7929}
7930
7931#[derive(Deref, DerefMut)]
7934pub struct WindowHandle<V> {
7935 #[deref]
7936 #[deref_mut]
7937 pub(crate) any_handle: AnyWindowHandle,
7938 state_type: PhantomData<fn(V) -> V>,
7939}
7940
7941impl<V> Debug for WindowHandle<V> {
7942 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
7943 f.debug_struct("WindowHandle")
7944 .field("any_handle", &self.any_handle.id.as_u64())
7945 .finish()
7946 }
7947}
7948
7949impl<V: 'static + Render> WindowHandle<V> {
7950 pub fn new(id: WindowId) -> Self {
7953 WindowHandle {
7954 any_handle: AnyWindowHandle {
7955 id,
7956 state_type: TypeId::of::<V>(),
7957 root_entity_type_name: std::any::type_name::<V>(),
7958 },
7959 state_type: PhantomData,
7960 }
7961 }
7962
7963 #[cfg(any(test, feature = "test-support"))]
7967 pub fn root<C>(&self, cx: &mut C) -> Result<Entity<V>>
7968 where
7969 C: AppContext,
7970 {
7971 cx.update_window(self.any_handle, |root_view, _, _| {
7972 root_view
7973 .downcast::<V>()
7974 .map_err(|_| anyhow!("the type of the window's root view has changed"))
7975 })?
7976 }
7977
7978 pub fn update<C, R>(
7982 &self,
7983 cx: &mut C,
7984 update: impl FnOnce(&mut V, &mut Window, &mut Context<V>) -> R,
7985 ) -> Result<R>
7986 where
7987 C: AppContext,
7988 {
7989 cx.update_window(self.any_handle, |root_view, window, cx| {
7990 let view = root_view
7991 .downcast::<V>()
7992 .map_err(|_| anyhow!("the type of the window's root view has changed"))?;
7993
7994 Ok(view.update(cx, |view, cx| update(view, window, cx)))
7995 })?
7996 }
7997
7998 pub fn read<'a>(&self, cx: &'a App) -> Result<&'a V> {
8002 let x = cx
8003 .windows
8004 .cell(self.id)
8005 .and_then(|cell| cell.borrow().core.root.clone())
8006 .map(|root_view| root_view.downcast::<V>())
8007 .context("window not found")?
8008 .map_err(|_| anyhow!("the type of the window's root view has changed"))?;
8009
8010 Ok(x.read(cx))
8011 }
8012
8013 pub fn read_with<C, R>(&self, cx: &C, read_with: impl FnOnce(&V, &App) -> R) -> Result<R>
8017 where
8018 C: AppContext,
8019 {
8020 cx.read_window(self, |root_view, cx| read_with(root_view.read(cx), cx))
8021 }
8022
8023 pub fn entity<C>(&self, cx: &C) -> Result<Entity<V>>
8027 where
8028 C: AppContext,
8029 {
8030 cx.read_window(self, |root_view, _cx| root_view)
8031 }
8032
8033 pub fn is_active(&self, cx: &mut App) -> Option<bool> {
8038 cx.update_window(self.any_handle, |_, window, _| window.is_window_active())
8039 .ok()
8040 }
8041}
8042
8043impl<V> Copy for WindowHandle<V> {}
8044
8045impl<V> Clone for WindowHandle<V> {
8046 fn clone(&self) -> Self {
8047 *self
8048 }
8049}
8050
8051impl<V> PartialEq for WindowHandle<V> {
8052 fn eq(&self, other: &Self) -> bool {
8053 self.any_handle == other.any_handle
8054 }
8055}
8056
8057impl<V> Eq for WindowHandle<V> {}
8058
8059impl<V> Hash for WindowHandle<V> {
8060 fn hash<H: Hasher>(&self, state: &mut H) {
8061 self.any_handle.hash(state);
8062 }
8063}
8064
8065impl<V: 'static> From<WindowHandle<V>> for AnyWindowHandle {
8066 fn from(val: WindowHandle<V>) -> Self {
8067 val.any_handle
8068 }
8069}
8070
8071#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
8073pub struct AnyWindowHandle {
8074 pub(crate) id: WindowId,
8075 state_type: TypeId,
8076 root_entity_type_name: &'static str,
8077}
8078
8079impl AnyWindowHandle {
8080 pub fn window_id(&self) -> WindowId {
8082 self.id
8083 }
8084
8085 pub fn metrics(&self, cx: &App) -> Option<WindowMetricsHandle> {
8092 cx.windows
8093 .cell(self.id)
8094 .map(|cell| cell.borrow().core.metrics_handle())
8095 }
8096
8097 pub fn root_entity_type_name(&self) -> &'static str {
8099 self.root_entity_type_name
8100 }
8101
8102 pub fn downcast<T: 'static>(&self) -> Option<WindowHandle<T>> {
8105 if TypeId::of::<T>() == self.state_type {
8106 Some(WindowHandle {
8107 any_handle: *self,
8108 state_type: PhantomData,
8109 })
8110 } else {
8111 None
8112 }
8113 }
8114
8115 pub fn update<C, R>(
8119 self,
8120 cx: &mut C,
8121 update: impl FnOnce(AnyView, &mut Window, &mut App) -> R,
8122 ) -> Result<R>
8123 where
8124 C: AppContext,
8125 {
8126 cx.update_window(self, update)
8127 }
8128
8129 pub fn read<T, C, R>(self, cx: &C, read: impl FnOnce(Entity<T>, &App) -> R) -> Result<R>
8133 where
8134 C: AppContext,
8135 T: 'static,
8136 {
8137 let view = self
8138 .downcast::<T>()
8139 .context("the type of the window's root view has changed")?;
8140
8141 cx.read_window(&view, read)
8142 }
8143}
8144
8145impl HasWindowHandle for Window<'_> {
8146 fn window_handle(&self) -> Result<raw_window_handle::WindowHandle<'_>, HandleError> {
8147 self.core.platform_window.window_handle()
8148 }
8149}
8150
8151impl HasDisplayHandle for Window<'_> {
8152 fn display_handle(
8153 &self,
8154 ) -> std::result::Result<raw_window_handle::DisplayHandle<'_>, HandleError> {
8155 self.core.platform_window.display_handle()
8156 }
8157}
8158
8159#[derive(Clone, Debug, Eq, PartialEq, Hash)]
8164pub enum ElementId {
8165 View(EntityId),
8167 Integer(u64),
8169 Name(SharedString),
8171 Uuid(Uuid),
8173 FocusHandle(FocusId),
8175 NamedInteger(SharedString, u64),
8177 Path(Arc<std::path::Path>),
8179 CodeLocation(core::panic::Location<'static>),
8181 NamedChild(Arc<ElementId>, SharedString),
8183 OpaqueId([u8; 20]),
8185}
8186
8187impl ElementId {
8188 pub fn named_usize(name: impl Into<SharedString>, integer: usize) -> ElementId {
8190 Self::NamedInteger(name.into(), integer as u64)
8191 }
8192}
8193
8194impl Display for ElementId {
8195 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
8196 match self {
8197 ElementId::View(entity_id) => write!(f, "view-{}", entity_id)?,
8198 ElementId::Integer(ix) => write!(f, "{}", ix)?,
8199 ElementId::Name(name) => write!(f, "{}", name)?,
8200 ElementId::FocusHandle(_) => write!(f, "FocusHandle")?,
8201 ElementId::NamedInteger(s, i) => write!(f, "{}-{}", s, i)?,
8202 ElementId::Uuid(uuid) => write!(f, "{}", uuid)?,
8203 ElementId::Path(path) => write!(f, "{}", path.display())?,
8204 ElementId::CodeLocation(location) => write!(f, "{}", location)?,
8205 ElementId::NamedChild(id, name) => write!(f, "{}-{}", id, name)?,
8206 ElementId::OpaqueId(opaque_id) => write!(f, "{:x?}", opaque_id)?,
8207 }
8208
8209 Ok(())
8210 }
8211}
8212
8213impl TryInto<SharedString> for ElementId {
8214 type Error = anyhow::Error;
8215
8216 fn try_into(self) -> anyhow::Result<SharedString> {
8217 if let ElementId::Name(name) = self {
8218 Ok(name)
8219 } else {
8220 anyhow::bail!("element id is not string")
8221 }
8222 }
8223}
8224
8225impl From<u64> for ElementId {
8226 fn from(id: u64) -> Self {
8227 ElementId::Integer(id)
8228 }
8229}
8230
8231impl From<usize> for ElementId {
8232 fn from(id: usize) -> Self {
8233 ElementId::Integer(id as u64)
8234 }
8235}
8236
8237impl From<i32> for ElementId {
8238 fn from(id: i32) -> Self {
8239 Self::Integer(id as u64)
8240 }
8241}
8242
8243impl From<SharedString> for ElementId {
8244 fn from(name: SharedString) -> Self {
8245 ElementId::Name(name)
8246 }
8247}
8248
8249impl From<String> for ElementId {
8250 fn from(name: String) -> Self {
8251 ElementId::Name(name.into())
8252 }
8253}
8254
8255impl From<Arc<str>> for ElementId {
8256 fn from(name: Arc<str>) -> Self {
8257 ElementId::Name(name.into())
8258 }
8259}
8260
8261impl From<Arc<std::path::Path>> for ElementId {
8262 fn from(path: Arc<std::path::Path>) -> Self {
8263 ElementId::Path(path)
8264 }
8265}
8266
8267impl From<&'static str> for ElementId {
8268 fn from(name: &'static str) -> Self {
8269 ElementId::Name(SharedString::new_static(name))
8270 }
8271}
8272
8273impl<'a> From<&'a FocusHandle> for ElementId {
8274 fn from(handle: &'a FocusHandle) -> Self {
8275 ElementId::FocusHandle(handle.id)
8276 }
8277}
8278
8279impl From<(&'static str, EntityId)> for ElementId {
8280 fn from((name, id): (&'static str, EntityId)) -> Self {
8281 ElementId::NamedInteger(SharedString::new_static(name), id.as_u64())
8282 }
8283}
8284
8285impl From<(&'static str, usize)> for ElementId {
8286 fn from((name, id): (&'static str, usize)) -> Self {
8287 ElementId::NamedInteger(SharedString::new_static(name), id as u64)
8288 }
8289}
8290
8291impl From<(SharedString, usize)> for ElementId {
8292 fn from((name, id): (SharedString, usize)) -> Self {
8293 ElementId::NamedInteger(name, id as u64)
8294 }
8295}
8296
8297impl From<(&'static str, u64)> for ElementId {
8298 fn from((name, id): (&'static str, u64)) -> Self {
8299 ElementId::NamedInteger(SharedString::new_static(name), id)
8300 }
8301}
8302
8303impl From<Uuid> for ElementId {
8304 fn from(value: Uuid) -> Self {
8305 Self::Uuid(value)
8306 }
8307}
8308
8309impl From<(&'static str, u32)> for ElementId {
8310 fn from((name, id): (&'static str, u32)) -> Self {
8311 ElementId::NamedInteger(SharedString::new_static(name), u64::from(id))
8312 }
8313}
8314
8315impl<T: Into<SharedString>> From<(ElementId, T)> for ElementId {
8316 fn from((id, name): (ElementId, T)) -> Self {
8317 ElementId::NamedChild(Arc::new(id), name.into())
8318 }
8319}
8320
8321impl From<&'static core::panic::Location<'static>> for ElementId {
8322 fn from(location: &'static core::panic::Location<'static>) -> Self {
8323 ElementId::CodeLocation(*location)
8324 }
8325}
8326
8327impl From<[u8; 20]> for ElementId {
8328 fn from(opaque_id: [u8; 20]) -> Self {
8329 ElementId::OpaqueId(opaque_id)
8330 }
8331}
8332
8333#[derive(Clone)]
8336pub struct PaintQuad {
8337 pub bounds: Bounds<Pixels>,
8339 pub corner_radii: Corners<Pixels>,
8341 pub background: Background,
8343 pub border_widths: Edges<Pixels>,
8345 pub border_color: Hsla,
8347 pub border_style: BorderStyle,
8349}
8350
8351impl PaintQuad {
8352 pub fn corner_radii(self, corner_radii: impl Into<Corners<Pixels>>) -> Self {
8354 PaintQuad {
8355 corner_radii: corner_radii.into(),
8356 ..self
8357 }
8358 }
8359
8360 pub fn border_widths(self, border_widths: impl Into<Edges<Pixels>>) -> Self {
8362 PaintQuad {
8363 border_widths: border_widths.into(),
8364 ..self
8365 }
8366 }
8367
8368 pub fn border_color(self, border_color: impl Into<Hsla>) -> Self {
8370 PaintQuad {
8371 border_color: border_color.into(),
8372 ..self
8373 }
8374 }
8375
8376 pub fn background(self, background: impl Into<Background>) -> Self {
8378 PaintQuad {
8379 background: background.into(),
8380 ..self
8381 }
8382 }
8383}
8384
8385pub fn quad(
8387 bounds: Bounds<Pixels>,
8388 corner_radii: impl Into<Corners<Pixels>>,
8389 background: impl Into<Background>,
8390 border_widths: impl Into<Edges<Pixels>>,
8391 border_color: impl Into<Hsla>,
8392 border_style: BorderStyle,
8393) -> PaintQuad {
8394 PaintQuad {
8395 bounds,
8396 corner_radii: corner_radii.into(),
8397 background: background.into(),
8398 border_widths: border_widths.into(),
8399 border_color: border_color.into(),
8400 border_style,
8401 }
8402}
8403
8404pub fn fill(bounds: impl Into<Bounds<Pixels>>, background: impl Into<Background>) -> PaintQuad {
8406 PaintQuad {
8407 bounds: bounds.into(),
8408 corner_radii: (0.).into(),
8409 background: background.into(),
8410 border_widths: (0.).into(),
8411 border_color: transparent_black(),
8412 border_style: BorderStyle::default(),
8413 }
8414}
8415
8416pub fn outline(
8418 bounds: impl Into<Bounds<Pixels>>,
8419 border_color: impl Into<Hsla>,
8420 border_style: BorderStyle,
8421) -> PaintQuad {
8422 PaintQuad {
8423 bounds: bounds.into(),
8424 corner_radii: (0.).into(),
8425 background: transparent_black().into(),
8426 border_widths: (1.).into(),
8427 border_color: border_color.into(),
8428 border_style,
8429 }
8430}
8431
8432#[inline(never)]
8433fn with_element_arena_erased(callback: &mut dyn FnMut(&mut Arena)) -> Result<(), AccessError> {
8434 if let Some(arena_pointer) = CURRENT_ELEMENT_ARENA.try_with(Cell::get)? {
8435 let arena_cell = unsafe { &*arena_pointer };
8438 callback(&mut arena_cell.borrow_mut());
8439 Ok(())
8440 } else {
8441 ELEMENT_ARENA.try_with(|arena| callback(&mut arena.borrow_mut()))
8442 }
8443}
8444
8445#[cfg(test)]
8446mod tests {
8447 use std::{
8448 cell::{Cell, RefCell},
8449 path::PathBuf,
8450 rc::Rc,
8451 time::Duration,
8452 };
8453
8454 use crate::{
8455 AnyWindowHandle, App, AppContext as _, ArenaClearNeeded, Bounds, ContentMask, Context,
8456 Corners, CustomRenderPrimitive, DispatchPhase, DragMoveEvent, Empty, ExternalDragPayload,
8457 ExternalPaths, FileDragPaths, FileDropEvent, FocusHandle, FocusId, FramePipeline,
8458 ImportedTextureHandle, InputEvent as _, InteractiveElement as _, IntoElement,
8459 KeyDownEvent, Keystroke, LongPressEvent, MouseButton, MouseDownEvent, MouseMoveEvent,
8460 ParentElement, Pixels, PlatformInput, Point, Render, RequestFrameOptions, ScaledPixels,
8461 StandardImmediatePipeline, StatefulInteractiveElement as _, Styled, TestAppContext,
8462 TouchDragEvent, TouchEvent, TouchId, TouchPhase, Underline, UnderlineStyle, Window,
8463 WindowAppearance, WindowMetrics, WindowOptions, canvas, div, gpu_canvas, hsla, point, px,
8464 size,
8465 };
8466
8467 #[gpui::test]
8471 fn test_window_visibility(cx: &mut TestAppContext) {
8472 use crate::WindowVisibility;
8473
8474 let window = cx.add_window(|_, _| EmptyView);
8475 let observed = Rc::new(RefCell::new(Vec::new()));
8476 let _subscription = window
8477 .update(cx, {
8478 let observed = observed.clone();
8479 move |_, window, _| {
8480 assert_eq!(window.visibility(), WindowVisibility::Visible);
8481 assert!(window.is_visible());
8482 window.observe_window_visibility(move |visibility, window, _| {
8483 assert_eq!(window.visibility(), visibility);
8484 observed.borrow_mut().push(visibility);
8485 })
8486 }
8487 })
8488 .unwrap();
8489 let test_window = cx.test_window(window.into());
8490 let frame_wake_count = test_window.frame_wake_count();
8491
8492 test_window.simulate_visibility_change(WindowVisibility::Hidden);
8493 assert_eq!(*observed.borrow(), [WindowVisibility::Hidden]);
8494 window
8495 .update(cx, |_, window, _| assert!(!window.is_visible()))
8496 .unwrap();
8497
8498 test_window.simulate_visibility_change(WindowVisibility::Hidden);
8500 assert_eq!(observed.borrow().len(), 1);
8501
8502 test_window.simulate_visibility_change(WindowVisibility::Visible);
8503 assert_eq!(
8504 *observed.borrow(),
8505 [WindowVisibility::Hidden, WindowVisibility::Visible]
8506 );
8507 window
8508 .update(cx, |_, window, _| assert!(window.is_visible()))
8509 .unwrap();
8510 assert_eq!(test_window.frame_wake_count(), frame_wake_count);
8511 }
8512
8513 #[gpui::test]
8516 fn a_window_draws_through_its_frame_pipeline(cx: &mut TestAppContext) {
8517 let recorded: Rc<RefCell<Vec<&'static str>>> = Rc::new(RefCell::new(Vec::new()));
8518 let recorder = recorded.clone();
8519 cx.update(move |cx| {
8520 cx.set_frame_pipeline_factory(Rc::new(move |_| {
8521 Box::new(RecordingPipeline(recorder.clone()))
8522 }));
8523 });
8524
8525 let window = cx.add_window(|_, _| EmptyView);
8526 recorded.borrow_mut().clear();
8527 cx.update_window(window.into(), |_, window, cx| window.draw(cx).clear(cx))
8528 .unwrap();
8529
8530 let phases = recorded.borrow();
8531 assert!(
8532 phases.starts_with(&[
8533 "begin_frame",
8534 "draw_roots",
8535 "finish_frame",
8536 "complete_frame",
8537 "end_frame"
8538 ]),
8539 "frame ran {phases:?}"
8540 );
8541 }
8542
8543 struct RecordingPipeline(Rc<RefCell<Vec<&'static str>>>);
8546
8547 impl FramePipeline for RecordingPipeline {
8548 fn begin_frame(&mut self, window: &mut Window, cx: &mut App) {
8549 self.0.borrow_mut().push("begin_frame");
8550 window.begin_frame(cx);
8551 }
8552
8553 fn draw_roots(&mut self, window: &mut Window, cx: &mut App) {
8554 self.0.borrow_mut().push("draw_roots");
8555 window.draw_roots(cx);
8556 }
8557
8558 fn finish_frame(&mut self, window: &mut Window, cx: &mut App) {
8559 self.0.borrow_mut().push("finish_frame");
8560 window.finish_frame(cx);
8561 }
8562
8563 fn complete_frame(&mut self, window: &mut Window, cx: &mut App) -> Option<FocusId> {
8564 self.0.borrow_mut().push("complete_frame");
8565 window.complete_frame(cx)
8566 }
8567
8568 fn end_frame(
8569 &mut self,
8570 window: &mut Window,
8571 cx: &mut App,
8572 focus_before_listeners: Option<FocusId>,
8573 ) {
8574 self.0.borrow_mut().push("end_frame");
8575 window.end_frame(cx, focus_before_listeners);
8576 }
8577 }
8578
8579 #[gpui::test]
8582 fn a_frame_that_skips_drawing_still_completes(cx: &mut TestAppContext) {
8583 cx.skip_drawing();
8584 let window = cx.add_window(|_, _| EmptyView);
8585
8586 cx.update_window(window.into(), |_, window, cx| window.draw(cx).clear(cx))
8587 .unwrap();
8588 }
8589
8590 #[cfg(debug_assertions)]
8593 #[gpui::test]
8594 #[should_panic(expected = "paint_roots ran while the frame was Evaluated")]
8595 fn a_frame_pass_out_of_order_is_rejected(cx: &mut TestAppContext) {
8596 let window = cx.add_window(|_, _| EmptyView);
8597
8598 cx.update_window(window.into(), |_, window, cx| {
8599 window.begin_frame(cx);
8600 let roots = window.evaluate_roots(cx);
8601 window.paint_roots(roots, cx);
8604 })
8605 .unwrap();
8606 }
8607
8608 struct PacingPipeline {
8610 frames: Rc<Cell<usize>>,
8611 asks: Rc<Cell<usize>>,
8612 allow: bool,
8613 }
8614
8615 impl FramePipeline for PacingPipeline {
8616 fn should_render(&mut self, is_dirty: bool, _metrics: &WindowMetrics) -> bool {
8617 self.asks.set(self.asks.get() + 1);
8618 self.allow && is_dirty
8619 }
8620
8621 fn draw(&mut self, window: &mut Window, cx: &mut App) -> ArenaClearNeeded {
8622 self.frames.set(self.frames.get() + 1);
8623 FramePipeline::draw(&mut StandardImmediatePipeline, window, cx)
8624 }
8625 }
8626
8627 fn install_pacing_pipeline(
8629 cx: &mut TestAppContext,
8630 frames: Rc<Cell<usize>>,
8631 asks: Rc<Cell<usize>>,
8632 allow: bool,
8633 ) {
8634 cx.update(move |cx| {
8635 cx.set_frame_pipeline_factory(Rc::new(move |_| {
8636 Box::new(PacingPipeline {
8637 frames: frames.clone(),
8638 asks: asks.clone(),
8639 allow,
8640 })
8641 }));
8642 });
8643 }
8644
8645 #[gpui::test]
8647 fn a_pipeline_can_defer_a_frame(cx: &mut TestAppContext) {
8648 let frames = Rc::new(Cell::new(0));
8649 let asks = Rc::new(Cell::new(0));
8650 install_pacing_pipeline(cx, frames.clone(), asks.clone(), false);
8651 let window = cx.add_window(|_, _| EmptyView);
8652 let test_window = cx.test_window(window.into());
8653 frames.set(0);
8654 asks.set(0);
8655
8656 window.update(cx, |_, _, cx| cx.notify()).unwrap();
8657 test_window.simulate_frame_request(RequestFrameOptions::default());
8658
8659 assert!(asks.get() > 0, "the pipeline was asked about the frame");
8660 assert_eq!(frames.get(), 0, "the deferred frame was never drawn");
8661 }
8662
8663 #[gpui::test]
8665 fn a_pipeline_can_allow_a_frame(cx: &mut TestAppContext) {
8666 let frames = Rc::new(Cell::new(0));
8667 let asks = Rc::new(Cell::new(0));
8668 install_pacing_pipeline(cx, frames.clone(), asks, true);
8669 let window = cx.add_window(|_, _| EmptyView);
8670 frames.set(0);
8671
8672 window.update(cx, |_, _, cx| cx.notify()).unwrap();
8673
8674 assert!(frames.get() > 0, "the allowed frame was drawn");
8675 }
8676
8677 #[gpui::test]
8678 fn test_fully_visible_bounds_preserve_layout_viewport(cx: &mut TestAppContext) {
8679 let window = cx.add_window(|_, _| EmptyView);
8680 let mut platform_window = cx.test_window(window.into());
8681 platform_window.simulate_resize(size(px(400.), px(800.)));
8682 window
8683 .update(cx, |_, window, _| {
8684 assert_eq!(
8685 window.visual_viewport_bounds(),
8686 Bounds::new(Point::default(), size(px(400.), px(800.)))
8687 );
8688 assert_eq!(
8689 window.fully_visible_bounds(),
8690 window.visual_viewport_bounds()
8691 );
8692 })
8693 .unwrap();
8694
8695 platform_window.simulate_frame_request(RequestFrameOptions::default());
8696 let wakes = platform_window.frame_wake_count();
8697 let visual_bounds = Bounds::new(point(px(10.), px(40.)), size(px(380.), px(460.)));
8698 platform_window.simulate_visual_viewport_change(visual_bounds);
8699 assert!(platform_window.frame_wake_count() > wakes);
8700 platform_window.simulate_frame_request(RequestFrameOptions::default());
8701 let wakes = platform_window.frame_wake_count();
8702 platform_window.simulate_insets_change(crate::WindowInsets {
8703 safe_area: crate::Edges {
8704 top: px(60.),
8705 right: px(20.),
8706 bottom: px(30.),
8707 left: px(-10.),
8708 },
8709 ime: crate::Edges {
8710 bottom: px(350.),
8711 ..Default::default()
8712 },
8713 });
8714 assert!(platform_window.frame_wake_count() > wakes);
8715 window
8716 .update(cx, |_, window, _| {
8717 assert_eq!(window.viewport_size(), size(px(400.), px(800.)));
8718 assert_eq!(window.visual_viewport_bounds(), visual_bounds);
8719 assert_eq!(
8720 window.fully_visible_bounds(),
8721 Bounds::new(point(px(10.), px(60.)), size(px(370.), px(390.)))
8722 );
8723 window.request_virtual_keyboard();
8724 window.dismiss_virtual_keyboard();
8725 assert_eq!(window.viewport_size(), size(px(400.), px(800.)));
8726 })
8727 .unwrap();
8728 assert_eq!(platform_window.virtual_keyboard_requests(), 1);
8729 assert_eq!(platform_window.virtual_keyboard_dismissals(), 1);
8730
8731 platform_window.simulate_insets_change(crate::WindowInsets {
8732 safe_area: crate::Edges {
8733 top: px(900.),
8734 left: px(500.),
8735 ..Default::default()
8736 },
8737 ..Default::default()
8738 });
8739 window
8740 .update(cx, |_, window, _| {
8741 assert_eq!(
8742 window.fully_visible_bounds().size,
8743 size(Pixels::ZERO, Pixels::ZERO)
8744 );
8745 })
8746 .unwrap();
8747 }
8748
8749 struct EmptyView;
8750
8751 impl Render for EmptyView {
8752 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
8753 div()
8754 }
8755 }
8756
8757 struct OpensWindowOnPaint {
8758 opened: Rc<Cell<bool>>,
8759 }
8760
8761 impl Render for OpensWindowOnPaint {
8762 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
8763 let opened = self.opened.clone();
8764 div()
8765 .size_full()
8766 .child(canvas(
8767 |_, _, _| {},
8768 move |_, _, _window, cx| {
8769 if !opened.replace(true) {
8770 cx.open_window(WindowOptions::default(), |_, cx| cx.new(|_| EmptyView))
8771 .unwrap();
8772 }
8773 },
8774 ))
8775 .child(div().child("after"))
8779 }
8780 }
8781
8782 #[test]
8788 fn test_window_opened_during_draw_defers_arena_clear() {
8789 let mut cx = TestAppContext::single();
8790
8791 let opened = Rc::new(Cell::new(false));
8792 let window = cx.add_window({
8794 let opened = opened.clone();
8795 move |_, _| OpensWindowOnPaint { opened }
8796 });
8797
8798 assert!(opened.get());
8799 assert_eq!(cx.windows().len(), 2);
8800
8801 cx.update_window(window.into(), |_, window, cx| window.draw(cx).clear(cx))
8804 .unwrap();
8805 }
8806
8807 #[test]
8808 fn test_scale_factor_change_preserves_bounds_and_survives_resize() {
8809 let mut cx = TestAppContext::single();
8810 let window = cx.add_window(|_, _| EmptyView);
8811 let handle: AnyWindowHandle = window.into();
8812 let window_state = |cx: &mut TestAppContext| {
8813 cx.update_window(handle, |_, window, _| {
8814 (
8815 window.scale_factor(),
8816 window.bounds(),
8817 window.viewport_size(),
8818 )
8819 })
8820 .unwrap()
8821 };
8822
8823 let (scale_factor, mut expected_bounds, _) = window_state(&mut cx);
8824 assert_eq!(scale_factor, 2.0);
8825
8826 for (scale_factor, resized_size) in [
8827 (1.0, size(px(800.), px(600.))),
8828 (1.25, size(px(640.), px(480.))),
8829 (2.0, size(px(1024.), px(768.))),
8830 ] {
8831 cx.simulate_window_scale_factor_change(handle, scale_factor);
8832 assert_eq!(
8833 window_state(&mut cx),
8834 (scale_factor, expected_bounds, expected_bounds.size)
8835 );
8836
8837 cx.simulate_window_resize(handle, resized_size);
8838 expected_bounds.size = resized_size;
8839 assert_eq!(
8840 window_state(&mut cx),
8841 (scale_factor, expected_bounds, resized_size)
8842 );
8843 }
8844 }
8845
8846 #[gpui::test]
8847 fn metrics_handle_agrees_with_the_window_and_tracks_resizes(cx: &mut TestAppContext) {
8848 let window = cx.add_window(|_, _| EmptyView);
8849 let handle: AnyWindowHandle = window.into();
8850
8851 let metrics = cx.read(|app| handle.metrics(app).expect("window is open"));
8852 assert_eq!(metrics.id(), handle.window_id());
8853 assert!(metrics.display_id().is_some());
8854
8855 let window_state = |cx: &mut TestAppContext| {
8857 cx.update_window(handle, |_, window, _| {
8858 assert_eq!(metrics.bounds(), window.bounds());
8859 assert_eq!(metrics.viewport(), window.visual_viewport_bounds());
8860 assert_eq!(metrics.content_size(), window.viewport_size());
8861 assert_eq!(metrics.scale_factor(), window.scale_factor());
8862 assert_eq!(metrics.is_active(), window.is_window_active());
8863 assert_eq!(metrics.is_fullscreen(), window.is_fullscreen());
8864 })
8865 .unwrap();
8866 };
8867 window_state(cx);
8868
8869 cx.simulate_window_resize(handle, size(px(800.), px(600.)));
8870 cx.simulate_window_scale_factor_change(handle, 1.25);
8871
8872 assert_eq!(metrics.content_size(), size(px(800.), px(600.)));
8875 assert_eq!(metrics.scale_factor(), 1.25);
8876 window_state(cx);
8877 }
8878
8879 #[gpui::test]
8883 fn metrics_track_viewport_changes_that_do_not_resize(cx: &mut TestAppContext) {
8884 let window = cx.add_window(|_, _| EmptyView);
8885 let handle: AnyWindowHandle = window.into();
8886 let metrics = cx.read(|app| handle.metrics(app).expect("window is open"));
8887
8888 let narrow = Bounds::new(point(px(0.), px(120.)), size(px(400.), px(680.)));
8889 assert_ne!(metrics.viewport(), narrow);
8890
8891 cx.simulate_window_visual_viewport_change(handle, narrow);
8892 assert_eq!(metrics.viewport(), narrow);
8893
8894 cx.update_window(handle, |_, window, _| {
8895 assert_eq!(metrics.viewport(), window.visual_viewport_bounds());
8896 assert_eq!(metrics.content_size(), window.viewport_size());
8898 })
8899 .unwrap();
8900 }
8901
8902 #[gpui::test]
8906 fn updating_a_window_from_within_its_own_update_reports_an_error(cx: &mut TestAppContext) {
8907 let window = cx.add_window(|_, _| EmptyView);
8908 let handle: AnyWindowHandle = window.into();
8909
8910 let nested_error = cx
8911 .update_window(handle, |_, _window, cx| {
8912 cx.update_window(handle, |_, _, _| ())
8913 .map_err(|error| error.to_string())
8914 .unwrap_err()
8915 })
8916 .expect("the outer update completes");
8917
8918 assert!(
8919 nested_error.contains("already being updated"),
8920 "unexpected error: {nested_error}"
8921 );
8922 }
8923
8924 #[gpui::test]
8927 fn a_window_can_be_updated_from_within_another_windows_update(cx: &mut TestAppContext) {
8928 let first: AnyWindowHandle = cx.add_window(|_, _| EmptyView).into();
8929 let second: AnyWindowHandle = cx.add_window(|_, _| EmptyView).into();
8930
8931 let expected = cx
8932 .update_window(second, |_, window, _| window.viewport_size())
8933 .expect("the window can be updated on its own");
8934
8935 let from_inner = cx
8936 .update_window(first, |_, _window, cx| {
8937 cx.update_window(second, |_, window, _| window.viewport_size())
8938 .expect("the inner update succeeds")
8939 })
8940 .expect("the outer update completes");
8941
8942 assert_eq!(from_inner, expected);
8943 }
8944
8945 #[gpui::test]
8948 fn metrics_handle_is_readable_from_another_thread(cx: &mut TestAppContext) {
8949 let window = cx.add_window(|_, _| EmptyView);
8950 let handle: AnyWindowHandle = window.into();
8951 cx.simulate_window_resize(handle, size(px(640.), px(480.)));
8952 let metrics = cx.read(|app| handle.metrics(app).expect("window is open"));
8953
8954 let (id, content_size, scale_factor) = std::thread::spawn(move || {
8955 (metrics.id(), metrics.content_size(), metrics.scale_factor())
8956 })
8957 .join()
8958 .expect("the metrics reader did not panic");
8959
8960 assert_eq!(id, handle.window_id());
8961 assert_eq!(content_size, size(px(640.), px(480.)));
8962 assert_eq!(scale_factor, 2.0);
8963 }
8964
8965 #[gpui::test]
8970 fn test_frame_waker_fires_on_frame_demand(cx: &mut TestAppContext) {
8971 let window = cx.add_window(|_, _| EmptyView);
8972 let test_window = cx.test_window(window.into());
8973
8974 assert!(
8978 test_window.frame_wake_count() >= 1,
8979 "opening a window must wake the frame source for the initial frame"
8980 );
8981
8982 test_window.simulate_frame_request(RequestFrameOptions::default());
8984
8985 let baseline = test_window.frame_wake_count();
8988 test_window.simulate_frame_request(RequestFrameOptions::default());
8989 window.update(cx, |_, _, _| {}).unwrap();
8990 assert_eq!(
8991 test_window.frame_wake_count(),
8992 baseline,
8993 "clean frames and non-notifying updates must not wake the frame source"
8994 );
8995
8996 window.update(cx, |_, _, cx| cx.notify()).unwrap();
8998 assert!(
8999 test_window.frame_wake_count() > baseline,
9000 "notifying a view in an idle window must wake the frame source"
9001 );
9002
9003 test_window.simulate_frame_request(RequestFrameOptions::default());
9005 let baseline = test_window.frame_wake_count();
9006 test_window.simulate_frame_request(RequestFrameOptions::default());
9007 assert_eq!(
9008 test_window.frame_wake_count(),
9009 baseline,
9010 "serving demand must return the window to idle"
9011 );
9012
9013 window
9015 .update(cx, |_, window, _| window.on_next_frame(|_, _| {}))
9016 .unwrap();
9017 assert!(
9018 test_window.frame_wake_count() > baseline,
9019 "scheduling a next-frame callback in an idle window must wake the frame source"
9020 );
9021 }
9022
9023 #[gpui::test]
9028 fn test_pending_next_frame_callbacks_are_not_stranded(cx: &mut TestAppContext) {
9029 let window = cx.add_window(|_, _| EmptyView);
9030 let test_window = cx.test_window(window.into());
9031 test_window.simulate_frame_request(RequestFrameOptions::default());
9034
9035 let callback_ran = Rc::new(Cell::new(false));
9036 window
9037 .update(cx, {
9038 let callback_ran = callback_ran.clone();
9039 move |_, window, _| {
9040 window.on_next_frame(move |_, _| callback_ran.set(true));
9041 }
9042 })
9043 .unwrap();
9044
9045 let baseline = test_window.frame_wake_count();
9046 test_window.simulate_frame_request(RequestFrameOptions::default());
9047 assert!(
9054 test_window.frame_wake_count() > baseline || callback_ran.get(),
9055 "a frame request with pending next-frame callbacks must either run them or re-arm the frame source"
9056 );
9057 }
9058
9059 #[gpui::test]
9060 fn test_window_reports_no_raw_handle_instead_of_panicking(cx: &mut TestAppContext) {
9061 use raw_window_handle::{HandleError, HasDisplayHandle as _, HasWindowHandle as _};
9062
9063 let window = cx.add_window(|_, _| EmptyView);
9064 window
9065 .update(cx, |_, window, _| {
9066 assert!(matches!(
9067 window.window_handle(),
9068 Err(HandleError::NotSupported)
9069 ));
9070 assert!(matches!(
9071 window.display_handle(),
9072 Err(HandleError::NotSupported)
9073 ));
9074 })
9075 .unwrap();
9076 }
9077
9078 #[gpui::test]
9079 fn test_appearance_change_runs_after_app_update(cx: &mut TestAppContext) {
9080 let window = cx.add_window(|_, _| EmptyView);
9081 let observed_appearance = Rc::new(Cell::new(None));
9082 let _subscription = window
9083 .update(cx, {
9084 let observed_appearance = observed_appearance.clone();
9085 move |_, window, _| {
9086 window.observe_window_appearance(move |window, _| {
9087 observed_appearance.set(Some(window.appearance()));
9088 })
9089 }
9090 })
9091 .unwrap();
9092 let test_window = cx.test_window(window.into());
9093
9094 cx.update(|_| {
9095 test_window.simulate_appearance_change(WindowAppearance::Dark);
9096 assert_eq!(observed_appearance.get(), None);
9097 });
9098 cx.run_until_parked();
9099
9100 assert_eq!(observed_appearance.get(), Some(WindowAppearance::Dark));
9101 }
9102
9103 #[gpui::test]
9104 fn queued_frame_callback_wakes_a_parked_render_loop(cx: &mut TestAppContext) {
9105 let window = cx.add_window(|_, _| Empty);
9106 let test_window = cx.test_window(window.into());
9107
9108 assert!(test_window.simulate_scheduled_frame());
9109 assert!(test_window.simulate_scheduled_frame());
9110 assert!(!test_window.frame_scheduled());
9111
9112 cx.update_window(window.into(), |_, window, _| {
9113 window.core.active.set(true);
9114 window.on_next_frame(|_, _| {});
9115 })
9116 .unwrap();
9117 assert!(
9118 test_window.frame_scheduled(),
9119 "queuing work on a parked window must wake the render loop"
9120 );
9121
9122 assert!(test_window.simulate_scheduled_frame());
9123 assert!(
9124 test_window.frame_scheduled(),
9125 "presenting the frame must await one compositor callback"
9126 );
9127 assert!(test_window.simulate_scheduled_frame());
9128 assert!(!test_window.frame_scheduled());
9129 }
9130
9131 #[gpui::test]
9132 fn pending_presentation_wakes_a_parked_render_loop(cx: &mut TestAppContext) {
9133 let window = cx.add_window(|_, _| Empty);
9134 let test_window = cx.test_window(window.into());
9135
9136 assert!(test_window.simulate_scheduled_frame());
9137 assert!(test_window.simulate_scheduled_frame());
9138 assert!(!test_window.frame_scheduled());
9139
9140 cx.update_window(window.into(), |_, window, cx| window.draw(cx).clear(cx))
9141 .unwrap();
9142
9143 assert!(
9144 test_window.frame_scheduled(),
9145 "a rendered scene awaiting presentation must wake the render loop"
9146 );
9147 }
9148
9149 #[gpui::test]
9150 fn callback_queued_during_a_frame_requests_a_follow_up(cx: &mut TestAppContext) {
9151 let window = cx.add_window(|_, _| Empty);
9152 let test_window = cx.test_window(window.into());
9153
9154 let callback_ran = Rc::new(Cell::new(false));
9155 cx.update_window(window.into(), |_, window, _| {
9156 window.core.active.set(true);
9159 let callback_ran = callback_ran.clone();
9160 window.on_next_frame(move |window, _| {
9161 window.on_next_frame(move |_, _| callback_ran.set(true));
9162 });
9163 })
9164 .unwrap();
9165
9166 assert!(test_window.simulate_scheduled_frame());
9167 assert!(!callback_ran.get());
9168 assert!(
9169 test_window.frame_scheduled(),
9170 "a callback queued mid-frame must schedule a follow-up before the loop parks"
9171 );
9172
9173 assert!(test_window.simulate_scheduled_frame());
9174 assert!(callback_ran.get());
9175 }
9176
9177 struct RootView {
9178 explicit_size: bool,
9179 child_bounds: Rc<Cell<Bounds<Pixels>>>,
9180 }
9181
9182 impl Render for RootView {
9183 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
9184 let child_bounds = self.child_bounds.clone();
9185 let root = div().flex().flex_col().child(
9186 canvas(
9187 move |bounds, _, _| child_bounds.set(bounds),
9188 |_, _, _, _| {},
9189 )
9190 .size_full(),
9191 );
9192 if self.explicit_size {
9193 root.w(px(300.)).h(px(200.))
9194 } else {
9195 root
9196 }
9197 }
9198 }
9199
9200 #[test]
9201 fn auto_sized_window_root_fills_the_window() {
9202 let mut cx = TestAppContext::single();
9203 let child_bounds = Rc::new(Cell::new(Bounds::default()));
9204 let window = cx.add_window({
9205 let child_bounds = child_bounds.clone();
9206 move |_, _| RootView {
9207 explicit_size: false,
9208 child_bounds,
9209 }
9210 });
9211
9212 let viewport_size = cx
9213 .update_window(window.into(), |_, window, cx| {
9214 window.draw(cx).clear(cx);
9215 window.viewport_size()
9216 })
9217 .unwrap();
9218
9219 assert_eq!(child_bounds.get().size, viewport_size);
9220 }
9221
9222 #[test]
9223 fn explicitly_sized_window_root_keeps_its_size() {
9224 let mut cx = TestAppContext::single();
9225 let child_bounds = Rc::new(Cell::new(Bounds::default()));
9226 let window = cx.add_window({
9227 let child_bounds = child_bounds.clone();
9228 move |_, _| RootView {
9229 explicit_size: true,
9230 child_bounds,
9231 }
9232 });
9233
9234 cx.update_window(window.into(), |_, window, cx| {
9235 window.draw(cx).clear(cx);
9236 })
9237 .unwrap();
9238
9239 assert_eq!(child_bounds.get().size, size(px(300.), px(200.)));
9240 }
9241
9242 struct FileDragView {
9243 path: PathBuf,
9244 observed_drag_moves: Rc<RefCell<Vec<Point<Pixels>>>>,
9245 observed_drops: Rc<RefCell<Vec<PathBuf>>>,
9246 }
9247
9248 struct FileDropExitView(Rc<Cell<usize>>);
9249
9250 impl Render for FileDropExitView {
9251 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
9252 div().size_full().on_file_drop_exit({
9253 let observed_file_drop_exit = self.0.clone();
9254 move |_, _, _| observed_file_drop_exit.set(observed_file_drop_exit.get() + 1)
9255 })
9256 }
9257 }
9258
9259 impl Render for FileDragView {
9260 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
9261 div()
9262 .id("file-drag")
9263 .size_full()
9264 .on_drag(self.path.clone(), |_, _, _, cx| cx.new(|_| Empty))
9265 .external_drag_payload(|path: &PathBuf, _, _| {
9266 Some(ExternalDragPayload::Files(FileDragPaths::new([(
9267 path.clone(),
9268 true,
9269 )])))
9270 })
9271 .on_drag_move({
9272 let observed_drag_moves = self.observed_drag_moves.clone();
9273 move |event: &DragMoveEvent<PathBuf>, _, _| {
9274 observed_drag_moves.borrow_mut().push(event.event.position);
9275 }
9276 })
9277 .on_drop({
9278 let observed_drops = self.observed_drops.clone();
9279 move |path: &PathBuf, _, _| observed_drops.borrow_mut().push(path.clone())
9280 })
9281 }
9282 }
9283
9284 #[gpui::test]
9285 fn file_drag_is_promoted_once_and_restored_in_source_window(cx: &mut TestAppContext) {
9286 struct Drag {
9287 window: AnyWindowHandle,
9288 observed_drag_moves: Rc<RefCell<Vec<Point<Pixels>>>>,
9289 observed_drops: Rc<RefCell<Vec<PathBuf>>>,
9290 }
9291
9292 fn start_drag(cx: &mut TestAppContext, path: PathBuf, platform_result: bool) -> Drag {
9293 let observed_drag_moves = Rc::new(RefCell::new(Vec::new()));
9294 let observed_drops = Rc::new(RefCell::new(Vec::new()));
9295 let window: AnyWindowHandle = cx
9296 .add_window({
9297 let observed_drag_moves = observed_drag_moves.clone();
9298 let observed_drops = observed_drops.clone();
9299 move |_, _| FileDragView {
9300 path,
9301 observed_drag_moves,
9302 observed_drops,
9303 }
9304 })
9305 .into();
9306 cx.test_window(window)
9307 .set_start_external_drag_result(platform_result);
9308
9309 let update_result = cx.update_window(window, |_, window, cx| {
9310 window.draw(cx).clear(cx);
9311 window.dispatch_event(
9312 MouseDownEvent {
9313 position: point(px(10.), px(10.)),
9314 button: MouseButton::Left,
9315 modifiers: Default::default(),
9316 click_count: 1,
9317 first_mouse: false,
9318 }
9319 .to_platform_input(),
9320 cx,
9321 );
9322 window.dispatch_event(
9323 MouseMoveEvent {
9324 position: point(px(20.), px(20.)),
9325 pressed_button: Some(MouseButton::Left),
9326 modifiers: Default::default(),
9327 }
9328 .to_platform_input(),
9329 cx,
9330 );
9331 assert!(cx.active_drag.is_some());
9332 });
9333 assert!(
9334 update_result.is_ok(),
9335 "failed to start drag: {update_result:?}"
9336 );
9337
9338 assert!(cx.test_window(window).external_drag_files().is_empty());
9339 Drag {
9340 window,
9341 observed_drag_moves,
9342 observed_drops,
9343 }
9344 }
9345
9346 let successful_path = PathBuf::from("/tmp/successful-drag");
9347 let successful = start_drag(cx, successful_path.clone(), true);
9348 let outside_position = point(px(-1.), px(20.));
9349 let update_result = cx.update_window(successful.window, |_, window, cx| {
9350 window.dispatch_event(
9351 MouseMoveEvent {
9352 position: outside_position,
9353 pressed_button: Some(MouseButton::Left),
9354 modifiers: Default::default(),
9355 }
9356 .to_platform_input(),
9357 cx,
9358 );
9359 assert!(cx.active_drag.is_none());
9360 });
9361 assert!(
9362 update_result.is_ok(),
9363 "failed to promote drag: {update_result:?}"
9364 );
9365 assert_eq!(
9366 cx.test_window(successful.window).external_drag_files(),
9367 [(successful_path.clone(), true)]
9368 );
9369 assert_eq!(
9372 successful.observed_drag_moves.borrow().last(),
9373 Some(&outside_position)
9374 );
9375
9376 let first_destination_exit_count = Rc::new(Cell::new(0));
9377 let first_destination: AnyWindowHandle = cx
9378 .add_window({
9379 let first_destination_exit_count = first_destination_exit_count.clone();
9380 move |_, _| FileDropExitView(first_destination_exit_count)
9381 })
9382 .into();
9383 let second_destination_exit_count = Rc::new(Cell::new(0));
9384 let second_destination: AnyWindowHandle = cx
9385 .add_window({
9386 let second_destination_exit_count = second_destination_exit_count.clone();
9387 move |_, _| FileDropExitView(second_destination_exit_count)
9388 })
9389 .into();
9390 let reentry_position = point(px(30.), px(30.));
9391 let external_paths = || ExternalPaths([successful_path.clone()].into_iter().collect());
9392 let update_result = cx.update_window(first_destination, |_, window, cx| {
9393 window.draw(cx).clear(cx);
9394 window.dispatch_event(
9395 FileDropEvent::Entered {
9396 position: reentry_position,
9397 paths: external_paths(),
9398 }
9399 .to_platform_input(),
9400 cx,
9401 );
9402 assert!(
9403 cx.active_drag
9404 .as_ref()
9405 .is_some_and(|drag| drag.value.downcast_ref::<ExternalPaths>().is_some())
9406 );
9407 window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx);
9408 assert!(cx.active_drag.is_none());
9409 assert_eq!(first_destination_exit_count.get(), 1);
9410 assert_eq!(second_destination_exit_count.get(), 0);
9411 });
9412 assert!(
9413 update_result.is_ok(),
9414 "failed to handle drag in first destination window: {update_result:?}"
9415 );
9416
9417 let update_result = cx.update_window(second_destination, |_, window, cx| {
9418 window.draw(cx).clear(cx);
9419 window.dispatch_event(
9420 PlatformInput::KeyDown(KeyDownEvent {
9421 keystroke: Keystroke::parse("down").expect("valid keystroke"),
9422 is_held: false,
9423 prefer_character_input: false,
9424 }),
9425 cx,
9426 );
9427 window.dispatch_event(
9428 FileDropEvent::Entered {
9429 position: reentry_position,
9430 paths: external_paths(),
9431 }
9432 .to_platform_input(),
9433 cx,
9434 );
9435 assert!(
9436 cx.active_drag
9437 .as_ref()
9438 .is_some_and(|drag| drag.value.downcast_ref::<ExternalPaths>().is_some())
9439 );
9440 assert_eq!(first_destination_exit_count.get(), 1);
9441 assert_eq!(second_destination_exit_count.get(), 0);
9442
9443 window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx);
9444 assert!(cx.active_drag.is_none());
9445 assert_eq!(first_destination_exit_count.get(), 1);
9446 assert_eq!(second_destination_exit_count.get(), 1);
9447 });
9448 assert!(
9449 update_result.is_ok(),
9450 "failed to handle drag in second destination window: {update_result:?}"
9451 );
9452
9453 let update_result = cx.update_window(successful.window, |_, window, cx| {
9454 window.dispatch_event(
9455 FileDropEvent::Entered {
9456 position: reentry_position,
9457 paths: external_paths(),
9458 }
9459 .to_platform_input(),
9460 cx,
9461 );
9462 assert!(
9463 cx.active_drag
9464 .as_ref()
9465 .is_some_and(|drag| drag.value.downcast_ref::<PathBuf>().is_some())
9466 );
9467 assert_eq!(
9468 successful.observed_drag_moves.borrow().last(),
9469 Some(&reentry_position)
9470 );
9471
9472 window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx);
9473 assert!(cx.active_drag.is_none());
9474
9475 window.dispatch_event(
9476 FileDropEvent::Entered {
9477 position: reentry_position,
9478 paths: external_paths(),
9479 }
9480 .to_platform_input(),
9481 cx,
9482 );
9483 assert!(
9484 cx.active_drag
9485 .as_ref()
9486 .is_some_and(|drag| drag.value.downcast_ref::<PathBuf>().is_some())
9487 );
9488
9489 window.dispatch_event(
9490 FileDropEvent::Submit {
9491 position: reentry_position,
9492 }
9493 .to_platform_input(),
9494 cx,
9495 );
9496 assert_eq!(
9497 successful.observed_drops.borrow().as_slice(),
9498 std::slice::from_ref(&successful_path)
9499 );
9500 assert!(cx.active_drag.is_none());
9501
9502 window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx);
9503 assert!(cx.active_drag.is_none());
9504 window.dispatch_event(FileDropEvent::Ended.to_platform_input(), cx);
9505 assert!(cx.active_drag.is_none());
9506
9507 window.dispatch_event(
9508 FileDropEvent::Entered {
9509 position: reentry_position,
9510 paths: external_paths(),
9511 }
9512 .to_platform_input(),
9513 cx,
9514 );
9515 assert!(
9516 cx.active_drag
9517 .as_ref()
9518 .is_some_and(|drag| drag.value.downcast_ref::<ExternalPaths>().is_some())
9519 );
9520 window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx);
9521 });
9522 assert!(
9523 update_result.is_ok(),
9524 "failed to restore drag in source window: {update_result:?}"
9525 );
9526
9527 let cancelled_path = PathBuf::from("/tmp/cancelled-drag");
9528 let cancelled = start_drag(cx, cancelled_path.clone(), true);
9529 let update_result = cx.update_window(cancelled.window, |_, window, cx| {
9530 window.dispatch_event(
9531 MouseMoveEvent {
9532 position: outside_position,
9533 pressed_button: Some(MouseButton::Left),
9534 modifiers: Default::default(),
9535 }
9536 .to_platform_input(),
9537 cx,
9538 );
9539 assert!(cx.active_drag.is_none());
9540
9541 window.dispatch_event(
9542 FileDropEvent::Entered {
9543 position: reentry_position,
9544 paths: ExternalPaths([cancelled_path].into_iter().collect()),
9545 }
9546 .to_platform_input(),
9547 cx,
9548 );
9549 assert!(
9550 cx.active_drag
9551 .as_ref()
9552 .is_some_and(|drag| drag.value.downcast_ref::<PathBuf>().is_some())
9553 );
9554 assert!(cx.stop_active_drag(window));
9555 assert!(cx.active_drag.is_none());
9556 });
9557 assert!(
9558 update_result.is_ok(),
9559 "failed to cancel restored drag: {update_result:?}"
9560 );
9561 assert!(!cx.update(|cx| cx.end_platform_drag(cancelled.window.window_id())));
9562
9563 let removed_path = PathBuf::from("/tmp/removed-window-drag");
9564 let removed = start_drag(cx, removed_path, true);
9565 let removed_window_id = removed.window.window_id();
9566 let update_result = cx.update_window(removed.window, |_, window, cx| {
9567 window.dispatch_event(
9568 MouseMoveEvent {
9569 position: outside_position,
9570 pressed_button: Some(MouseButton::Left),
9571 modifiers: Default::default(),
9572 }
9573 .to_platform_input(),
9574 cx,
9575 );
9576 assert!(cx.active_drag.is_none());
9577 window.remove_window();
9578 });
9579 assert!(
9580 update_result.is_ok(),
9581 "failed to remove drag source window: {update_result:?}"
9582 );
9583 assert!(!cx.update(|cx| cx.end_platform_drag(removed_window_id)));
9584
9585 let failed_path = PathBuf::from("/tmp/failed-drag");
9586 let failed = start_drag(cx, failed_path.clone(), false);
9587 let update_result = cx.update_window(failed.window, |_, window, cx| {
9588 for x_position in [-1., -2.] {
9589 window.dispatch_event(
9590 MouseMoveEvent {
9591 position: point(px(x_position), px(20.)),
9592 pressed_button: Some(MouseButton::Left),
9593 modifiers: Default::default(),
9594 }
9595 .to_platform_input(),
9596 cx,
9597 );
9598 }
9599 assert!(cx.active_drag.is_some());
9600 });
9601 assert!(
9602 update_result.is_ok(),
9603 "failed to retain drag after platform failure: {update_result:?}"
9604 );
9605 assert_eq!(
9606 cx.test_window(failed.window).external_drag_files(),
9607 [(failed_path, true)]
9608 );
9609 }
9610
9611 struct FocusForwarder {
9612 a: FocusHandle,
9613 b: FocusHandle,
9614 }
9615
9616 impl Render for FocusForwarder {
9617 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
9618 div()
9619 .size_full()
9620 .child(div().w(px(50.)).h(px(50.)).track_focus(&self.a))
9621 .child(div().w(px(50.)).h(px(50.)).track_focus(&self.b))
9622 }
9623 }
9624
9625 #[gpui::test]
9629 fn test_focus_moved_by_focus_listener_is_dispatched(cx: &mut TestAppContext) {
9630 let b_focus_count = Rc::new(Cell::new(0));
9631 let window = cx.add_window({
9632 let b_focus_count = b_focus_count.clone();
9633 move |window, cx| {
9634 let a = cx.focus_handle();
9635 let b = cx.focus_handle();
9636 cx.on_focus(&a, window, |this: &mut FocusForwarder, window, cx| {
9637 let b = this.b.clone();
9638 window.focus(&b, cx);
9639 })
9640 .detach();
9641 cx.on_focus(&b, window, move |_, _, _| {
9642 b_focus_count.set(b_focus_count.get() + 1);
9643 })
9644 .detach();
9645 FocusForwarder { a, b }
9646 }
9647 });
9648
9649 window
9650 .update(cx, |_, window, _| window.activate_window())
9651 .unwrap();
9652 cx.executor().run_until_parked();
9653
9654 window
9655 .update(cx, |this, window, cx| {
9656 let a = this.a.clone();
9657 window.focus(&a, cx);
9658 })
9659 .unwrap();
9660 cx.executor().run_until_parked();
9661
9662 window
9663 .update(cx, |this, window, _| {
9664 assert!(this.b.is_focused(window));
9665 })
9666 .unwrap();
9667 assert_eq!(b_focus_count.get(), 1);
9668 }
9669
9670 #[gpui::test]
9671 fn claimed_touch_drag_receives_movement_and_release(cx: &mut TestAppContext) {
9672 let events = Rc::new(RefCell::new(Vec::new()));
9673 let window = cx.add_window({
9674 let events = events.clone();
9675 move |_, _| TouchDragListener { events }
9676 });
9677 let touch = TouchId(1);
9678
9679 dispatch_touch(window, cx, touch, TouchPhase::Started, 10.);
9680 dispatch_touch(window, cx, touch, TouchPhase::Moved, 30.);
9681 dispatch_touch(window, cx, touch, TouchPhase::Ended, 40.);
9682
9683 assert_eq!(
9684 events.borrow().as_slice(),
9685 [
9686 (TouchPhase::Started, px(10.)),
9687 (TouchPhase::Moved, px(30.)),
9688 (TouchPhase::Ended, px(40.)),
9689 ]
9690 );
9691 }
9692
9693 struct TouchDragListener {
9694 events: Rc<RefCell<Vec<(TouchPhase, Pixels)>>>,
9695 }
9696
9697 impl Render for TouchDragListener {
9698 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
9699 let events = self.events.clone();
9700 canvas(
9701 |_, _, _| {},
9702 move |_, _, window, _| {
9703 window.on_mouse_event(move |event: &TouchDragEvent, phase, window, _cx| {
9704 if phase != DispatchPhase::Bubble {
9705 return;
9706 }
9707 events.borrow_mut().push((event.phase, event.position.x));
9708 if event.phase == TouchPhase::Started {
9709 window.prevent_default();
9710 }
9711 });
9712 },
9713 )
9714 }
9715 }
9716
9717 #[gpui::test]
9718 fn long_press_is_claimed_only_when_started_prevents_default(cx: &mut TestAppContext) {
9719 for response in [
9720 LongPressResponse::PreventDefault,
9721 LongPressResponse::StopPropagation,
9722 LongPressResponse::None,
9723 ] {
9724 let phases = Rc::new(RefCell::new(Vec::new()));
9725 let window = cx.add_window({
9726 let phases = phases.clone();
9727 move |_, _| LongPressListener { phases, response }
9728 });
9729 dispatch_touch(window, cx, TouchId(1), TouchPhase::Started, 0.);
9730 cx.executor().advance_clock(Duration::from_millis(501));
9731 cx.executor().run_until_parked();
9732 window
9733 .update(cx, |_, window, _| {
9734 assert_eq!(
9735 window.core.long_press_capture.is_some(),
9736 response == LongPressResponse::PreventDefault
9737 );
9738 })
9739 .unwrap();
9740 dispatch_touch(window, cx, TouchId(1), TouchPhase::Moved, 2.);
9741 dispatch_touch(window, cx, TouchId(1), TouchPhase::Ended, 2.);
9742 window
9743 .update(cx, |_, window, _| {
9744 assert!(window.core.long_press_capture.is_none());
9745 })
9746 .unwrap();
9747
9748 let phases = phases.borrow();
9749 if response == LongPressResponse::PreventDefault {
9750 assert_eq!(
9751 phases.as_slice(),
9752 [TouchPhase::Started, TouchPhase::Moved, TouchPhase::Ended]
9753 );
9754 } else {
9755 assert_eq!(phases.as_slice(), [TouchPhase::Started]);
9756 }
9757 }
9758 }
9759
9760 #[gpui::test]
9761 fn stale_default_prevention_does_not_claim_long_press(cx: &mut TestAppContext) {
9762 let phases = Rc::new(RefCell::new(Vec::new()));
9763 let window = cx.add_window({
9764 let phases = phases.clone();
9765 move |_, _| LongPressListener {
9766 phases,
9767 response: LongPressResponse::None,
9768 }
9769 });
9770 window
9771 .update(cx, |_, window, _| {
9772 window.prevent_default();
9773 })
9774 .unwrap();
9775
9776 dispatch_touch(window, cx, TouchId(1), TouchPhase::Started, 0.);
9777 cx.executor().advance_clock(Duration::from_millis(501));
9778 cx.executor().run_until_parked();
9779 dispatch_touch(window, cx, TouchId(1), TouchPhase::Moved, 2.);
9780
9781 assert_eq!(phases.borrow().as_slice(), [TouchPhase::Started]);
9782 }
9783
9784 #[gpui::test]
9785 fn resolved_touch_cancels_scheduled_long_press(cx: &mut TestAppContext) {
9786 for (phase, position) in [
9787 (TouchPhase::Ended, 0.),
9788 (TouchPhase::Cancelled, 0.),
9789 (TouchPhase::Moved, 20.),
9790 ] {
9791 let phases = Rc::new(RefCell::new(Vec::new()));
9792 let window = cx.add_window({
9793 let phases = phases.clone();
9794 move |_, _| LongPressListener {
9795 phases,
9796 response: LongPressResponse::PreventDefault,
9797 }
9798 });
9799 dispatch_touch(window, cx, TouchId(1), TouchPhase::Started, 0.);
9800 dispatch_touch(window, cx, TouchId(1), phase, position);
9801 cx.executor().advance_clock(Duration::from_millis(501));
9802 cx.executor().run_until_parked();
9803
9804 assert!(phases.borrow().is_empty(), "{phase:?} allowed long press");
9805 }
9806 }
9807
9808 #[gpui::test]
9809 fn stale_long_press_timer_cannot_affect_replacement_touch(cx: &mut TestAppContext) {
9810 let phases = Rc::new(RefCell::new(Vec::new()));
9811 let window = cx.add_window({
9812 let phases = phases.clone();
9813 move |_, _| LongPressListener {
9814 phases,
9815 response: LongPressResponse::PreventDefault,
9816 }
9817 });
9818 let first_touch = TouchId(1);
9819 dispatch_touch(window, cx, first_touch, TouchPhase::Started, 0.);
9820 cx.executor().advance_clock(Duration::from_millis(250));
9821 dispatch_touch(window, cx, first_touch, TouchPhase::Cancelled, 0.);
9822 dispatch_touch(window, cx, TouchId(2), TouchPhase::Started, 10.);
9823
9824 cx.executor().advance_clock(Duration::from_millis(251));
9825 cx.executor().run_until_parked();
9826 assert!(phases.borrow().is_empty());
9827
9828 cx.executor().advance_clock(Duration::from_millis(250));
9829 cx.executor().run_until_parked();
9830 assert_eq!(phases.borrow().as_slice(), [TouchPhase::Started]);
9831 }
9832
9833 #[gpui::test]
9834 fn test_underline_exclusions_preserve_device_geometry(cx: &mut TestAppContext) {
9835 test_underline_paint_at_scales(cx, |window| {
9836 let (numerator, denominator) = match window.scale_factor() {
9837 1.0 => (1, 1),
9838 1.25 => (5, 4),
9839 1.5 => (3, 2),
9840 2.0 => (2, 1),
9841 3.0 => (3, 1),
9842 scale => panic!("unexpected scale {scale}"),
9843 };
9844 let snap = |quarters: i32| {
9845 let scaled = quarters * numerator;
9846 let divisor = 4 * denominator;
9847 let rounded =
9848 scaled.abs() / divisor + i32::from(2 * (scaled.abs() % divisor) > divisor);
9849 ScaledPixels((scaled.signum() * rounded) as f32)
9850 };
9851 let stroke = |quarters| {
9852 if quarters == 0 {
9853 ScaledPixels(0.)
9854 } else {
9855 snap(quarters).max(ScaledPixels(1.))
9856 }
9857 };
9858
9859 for origin in [-101, -3, -2, -1, 0, 1, 2, 3, 101] {
9860 for width in [0, 1, 3, 84] {
9861 for thickness in [0, 1, 4, 7] {
9862 for wavy in [false, true] {
9863 let style = UnderlineStyle {
9864 thickness: px(thickness as f32 / 4.),
9865 color: Some(hsla(0.25, 0.5, 0.75, 0.5)),
9866 wavy,
9867 };
9868 let bounds = Bounds::new(
9869 point(snap(origin), snap(-origin)),
9870 size(
9871 stroke(width),
9872 stroke(thickness) * if wavy { 3. } else { 1. },
9873 ),
9874 );
9875 let origin = point(px(origin as f32 / 4.), px(-origin as f32 / 4.));
9876 let width = px(width as f32 / 4.);
9877 assert_eq!(window.underline_bounds(origin, width, &style), bounds);
9878 let original = paint_test_underlines(window, |window| {
9879 window.paint_underline(origin, width, &style);
9880 })
9881 .to_vec();
9882 assert_eq!(original.len(), usize::from(!bounds.is_empty()));
9883 if let Some(underline) = original.first() {
9884 assert_eq!(underline.bounds, bounds);
9885 assert_eq!(underline.thickness, stroke(thickness));
9886 assert_eq!(underline.wavy, wavy.into());
9887 assert_eq!(underline.color, hsla(0.25, 0.5, 0.75, 0.5));
9888 }
9889
9890 let underlines = paint_test_underlines(window, |window| {
9891 window.paint_underline_with_exclusions(origin, width, &style, &[]);
9892 });
9893 assert_eq!(underlines.len(), original.len());
9894 for (actual, original) in underlines.iter().zip(&original) {
9895 assert_same_underline_geometry(actual, original);
9896 assert_eq!(actual.content_mask, original.content_mask);
9897 }
9898
9899 let left = bounds.left().0 as i32;
9900 let right = bounds.right().0 as i32;
9901 let exclusions = [
9902 (15, 25),
9903 (3, 7),
9904 (5, 10),
9905 (10, 12),
9906 (0, 0),
9907 (4, 2),
9908 (-50, -1),
9909 (70, 100),
9910 ]
9911 .map(|(start, end)| {
9912 ScaledPixels((left + start) as f32)
9913 ..ScaledPixels((left + end) as f32)
9914 });
9915 let underlines = paint_test_underlines(window, |window| {
9916 window.paint_underline_with_exclusions(
9917 origin,
9918 width,
9919 &style,
9920 &exclusions,
9921 );
9922 });
9923 let expected = (left..right)
9924 .filter(|column| {
9925 !bounds.is_empty()
9926 && !exclusions.iter().any(|span| {
9927 span.contains(&ScaledPixels(*column as f32))
9928 })
9929 })
9930 .collect::<Vec<_>>();
9931 assert_eq!(underline_device_columns(underlines), expected);
9932 for underline in underlines {
9933 assert_same_underline_geometry(underline, &original[0]);
9934 }
9935 }
9936 }
9937 }
9938 }
9939 });
9940 }
9941
9942 #[gpui::test]
9943 fn test_underline_exclusions_preserve_helvetica_warning_edge(cx: &mut TestAppContext) {
9944 test_underline_paint_at_scales(cx, |window| {
9945 if window.scale_factor() != 2.0 {
9946 return;
9947 }
9948 let style = UnderlineStyle {
9949 thickness: px(1.),
9950 color: None,
9951 wavy: true,
9952 };
9953 let origin = point(px(2.888_183_8), px(15.357_917));
9954 let width = px(23.848_145) - origin.x;
9955 let original = paint_test_underlines(window, |window| {
9956 window.paint_underline(origin, width, &style);
9957 })[0];
9958 assert_eq!(original.bounds.left(), ScaledPixels(6.));
9959 assert_eq!(original.bounds.right(), ScaledPixels(48.));
9960 let point_bounds = window.underline_bounds(origin, px(10.829_102), &style);
9961 assert_eq!(point_bounds.left(), ScaledPixels(6.));
9962 assert_eq!(point_bounds.right(), ScaledPixels(28.));
9963
9964 let underlines = paint_test_underlines(window, |window| {
9965 window.paint_underline_with_exclusions(
9966 origin,
9967 width,
9968 &style,
9969 &[point_bounds.left()..point_bounds.right()],
9970 );
9971 });
9972 assert_eq!(underlines.len(), 1);
9973 assert_same_underline_geometry(&underlines[0], &original);
9974 assert_eq!(
9975 underline_device_columns(underlines),
9976 (28..48).collect::<Vec<_>>()
9977 );
9978 });
9979 }
9980
9981 #[gpui::test]
9982 fn test_underline_exclusions_preserve_content_mask_and_opacity(cx: &mut TestAppContext) {
9983 test_underline_paint_at_scales(cx, |window| {
9984 let scale = window.scale_factor();
9985 let original_mask = window.content_mask();
9986 let original_opacity = window.element_opacity();
9987 let mask = ContentMask {
9988 bounds: Bounds::from_corners(point(px(4.), px(10.)), point(px(16.), px(12.))),
9989 };
9990 let style = UnderlineStyle {
9991 thickness: px(2.),
9992 color: Some(hsla(0.25, 0.5, 0.75, 0.5)),
9993 wavy: true,
9994 };
9995 let origin = point(px(-1.25), px(9.25));
9996 let width = px(24.);
9997 window.with_content_mask(Some(mask), |window| {
9998 window.with_element_opacity(Some(0.5), |window| {
9999 window.with_element_opacity(Some(0.5), |window| {
10000 let original = paint_test_underlines(window, |window| {
10001 window.paint_underline(origin, width, &style);
10002 })[0];
10003 assert_eq!(original.color, hsla(0.25, 0.5, 0.75, 0.125));
10004 let underlines = paint_test_underlines(window, |window| {
10005 window.paint_underline_with_exclusions(
10006 origin,
10007 width,
10008 &style,
10009 &[ScaledPixels(6. * scale)..ScaledPixels(8. * scale)],
10010 );
10011 });
10012 assert_eq!(underlines.len(), 2);
10013 let expected = [(4. * scale, 6. * scale), (8. * scale, 16. * scale)].map(
10014 |(left, right)| {
10015 Bounds::from_corners(
10016 point(ScaledPixels(left), ScaledPixels((10. * scale).floor())),
10017 point(ScaledPixels(right), ScaledPixels((12. * scale).ceil())),
10018 )
10019 },
10020 );
10021 assert_eq!(
10022 underlines
10023 .iter()
10024 .map(|underline| underline.content_mask.bounds)
10025 .collect::<Vec<_>>(),
10026 expected
10027 );
10028 for underline in underlines {
10029 assert_same_underline_geometry(underline, &original);
10030 }
10031 });
10032 });
10033 });
10034 assert_eq!(window.content_mask(), original_mask);
10035 assert_eq!(window.element_opacity(), original_opacity);
10036 });
10037 }
10038
10039 #[gpui::test]
10040 fn test_underline_exclusions_adversarial_permutations(cx: &mut TestAppContext) {
10041 test_underline_paint_at_scales(cx, |window| {
10042 let style = UnderlineStyle {
10043 thickness: px(1.),
10044 color: None,
10045 wavy: true,
10046 };
10047 let origin = point(px(-4.), px(-1.));
10048 let width = px(8.);
10049 let left = (-4. * window.scale_factor()) as i32;
10050 let right = (4. * window.scale_factor()) as i32;
10051 let endpoints = [-16, -8, -1, 0, 1, 8, 16];
10052 for first_start in endpoints {
10053 for first_end in endpoints {
10054 for second_start in endpoints {
10055 for second_end in endpoints {
10056 let exclusions = [first_start..first_end, second_start..second_end];
10057 let expected = (left..right)
10058 .filter(|column| {
10059 !exclusions.iter().any(|span| span.contains(column))
10060 })
10061 .collect::<Vec<_>>();
10062 let exclusions = exclusions.map(|span| {
10063 ScaledPixels(span.start as f32)..ScaledPixels(span.end as f32)
10064 });
10065 let underlines = paint_test_underlines(window, |window| {
10066 window.paint_underline_with_exclusions(
10067 origin,
10068 width,
10069 &style,
10070 &exclusions,
10071 );
10072 });
10073 assert_eq!(
10074 underline_device_columns(underlines),
10075 expected,
10076 "{exclusions:?}"
10077 );
10078 }
10079 }
10080 }
10081 }
10082 });
10083 }
10084
10085 #[derive(Clone, Copy, PartialEq)]
10086 enum LongPressResponse {
10087 PreventDefault,
10088 StopPropagation,
10089 None,
10090 }
10091
10092 struct LongPressListener {
10093 phases: Rc<RefCell<Vec<TouchPhase>>>,
10094 response: LongPressResponse,
10095 }
10096
10097 impl Render for LongPressListener {
10098 fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
10099 let entity = cx.entity();
10100 let phases = self.phases.clone();
10101 let response = self.response;
10102 canvas(
10103 |_, _, _| {},
10104 move |_, _, window, _| {
10105 window.on_mouse_event(move |event: &LongPressEvent, phase, window, cx| {
10106 if phase != DispatchPhase::Bubble {
10107 return;
10108 }
10109 phases.borrow_mut().push(event.phase);
10110 match response {
10111 LongPressResponse::PreventDefault => {
10112 window.capture_long_press(&entity);
10113 window.prevent_default();
10114 }
10115 LongPressResponse::StopPropagation => cx.stop_propagation(),
10116 LongPressResponse::None => {}
10117 }
10118 });
10119 },
10120 )
10121 }
10122 }
10123
10124 struct UnderlineTestView(Rc<dyn Fn(&mut Window)>);
10125
10126 impl Render for UnderlineTestView {
10127 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
10128 let paint = self.0.clone();
10129 canvas(
10130 |_, _, _| {},
10131 move |_, _, window, _| {
10132 window.frame_state.content_mask_stack.push(ContentMask {
10133 bounds: Bounds::from_corners(
10134 point(px(-1000.), px(-1000.)),
10135 point(px(1000.), px(1000.)),
10136 ),
10137 });
10138 paint(window);
10139 window.frame_state.content_mask_stack.pop();
10140 },
10141 )
10142 .size_full()
10143 }
10144 }
10145
10146 fn test_underline_paint_at_scales(
10147 cx: &mut TestAppContext,
10148 paint: impl Fn(&mut Window) + 'static,
10149 ) {
10150 let window = cx.add_window(move |_, _| UnderlineTestView(Rc::new(paint)));
10151 for scale in [1., 1.25, 1.5, 2., 3.] {
10152 cx.simulate_window_scale_factor_change(window.into(), scale);
10153 cx.update_window(window.into(), |_, window, cx| window.draw(cx).clear(cx))
10154 .unwrap();
10155 }
10156 }
10157
10158 fn paint_test_underlines<'a>(
10159 window: &'a mut Window<'_>,
10160 paint: impl FnOnce(&mut Window),
10161 ) -> &'a [Underline] {
10162 window.frame_state.next_frame.scene.clear();
10163 paint(window);
10164 &window.frame_state.next_frame.scene.underlines
10165 }
10166
10167 fn assert_same_underline_geometry(actual: &Underline, expected: &Underline) {
10168 assert_eq!(actual.bounds, expected.bounds);
10169 assert_eq!(actual.thickness, expected.thickness);
10170 assert_eq!(actual.wavy, expected.wavy);
10171 assert_eq!(actual.color, expected.color);
10172 assert_eq!(actual.pad, expected.pad);
10173 }
10174
10175 fn underline_device_columns(underlines: &[Underline]) -> Vec<i32> {
10176 underlines
10177 .iter()
10178 .flat_map(|underline| {
10179 let bounds = underline.bounds.intersect(&underline.content_mask.bounds);
10180 bounds.left().0 as i32..bounds.right().0 as i32
10181 })
10182 .collect()
10183 }
10184
10185 fn dispatch_touch<T: 'static>(
10186 window: crate::WindowHandle<T>,
10187 cx: &mut TestAppContext,
10188 id: TouchId,
10189 phase: TouchPhase,
10190 x: f32,
10191 ) {
10192 window
10193 .update(cx, |_, window, cx| {
10194 window.dispatch_event(
10195 TouchEvent {
10196 id,
10197 phase,
10198 position: point(px(x), px(0.)),
10199 predicted_position: None,
10200 force: None,
10201 }
10202 .to_platform_input(),
10203 cx,
10204 );
10205 })
10206 .unwrap();
10207 }
10208
10209 #[gpui::test]
10214 fn a_painted_imported_texture_converts_its_radii_at_paint_time(cx: &mut TestAppContext) {
10215 struct PaintedTexture {
10216 handle: ImportedTextureHandle,
10217 bounds: Bounds<Pixels>,
10218 corner_radii: Corners<Pixels>,
10219 opacity: f32,
10220 flip_v: bool,
10221 }
10222
10223 impl Render for PaintedTexture {
10224 fn render(
10225 &mut self,
10226 _window: &mut Window,
10227 _cx: &mut Context<Self>,
10228 ) -> impl IntoElement {
10229 let handle = self.handle.clone();
10230 let bounds = self.bounds;
10231 let corner_radii = self.corner_radii;
10232 let opacity = self.opacity;
10233 let flip_v = self.flip_v;
10234 gpu_canvas(move |ctx| {
10235 ctx.paint_texture(handle.clone(), corner_radii, opacity, flip_v);
10236 })
10237 .w(bounds.size.width)
10238 .h(bounds.size.height)
10239 }
10240 }
10241
10242 let corner_radii = Corners {
10243 top_left: px(4.),
10244 top_right: px(0.),
10245 bottom_right: px(0.),
10246 bottom_left: px(0.),
10247 };
10248 let window = cx.add_window(|_, _| PaintedTexture {
10249 handle: ImportedTextureHandle::new(()),
10250 bounds: Bounds::new(point(px(10.), px(20.)), size(px(30.), px(40.))),
10251 corner_radii,
10252 opacity: 0.5,
10253 flip_v: true,
10254 });
10255 cx.update_window(window.into(), |_, window, cx| window.draw(cx).clear(cx))
10256 .unwrap();
10257
10258 cx.update_window(window.into(), |_, window, _| {
10259 let painted = window.painted_imported_textures();
10260 assert_eq!(painted.len(), 1, "the window should have painted the texture");
10261
10262 let CustomRenderPrimitive::Texture {
10263 radii,
10264 opacity,
10265 flip_v,
10266 bounds,
10267 ..
10268 } = &painted[0];
10269 assert_eq!(
10270 *radii,
10271 corner_radii.scale(window.scale_factor()),
10272 "the radii must be converted to scaled pixels at paint time"
10273 );
10274 assert_eq!(*opacity, 0.5);
10275 assert!(*flip_v);
10276 assert_eq!(bounds.size.width, ScaledPixels(30. * window.scale_factor()));
10277 assert_eq!(bounds.size.height, ScaledPixels(40. * window.scale_factor()));
10278 })
10279 .unwrap();
10280 }
10281}
10282
10283#[cfg(all(test, any(feature = "inspector", debug_assertions)))]
10284mod inspector_tests {
10285 use super::*;
10286 use crate::{
10287 DivInspectorState, InspectorElementId, MouseDownEvent, StyleRefinement, TestAppContext, div,
10288 };
10289
10290 #[gpui::test]
10291 fn inspector_only_tracks_its_open_window(cx: &mut TestAppContext) {
10292 let windows = [
10293 cx.add_window(|_, cx| InspectorTestRoot {
10294 child: cx.new(|_| InspectorTestView::default()),
10295 }),
10296 cx.add_window(|_, cx| InspectorTestRoot {
10297 child: cx.new(|_| InspectorTestView::default()),
10298 }),
10299 ];
10300 for window in windows {
10301 assert_closed_inspector(window.into(), cx);
10302 }
10303 for _ in 0..2 {
10304 cx.update_window(windows[0].into(), |root, window, cx| {
10305 let root = root.downcast::<InspectorTestRoot>().expect("test root");
10306 let child_widths = root.read(cx).child.read(cx).child_widths.clone();
10307 window.toggle_inspector(cx);
10308 window.draw(cx).clear(cx);
10309 assert_eq!(child_widths.borrow().as_slice(), &[px(10.); 3]);
10310 let path = window
10311 .frame_state
10312 .rendered_frame
10313 .next_inspector_instance_ids
10314 .iter()
10315 .find_map(|(path, count)| (*count == 3).then(|| path.clone()))
10316 .expect("anonymous siblings share an inspector path");
10317 let selected_id = InspectorElementId {
10318 path,
10319 instance_id: 1,
10320 };
10321 let position = window
10322 .frame_state
10323 .rendered_frame
10324 .hitboxes
10325 .iter()
10326 .find(|hitbox| {
10327 window
10328 .frame_state
10329 .rendered_frame
10330 .inspector_hitboxes
10331 .get(&hitbox.id)
10332 == Some(&selected_id)
10333 })
10334 .expect("middle sibling is pickable")
10335 .bounds
10336 .center();
10337 window.simulate_mouse_move(position, cx);
10338 window.dispatch_event(
10339 PlatformInput::MouseDown(MouseDownEvent {
10340 position,
10341 button: MouseButton::Left,
10342 modifiers: Modifiers::default(),
10343 click_count: 1,
10344 first_mouse: false,
10345 }),
10346 cx,
10347 );
10348 window.dispatch_event(
10349 PlatformInput::MouseUp(MouseUpEvent {
10350 position,
10351 button: MouseButton::Left,
10352 modifiers: Modifiers::default(),
10353 click_count: 1,
10354 }),
10355 cx,
10356 );
10357 assert!(!window.is_inspector_picking(cx));
10358 assert_eq!(
10359 window
10360 .core
10361 .inspector
10362 .as_ref()
10363 .expect("open inspector")
10364 .read(cx)
10365 .active_element_id(),
10366 Some(&selected_id)
10367 );
10368 window.draw(cx).clear(cx);
10369 window.with_inspector_state::<DivInspectorState, _>(
10370 Some(&selected_id),
10371 cx,
10372 |state, _| {
10373 let state = state.as_mut().expect("selected div has style state");
10374 assert_eq!(
10375 state.base_style.size.width,
10376 Some(crate::Length::from(px(10.)))
10377 );
10378 state.base_style.size.width = Some(crate::Length::from(px(25.)));
10379 },
10380 );
10381 window.refresh();
10382 window.draw(cx).clear(cx);
10383 assert_eq!(
10384 child_widths.borrow().as_slice(),
10385 &[px(10.), px(25.), px(10.)]
10386 );
10387 })
10388 .expect("pick and edit a cached child");
10389 assert_closed_inspector(windows[1].into(), cx);
10390 windows[0]
10391 .update(cx, |_, window, cx| window.toggle_inspector(cx))
10392 .expect("close inspector");
10393 assert_closed_inspector(windows[0].into(), cx);
10394 }
10395 }
10396
10397 struct InspectorTestRoot {
10398 child: Entity<InspectorTestView>,
10399 }
10400
10401 impl Render for InspectorTestRoot {
10402 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
10403 self.child
10404 .clone()
10405 .cached(StyleRefinement::default().size(px(100.)))
10406 }
10407 }
10408
10409 #[derive(Default)]
10410 struct InspectorTestView {
10411 child_widths: Rc<RefCell<Vec<Pixels>>>,
10412 }
10413
10414 impl Render for InspectorTestView {
10415 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
10416 let child_widths = self.child_widths.clone();
10417 div()
10418 .on_children_prepainted(move |bounds, _, _| {
10419 *child_widths.borrow_mut() =
10420 bounds.iter().map(|bounds| bounds.size.width).collect();
10421 })
10422 .with_dynamic_prepaint_order(|_, _| SmallVec::from_iter([2, 0, 1]))
10423 .id("inspector-root")
10424 .flex()
10425 .children((0..3).map(|_| div().size(px(10.)).flex_shrink_0()))
10426 }
10427 }
10428
10429 fn assert_closed_inspector(window: AnyWindowHandle, cx: &mut TestAppContext) {
10430 cx.update_window(window, |root, window, cx| {
10431 window.draw(cx).clear(cx);
10432 window.draw(cx).clear(cx);
10433 let root = root.downcast::<InspectorTestRoot>().expect("test root");
10434 assert_eq!(
10435 root.read(cx)
10436 .child
10437 .read(cx)
10438 .child_widths
10439 .borrow()
10440 .as_slice(),
10441 &[px(10.); 3]
10442 );
10443 for frame in [
10444 &window.frame_state.rendered_frame,
10445 &window.frame_state.next_frame,
10446 ] {
10447 assert_eq!(frame.next_inspector_instance_ids.capacity(), 0);
10448 assert_eq!(frame.inspector_hitboxes.capacity(), 0);
10449 }
10450 })
10451 .expect("closed inspector has no bookkeeping and no style overrides");
10452 }
10453}