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 #[doc(hidden)]
3872 #[profiling::function]
3873 pub fn present(&mut self) {
3874 #[cfg(feature = "profiler")]
3875 let _foreground_turn = profiler::journal::foreground_turn();
3876 #[cfg(feature = "profiler")]
3877 let present_start = Instant::now();
3878 self.core
3879 .platform_window
3880 .present(&mut |renderer| renderer.draw(&self.frame_state.rendered_frame.scene));
3881 #[cfg(feature = "profiler")]
3882 self.core.window_profiler.record_present(
3883 present_start,
3884 Instant::now(),
3885 self.core.active.get(),
3886 !self.core.next_frame_callbacks.borrow().is_empty(),
3887 );
3888 self.core.needs_present.set(false);
3889 profiling::finish_frame!();
3890 }
3891
3892 #[cfg(all(test, feature = "profiler"))]
3894 pub(crate) fn present_if_needed(&mut self) {
3895 if self.core.needs_present.get() {
3896 self.present();
3897 }
3898 }
3899
3900 #[cfg(feature = "profiler")]
3902 pub fn input_latency_snapshot(&self) -> profiler::InputLatencySnapshot {
3903 self.core.window_profiler.input_latency_snapshot()
3904 }
3905
3906 #[cfg(feature = "profiler")]
3908 pub fn frame_duration_snapshot(&self) -> profiler::FrameDurationSnapshot {
3909 self.core.window_profiler.frame_duration_snapshot()
3910 }
3911
3912 #[cfg(feature = "profiler")]
3914 pub fn debug_frame_overlay_mode(&self) -> DebugFrameOverlayMode {
3915 self.core.debug_frame_overlay.mode()
3916 }
3917
3918 #[cfg(feature = "profiler")]
3920 pub fn set_debug_frame_overlay_mode(&mut self, mode: DebugFrameOverlayMode) {
3921 self.core.debug_frame_overlay.set_mode(mode);
3922 self.refresh();
3923 }
3924
3925 #[cfg(feature = "profiler")]
3928 pub fn cycle_debug_frame_overlay_mode(&mut self) {
3929 self.set_debug_frame_overlay_mode(self.core.debug_frame_overlay.mode().next());
3930 }
3931
3932 #[cfg(feature = "profiler")]
3935 pub fn reset_debug_frame_overlay_stats(&mut self) {
3936 self.core.debug_frame_overlay.reset_stats();
3937 self.refresh();
3938 }
3939
3940 pub fn evaluate_roots(&mut self, cx: &mut App) -> PreparedRoots {
3946 #[cfg(debug_assertions)]
3947 self.frame_state.frame_phase.enter(
3948 &[FramePhase::Begun],
3949 FramePhase::Evaluated,
3950 "evaluate_roots",
3951 );
3952 let root = self.core.root.as_ref().unwrap().clone().into_any_element();
3953
3954 let mut prompt = None;
3957 let mut drag = None;
3958 if let Some(handle) = self.core.prompt.take() {
3959 prompt = Some(handle.view.any_view().into_any_element());
3960 self.core.prompt = Some(handle);
3961 } else if let Some(active_drag) = cx.active_drag.take() {
3962 let origin = self.mouse_position() - active_drag.cursor_offset;
3963 drag = Some((active_drag.view.clone().into_any_element(), origin));
3964 cx.active_drag = Some(active_drag);
3965 }
3966
3967 PreparedRoots {
3968 root,
3969 prompt,
3970 tooltip: None,
3971 drag,
3972 inspector: None,
3973 }
3974 }
3975
3976 pub fn layout_roots(&mut self, roots: &mut PreparedRoots, cx: &mut App) {
3979 #[cfg(debug_assertions)]
3980 self.frame_state.frame_phase.enter(
3981 &[FramePhase::Evaluated],
3982 FramePhase::LaidOut,
3983 "layout_roots",
3984 );
3985 self.core.invalidator.set_phase(DrawPhase::Prepaint);
3986 self.frame_state.tooltip_bounds.take();
3987
3988 self.core.a11y.sync_active_flag();
3989 if self.core.a11y.is_active() {
3990 self.core.a11y.begin_frame();
3991 }
3992
3993 #[cfg(any(feature = "inspector", debug_assertions))]
3994 let inspector_width: Pixels = rems(30.0).to_pixels(self.rem_size());
3995 let root_size = {
3996 #[cfg(any(feature = "inspector", debug_assertions))]
3997 {
3998 if self.core.inspector.is_some() {
3999 let mut size = self.core.viewport_size;
4000 size.width = (size.width - inspector_width).max(px(0.0));
4001 size
4002 } else {
4003 self.core.viewport_size
4004 }
4005 }
4006 #[cfg(not(any(feature = "inspector", debug_assertions)))]
4007 {
4008 self.core.viewport_size
4009 }
4010 };
4011
4012 let scale_factor = self.scale_factor();
4016 let root_layout_id = roots.root.request_layout(self, cx);
4017 self.frame_state.layout_session.stretch_auto_size_to_fill(
4018 root_layout_id,
4019 root_size,
4020 scale_factor,
4021 );
4022 roots
4023 .root
4024 .prepaint_as_root(Point::default(), root_size.into(), self, cx);
4025
4026 #[cfg(any(feature = "inspector", debug_assertions))]
4027 {
4028 roots.inspector = self.prepaint_inspector(inspector_width, cx);
4029 }
4030
4031 self.prepaint_deferred_draws(cx);
4032
4033 if let Some(prompt) = roots.prompt.as_mut() {
4034 let prompt_layout_id = prompt.request_layout(self, cx);
4035 self.frame_state.layout_session.stretch_auto_size_to_fill(
4036 prompt_layout_id,
4037 root_size,
4038 scale_factor,
4039 );
4040 prompt.prepaint_as_root(Point::default(), root_size.into(), self, cx);
4041 } else if let Some((drag, origin)) = roots.drag.as_mut() {
4042 drag.prepaint_as_root(*origin, AvailableSpace::min_size(), self, cx);
4043 } else {
4044 roots.tooltip = self.prepaint_tooltip(cx);
4045 }
4046
4047 self.core.mouse_hit_test = self
4048 .frame_state
4049 .next_frame
4050 .hit_test(self.core.mouse_position);
4051 }
4052
4053 pub fn paint_roots(&mut self, mut roots: PreparedRoots, cx: &mut App) {
4055 #[cfg(debug_assertions)]
4056 self.frame_state.frame_phase.enter(
4057 &[FramePhase::LaidOut],
4058 FramePhase::Painted,
4059 "paint_roots",
4060 );
4061 self.core.invalidator.set_phase(DrawPhase::Paint);
4062 roots.root.paint(self, cx);
4063
4064 #[cfg(any(feature = "inspector", debug_assertions))]
4065 self.paint_inspector(roots.inspector.take(), cx);
4066
4067 self.paint_deferred_draws(cx);
4068
4069 if let Some(mut prompt) = roots.prompt.take() {
4070 prompt.paint(self, cx);
4071 } else if let Some((mut drag, _)) = roots.drag.take() {
4072 drag.paint(self, cx);
4073 } else if let Some(mut tooltip) = roots.tooltip.take() {
4074 tooltip.paint(self, cx);
4075 }
4076
4077 #[cfg(any(feature = "inspector", debug_assertions))]
4078 self.paint_inspector_hitbox(cx);
4079
4080 let a11y_active_start_of_frame = self.core.a11y.is_active();
4082 self.core.a11y.sync_active_flag();
4083 let a11y_active_end_of_frame = self.core.a11y.is_active();
4084
4085 let should_send_a11y_update = a11y_active_start_of_frame && a11y_active_end_of_frame;
4086
4087 if a11y_active_start_of_frame {
4088 let frame_info = crate::window::a11y::debug::FrameDebugInfo {
4091 viewport_size: self.core.viewport_size,
4092 scale_factor: self.core.scale_factor,
4093 tab_stop_count: self.frame_state.next_frame.tab_stops.tab_stop_count(),
4094 };
4095 let tree_update = self.core.a11y.end_frame(frame_info);
4097
4098 if should_send_a11y_update {
4099 log::debug!(
4100 "Sending a11y tree update: {} nodes",
4101 tree_update.nodes.len()
4102 );
4103 self.core.platform_window.a11y_tree_update(tree_update);
4104 }
4105 }
4106 }
4107
4108 pub fn draw_roots(&mut self, cx: &mut App) {
4114 let mut roots = self.evaluate_roots(cx);
4115 self.layout_roots(&mut roots, cx);
4116 self.paint_roots(roots, cx);
4117 }
4118
4119 fn prepaint_tooltip(&mut self, cx: &mut App) -> Option<AnyElement> {
4120 for tooltip_request_index in (0..self.frame_state.next_frame.tooltip_requests.len()).rev() {
4122 let Some(Some(tooltip_request)) = self
4123 .frame_state
4124 .next_frame
4125 .tooltip_requests
4126 .get(tooltip_request_index)
4127 .cloned()
4128 else {
4129 log::error!("Unexpectedly absent TooltipRequest");
4130 continue;
4131 };
4132 let mut element = tooltip_request.tooltip.view.clone().into_any_element();
4133 let mouse_position = tooltip_request.tooltip.mouse_position;
4134 let tooltip_size = element.layout_as_root(AvailableSpace::min_size(), self, cx);
4135
4136 let mut tooltip_bounds =
4137 Bounds::new(mouse_position + point(px(1.), px(1.)), tooltip_size);
4138 let window_bounds = Bounds {
4139 origin: Point::default(),
4140 size: self.viewport_size(),
4141 };
4142
4143 if tooltip_bounds.right() > window_bounds.right() {
4144 let new_x = mouse_position.x - tooltip_bounds.size.width - px(1.);
4145 if new_x >= Pixels::ZERO {
4146 tooltip_bounds.origin.x = new_x;
4147 } else {
4148 tooltip_bounds.origin.x = cmp::max(
4149 Pixels::ZERO,
4150 tooltip_bounds.origin.x - tooltip_bounds.right() - window_bounds.right(),
4151 );
4152 }
4153 }
4154
4155 if tooltip_bounds.bottom() > window_bounds.bottom() {
4156 let new_y = mouse_position.y - tooltip_bounds.size.height - px(1.);
4157 if new_y >= Pixels::ZERO {
4158 tooltip_bounds.origin.y = new_y;
4159 } else {
4160 tooltip_bounds.origin.y = cmp::max(
4161 Pixels::ZERO,
4162 tooltip_bounds.origin.y - tooltip_bounds.bottom() - window_bounds.bottom(),
4163 );
4164 }
4165 }
4166
4167 let is_visible =
4171 (tooltip_request.tooltip.check_visible_and_update)(tooltip_bounds, self, cx);
4172 if !is_visible {
4173 continue;
4174 }
4175
4176 self.with_absolute_element_offset(tooltip_bounds.origin, |window| {
4177 element.prepaint(window, cx)
4178 });
4179
4180 self.frame_state.tooltip_bounds = Some(TooltipBounds {
4181 id: tooltip_request.id,
4182 bounds: tooltip_bounds,
4183 });
4184 return Some(element);
4185 }
4186 None
4187 }
4188
4189 fn prepaint_deferred_draws(&mut self, cx: &mut App) {
4190 assert_eq!(self.frame_state.element_id_stack.len(), 0);
4191
4192 let mut round_start = 0;
4203 let mut depth = 0;
4204 loop {
4205 let round_end = self.frame_state.next_frame.deferred_draws.len();
4206 if round_start == round_end {
4207 break;
4208 }
4209 assert!(depth < 10, "Exceeded maximum (10) deferred depth");
4211 depth += 1;
4212
4213 let mut traversal_order = (round_start..round_end).collect::<SmallVec<[usize; 8]>>();
4215 traversal_order
4216 .sort_by_key(|ix| self.frame_state.next_frame.deferred_draws[*ix].priority);
4217
4218 for deferred_draw_ix in traversal_order {
4219 let (element, parent_node, current_view, rem_size, absolute_offset, prepaint_range) = {
4220 let deferred_draw =
4221 &mut self.frame_state.next_frame.deferred_draws[deferred_draw_ix];
4222 self.frame_state
4223 .element_id_stack
4224 .clone_from(&deferred_draw.element_id_stack);
4225 self.frame_state
4226 .text_style_stack
4227 .clone_from(&deferred_draw.text_style_stack);
4228 (
4229 deferred_draw.element.take(),
4230 deferred_draw.parent_node,
4231 deferred_draw.current_view,
4232 deferred_draw.rem_size,
4233 deferred_draw.absolute_offset,
4234 deferred_draw.prepaint_range.clone(),
4235 )
4236 };
4237 self.frame_state
4238 .next_frame
4239 .dispatch_tree
4240 .set_active_node(parent_node);
4241
4242 let prepaint_start = self.prepaint_index();
4243 if let Some(mut element) = element {
4244 self.with_rendered_view(current_view, |window| {
4245 window.with_rem_size(Some(rem_size), |window| {
4246 window.with_absolute_element_offset(absolute_offset, |window| {
4247 element.prepaint(window, cx);
4248 });
4249 });
4250 });
4251 self.frame_state.next_frame.deferred_draws[deferred_draw_ix].element =
4252 Some(element);
4253 } else {
4254 self.reuse_prepaint(prepaint_range);
4255 }
4256 let prepaint_end = self.prepaint_index();
4257 self.frame_state.next_frame.deferred_draws[deferred_draw_ix].prepaint_range =
4258 prepaint_start..prepaint_end;
4259 }
4260
4261 self.frame_state.element_id_stack.clear();
4262 self.frame_state.text_style_stack.clear();
4263 round_start = round_end;
4264 }
4265 }
4266
4267 fn paint_deferred_draws(&mut self, cx: &mut App) {
4268 assert_eq!(self.frame_state.element_id_stack.len(), 0);
4269
4270 if self.frame_state.next_frame.deferred_draws.len() == 0 {
4273 return;
4274 }
4275
4276 let traversal_order = self.deferred_draw_traversal_order();
4277 let mut deferred_draws = mem::take(&mut self.frame_state.next_frame.deferred_draws);
4278 for deferred_draw_ix in traversal_order {
4279 let mut deferred_draw = &mut deferred_draws[deferred_draw_ix];
4280 self.frame_state
4281 .element_id_stack
4282 .clone_from(&deferred_draw.element_id_stack);
4283 self.frame_state
4284 .next_frame
4285 .dispatch_tree
4286 .set_active_node(deferred_draw.parent_node);
4287
4288 let paint_start = self.paint_index();
4289 let content_mask = deferred_draw.content_mask;
4290 if let Some(element) = deferred_draw.element.as_mut() {
4291 self.with_rendered_view(deferred_draw.current_view, |window| {
4292 window.with_content_mask(content_mask, |window| {
4293 window.with_rem_size(Some(deferred_draw.rem_size), |window| {
4294 element.paint(window, cx);
4295 });
4296 })
4297 })
4298 } else {
4299 self.reuse_paint(deferred_draw.paint_range.clone());
4300 }
4301 let paint_end = self.paint_index();
4302 deferred_draw.paint_range = paint_start..paint_end;
4303 }
4304 self.frame_state.next_frame.deferred_draws = deferred_draws;
4305 self.frame_state.element_id_stack.clear();
4306 }
4307
4308 fn deferred_draw_traversal_order(&mut self) -> SmallVec<[usize; 8]> {
4309 let deferred_count = self.frame_state.next_frame.deferred_draws.len();
4310 let mut sorted_indices = (0..deferred_count).collect::<SmallVec<[_; 8]>>();
4311 sorted_indices.sort_by_key(|ix| self.frame_state.next_frame.deferred_draws[*ix].priority);
4312 sorted_indices
4313 }
4314
4315 pub(crate) fn prepaint_index(&self) -> PrepaintStateIndex {
4316 PrepaintStateIndex {
4317 hitboxes_index: self.frame_state.next_frame.hitboxes.len(),
4318 tooltips_index: self.frame_state.next_frame.tooltip_requests.len(),
4319 deferred_draws_index: self.frame_state.next_frame.deferred_draws.len(),
4320 dispatch_tree_index: self.frame_state.next_frame.dispatch_tree.len(),
4321 accessed_element_states_index: self
4322 .frame_state
4323 .next_frame
4324 .accessed_element_states
4325 .len(),
4326 line_layout_index: self.core.text_system.layout_index(),
4327 }
4328 }
4329
4330 pub(crate) fn reuse_prepaint(&mut self, range: Range<PrepaintStateIndex>) {
4331 self.frame_state.next_frame.hitboxes.extend(
4332 self.frame_state.rendered_frame.hitboxes
4333 [range.start.hitboxes_index..range.end.hitboxes_index]
4334 .iter()
4335 .cloned(),
4336 );
4337 self.frame_state.next_frame.hitbox_filters.extend(
4338 self.frame_state.rendered_frame.hitbox_filters
4339 [range.start.hitboxes_index..range.end.hitboxes_index]
4340 .iter()
4341 .cloned(),
4342 );
4343 self.frame_state.next_frame.tooltip_requests.extend(
4344 self.frame_state.rendered_frame.tooltip_requests
4345 [range.start.tooltips_index..range.end.tooltips_index]
4346 .iter_mut()
4347 .map(|request| request.take()),
4348 );
4349 self.frame_state.next_frame.accessed_element_states.extend(
4350 self.frame_state.rendered_frame.accessed_element_states[range
4351 .start
4352 .accessed_element_states_index
4353 ..range.end.accessed_element_states_index]
4354 .iter()
4355 .map(|(id, type_id)| (id.clone(), *type_id)),
4356 );
4357 self.core
4358 .text_system
4359 .reuse_layouts(range.start.line_layout_index..range.end.line_layout_index);
4360
4361 let reused_subtree = self.frame_state.next_frame.dispatch_tree.reuse_subtree(
4362 range.start.dispatch_tree_index..range.end.dispatch_tree_index,
4363 &mut self.frame_state.rendered_frame.dispatch_tree,
4364 self.core.focus,
4365 );
4366
4367 if reused_subtree.contains_focus() {
4368 self.frame_state.next_frame.focus = self.core.focus;
4369 }
4370
4371 self.frame_state.next_frame.deferred_draws.extend(
4372 self.frame_state.rendered_frame.deferred_draws
4373 [range.start.deferred_draws_index..range.end.deferred_draws_index]
4374 .iter()
4375 .map(|deferred_draw| DeferredDraw {
4376 current_view: deferred_draw.current_view,
4377 parent_node: reused_subtree.refresh_node_id(deferred_draw.parent_node),
4378 element_id_stack: deferred_draw.element_id_stack.clone(),
4379 text_style_stack: deferred_draw.text_style_stack.clone(),
4380 content_mask: deferred_draw.content_mask,
4381 rem_size: deferred_draw.rem_size,
4382 priority: deferred_draw.priority,
4383 element: None,
4384 absolute_offset: deferred_draw.absolute_offset,
4385 prepaint_range: deferred_draw.prepaint_range.clone(),
4386 paint_range: deferred_draw.paint_range.clone(),
4387 }),
4388 );
4389 }
4390
4391 pub(crate) fn paint_index(&self) -> PaintIndex {
4392 PaintIndex {
4393 scene_index: self.frame_state.next_frame.scene.len(),
4394 #[cfg(any(test, feature = "test-support"))]
4395 debug_bounds_index: self.frame_state.next_frame.debug_bounds_records.len(),
4396 mouse_listeners_index: self.frame_state.next_frame.mouse_listeners.len(),
4397 input_handlers_index: self.frame_state.next_frame.input_handlers.len(),
4398 cursor_styles_index: self.frame_state.next_frame.cursor_styles.len(),
4399 accessed_element_states_index: self
4400 .frame_state
4401 .next_frame
4402 .accessed_element_states
4403 .len(),
4404 tab_handle_index: self.frame_state.next_frame.tab_stops.paint_index(),
4405 line_layout_index: self.core.text_system.layout_index(),
4406 }
4407 }
4408
4409 pub(crate) fn reuse_paint(&mut self, range: Range<PaintIndex>) {
4410 #[cfg(any(test, feature = "test-support"))]
4412 for (selector, bounds) in &self.frame_state.rendered_frame.debug_bounds_records
4413 [range.start.debug_bounds_index..range.end.debug_bounds_index]
4414 {
4415 self.frame_state
4416 .next_frame
4417 .record_debug_bounds(selector.clone(), *bounds);
4418 }
4419 self.frame_state.next_frame.cursor_styles.extend(
4420 self.frame_state.rendered_frame.cursor_styles
4421 [range.start.cursor_styles_index..range.end.cursor_styles_index]
4422 .iter()
4423 .cloned(),
4424 );
4425 self.frame_state.next_frame.input_handlers.extend(
4426 self.frame_state.rendered_frame.input_handlers
4427 [range.start.input_handlers_index..range.end.input_handlers_index]
4428 .iter_mut()
4429 .map(|handler| handler.take()),
4430 );
4431 self.frame_state.next_frame.mouse_listeners.extend(
4432 self.frame_state.rendered_frame.mouse_listeners
4433 [range.start.mouse_listeners_index..range.end.mouse_listeners_index]
4434 .iter_mut()
4435 .map(|listener| listener.take()),
4436 );
4437 self.frame_state.next_frame.accessed_element_states.extend(
4438 self.frame_state.rendered_frame.accessed_element_states[range
4439 .start
4440 .accessed_element_states_index
4441 ..range.end.accessed_element_states_index]
4442 .iter()
4443 .map(|(id, type_id)| (id.clone(), *type_id)),
4444 );
4445 self.frame_state.next_frame.tab_stops.replay(
4446 &self.frame_state.rendered_frame.tab_stops.insertion_history
4447 [range.start.tab_handle_index..range.end.tab_handle_index],
4448 );
4449
4450 self.core
4451 .text_system
4452 .reuse_layouts(range.start.line_layout_index..range.end.line_layout_index);
4453 self.frame_state.next_frame.scene.replay(
4454 range.start.scene_index..range.end.scene_index,
4455 &self.frame_state.rendered_frame.scene,
4456 );
4457 }
4458
4459 pub fn with_text_style<F, R>(&mut self, style: Option<TextStyleRefinement>, f: F) -> R
4463 where
4464 F: FnOnce(&mut Self) -> R,
4465 {
4466 self.core.invalidator.debug_assert_paint_or_prepaint();
4467 if let Some(style) = style {
4468 self.frame_state.text_style_stack.push(style);
4469 let result = f(self);
4470 self.frame_state.text_style_stack.pop();
4471 result
4472 } else {
4473 f(self)
4474 }
4475 }
4476
4477 pub fn set_cursor_style(&mut self, style: CursorStyle, hitbox: &Hitbox) {
4480 self.core.invalidator.debug_assert_paint();
4481 self.frame_state
4482 .next_frame
4483 .cursor_styles
4484 .push(CursorStyleRequest {
4485 hitbox_id: Some(hitbox.id),
4486 style,
4487 });
4488 }
4489
4490 pub fn set_window_cursor_style(&mut self, style: CursorStyle) {
4495 self.core.invalidator.debug_assert_paint();
4496 self.frame_state
4497 .next_frame
4498 .cursor_styles
4499 .push(CursorStyleRequest {
4500 hitbox_id: None,
4501 style,
4502 })
4503 }
4504
4505 pub fn set_tooltip(&mut self, tooltip: AnyTooltip) -> TooltipId {
4508 self.core.invalidator.debug_assert_prepaint();
4509 let id = TooltipId(post_inc(&mut self.frame_state.next_tooltip_id.0));
4510 self.frame_state
4511 .next_frame
4512 .tooltip_requests
4513 .push(Some(TooltipRequest { id, tooltip }));
4514 id
4515 }
4516
4517 #[inline]
4522 pub fn with_content_mask<R>(
4523 &mut self,
4524 mask: Option<ContentMask<Pixels>>,
4525 f: impl FnOnce(&mut Self) -> R,
4526 ) -> R {
4527 self.core.invalidator.debug_assert_paint_or_prepaint();
4528 if let Some(mask) = mask {
4529 let mask = mask.intersect(&self.content_mask());
4530 self.frame_state.content_mask_stack.push(mask);
4531 let result = f(self);
4532 self.frame_state.content_mask_stack.pop();
4533 result
4534 } else {
4535 f(self)
4536 }
4537 }
4538
4539 pub fn with_element_offset<R>(
4542 &mut self,
4543 offset: Point<Pixels>,
4544 f: impl FnOnce(&mut Self) -> R,
4545 ) -> R {
4546 self.core.invalidator.debug_assert_prepaint();
4547
4548 if offset.is_zero() {
4549 return f(self);
4550 };
4551
4552 let abs_offset = self.element_offset() + offset;
4553 self.with_absolute_element_offset(abs_offset, f)
4554 }
4555
4556 pub fn with_absolute_element_offset<R>(
4560 &mut self,
4561 offset: Point<Pixels>,
4562 f: impl FnOnce(&mut Self) -> R,
4563 ) -> R {
4564 self.core.invalidator.debug_assert_prepaint();
4565 self.frame_state.element_offset_stack.push(offset);
4566 let result = f(self);
4567 self.frame_state.element_offset_stack.pop();
4568 result
4569 }
4570
4571 pub(crate) fn with_element_opacity<R>(
4572 &mut self,
4573 opacity: Option<f32>,
4574 f: impl FnOnce(&mut Self) -> R,
4575 ) -> R {
4576 self.core.invalidator.debug_assert_paint_or_prepaint();
4577
4578 let Some(opacity) = opacity else {
4579 return f(self);
4580 };
4581
4582 let previous_opacity = self.frame_state.element_opacity;
4583 self.frame_state.element_opacity = previous_opacity * opacity;
4584 let result = f(self);
4585 self.frame_state.element_opacity = previous_opacity;
4586 result
4587 }
4588
4589 pub fn transact<T, U>(&mut self, f: impl FnOnce(&mut Self) -> Result<T, U>) -> Result<T, U> {
4595 self.core.invalidator.debug_assert_prepaint();
4596 let index = self.prepaint_index();
4597 let result = f(self);
4598 if result.is_err() {
4599 self.frame_state
4600 .next_frame
4601 .hitboxes
4602 .truncate(index.hitboxes_index);
4603 self.frame_state
4604 .next_frame
4605 .tooltip_requests
4606 .truncate(index.tooltips_index);
4607 self.frame_state
4608 .next_frame
4609 .deferred_draws
4610 .truncate(index.deferred_draws_index);
4611 self.frame_state
4612 .next_frame
4613 .dispatch_tree
4614 .truncate(index.dispatch_tree_index);
4615 self.frame_state
4616 .next_frame
4617 .accessed_element_states
4618 .truncate(index.accessed_element_states_index);
4619 self.core
4620 .text_system
4621 .truncate_layouts(index.line_layout_index);
4622 }
4623 result
4624 }
4625
4626 pub fn request_autoscroll(&mut self, bounds: Bounds<Pixels>) {
4632 self.core.invalidator.debug_assert_prepaint();
4633 self.frame_state.requested_autoscroll = Some(bounds);
4634 }
4635
4636 pub fn take_autoscroll(&mut self) -> Option<Bounds<Pixels>> {
4639 self.core.invalidator.debug_assert_prepaint();
4640 self.frame_state.requested_autoscroll.take()
4641 }
4642
4643 pub fn use_asset<A: Asset>(&mut self, source: &A::Source, cx: &mut App) -> Option<A::Output> {
4649 cx.asset_entry::<A>(source).use_by(self.current_view())
4650 }
4651
4652 pub fn get_asset<A: Asset>(&mut self, source: &A::Source, cx: &mut App) -> Option<A::Output> {
4658 cx.fetch_asset::<A>(source)
4659 }
4660 pub fn element_offset(&self) -> Point<Pixels> {
4663 self.core.invalidator.debug_assert_prepaint();
4664 self.frame_state
4665 .element_offset_stack
4666 .last()
4667 .copied()
4668 .unwrap_or_default()
4669 }
4670
4671 #[inline]
4674 pub(crate) fn element_opacity(&self) -> f32 {
4675 self.core.invalidator.debug_assert_paint_or_prepaint();
4676 self.frame_state.element_opacity
4677 }
4678
4679 pub fn content_mask(&self) -> ContentMask<Pixels> {
4681 self.core.invalidator.debug_assert_paint_or_prepaint();
4682 self.frame_state
4683 .content_mask_stack
4684 .last()
4685 .cloned()
4686 .unwrap_or_else(|| ContentMask {
4687 bounds: Bounds {
4688 origin: Point::default(),
4689 size: self.core.viewport_size,
4690 },
4691 })
4692 }
4693
4694 pub fn with_element_namespace<R>(
4697 &mut self,
4698 element_id: impl Into<ElementId>,
4699 f: impl FnOnce(&mut Self) -> R,
4700 ) -> R {
4701 self.frame_state.element_id_stack.push(element_id.into());
4702 let result = f(self);
4703 self.frame_state.element_id_stack.pop();
4704 result
4705 }
4706
4707 pub fn use_keyed_state<S: 'static>(
4709 &mut self,
4710 key: impl Into<ElementId>,
4711 cx: &mut App,
4712 init: impl FnOnce(&mut Self, &mut Context<S>) -> S,
4713 ) -> Entity<S> {
4714 let current_view = self.current_view();
4715 self.with_global_id(key.into(), |global_id, window| {
4716 window.with_element_state(global_id, |state: Option<Entity<S>>, window| {
4717 if let Some(state) = state {
4718 (state.clone(), state)
4719 } else {
4720 let new_state = cx.new(|cx| init(window, cx));
4721 Self::observe_keyed_state(&new_state, current_view, cx);
4722 (new_state.clone(), new_state)
4723 }
4724 })
4725 })
4726 }
4727
4728 #[track_caller]
4734 pub fn use_state<S: 'static>(
4735 &mut self,
4736 cx: &mut App,
4737 init: impl FnOnce(&mut Self, &mut Context<S>) -> S,
4738 ) -> Entity<S> {
4739 self.use_keyed_state(
4740 ElementId::CodeLocation(*core::panic::Location::caller()),
4741 cx,
4742 init,
4743 )
4744 }
4745
4746 #[inline(always)]
4751 pub fn with_element_state<S, R>(
4752 &mut self,
4753 global_id: &GlobalElementId,
4754 f: impl FnOnce(Option<S>, &mut Self) -> (R, S),
4755 ) -> R
4756 where
4757 S: 'static,
4758 {
4759 self.core.invalidator.debug_assert_paint_or_prepaint();
4760
4761 let (key, state) = self.take_element_state(global_id, TypeId::of::<S>());
4762
4763 if let Some(any) = state {
4764 let ElementStateBox {
4765 inner,
4766 #[cfg(debug_assertions)]
4767 type_name,
4768 } = any;
4769 let mut state_box = inner
4771 .downcast::<Option<S>>()
4772 .map_err(|_| {
4773 #[cfg(debug_assertions)]
4774 {
4775 anyhow::anyhow!(
4776 "invalid element state type for id, requested {:?}, actual: {:?}",
4777 std::any::type_name::<S>(),
4778 type_name
4779 )
4780 }
4781
4782 #[cfg(not(debug_assertions))]
4783 {
4784 anyhow::anyhow!(
4785 "invalid element state type for id, requested {:?}",
4786 std::any::type_name::<S>(),
4787 )
4788 }
4789 })
4790 .unwrap();
4791
4792 let state = state_box.take().expect(
4793 "reentrant call to with_element_state for the same state type and element id",
4794 );
4795 let (result, state) = f(Some(state), self);
4796 state_box.replace(state);
4797 self.insert_element_state(
4798 key,
4799 ElementStateBox {
4800 inner: state_box,
4801 #[cfg(debug_assertions)]
4802 type_name,
4803 },
4804 );
4805 result
4806 } else {
4807 let (result, state) = f(None, self);
4808 self.insert_element_state(
4809 key,
4810 ElementStateBox {
4811 inner: Box::new(Some(state)),
4812 #[cfg(debug_assertions)]
4813 type_name: std::any::type_name::<S>(),
4814 },
4815 );
4816 result
4817 }
4818 }
4819
4820 pub fn with_optional_element_state<S, R>(
4827 &mut self,
4828 global_id: Option<&GlobalElementId>,
4829 f: impl FnOnce(Option<Option<S>>, &mut Self) -> (R, Option<S>),
4830 ) -> R
4831 where
4832 S: 'static,
4833 {
4834 self.core.invalidator.debug_assert_paint_or_prepaint();
4835
4836 if let Some(global_id) = global_id {
4837 self.with_element_state(global_id, |state, cx| {
4838 let (result, state) = f(Some(state), cx);
4839 let state =
4840 state.expect("you must return some state when you pass some element id");
4841 (result, state)
4842 })
4843 } else {
4844 let (result, state) = f(None, self);
4845 debug_assert!(
4846 state.is_none(),
4847 "you must not return an element state when passing None for the global id"
4848 );
4849 result
4850 }
4851 }
4852
4853 #[inline]
4855 pub fn with_tab_group<R>(&mut self, index: Option<isize>, f: impl FnOnce(&mut Self) -> R) -> R {
4856 if let Some(index) = index {
4857 self.frame_state.next_frame.tab_stops.begin_group(index);
4858 let result = f(self);
4859 self.frame_state.next_frame.tab_stops.end_group();
4860 result
4861 } else {
4862 f(self)
4863 }
4864 }
4865
4866 pub fn defer_draw(
4875 &mut self,
4876 element: AnyElement,
4877 absolute_offset: Point<Pixels>,
4878 priority: usize,
4879 content_mask: Option<ContentMask<Pixels>>,
4880 ) {
4881 self.core.invalidator.debug_assert_prepaint();
4882 let parent_node = self
4883 .frame_state
4884 .next_frame
4885 .dispatch_tree
4886 .active_node_id()
4887 .unwrap();
4888 self.frame_state
4889 .next_frame
4890 .deferred_draws
4891 .push(DeferredDraw {
4892 current_view: self.current_view(),
4893 parent_node,
4894 element_id_stack: self.frame_state.element_id_stack.clone(),
4895 text_style_stack: self.frame_state.text_style_stack.clone(),
4896 content_mask,
4897 rem_size: self.rem_size(),
4898 priority,
4899 element: Some(element),
4900 absolute_offset,
4901 prepaint_range: PrepaintStateIndex::default()..PrepaintStateIndex::default(),
4902 paint_range: PaintIndex::default()..PaintIndex::default(),
4903 });
4904 }
4905
4906 pub fn paint_layer<R>(&mut self, bounds: Bounds<Pixels>, f: impl FnOnce(&mut Self) -> R) -> R {
4912 self.core.invalidator.debug_assert_paint();
4913
4914 let content_mask = self.content_mask();
4915 let clipped_bounds = bounds.intersect(&content_mask.bounds);
4916 if !clipped_bounds.is_empty() {
4917 self.frame_state
4918 .next_frame
4919 .scene
4920 .push_layer(self.cover_bounds(clipped_bounds));
4921 }
4922
4923 let result = f(self);
4924
4925 if !clipped_bounds.is_empty() {
4926 self.frame_state.next_frame.scene.pop_layer();
4927 }
4928
4929 result
4930 }
4931
4932 pub fn paint_drop_shadows(
4939 &mut self,
4940 bounds: Bounds<Pixels>,
4941 corner_radii: Corners<Pixels>,
4942 shadows: &[BoxShadow],
4943 ) {
4944 self.core.invalidator.debug_assert_paint();
4945
4946 let scale_factor = self.scale_factor();
4947 let content_mask = self.snapped_content_mask();
4948 let opacity = self.element_opacity();
4949 let element_bounds = self.cover_bounds(bounds);
4950 let element_corner_radii = corner_radii.scale(scale_factor);
4951 for shadow in shadows {
4952 if shadow.inset {
4953 continue;
4954 }
4955 let shadow_bounds = (bounds + shadow.offset).dilate(shadow.spread_radius);
4956 self.frame_state.next_frame.scene.insert_primitive(Shadow {
4957 order: 0,
4958 blur_radius: shadow.blur_radius.scale(scale_factor),
4959 bounds: self.cover_bounds(shadow_bounds),
4960 content_mask,
4961 corner_radii: corner_radii.scale(scale_factor),
4962 color: shadow.color.opacity(opacity),
4963 element_bounds,
4964 element_corner_radii,
4965 inset: 0,
4966 pad: 0,
4967 });
4968 }
4969 }
4970
4971 pub fn paint_inset_shadows(
4976 &mut self,
4977 bounds: Bounds<Pixels>,
4978 corner_radii: Corners<Pixels>,
4979 shadows: &[BoxShadow],
4980 ) {
4981 self.core.invalidator.debug_assert_paint();
4982
4983 let scale_factor = self.scale_factor();
4984 let content_mask = self.snapped_content_mask();
4985 let opacity = self.element_opacity();
4986 let element_bounds = self.cover_bounds(bounds);
4987 let element_corner_radii = corner_radii.scale(scale_factor);
4988 for shadow in shadows {
4989 if !shadow.inset {
4990 continue;
4991 }
4992 let hole = (bounds + shadow.offset).dilate(-shadow.spread_radius);
4993 let zero = Pixels::ZERO;
4996 let hole_corner_radii = Corners {
4997 top_left: (corner_radii.top_left - shadow.spread_radius).max(zero),
4998 top_right: (corner_radii.top_right - shadow.spread_radius).max(zero),
4999 bottom_right: (corner_radii.bottom_right - shadow.spread_radius).max(zero),
5000 bottom_left: (corner_radii.bottom_left - shadow.spread_radius).max(zero),
5001 };
5002 self.frame_state.next_frame.scene.insert_primitive(Shadow {
5003 order: 0,
5004 blur_radius: shadow.blur_radius.scale(scale_factor),
5005 bounds: self.cover_bounds(hole),
5006 content_mask,
5007 corner_radii: hole_corner_radii.scale(scale_factor),
5008 color: shadow.color.opacity(opacity),
5009 element_bounds,
5010 element_corner_radii,
5011 inset: 1,
5012 pad: 0,
5013 });
5014 }
5015 }
5016
5017 fn largest_border_interior(quad: &Quad) -> Bounds<ScaledPixels> {
5018 let radii = &quad.corner_radii;
5019 let widths = &quad.border_widths;
5020 let edge_radii = Edges {
5021 top: radii.top_left.max(radii.top_right),
5022 right: radii.top_right.max(radii.bottom_right),
5023 bottom: radii.bottom_left.max(radii.bottom_right),
5024 left: radii.top_left.max(radii.bottom_left),
5025 };
5026
5027 let antialias_inset = point(ScaledPixels(1.0), ScaledPixels(1.0));
5028 let inset_bounds = |top_left_inset, bottom_right_inset| {
5029 Bounds::from_corners(
5030 quad.bounds.origin + top_left_inset + antialias_inset,
5031 quad.bounds.bottom_right() - bottom_right_inset - antialias_inset,
5032 )
5033 };
5034
5035 let horizontal_band = inset_bounds(
5038 point(widths.left, widths.top.max(edge_radii.top)),
5039 point(widths.right, widths.bottom.max(edge_radii.bottom)),
5040 );
5041 let vertical_band = inset_bounds(
5042 point(widths.left.max(edge_radii.left), widths.top),
5043 point(widths.right.max(edge_radii.right), widths.bottom),
5044 );
5045
5046 let area = |bounds: &Bounds<ScaledPixels>| {
5047 bounds.size.width.0.max(0.) * bounds.size.height.0.max(0.)
5048 };
5049 if area(&horizontal_band) >= area(&vertical_band) {
5050 horizontal_band
5051 } else {
5052 vertical_band
5053 }
5054 }
5055
5056 pub(crate) fn paint_imported_texture(
5063 &mut self,
5064 handle: ImportedTextureHandle,
5065 bounds: Bounds<Pixels>,
5066 corner_radii: Corners<Pixels>,
5067 opacity: f32,
5068 flip_v: bool,
5069 ) {
5070 self.core.invalidator.debug_assert_paint();
5071
5072 let primitive = CustomRenderPrimitive::Texture {
5073 order: 0,
5074 handle,
5075 bounds: self.snap_bounds(bounds),
5076 content_mask: self.snapped_content_mask(),
5077 radii: corner_radii.scale(self.scale_factor()),
5078 opacity: opacity * self.element_opacity(),
5079 flip_v,
5080 };
5081 self.frame_state
5082 .next_frame
5083 .scene
5084 .insert_primitive(primitive);
5085 }
5086
5087 pub fn paint_quad(&mut self, quad: PaintQuad) {
5098 self.core.invalidator.debug_assert_paint();
5099
5100 let opacity = self.element_opacity();
5101 let snapped_bounds = self.snap_bounds(quad.bounds);
5102 let snapped_border_widths = self.snap_border_widths(quad.border_widths);
5103 let quad = Quad {
5104 order: 0,
5105 bounds: snapped_bounds,
5106 content_mask: self.snapped_content_mask(),
5107 background: quad.background.opacity(opacity),
5108 border_color: quad.border_color.opacity(opacity),
5109 corner_radii: quad.corner_radii.scale(self.scale_factor()),
5110 border_widths: snapped_border_widths,
5111 border_style: quad.border_style,
5112 };
5113
5114 if !quad.background.is_transparent() {
5115 self.frame_state.next_frame.scene.insert_primitive(quad);
5116 return;
5117 }
5118
5119 let outer_bounds = quad.bounds;
5122 let inner_bounds = Self::largest_border_interior(&quad);
5123
5124 if inner_bounds.is_empty() {
5125 self.frame_state.next_frame.scene.insert_primitive(quad);
5126 return;
5127 }
5128
5129 let strips = [
5130 Bounds::from_corners(
5132 outer_bounds.origin,
5133 point(outer_bounds.right(), inner_bounds.top()),
5134 ),
5135 Bounds::from_corners(
5137 point(outer_bounds.left(), inner_bounds.bottom()),
5138 outer_bounds.bottom_right(),
5139 ),
5140 Bounds::from_corners(
5142 point(outer_bounds.left(), inner_bounds.top()),
5143 inner_bounds.bottom_left(),
5144 ),
5145 Bounds::from_corners(
5147 inner_bounds.top_right(),
5148 point(outer_bounds.right(), inner_bounds.bottom()),
5149 ),
5150 ];
5151
5152 for strip in strips {
5153 let content_mask_bounds = quad.content_mask.bounds.intersect(&strip);
5154 if !content_mask_bounds.is_empty() {
5155 self.frame_state.next_frame.scene.insert_primitive(Quad {
5156 content_mask: ContentMask {
5157 bounds: content_mask_bounds,
5158 },
5159 ..quad
5160 });
5161 }
5162 }
5163 }
5164
5165 pub fn paint_path(&mut self, mut path: Path<Pixels>, color: impl Into<Background>) {
5170 self.core.invalidator.debug_assert_paint();
5171
5172 let scale_factor = self.scale_factor();
5173 let content_mask = self.content_mask();
5174 let opacity = self.element_opacity();
5175 path.content_mask = content_mask;
5176 let color: Background = color.into();
5177 path.color = color.opacity(opacity);
5178 self.frame_state
5179 .next_frame
5180 .scene
5181 .insert_primitive(path.scale(scale_factor));
5182 }
5183
5184 pub fn paint_underline(
5188 &mut self,
5189 origin: Point<Pixels>,
5190 width: Pixels,
5191 style: &UnderlineStyle,
5192 ) {
5193 self.core.invalidator.debug_assert_paint();
5194 let underline = self.underline(origin, width, style);
5195 self.frame_state
5196 .next_frame
5197 .scene
5198 .insert_primitive(underline);
5199 }
5200
5201 pub fn paint_strikethrough(
5205 &mut self,
5206 origin: Point<Pixels>,
5207 width: Pixels,
5208 style: &StrikethroughStyle,
5209 ) {
5210 self.core.invalidator.debug_assert_paint();
5211
5212 let scale_factor = self.scale_factor();
5213 let height = style.thickness;
5214 let bounds = Bounds {
5215 origin: origin.map(|c| ScaledPixels(round_to_device_pixel(c.0, scale_factor))),
5216 size: size(self.snap_stroke(width), self.snap_stroke(height)),
5217 };
5218 let opacity = self.element_opacity();
5219
5220 self.frame_state
5221 .next_frame
5222 .scene
5223 .insert_primitive(Underline {
5224 order: 0,
5225 pad: 0,
5226 bounds,
5227 content_mask: self.snapped_content_mask(),
5228 thickness: self.snap_stroke(style.thickness),
5229 color: style.color.unwrap_or_default().opacity(opacity),
5230 wavy: false.into(),
5231 });
5232 }
5233
5234 pub fn paint_glyph(
5243 &mut self,
5244 origin: Point<Pixels>,
5245 font_id: FontId,
5246 glyph_id: GlyphId,
5247 font_size: Pixels,
5248 color: Hsla,
5249 ) -> Result<()> {
5250 self.core.invalidator.debug_assert_paint();
5251
5252 let element_opacity = self.element_opacity();
5253 let scale_factor = self.scale_factor();
5254 let glyph_origin = origin.scale(scale_factor);
5255
5256 let quantized_origin = Point::new(
5257 round_half_toward_zero(glyph_origin.x.0 * SUBPIXEL_VARIANTS_X as f32)
5258 / SUBPIXEL_VARIANTS_X as f32,
5259 round_half_toward_zero(glyph_origin.y.0 * SUBPIXEL_VARIANTS_Y as f32)
5260 / SUBPIXEL_VARIANTS_Y as f32,
5261 );
5262 let subpixel_variant = Point::new(
5263 (quantized_origin.x.fract() * SUBPIXEL_VARIANTS_X as f32) as u8,
5264 (quantized_origin.y.fract() * SUBPIXEL_VARIANTS_Y as f32) as u8,
5265 );
5266 let integer_origin = quantized_origin.map(|c| ScaledPixels(c.trunc()));
5267 let subpixel_rendering = self.should_use_subpixel_rendering(font_id, font_size);
5268 let dilation = self.text_system().glyph_dilation_for_color(color);
5269 let params = RenderGlyphParams {
5270 font_id,
5271 glyph_id,
5272 font_size,
5273 subpixel_variant,
5274 scale_factor,
5275 is_emoji: false,
5276 subpixel_rendering,
5277 dilation,
5278 };
5279
5280 let raster_bounds = self.text_system().raster_bounds(¶ms)?;
5281 if !raster_bounds.is_zero() {
5282 let tile = self
5283 .core
5284 .sprite_atlas
5285 .get_or_insert_with(params.clone().into(), &mut || {
5286 let (size, bytes) = self.text_system().rasterize_glyph(¶ms)?;
5287 Ok(Some((size, Cow::Owned(bytes))))
5288 })?
5289 .expect("Callback above only errors or returns Some");
5290 let bounds = Bounds {
5291 origin: integer_origin + raster_bounds.origin.map(Into::into),
5292 size: tile.bounds.size.map(Into::into),
5293 };
5294 let content_mask = self.snapped_content_mask();
5295
5296 if subpixel_rendering {
5297 self.frame_state
5298 .next_frame
5299 .scene
5300 .insert_primitive(SubpixelSprite {
5301 order: 0,
5302 pad: 0,
5303 bounds,
5304 content_mask,
5305 color: color.opacity(element_opacity),
5306 tile,
5307 transformation: TransformationMatrix::unit(),
5308 });
5309 } else {
5310 self.frame_state
5311 .next_frame
5312 .scene
5313 .insert_primitive(MonochromeSprite {
5314 order: 0,
5315 pad: 0,
5316 bounds,
5317 content_mask,
5318 color: color.opacity(element_opacity),
5319 tile,
5320 transformation: TransformationMatrix::unit(),
5321 });
5322 }
5323 }
5324 Ok(())
5325 }
5326
5327 fn should_use_subpixel_rendering(&self, font_id: FontId, font_size: Pixels) -> bool {
5328 if self.core.platform_window.background_appearance() != WindowBackgroundAppearance::Opaque {
5329 return false;
5330 }
5331
5332 if !self.core.platform_window.is_subpixel_rendering_supported() {
5333 return false;
5334 }
5335
5336 let mode = match self.core.text_rendering_mode.get() {
5337 TextRenderingMode::PlatformDefault => self
5338 .text_system()
5339 .recommended_rendering_mode(font_id, font_size),
5340 mode => mode,
5341 };
5342
5343 mode == TextRenderingMode::Subpixel
5344 }
5345
5346 pub fn paint_emoji(
5355 &mut self,
5356 origin: Point<Pixels>,
5357 font_id: FontId,
5358 glyph_id: GlyphId,
5359 font_size: Pixels,
5360 ) -> Result<()> {
5361 self.core.invalidator.debug_assert_paint();
5362
5363 let scale_factor = self.scale_factor();
5364 let glyph_origin = origin.scale(scale_factor);
5365 let integer_origin = glyph_origin.map(|c| ScaledPixels(round_half_toward_zero(c.0)));
5366 let params = RenderGlyphParams {
5367 font_id,
5368 glyph_id,
5369 font_size,
5370 subpixel_variant: Default::default(),
5371 scale_factor,
5372 is_emoji: true,
5373 subpixel_rendering: false,
5374 dilation: 0,
5375 };
5376
5377 let raster_bounds = self.text_system().raster_bounds(¶ms)?;
5378 if !raster_bounds.is_zero() {
5379 let tile = self
5380 .core
5381 .sprite_atlas
5382 .get_or_insert_with(params.clone().into(), &mut || {
5383 let (size, bytes) = self.text_system().rasterize_glyph(¶ms)?;
5384 Ok(Some((size, Cow::Owned(bytes))))
5385 })?
5386 .expect("Callback above only errors or returns Some");
5387
5388 let bounds = Bounds {
5389 origin: integer_origin + raster_bounds.origin.map(Into::into),
5390 size: tile.bounds.size.map(Into::into),
5391 };
5392 let content_mask = self.snapped_content_mask();
5393 let opacity = self.element_opacity();
5394
5395 self.frame_state
5396 .next_frame
5397 .scene
5398 .insert_primitive(PolychromeSprite {
5399 order: 0,
5400 pad: 0,
5401 grayscale: false.into(),
5402 bounds,
5403 corner_radii: Default::default(),
5404 content_mask,
5405 tile,
5406 opacity,
5407 });
5408 }
5409 Ok(())
5410 }
5411
5412 pub fn paint_svg(
5416 &mut self,
5417 bounds: Bounds<Pixels>,
5418 path: SharedString,
5419 mut data: Option<&[u8]>,
5420 transformation: TransformationMatrix,
5421 color: Hsla,
5422 cx: &App,
5423 ) -> Result<()> {
5424 self.core.invalidator.debug_assert_paint();
5425
5426 let element_opacity = self.element_opacity();
5427 let bounds = self.snap_bounds(bounds);
5428
5429 let params = RenderSvgParams {
5430 path,
5431 size: bounds.size.map(|pixels| {
5432 DevicePixels::from((pixels.0 * SMOOTH_SVG_SCALE_FACTOR).ceil() as i32)
5433 }),
5434 };
5435
5436 let Some(tile) =
5437 self.core
5438 .sprite_atlas
5439 .get_or_insert_with(params.clone().into(), &mut || {
5440 let Some((size, bytes)) = cx.svg_renderer.render_alpha_mask(¶ms, data)?
5441 else {
5442 return Ok(None);
5443 };
5444 Ok(Some((size, Cow::Owned(bytes))))
5445 })?
5446 else {
5447 return Ok(());
5448 };
5449 let content_mask = self.snapped_content_mask();
5450 let svg_bounds = Bounds {
5451 origin: bounds.center()
5452 - Point::new(
5453 ScaledPixels(tile.bounds.size.width.0 as f32 / SMOOTH_SVG_SCALE_FACTOR / 2.),
5454 ScaledPixels(tile.bounds.size.height.0 as f32 / SMOOTH_SVG_SCALE_FACTOR / 2.),
5455 ),
5456 size: tile
5457 .bounds
5458 .size
5459 .map(|value| ScaledPixels(value.0 as f32 / SMOOTH_SVG_SCALE_FACTOR)),
5460 };
5461 let final_bounds = svg_bounds
5462 .map_origin(|value| ScaledPixels(round_half_toward_zero(value.0)))
5463 .map_size(|size| size.ceil());
5464
5465 self.frame_state
5466 .next_frame
5467 .scene
5468 .insert_primitive(MonochromeSprite {
5469 order: 0,
5470 pad: 0,
5471 bounds: final_bounds,
5472 content_mask,
5473 color: color.opacity(element_opacity),
5474 tile,
5475 transformation,
5476 });
5477
5478 Ok(())
5479 }
5480
5481 pub fn paint_image(
5491 &mut self,
5492 bounds: Bounds<Pixels>,
5493 image_bounds: Bounds<Pixels>,
5494 corner_radii: Corners<Pixels>,
5495 data: Arc<RenderImage>,
5496 frame_index: usize,
5497 grayscale: bool,
5498 ) -> Result<()> {
5499 self.core.invalidator.debug_assert_paint();
5500
5501 let visible_bounds = bounds.intersect(&image_bounds);
5502 if visible_bounds.size.width <= Pixels::ZERO || visible_bounds.size.height <= Pixels::ZERO {
5503 return Ok(());
5504 }
5505 if image_bounds.size.width <= Pixels::ZERO || image_bounds.size.height <= Pixels::ZERO {
5506 return Ok(());
5507 }
5508
5509 let params = RenderImageParams {
5510 image_id: data.id,
5511 frame_index,
5512 };
5513
5514 let tile = self
5515 .core
5516 .sprite_atlas
5517 .get_or_insert_with(params.into(), &mut || {
5518 Ok(Some((
5519 data.size(frame_index),
5520 Cow::Borrowed(
5521 data.as_bytes(frame_index)
5522 .expect("It's the caller's job to pass a valid frame index"),
5523 ),
5524 )))
5525 })?
5526 .expect("Callback above only returns Some");
5527
5528 let visible_bounds_snapped = self.snap_bounds(visible_bounds);
5529
5530 let sub_tile = if visible_bounds == image_bounds {
5531 tile
5532 } else {
5533 let x_offset_ratio =
5534 (visible_bounds.origin.x - image_bounds.origin.x) / image_bounds.size.width;
5535 let y_offset_ratio =
5536 (visible_bounds.origin.y - image_bounds.origin.y) / image_bounds.size.height;
5537 let width_ratio = visible_bounds.size.width / image_bounds.size.width;
5538 let height_ratio = visible_bounds.size.height / image_bounds.size.height;
5539
5540 let tile_origin_x = tile.bounds.origin.x.0;
5541 let tile_origin_y = tile.bounds.origin.y.0;
5542 let tile_width = tile.bounds.size.width.0;
5543 let tile_height = tile.bounds.size.height.0;
5544
5545 let sub_origin_x = tile_origin_x + (x_offset_ratio * tile_width as f32).round() as i32;
5546 let sub_origin_y = tile_origin_y + (y_offset_ratio * tile_height as f32).round() as i32;
5547 let sub_width = (width_ratio * tile_width as f32).round() as i32;
5548 let sub_height = (height_ratio * tile_height as f32).round() as i32;
5549
5550 let max_x = tile_origin_x + tile_width;
5551 let max_y = tile_origin_y + tile_height;
5552
5553 let clamped_origin_x = sub_origin_x.clamp(tile_origin_x, max_x);
5554 let clamped_origin_y = sub_origin_y.clamp(tile_origin_y, max_y);
5555 let clamped_width = sub_width.min(max_x - clamped_origin_x).max(0);
5556 let clamped_height = sub_height.min(max_y - clamped_origin_y).max(0);
5557
5558 AtlasTile {
5559 bounds: Bounds {
5560 origin: point(
5561 DevicePixels(clamped_origin_x),
5562 DevicePixels(clamped_origin_y),
5563 ),
5564 size: size(DevicePixels(clamped_width), DevicePixels(clamped_height)),
5565 },
5566 ..tile
5567 }
5568 };
5569
5570 let content_mask = self.snapped_content_mask();
5571 let corner_radii = corner_radii
5572 .clamp_radii_for_quad_size(visible_bounds.size)
5573 .scale(self.scale_factor());
5574 let opacity = self.element_opacity();
5575
5576 self.frame_state
5577 .next_frame
5578 .scene
5579 .insert_primitive(PolychromeSprite {
5580 order: 0,
5581 pad: 0,
5582 grayscale: grayscale.into(),
5583 bounds: visible_bounds_snapped,
5584 content_mask,
5585 corner_radii,
5586 tile: sub_tile,
5587 opacity,
5588 });
5589 Ok(())
5590 }
5591
5592 pub fn drop_image(&mut self, data: Arc<RenderImage>) -> Result<()> {
5594 for frame_index in 0..data.frame_count() {
5595 let params = RenderImageParams {
5596 image_id: data.id,
5597 frame_index,
5598 };
5599
5600 self.core.sprite_atlas.remove(¶ms.clone().into());
5601 }
5602
5603 Ok(())
5604 }
5605
5606 #[cfg(any(test, feature = "test-support"))]
5608 pub fn has_image_atlas_entry(&self, data: &RenderImage) -> bool {
5609 data.frame_count() > 0
5610 && (0..data.frame_count()).all(|frame_index| {
5611 self.core.sprite_atlas.contains(
5612 &RenderImageParams {
5613 image_id: data.id,
5614 frame_index,
5615 }
5616 .into(),
5617 )
5618 })
5619 }
5620
5621 #[must_use]
5628 pub fn request_layout(
5629 &mut self,
5630 style: Style,
5631 children: impl IntoIterator<Item = LayoutId>,
5632 cx: &mut App,
5633 ) -> LayoutId {
5634 self.core.invalidator.debug_assert_prepaint();
5635
5636 cx.layout_id_buffer.clear();
5637 cx.layout_id_buffer.extend(children);
5638 let rem_size = self.rem_size();
5639 let scale_factor = self.scale_factor();
5640 let engine_style = to_engine_layout_style(&style);
5641
5642 self.frame_state.layout_session.request_layout(
5643 &engine_style,
5644 rem_size,
5645 scale_factor,
5646 &cx.layout_id_buffer,
5647 )
5648 }
5649
5650 pub fn request_measured_layout<F>(&mut self, style: Style, measure: F) -> LayoutId
5660 where
5661 F: Fn(Size<Option<Pixels>>, Size<AvailableSpace>, &mut Window, &mut App) -> Size<Pixels>
5662 + 'static,
5663 {
5664 self.core.invalidator.debug_assert_prepaint();
5665
5666 let rem_size = self.rem_size();
5667 let scale_factor = self.scale_factor();
5668 let engine_style = to_engine_layout_style(&style);
5669 let measure = move |known_dimensions, available_space, context: &mut dyn MeasureContext| {
5670 let MeasureHandles { window, app } = context.handles();
5671 let (core, frame_state) = window;
5672 let core = core
5673 .downcast_mut::<WindowHostCore>()
5674 .expect("measure context window half should be a window's core");
5675 let frame_state = frame_state
5676 .downcast_mut::<WindowFrameState>()
5677 .expect("measure context window half should be a window's frame state");
5678 let cx = app
5679 .downcast_mut::<App>()
5680 .expect("measure context app should be an App");
5681 measure(
5682 known_dimensions,
5683 available_space,
5684 &mut Window { core, frame_state },
5685 cx,
5686 )
5687 };
5688 self.frame_state.layout_session.request_measured_layout(
5689 &engine_style,
5690 rem_size,
5691 scale_factor,
5692 Box::new(measure),
5693 )
5694 }
5695
5696 pub(crate) fn compute_layout(
5702 &mut self,
5703 layout_id: LayoutId,
5704 available_space: Size<AvailableSpace>,
5705 cx: &mut App,
5706 ) {
5707 self.core.invalidator.debug_assert_prepaint();
5708
5709 let scale_factor = self.scale_factor();
5710 let layout_session = self.frame_state.layout_session.clone();
5711 let core = &mut *self.core;
5712 let frame_state = &mut *self.frame_state;
5713 layout_session.compute_layout(
5714 layout_id,
5715 available_space,
5716 scale_factor,
5717 &mut WindowMeasureContext {
5718 core,
5719 frame_state,
5720 cx,
5721 },
5722 );
5723 }
5724
5725 pub fn layout_bounds(&mut self, layout_id: LayoutId) -> Bounds<Pixels> {
5731 self.core.invalidator.debug_assert_prepaint();
5732
5733 let scale_factor = self.scale_factor();
5734 let mut bounds = self
5735 .frame_state
5736 .layout_session
5737 .layout_bounds(layout_id, scale_factor)
5738 .map(Into::into);
5739 let snapped_offset = self.pixel_snap_point(self.element_offset());
5740 bounds.origin += snapped_offset;
5741 bounds
5742 }
5743
5744 pub fn insert_hitbox(&mut self, bounds: Bounds<Pixels>, behavior: HitboxBehavior) -> Hitbox {
5751 self.insert_hitbox_with_filter(bounds, behavior, None)
5752 }
5753
5754 pub fn insert_hitbox_with_filter(
5759 &mut self,
5760 bounds: Bounds<Pixels>,
5761 behavior: HitboxBehavior,
5762 filter: Option<HitboxFilter>,
5763 ) -> Hitbox {
5764 self.core.invalidator.debug_assert_prepaint();
5765
5766 let content_mask = self.content_mask();
5767 let mut id = self.frame_state.next_hitbox_id;
5768 self.frame_state.next_hitbox_id = self.frame_state.next_hitbox_id.next();
5769 let hitbox = Hitbox {
5770 id,
5771 bounds,
5772 content_mask,
5773 behavior,
5774 };
5775 self.frame_state.next_frame.hitboxes.push(hitbox.clone());
5776 self.frame_state.next_frame.hitbox_filters.push(filter);
5777 hitbox
5778 }
5779
5780 pub fn insert_window_control_hitbox(&mut self, area: WindowControlArea, hitbox: Hitbox) {
5784 self.core.invalidator.debug_assert_paint();
5785 self.frame_state
5786 .next_frame
5787 .window_control_hitboxes
5788 .push((area, hitbox));
5789 }
5790
5791 pub fn set_key_context(&mut self, context: KeyContext) {
5796 self.core.invalidator.debug_assert_paint();
5797 self.frame_state
5798 .next_frame
5799 .dispatch_tree
5800 .set_key_context(context);
5801 }
5802
5803 pub fn set_focus_handle(&mut self, focus_handle: &FocusHandle, _: &App) {
5808 self.core.invalidator.debug_assert_prepaint();
5809 if focus_handle.is_focused(self) {
5810 self.frame_state.next_frame.focus = Some(focus_handle.id);
5811 }
5812 self.frame_state
5813 .next_frame
5814 .dispatch_tree
5815 .set_focus_id(focus_handle.id);
5816 }
5817
5818 pub fn set_view_id(&mut self, view_id: EntityId) {
5824 self.core.invalidator.debug_assert_prepaint();
5825 self.frame_state
5826 .next_frame
5827 .dispatch_tree
5828 .set_view_id(view_id);
5829 }
5830
5831 pub fn current_view(&self) -> EntityId {
5833 self.core.invalidator.debug_assert_paint_or_prepaint();
5834 self.frame_state
5835 .rendered_entity_stack
5836 .last()
5837 .copied()
5838 .unwrap()
5839 }
5840
5841 #[inline]
5842 pub(crate) fn with_rendered_view<R>(
5843 &mut self,
5844 id: EntityId,
5845 f: impl FnOnce(&mut Self) -> R,
5846 ) -> R {
5847 self.frame_state.rendered_entity_stack.push(id);
5848 let result = f(self);
5849 self.frame_state.rendered_entity_stack.pop();
5850 result
5851 }
5852
5853 pub fn with_image_cache<F, R>(&mut self, image_cache: Option<AnyImageCache>, f: F) -> R
5855 where
5856 F: FnOnce(&mut Self) -> R,
5857 {
5858 if let Some(image_cache) = image_cache {
5859 self.frame_state.image_cache_stack.push(image_cache);
5860 let result = f(self);
5861 self.frame_state.image_cache_stack.pop();
5862 result
5863 } else {
5864 f(self)
5865 }
5866 }
5867
5868 pub fn handle_input(
5877 &mut self,
5878 focus_handle: &FocusHandle,
5879 input_handler: impl InputHandler,
5880 cx: &App,
5881 ) {
5882 self.core.invalidator.debug_assert_paint();
5883
5884 if focus_handle.is_focused(self) {
5885 let cx = self.to_async(cx);
5886 self.frame_state
5887 .next_frame
5888 .input_handlers
5889 .push(Some(new_platform_input_handler(
5890 cx,
5891 Box::new(input_handler),
5892 )));
5893 }
5894 }
5895
5896 fn apply_text_input_configuration(&mut self, cx: &mut App) {
5901 let configuration = match self.core.platform_window.take_input_handler() {
5902 Some(mut input_handler) => {
5903 let configuration = input_handler.text_input_configuration(self, cx);
5904 self.core.platform_window.set_input_handler(input_handler);
5905 configuration
5906 }
5907 None => TextInputConfiguration::default(),
5908 };
5909 if self.core.last_text_input_configuration.as_ref() != Some(&configuration) {
5910 self.core
5911 .platform_window
5912 .set_text_input_configuration(configuration.clone());
5913 self.core.last_text_input_configuration = Some(configuration);
5914 }
5915 }
5916
5917 pub fn on_mouse_event<Event: MouseEvent>(
5923 &mut self,
5924 mut listener: impl FnMut(&Event, DispatchPhase, &mut Window, &mut App) + 'static,
5925 ) {
5926 self.core.invalidator.debug_assert_paint();
5927
5928 self.frame_state
5929 .next_frame
5930 .mouse_listeners
5931 .push(Some(Box::new(
5932 move |event: &dyn Any, phase: DispatchPhase, window: &mut Window, cx: &mut App| {
5933 if let Some(event) = event.downcast_ref() {
5934 listener(event, phase, window, cx)
5935 }
5936 },
5937 )));
5938 }
5939
5940 pub fn on_key_event<Event: KeyEvent>(
5949 &mut self,
5950 listener: impl Fn(&Event, DispatchPhase, &mut Window, &mut App) + 'static,
5951 ) {
5952 self.core.invalidator.debug_assert_paint();
5953
5954 self.frame_state
5955 .next_frame
5956 .dispatch_tree
5957 .on_key_event(Rc::new(
5958 move |event: &dyn Any, phase, window: &mut Window, cx: &mut App| {
5959 if let Some(event) = event.downcast_ref::<Event>() {
5960 listener(event, phase, window, cx)
5961 }
5962 },
5963 ));
5964 }
5965
5966 pub fn on_modifiers_changed(
5973 &mut self,
5974 listener: impl Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static,
5975 ) {
5976 self.core.invalidator.debug_assert_paint();
5977
5978 self.frame_state
5979 .next_frame
5980 .dispatch_tree
5981 .on_modifiers_changed(Rc::new(
5982 move |event: &ModifiersChangedEvent, window: &mut Window, cx: &mut App| {
5983 listener(event, window, cx)
5984 },
5985 ));
5986 }
5987
5988 pub fn on_focus_in(
5992 &mut self,
5993 handle: &FocusHandle,
5994 cx: &mut App,
5995 mut listener: impl FnMut(&mut Window, &mut App) + 'static,
5996 ) -> Subscription {
5997 let focus_id = handle.id;
5998 let (subscription, activate) =
5999 self.new_focus_listener(Box::new(move |event, window, cx| {
6000 if event.is_focus_in(focus_id) {
6001 listener(window, cx);
6002 }
6003 true
6004 }));
6005 cx.defer(move |_| activate());
6006 subscription
6007 }
6008
6009 pub fn on_focus_out(
6012 &mut self,
6013 handle: &FocusHandle,
6014 cx: &mut App,
6015 mut listener: impl FnMut(FocusOutEvent, &mut Window, &mut App) + 'static,
6016 ) -> Subscription {
6017 let focus_id = handle.id;
6018 let (subscription, activate) =
6019 self.new_focus_listener(Box::new(move |event, window, cx| {
6020 if let Some(blurred_id) = event.previous_focus_path.last().copied()
6021 && event.is_focus_out(focus_id)
6022 {
6023 let event = FocusOutEvent {
6024 blurred: WeakFocusHandle {
6025 id: blurred_id,
6026 handles: Arc::downgrade(&cx.focus_handles),
6027 },
6028 };
6029 listener(event, window, cx)
6030 }
6031 true
6032 }));
6033 cx.defer(move |_| activate());
6034 subscription
6035 }
6036
6037 fn reset_cursor_style(&self, cx: &mut App) {
6038 if self.is_window_hovered() {
6040 let style = self
6041 .frame_state
6042 .rendered_frame
6043 .cursor_style(self)
6044 .unwrap_or(CursorStyle::Arrow);
6045 cx.platform.set_cursor_style(style);
6046 }
6047 }
6048
6049 pub fn dispatch_keystroke(&mut self, keystroke: Keystroke, cx: &mut App) -> bool {
6052 let keystroke = keystroke.with_simulated_ime();
6053 let result = self.dispatch_event(
6054 PlatformInput::KeyDown(KeyDownEvent {
6055 keystroke: keystroke.clone(),
6056 is_held: false,
6057 prefer_character_input: false,
6058 }),
6059 cx,
6060 );
6061 if !result.propagate {
6062 return true;
6063 }
6064
6065 if let Some(input) = keystroke.key_char
6066 && let Some(mut input_handler) = self.core.platform_window.take_input_handler()
6067 {
6068 input_handler.dispatch_input(&input, self, cx);
6069 self.core.platform_window.set_input_handler(input_handler);
6070 return true;
6071 }
6072
6073 false
6074 }
6075
6076 pub fn keystroke_text_for(&self, action: &dyn Action) -> String {
6079 self.highest_precedence_binding_for_action(action)
6080 .map(|binding| {
6081 binding
6082 .keystrokes()
6083 .iter()
6084 .map(ToString::to_string)
6085 .collect::<Vec<_>>()
6086 .join(" ")
6087 })
6088 .unwrap_or_else(|| action.name().to_string())
6089 }
6090
6091 #[profiling::function]
6093 pub fn dispatch_event(&mut self, event: PlatformInput, cx: &mut App) -> DispatchEventResult {
6094 #[cfg(feature = "profiler")]
6095 self.core.window_profiler.begin_input(event.kind_name());
6096 let update_count_before = self.core.invalidator.update_count();
6097 let old_modality = self.core.last_input_modality;
6101 self.core.last_input_modality = match &event {
6102 PlatformInput::KeyDown(_) => InputModality::Keyboard,
6103 PlatformInput::MouseMove(_) | PlatformInput::MouseDown(_) => InputModality::Mouse,
6104 PlatformInput::Touch(_) => InputModality::Touch,
6105 _ => self.core.last_input_modality,
6106 };
6107 if self.core.last_input_modality != old_modality {
6108 self.refresh();
6109 }
6110
6111 cx.propagate_event = true;
6113 self.core.default_prevented = false;
6115
6116 let event = match event {
6117 PlatformInput::MouseMove(mouse_move) => {
6120 self.core.mouse_position = mouse_move.position;
6121 self.core.modifiers = mouse_move.modifiers;
6122 PlatformInput::MouseMove(mouse_move)
6123 }
6124 PlatformInput::MouseDown(mouse_down) => {
6125 self.core.mouse_position = mouse_down.position;
6126 self.core.modifiers = mouse_down.modifiers;
6127 PlatformInput::MouseDown(mouse_down)
6128 }
6129 PlatformInput::MouseUp(mouse_up) => {
6130 self.core.mouse_position = mouse_up.position;
6131 self.core.modifiers = mouse_up.modifiers;
6132 PlatformInput::MouseUp(mouse_up)
6133 }
6134 PlatformInput::MousePressure(mouse_pressure) => {
6135 PlatformInput::MousePressure(mouse_pressure)
6136 }
6137 PlatformInput::MouseExited(mouse_exited) => {
6138 self.core.modifiers = mouse_exited.modifiers;
6139 PlatformInput::MouseExited(mouse_exited)
6140 }
6141 PlatformInput::ModifiersChanged(modifiers_changed) => {
6142 self.core.modifiers = modifiers_changed.modifiers;
6143 self.core.capslock = modifiers_changed.capslock;
6144 PlatformInput::ModifiersChanged(modifiers_changed)
6145 }
6146 PlatformInput::ScrollWheel(scroll_wheel) => {
6147 self.core.mouse_position = scroll_wheel.position;
6148 self.core.modifiers = scroll_wheel.modifiers;
6149 PlatformInput::ScrollWheel(scroll_wheel)
6150 }
6151 PlatformInput::Pinch(pinch) => {
6152 self.core.mouse_position = pinch.position;
6153 self.core.modifiers = pinch.modifiers;
6154 PlatformInput::Pinch(pinch)
6155 }
6156 PlatformInput::FileDrop(file_drop) => match file_drop {
6159 FileDropEvent::Entered { position, paths } => {
6160 self.core.mouse_position = position;
6161 let source_window = self.core.handle.window_id();
6162 if !cx.restore_platform_drag(source_window) && cx.active_drag.is_none() {
6163 cx.active_drag = Some(AnyDrag {
6164 value: Arc::new(paths.clone()),
6165 view: cx.new(|_| paths).into(),
6166 cursor_offset: position,
6167 cursor_style: None,
6168 external_payload_source: None,
6169 });
6170 }
6171 PlatformInput::MouseMove(MouseMoveEvent {
6172 position,
6173 pressed_button: Some(MouseButton::Left),
6174 modifiers: Modifiers::default(),
6175 })
6176 }
6177 FileDropEvent::Pending { position } => {
6178 self.core.mouse_position = position;
6179 PlatformInput::MouseMove(MouseMoveEvent {
6180 position,
6181 pressed_button: Some(MouseButton::Left),
6182 modifiers: Modifiers::default(),
6183 })
6184 }
6185 FileDropEvent::Submit { position } => {
6186 cx.activate(true);
6187 self.core.mouse_position = position;
6188 PlatformInput::MouseUp(MouseUpEvent {
6189 button: MouseButton::Left,
6190 position,
6191 modifiers: Modifiers::default(),
6192 click_count: 1,
6193 })
6194 }
6195 FileDropEvent::Exited => {
6196 if !cx.hand_restored_drag_to_platform(self.core.handle.window_id()) {
6197 cx.active_drag.take();
6198 }
6199 self.refresh();
6200 PlatformInput::FileDrop(FileDropEvent::Exited)
6201 }
6202 FileDropEvent::Ended => {
6203 cx.end_platform_drag(self.core.handle.window_id());
6204 self.refresh();
6205 PlatformInput::FileDrop(FileDropEvent::Ended)
6206 }
6207 },
6208 PlatformInput::Touch(touch) => PlatformInput::Touch(touch),
6209 PlatformInput::LongPress(long_press) => {
6210 self.core.mouse_position = if long_press.phase == crate::TouchPhase::Started {
6211 long_press.start_position
6212 } else {
6213 long_press.position
6214 };
6215 if long_press.phase == crate::TouchPhase::Started {
6216 self.core.long_press_capture = None;
6217 }
6218 PlatformInput::LongPress(long_press)
6219 }
6220 PlatformInput::TouchDrag(touch_drag) => {
6221 self.core.mouse_position = touch_drag.start_position;
6222 PlatformInput::TouchDrag(touch_drag)
6223 }
6224 PlatformInput::KeyDown(_) | PlatformInput::KeyUp(_) => event,
6225 };
6226
6227 if let Some(any_mouse_event) = event.mouse_event() {
6228 self.dispatch_mouse_event(any_mouse_event, cx);
6229 } else if let Some(any_key_event) = event.keyboard_event() {
6230 self.dispatch_key_event(any_key_event, cx);
6231 } else if let Some(touch_event) = event.touch_event() {
6232 self.dispatch_touch_event(touch_event, cx);
6233 }
6234 if let PlatformInput::LongPress(long_press) = &event {
6235 match long_press.phase {
6236 crate::TouchPhase::Started if !self.core.default_prevented => {
6237 self.core.long_press_capture = None;
6238 }
6239 crate::TouchPhase::Ended | crate::TouchPhase::Cancelled => {
6240 self.core.long_press_capture = None;
6241 }
6242 crate::TouchPhase::Started | crate::TouchPhase::Moved => {}
6243 }
6244 }
6245
6246 self.promote_external_drag_to_platform(&event, cx);
6249
6250 let caused_invalidation = self.core.invalidator.update_count() > update_count_before;
6251 if caused_invalidation {
6252 self.core.input_rate_tracker.borrow_mut().record_input();
6253 }
6254 #[cfg(feature = "profiler")]
6255 self.core.window_profiler.end_input(caused_invalidation);
6256
6257 DispatchEventResult {
6258 propagate: cx.propagate_event,
6259 default_prevented: self.core.default_prevented,
6260 }
6261 }
6262
6263 fn promote_external_drag_to_platform(&mut self, event: &PlatformInput, cx: &mut App) {
6264 let PlatformInput::MouseMove(mouse_move) = event else {
6265 return;
6266 };
6267 if mouse_move.pressed_button != Some(MouseButton::Left) {
6268 return;
6269 }
6270 if Bounds::new(Point::default(), self.core.viewport_size).contains(&mouse_move.position) {
6271 return;
6272 }
6273 if !self.core.platform_window.can_start_external_drag() {
6274 return;
6275 }
6276 let Some(payload_source) = cx
6277 .active_drag
6278 .as_mut()
6279 .and_then(|drag| drag.external_payload_source.take())
6280 else {
6281 return;
6282 };
6283 let Some(payload) = payload_source(self, cx) else {
6284 return;
6285 };
6286 if self.core.platform_window.start_external_drag(&payload)
6287 && cx.hand_active_drag_to_platform(self.core.handle.window_id())
6288 {
6289 self.refresh();
6290 }
6291 }
6292
6293 pub fn touch_prediction_enabled(&self) -> bool {
6297 self.core.touch_prediction_enabled
6298 }
6299
6300 pub fn set_touch_prediction_enabled(&mut self, enabled: bool) {
6304 self.core.touch_prediction_enabled = enabled;
6305 }
6306
6307 fn dispatch_touch_event(&mut self, event: &TouchEvent, cx: &mut App) {
6311 let mut event = event.clone();
6312 if !self.core.touch_prediction_enabled {
6313 event.predicted_position = None;
6314 }
6315 let recognized_gestures = self.core.touch_gestures.handle_event(&event);
6316 if event.phase == crate::TouchPhase::Started
6317 && let Some(touch_drag) = self.core.touch_gestures.offer_touch_drag(event.id)
6318 {
6319 self.dispatch_recognized_touch_gesture(touch_drag, cx);
6320 }
6321 if event.phase == crate::TouchPhase::Started
6322 && self.core.touch_gestures.pending_long_press().is_some()
6323 {
6324 self.core.long_press_capture = None;
6325 }
6326 let mut tapped = false;
6327 for gesture in recognized_gestures {
6328 tapped |= matches!(gesture, RecognizedTouchGesture::Tap { .. });
6329 self.dispatch_recognized_touch_gesture(gesture, cx);
6330 }
6331 if event.phase == crate::TouchPhase::Started {
6332 self.schedule_long_press_timer(cx);
6333 } else if self.core.touch_gestures.pending_long_press().is_none() {
6334 self.core.long_press_timer.take();
6335 }
6336 if tapped && self.core.invalidator.is_dirty() {
6342 self.draw(cx).clear(cx);
6343 }
6344 if self.core.touch_gestures.has_momentum() {
6345 self.schedule_touch_momentum_tick();
6346 }
6347 }
6348
6349 fn dispatch_recognized_touch_gesture(&mut self, gesture: RecognizedTouchGesture, cx: &mut App) {
6350 match gesture {
6351 RecognizedTouchGesture::Scroll(scroll_wheel) => {
6352 self.core.mouse_position = scroll_wheel.position;
6353 cx.propagate_event = true;
6354 self.dispatch_mouse_event(&scroll_wheel, cx);
6355 }
6356 RecognizedTouchGesture::Tap { down, up } => {
6357 self.core.mouse_position = up.position;
6358 cx.propagate_event = true;
6359 self.dispatch_mouse_event(&down, cx);
6360 cx.propagate_event = true;
6361 self.dispatch_mouse_event(&up, cx);
6362 }
6363 RecognizedTouchGesture::TouchDrag(touch_drag) => {
6364 self.core.mouse_position = touch_drag.start_position;
6365 cx.propagate_event = true;
6366 self.core.default_prevented = false;
6367 let started = touch_drag.phase == crate::TouchPhase::Started;
6368 self.dispatch_mouse_event(&touch_drag, cx);
6369 if started {
6370 self.core
6371 .touch_gestures
6372 .resolve_touch_drag(self.core.default_prevented);
6373 }
6374 }
6375 RecognizedTouchGesture::LongPress(long_press) => {
6376 self.core.mouse_position = if long_press.phase == crate::TouchPhase::Started {
6377 long_press.start_position
6378 } else {
6379 long_press.position
6380 };
6381 cx.propagate_event = true;
6382 self.core.default_prevented = false;
6383 let started = long_press.phase == crate::TouchPhase::Started;
6384 let ended = matches!(
6385 long_press.phase,
6386 crate::TouchPhase::Ended | crate::TouchPhase::Cancelled
6387 );
6388 self.dispatch_mouse_event(&long_press, cx);
6389 if started {
6390 let claimed = self.core.default_prevented;
6391 self.core.touch_gestures.resolve_long_press(claimed);
6392 if !claimed {
6393 self.core.long_press_capture = None;
6394 }
6395 }
6396 if ended {
6397 self.core.long_press_capture = None;
6398 }
6399 }
6400 }
6401 }
6402
6403 fn schedule_long_press_timer(&mut self, cx: &mut App) {
6404 self.core.long_press_timer.take();
6405 let Some((touch_id, duration)) = self.core.touch_gestures.pending_long_press() else {
6406 return;
6407 };
6408 self.core.long_press_timer = Some(self.spawn(cx, async move |cx| {
6409 cx.background_executor.timer(duration).await;
6410 cx.update(move |window, cx| {
6411 window.core.long_press_timer.take();
6412 if let Some(gesture) = window.core.touch_gestures.offer_long_press(touch_id) {
6413 window.dispatch_recognized_touch_gesture(gesture, cx);
6414 }
6415 })
6416 .log_err();
6417 }));
6418 }
6419
6420 fn schedule_touch_momentum_tick(&mut self) {
6421 self.on_next_frame(|window, cx| {
6422 if let Some(gesture) = window.core.touch_gestures.tick_momentum() {
6423 window.dispatch_recognized_touch_gesture(gesture, cx);
6424 }
6425 if window.core.touch_gestures.has_momentum() {
6426 window.schedule_touch_momentum_tick();
6427 }
6428 });
6429 }
6430
6431 fn dispatch_mouse_event(&mut self, event: &dyn Any, cx: &mut App) {
6432 let hit_test = self
6433 .frame_state
6434 .rendered_frame
6435 .hit_test(self.mouse_position());
6436 if hit_test != self.core.mouse_hit_test {
6437 self.core.mouse_hit_test = hit_test;
6438 self.reset_cursor_style(cx);
6439 }
6440
6441 #[cfg(any(feature = "inspector", debug_assertions))]
6442 if self.is_inspector_picking(cx) {
6443 self.handle_inspector_mouse_event(event, cx);
6444 return;
6446 }
6447
6448 let mut mouse_listeners = mem::take(&mut self.frame_state.rendered_frame.mouse_listeners);
6449
6450 for listener in &mut mouse_listeners {
6453 let listener = listener.as_mut().unwrap();
6454 listener(event, DispatchPhase::Capture, self, cx);
6455 if !cx.propagate_event {
6456 break;
6457 }
6458 }
6459
6460 if cx.propagate_event {
6462 for listener in mouse_listeners.iter_mut().rev() {
6463 let listener = listener.as_mut().unwrap();
6464 listener(event, DispatchPhase::Bubble, self, cx);
6465 if !cx.propagate_event {
6466 break;
6467 }
6468 }
6469 }
6470
6471 self.frame_state.rendered_frame.mouse_listeners = mouse_listeners;
6472
6473 if cx.has_active_drag() {
6474 if event.is::<MouseMoveEvent>() {
6475 self.refresh();
6478 } else if event.is::<MouseUpEvent>() {
6479 cx.active_drag = None;
6482 self.refresh();
6483 }
6484 }
6485
6486 if event.is::<MouseUpEvent>() && self.core.captured_hitbox.is_some() {
6488 self.core.captured_hitbox = None;
6489 }
6490 }
6491
6492 fn input_preference(&mut self, event: &dyn Any, cx: &mut App) -> InputPreference {
6495 let prefer_character_input = event
6496 .downcast_ref::<KeyDownEvent>()
6497 .is_some_and(|key_down_event| key_down_event.prefer_character_input);
6498 if !prefer_character_input {
6499 return InputPreference::KeyBindings;
6500 }
6501 let Some(mut input_handler) = self.core.platform_window.take_input_handler() else {
6502 return InputPreference::KeyBindings;
6503 };
6504 let accepts_text_input = input_handler.accepts_text_input(self, cx);
6505 self.core.platform_window.set_input_handler(input_handler);
6506 if accepts_text_input {
6507 InputPreference::CharacterInput
6508 } else {
6509 InputPreference::KeyBindings
6510 }
6511 }
6512
6513 fn dispatch_key_event(&mut self, event: &dyn Any, cx: &mut App) {
6514 if self.core.invalidator.is_dirty() {
6515 self.draw(cx).clear(cx);
6516 }
6517
6518 let node_id = self.focus_node_id_in_rendered_frame(self.core.focus);
6519 let dispatch_path = self
6520 .frame_state
6521 .rendered_frame
6522 .dispatch_tree
6523 .dispatch_path(node_id);
6524
6525 let mut keystroke: Option<Keystroke> = None;
6526
6527 if let Some(event) = event.downcast_ref::<ModifiersChangedEvent>() {
6528 if event.modifiers.number_of_modifiers() == 0
6529 && self.core.pending_modifier.modifiers.number_of_modifiers() == 1
6530 && !self.core.pending_modifier.saw_other_input
6531 {
6532 let key = match self.core.pending_modifier.modifiers {
6533 modifiers if modifiers.shift => Some("shift"),
6534 modifiers if modifiers.control => Some("control"),
6535 modifiers if modifiers.alt => Some("alt"),
6536 modifiers if modifiers.platform => Some("platform"),
6537 modifiers if modifiers.function => Some("function"),
6538 _ => None,
6539 };
6540 if let Some(key) = key {
6541 keystroke = Some(Keystroke {
6542 key: key.to_string(),
6543 key_char: None,
6544 modifiers: Modifiers::default(),
6545 });
6546 }
6547 }
6548
6549 if self.core.pending_modifier.modifiers.number_of_modifiers() == 0
6550 && event.modifiers.number_of_modifiers() == 1
6551 {
6552 self.core.pending_modifier.saw_other_input = false
6553 } else if event.modifiers.number_of_modifiers() > 1 {
6554 self.core.pending_modifier.saw_other_input = true
6555 }
6556 self.core.pending_modifier.modifiers = event.modifiers
6557 } else if let Some(key_down_event) = event.downcast_ref::<KeyDownEvent>() {
6558 self.core.pending_modifier.saw_other_input = true;
6559 keystroke = Some(key_down_event.keystroke.clone());
6560 if key_down_event.keystroke.key_char.is_some()
6561 && matches!(
6562 cx.cursor_hide_mode,
6563 CursorHideMode::OnTyping | CursorHideMode::OnTypingAndAction
6564 )
6565 {
6566 cx.platform.hide_cursor_until_mouse_moves();
6567 }
6568 }
6569
6570 let Some(keystroke) = keystroke else {
6571 self.finish_dispatch_key_event(
6572 event,
6573 None,
6574 InputPreference::KeyBindings,
6575 dispatch_path,
6576 self.context_stack(),
6577 cx,
6578 );
6579 return;
6580 };
6581
6582 let input_preference = self.input_preference(event, cx);
6583
6584 cx.propagate_event = true;
6585 self.dispatch_keystroke_interceptors(
6586 &keystroke,
6587 input_preference,
6588 self.context_stack(),
6589 cx,
6590 );
6591 if !cx.propagate_event {
6592 self.finish_dispatch_key_event(
6593 event,
6594 Some(&keystroke),
6595 input_preference,
6596 dispatch_path,
6597 self.context_stack(),
6598 cx,
6599 );
6600 return;
6601 }
6602
6603 let mut currently_pending = self.core.pending_input.take().unwrap_or_default();
6604 if currently_pending.focus.is_some() && currently_pending.focus != self.core.focus {
6605 currently_pending = PendingInput::default();
6606 }
6607
6608 let match_result = self.frame_state.rendered_frame.dispatch_tree.dispatch_key(
6609 currently_pending.keystrokes,
6610 keystroke.clone(),
6611 &dispatch_path,
6612 );
6613
6614 if !match_result.to_replay.is_empty() {
6615 self.replay_pending_input(match_result.to_replay, cx);
6616 cx.propagate_event = true;
6617 }
6618
6619 if !match_result.pending.is_empty() {
6620 let previous_timeout = currently_pending.timeout.take();
6621 currently_pending.keystrokes = match_result.pending;
6622 currently_pending.focus = self.core.focus;
6623
6624 let text_input_requires_timeout = event
6625 .downcast_ref::<KeyDownEvent>()
6626 .filter(|key_down| key_down.keystroke.key_char.is_some())
6627 .and_then(|_| self.core.platform_window.take_input_handler())
6628 .map_or(false, |mut input_handler| {
6629 let accepts = input_handler.accepts_text_input(self, cx);
6630 self.core.platform_window.set_input_handler(input_handler);
6631 accepts
6632 });
6633
6634 let needs_timeout = previous_timeout.is_some()
6635 || match_result.pending_has_binding
6636 || text_input_requires_timeout;
6637 currently_pending.timeout = if needs_timeout {
6638 match previous_timeout {
6639 Some(mut timeout) if timeout.is_paused() => {
6640 timeout.reset_duration(PENDING_INPUT_TIMEOUT);
6641 Some(timeout)
6642 }
6643 previous_timeout => {
6644 drop(previous_timeout);
6645 Some(self.new_pending_input_timeout(PENDING_INPUT_TIMEOUT, cx))
6646 }
6647 }
6648 } else {
6649 None
6650 };
6651 self.core.pending_input = Some(currently_pending);
6652 self.pending_input_changed(cx);
6653 cx.propagate_event = false;
6654 return;
6655 }
6656
6657 let input_preference = self.input_preference(event, cx);
6659 if input_preference == InputPreference::KeyBindings {
6660 for binding in match_result.bindings {
6661 self.dispatch_action_on_node(node_id, binding.action.as_ref(), cx);
6662 if !cx.propagate_event {
6663 self.dispatch_keystroke_observers(
6664 &keystroke,
6665 input_preference,
6666 Some(binding.action.as_ref()),
6667 match_result.context_stack,
6668 cx,
6669 );
6670 self.pending_input_changed(cx);
6671 return;
6672 }
6673 }
6674 }
6675
6676 self.finish_dispatch_key_event(
6677 event,
6678 Some(&keystroke),
6679 input_preference,
6680 dispatch_path,
6681 match_result.context_stack,
6682 cx,
6683 );
6684 self.pending_input_changed(cx);
6685 }
6686
6687 fn new_pending_input_timeout(&self, duration: Duration, cx: &App) -> PendingInputTimeout {
6688 let (started_at, task) = self.start_pending_input_timeout(duration, cx);
6689 PendingInputTimeout {
6690 duration,
6691 remaining: duration,
6692 state: PendingInputTimeoutState::Running { started_at, task },
6693 }
6694 }
6695
6696 fn start_pending_input_timeout(&self, remaining: Duration, cx: &App) -> (Instant, Task<()>) {
6697 let started_at = cx.background_executor().now();
6698 let task = self.spawn(cx, async move |cx| {
6699 cx.background_executor.timer(remaining).await;
6700 cx.update(move |window, cx| {
6701 let Some(currently_pending) = window
6702 .core
6703 .pending_input
6704 .take()
6705 .filter(|pending| pending.focus == window.core.focus)
6706 else {
6707 return;
6708 };
6709
6710 let node_id = window.focus_node_id_in_rendered_frame(window.core.focus);
6711 let dispatch_path = window
6712 .frame_state
6713 .rendered_frame
6714 .dispatch_tree
6715 .dispatch_path(node_id);
6716
6717 let to_replay = window
6718 .frame_state
6719 .rendered_frame
6720 .dispatch_tree
6721 .flush_dispatch(currently_pending.keystrokes, &dispatch_path);
6722
6723 window.pending_input_changed(cx);
6724 window.replay_pending_input(to_replay, cx)
6725 })
6726 .log_err();
6727 });
6728 (started_at, task)
6729 }
6730
6731 fn finish_dispatch_key_event(
6732 &mut self,
6733 event: &dyn Any,
6734 recognized_keystroke: Option<&Keystroke>,
6735 input_preference: InputPreference,
6736 dispatch_path: SmallVec<[DispatchNodeId; 32]>,
6737 context_stack: Vec<KeyContext>,
6738 cx: &mut App,
6739 ) {
6740 self.dispatch_key_down_up_event(event, &dispatch_path, cx);
6741 if !cx.propagate_event {
6742 return;
6743 }
6744
6745 self.dispatch_modifiers_changed_event(event, &dispatch_path, cx);
6746 if !cx.propagate_event {
6747 return;
6748 }
6749
6750 if let Some(keystroke) = recognized_keystroke {
6751 self.dispatch_keystroke_observers(keystroke, input_preference, None, context_stack, cx);
6752 }
6753 }
6754
6755 pub(crate) fn pending_input_changed(&mut self, cx: &mut App) {
6756 self.core
6757 .pending_input_observers
6758 .clone()
6759 .retain(&(), |callback| callback(self, cx));
6760 }
6761
6762 fn defer_pending_input_changed(&self, cx: &mut App) {
6763 let window_handle = self.core.handle;
6766 cx.defer(move |cx| {
6767 window_handle
6768 .update(cx, |_, window, cx| {
6769 window.pending_input_changed(cx);
6770 })
6771 .ok();
6772 });
6773 }
6774
6775 fn dispatch_key_down_up_event(
6776 &mut self,
6777 event: &dyn Any,
6778 dispatch_path: &SmallVec<[DispatchNodeId; 32]>,
6779 cx: &mut App,
6780 ) {
6781 for node_id in dispatch_path {
6783 let node = self.frame_state.rendered_frame.dispatch_tree.node(*node_id);
6784
6785 for key_listener in node.key_listeners.clone() {
6786 key_listener(event, DispatchPhase::Capture, self, cx);
6787 if !cx.propagate_event {
6788 return;
6789 }
6790 }
6791 }
6792
6793 for node_id in dispatch_path.iter().rev() {
6795 let node = self.frame_state.rendered_frame.dispatch_tree.node(*node_id);
6797 for key_listener in node.key_listeners.clone() {
6798 key_listener(event, DispatchPhase::Bubble, self, cx);
6799 if !cx.propagate_event {
6800 return;
6801 }
6802 }
6803 }
6804 }
6805
6806 fn dispatch_modifiers_changed_event(
6807 &mut self,
6808 event: &dyn Any,
6809 dispatch_path: &SmallVec<[DispatchNodeId; 32]>,
6810 cx: &mut App,
6811 ) {
6812 let Some(event) = event.downcast_ref::<ModifiersChangedEvent>() else {
6813 return;
6814 };
6815 for node_id in dispatch_path.iter().rev() {
6816 let node = self.frame_state.rendered_frame.dispatch_tree.node(*node_id);
6817 for listener in node.modifiers_changed_listeners.clone() {
6818 listener(event, self, cx);
6819 if !cx.propagate_event {
6820 return;
6821 }
6822 }
6823 }
6824 }
6825
6826 pub fn has_pending_keystrokes(&self) -> bool {
6828 self.pending_input().is_some()
6829 }
6830
6831 #[cfg(test)]
6832 pub(crate) fn pending_input_is_none(&self) -> bool {
6833 self.core.pending_input.is_none()
6834 }
6835
6836 pub(crate) fn clear_pending_keystrokes(&mut self, cx: &mut App) {
6837 if self.core.pending_input.take().is_some() {
6838 self.defer_pending_input_changed(cx);
6839 }
6840 }
6841
6842 pub fn pending_input(&self) -> Option<PendingInputStatus<'_>> {
6845 self.core
6846 .pending_input
6847 .as_ref()
6848 .filter(|pending_input| pending_input.focus == self.core.focus)
6849 .map(|pending_input| PendingInputStatus {
6850 keystrokes: pending_input.keystrokes.as_slice(),
6851 timeout: pending_input
6852 .timeout
6853 .as_ref()
6854 .map(PendingInputTimeout::status),
6855 })
6856 }
6857
6858 pub fn set_pending_input_timeout_paused<T: 'static>(
6863 &mut self,
6864 owner: &Entity<T>,
6865 paused: bool,
6866 cx: &mut App,
6867 ) -> bool {
6868 let owner_id = owner.entity_id();
6869 if !paused {
6870 return self.resume_pending_input_timeout(owner_id, cx);
6871 }
6872
6873 let timeout = self
6874 .core
6875 .pending_input
6876 .as_ref()
6877 .filter(|pending_input| pending_input.focus == self.core.focus)
6878 .and_then(|pending_input| pending_input.timeout.as_ref());
6879 let Some(timeout) = timeout else {
6880 return false;
6881 };
6882 if timeout.is_paused() {
6883 return false;
6884 }
6885
6886 let release_subscription = self.observe_release(owner, cx, move |_, window, cx| {
6887 window.resume_pending_input_timeout(owner_id, cx);
6888 });
6889 let now = cx.background_executor().now();
6890 let changed = self
6891 .core
6892 .pending_input
6893 .as_mut()
6894 .filter(|pending_input| pending_input.focus == self.core.focus)
6895 .and_then(|pending_input| pending_input.timeout.as_mut())
6896 .is_some_and(|timeout| {
6897 timeout.pause(
6898 PendingInputTimeoutPause {
6899 owner_id,
6900 _release_subscription: release_subscription,
6901 },
6902 now,
6903 )
6904 });
6905
6906 if changed {
6907 self.defer_pending_input_changed(cx);
6908 }
6909 changed
6910 }
6911
6912 fn resume_pending_input_timeout(&mut self, owner_id: EntityId, cx: &mut App) -> bool {
6913 let Some(remaining) = self
6914 .core
6915 .pending_input
6916 .as_ref()
6917 .and_then(|pending_input| pending_input.timeout.as_ref())
6918 .filter(|timeout| timeout.pause_owner_id() == Some(owner_id))
6919 .map(|timeout| timeout.remaining)
6920 else {
6921 return false;
6922 };
6923
6924 let (started_at, task) = self.start_pending_input_timeout(remaining, cx);
6925 let changed = self
6926 .core
6927 .pending_input
6928 .as_mut()
6929 .and_then(|pending_input| pending_input.timeout.as_mut())
6930 .is_some_and(|timeout| timeout.resume(owner_id, started_at, task));
6931
6932 if changed {
6933 self.defer_pending_input_changed(cx);
6934 }
6935 changed
6936 }
6937
6938 pub fn pending_input_keystrokes(&self) -> Option<&[Keystroke]> {
6940 self.pending_input()
6941 .map(|pending_input| pending_input.keystrokes())
6942 }
6943
6944 fn replay_pending_input(&mut self, replays: SmallVec<[Replay; 1]>, cx: &mut App) {
6945 let node_id = self.focus_node_id_in_rendered_frame(self.core.focus);
6946 let dispatch_path = self
6947 .frame_state
6948 .rendered_frame
6949 .dispatch_tree
6950 .dispatch_path(node_id);
6951
6952 'replay: for replay in replays {
6953 let event = KeyDownEvent {
6954 keystroke: replay.keystroke.clone(),
6955 is_held: false,
6956 prefer_character_input: true,
6957 };
6958
6959 cx.propagate_event = true;
6960 for binding in replay.bindings {
6961 self.dispatch_action_on_node(node_id, binding.action.as_ref(), cx);
6962 if !cx.propagate_event {
6963 self.dispatch_keystroke_observers(
6964 &replay.keystroke,
6965 InputPreference::KeyBindings,
6966 Some(binding.action.as_ref()),
6967 Vec::default(),
6968 cx,
6969 );
6970 continue 'replay;
6971 }
6972 }
6973
6974 self.dispatch_key_down_up_event(&event, &dispatch_path, cx);
6975 if !cx.propagate_event {
6976 continue 'replay;
6977 }
6978 if let Some(input) = replay.keystroke.key_char.as_ref().cloned()
6979 && let Some(mut input_handler) = self.core.platform_window.take_input_handler()
6980 {
6981 input_handler.dispatch_input(&input, self, cx);
6982 self.core.platform_window.set_input_handler(input_handler)
6983 }
6984 }
6985 }
6986
6987 fn focus_node_id_in_rendered_frame(&self, focus_id: Option<FocusId>) -> DispatchNodeId {
6988 focus_id
6989 .and_then(|focus_id| {
6990 self.frame_state
6991 .rendered_frame
6992 .dispatch_tree
6993 .focusable_node_id(focus_id)
6994 })
6995 .unwrap_or_else(|| self.frame_state.rendered_frame.dispatch_tree.root_node_id())
6996 }
6997
6998 fn dispatch_action_on_node(
6999 &mut self,
7000 node_id: DispatchNodeId,
7001 action: &dyn Action,
7002 cx: &mut App,
7003 ) {
7004 self.dispatch_action_on_node_inner(node_id, action, cx);
7005
7006 if !cx.propagate_event
7007 && cx.cursor_hide_mode == CursorHideMode::OnTypingAndAction
7008 && self.last_input_was_keyboard()
7009 {
7010 cx.platform.hide_cursor_until_mouse_moves();
7011 }
7012 }
7013
7014 fn dispatch_action_on_node_inner(
7015 &mut self,
7016 node_id: DispatchNodeId,
7017 action: &dyn Action,
7018 cx: &mut App,
7019 ) {
7020 let dispatch_path = self
7021 .frame_state
7022 .rendered_frame
7023 .dispatch_tree
7024 .dispatch_path(node_id);
7025
7026 cx.propagate_event = true;
7028 if let Some(mut global_listeners) = cx
7029 .global_action_listeners
7030 .remove(&action.as_any().type_id())
7031 {
7032 for listener in &global_listeners {
7033 #[cfg(feature = "profiler")]
7034 self.core.window_profiler.begin_action_handler(action, cx);
7035 listener(action.as_any(), DispatchPhase::Capture, cx);
7036 #[cfg(feature = "profiler")]
7037 self.core.window_profiler.end_action_handler();
7038 if !cx.propagate_event {
7039 break;
7040 }
7041 }
7042
7043 global_listeners.extend(
7044 cx.global_action_listeners
7045 .remove(&action.as_any().type_id())
7046 .unwrap_or_default(),
7047 );
7048
7049 cx.global_action_listeners
7050 .insert(action.as_any().type_id(), global_listeners);
7051 }
7052
7053 if !cx.propagate_event {
7054 return;
7055 }
7056
7057 for node_id in &dispatch_path {
7059 let node = self.frame_state.rendered_frame.dispatch_tree.node(*node_id);
7060 for DispatchActionListener {
7061 action_type,
7062 listener,
7063 } in node.action_listeners.clone()
7064 {
7065 let any_action = action.as_any();
7066 if action_type == any_action.type_id() {
7067 #[cfg(feature = "profiler")]
7068 self.core.window_profiler.begin_action_handler(action, cx);
7069 listener(any_action, DispatchPhase::Capture, self, cx);
7070 #[cfg(feature = "profiler")]
7071 self.core.window_profiler.end_action_handler();
7072
7073 if !cx.propagate_event {
7074 return;
7075 }
7076 }
7077 }
7078 }
7079
7080 for node_id in dispatch_path.iter().rev() {
7082 let node = self.frame_state.rendered_frame.dispatch_tree.node(*node_id);
7083 for DispatchActionListener {
7084 action_type,
7085 listener,
7086 } in node.action_listeners.clone()
7087 {
7088 let any_action = action.as_any();
7089 if action_type == any_action.type_id() {
7090 cx.propagate_event = false; #[cfg(feature = "profiler")]
7092 self.core.window_profiler.begin_action_handler(action, cx);
7093 listener(any_action, DispatchPhase::Bubble, self, cx);
7094 #[cfg(feature = "profiler")]
7095 self.core.window_profiler.end_action_handler();
7096
7097 if !cx.propagate_event {
7098 return;
7099 }
7100 }
7101 }
7102 }
7103
7104 if let Some(mut global_listeners) = cx
7106 .global_action_listeners
7107 .remove(&action.as_any().type_id())
7108 {
7109 for listener in global_listeners.iter().rev() {
7110 cx.propagate_event = false; #[cfg(feature = "profiler")]
7113 self.core.window_profiler.begin_action_handler(action, cx);
7114 listener(action.as_any(), DispatchPhase::Bubble, cx);
7115 #[cfg(feature = "profiler")]
7116 self.core.window_profiler.end_action_handler();
7117 if !cx.propagate_event {
7118 break;
7119 }
7120 }
7121
7122 global_listeners.extend(
7123 cx.global_action_listeners
7124 .remove(&action.as_any().type_id())
7125 .unwrap_or_default(),
7126 );
7127
7128 cx.global_action_listeners
7129 .insert(action.as_any().type_id(), global_listeners);
7130 }
7131 }
7132
7133 pub fn observe_global<G: Global>(
7136 &mut self,
7137 cx: &mut App,
7138 f: impl Fn(&mut Window, &mut App) + 'static,
7139 ) -> Subscription {
7140 let window_handle = self.core.handle;
7141 let (subscription, activate) = cx.global_observers.insert(
7142 TypeId::of::<G>(),
7143 Box::new(move |cx| {
7144 window_handle
7145 .update(cx, |_, window, cx| f(window, cx))
7146 .is_ok()
7147 }),
7148 );
7149 cx.defer(move |_| activate());
7150 subscription
7151 }
7152
7153 pub fn activate_window(&self) {
7155 self.core.platform_window.activate();
7156 }
7157
7158 pub fn request_attention(&self) {
7160 self.core.platform_window.request_attention();
7161 }
7162
7163 pub fn minimize_window(&self) {
7165 self.core.platform_window.minimize();
7166 }
7167
7168 pub fn toggle_fullscreen(&self) {
7170 self.core.platform_window.toggle_fullscreen();
7171 }
7172
7173 pub fn toggle_simple_fullscreen(&self) {
7178 self.core.platform_window.toggle_simple_fullscreen();
7179 }
7180
7181 pub fn invalidate_character_coordinates(&self) {
7183 self.on_next_frame(|window, cx| {
7184 if let Some(mut input_handler) = window.core.platform_window.take_input_handler() {
7185 if let Some(bounds) = input_handler.selected_bounds(window, cx) {
7186 window.core.platform_window.update_ime_position(bounds);
7187 }
7188 window.core.platform_window.set_input_handler(input_handler);
7189 }
7190 });
7191 }
7192
7193 pub fn prompt<T>(
7197 &mut self,
7198 level: PromptLevel,
7199 message: &str,
7200 detail: Option<&str>,
7201 answers: &[T],
7202 cx: &mut App,
7203 ) -> oneshot::Receiver<usize>
7204 where
7205 T: Clone + Into<PromptButton>,
7206 {
7207 let prompt_builder = cx.prompt_builder.take();
7208 let Some(prompt_builder) = prompt_builder else {
7209 unreachable!("Re-entrant window prompting is not supported by GPUI");
7210 };
7211
7212 let answers = answers
7213 .iter()
7214 .map(|answer| answer.clone().into())
7215 .collect::<Vec<_>>();
7216
7217 let receiver = match &prompt_builder {
7218 PromptBuilder::Default => self
7219 .core
7220 .platform_window
7221 .prompt(level, message, detail, &answers)
7222 .unwrap_or_else(|| {
7223 self.build_custom_prompt(&prompt_builder, level, message, detail, &answers, cx)
7224 }),
7225 PromptBuilder::Custom(_) => {
7226 self.build_custom_prompt(&prompt_builder, level, message, detail, &answers, cx)
7227 }
7228 };
7229
7230 cx.prompt_builder = Some(prompt_builder);
7231
7232 receiver
7233 }
7234
7235 fn build_custom_prompt(
7236 &mut self,
7237 prompt_builder: &PromptBuilder,
7238 level: PromptLevel,
7239 message: &str,
7240 detail: Option<&str>,
7241 answers: &[PromptButton],
7242 cx: &mut App,
7243 ) -> oneshot::Receiver<usize> {
7244 let (sender, receiver) = oneshot::channel();
7245 let handle = PromptHandle::new(sender);
7246 let handle = (prompt_builder)(level, message, detail, answers, handle, self, cx);
7247 self.core.prompt = Some(handle);
7248 receiver
7249 }
7250
7251 pub fn has_active_prompt(&self) -> bool {
7256 self.core.prompt.is_some()
7257 }
7258
7259 pub fn context_stack(&self) -> Vec<KeyContext> {
7261 let node_id = self.focus_node_id_in_rendered_frame(self.core.focus);
7262 let dispatch_tree = &self.frame_state.rendered_frame.dispatch_tree;
7263 dispatch_tree
7264 .dispatch_path(node_id)
7265 .iter()
7266 .filter_map(move |&node_id| dispatch_tree.node(node_id).context.clone())
7267 .collect()
7268 }
7269
7270 pub fn available_actions(&self, cx: &App) -> Vec<Box<dyn Action>> {
7272 let node_id = self.focus_node_id_in_rendered_frame(self.core.focus);
7273 let mut actions = self
7274 .frame_state
7275 .rendered_frame
7276 .dispatch_tree
7277 .available_actions(node_id);
7278 for action_type in cx.global_action_listeners.keys() {
7279 if let Err(ix) = actions.binary_search_by_key(action_type, |a| a.as_any().type_id()) {
7280 let action = cx.actions.build_action_type(action_type).ok();
7281 if let Some(action) = action {
7282 actions.insert(ix, action);
7283 }
7284 }
7285 }
7286 actions
7287 }
7288
7289 pub fn bindings_for_action(&self, action: &dyn Action) -> Vec<KeyBinding> {
7292 self.frame_state
7293 .rendered_frame
7294 .dispatch_tree
7295 .bindings_for_action(
7296 action,
7297 &self.frame_state.rendered_frame.dispatch_tree.context_stack,
7298 )
7299 }
7300
7301 pub fn highest_precedence_binding_for_action(&self, action: &dyn Action) -> Option<KeyBinding> {
7304 self.frame_state
7305 .rendered_frame
7306 .dispatch_tree
7307 .highest_precedence_binding_for_action(
7308 action,
7309 &self.frame_state.rendered_frame.dispatch_tree.context_stack,
7310 )
7311 }
7312
7313 pub fn bindings_for_action_in_context(
7315 &self,
7316 action: &dyn Action,
7317 context: KeyContext,
7318 ) -> Vec<KeyBinding> {
7319 let dispatch_tree = &self.frame_state.rendered_frame.dispatch_tree;
7320 dispatch_tree.bindings_for_action(action, &[context])
7321 }
7322
7323 pub fn highest_precedence_binding_for_action_in_context(
7326 &self,
7327 action: &dyn Action,
7328 context: KeyContext,
7329 ) -> Option<KeyBinding> {
7330 let dispatch_tree = &self.frame_state.rendered_frame.dispatch_tree;
7331 dispatch_tree.highest_precedence_binding_for_action(action, &[context])
7332 }
7333
7334 pub fn bindings_for_action_in(
7338 &self,
7339 action: &dyn Action,
7340 focus_handle: &FocusHandle,
7341 ) -> Vec<KeyBinding> {
7342 let dispatch_tree = &self.frame_state.rendered_frame.dispatch_tree;
7343 let Some(context_stack) = self.context_stack_for_focus_handle(focus_handle) else {
7344 return vec![];
7345 };
7346 dispatch_tree.bindings_for_action(action, &context_stack)
7347 }
7348
7349 pub fn highest_precedence_binding_for_action_in(
7353 &self,
7354 action: &dyn Action,
7355 focus_handle: &FocusHandle,
7356 ) -> Option<KeyBinding> {
7357 let dispatch_tree = &self.frame_state.rendered_frame.dispatch_tree;
7358 let context_stack = self.context_stack_for_focus_handle(focus_handle)?;
7359 dispatch_tree.highest_precedence_binding_for_action(action, &context_stack)
7360 }
7361
7362 pub fn possible_bindings_for_input(&self, input: &[Keystroke]) -> Vec<KeyBinding> {
7364 self.frame_state
7365 .rendered_frame
7366 .dispatch_tree
7367 .possible_next_bindings_for_input(input, &self.context_stack())
7368 }
7369
7370 fn context_stack_for_focus_handle(
7371 &self,
7372 focus_handle: &FocusHandle,
7373 ) -> Option<Vec<KeyContext>> {
7374 let dispatch_tree = &self.frame_state.rendered_frame.dispatch_tree;
7375 let node_id = dispatch_tree.focusable_node_id(focus_handle.id)?;
7376 let context_stack: Vec<_> = dispatch_tree
7377 .dispatch_path(node_id)
7378 .into_iter()
7379 .filter_map(|node_id| dispatch_tree.node(node_id).context.clone())
7380 .collect();
7381 Some(context_stack)
7382 }
7383
7384 pub fn listener_for<T: 'static, E>(
7386 &self,
7387 view: &Entity<T>,
7388 f: impl Fn(&mut T, &E, &mut Window, &mut Context<T>) + 'static,
7389 ) -> impl Fn(&E, &mut Window, &mut App) + 'static {
7390 let view = view.downgrade();
7391 move |e: &E, window: &mut Window, cx: &mut App| {
7392 view.update(cx, |view, cx| f(view, e, window, cx)).ok();
7393 }
7394 }
7395
7396 pub fn handler_for<E: 'static, Callback: Fn(&mut E, &mut Window, &mut Context<E>) + 'static>(
7398 &self,
7399 entity: &Entity<E>,
7400 f: Callback,
7401 ) -> impl Fn(&mut Window, &mut App) + 'static {
7402 let entity = entity.downgrade();
7403 move |window: &mut Window, cx: &mut App| {
7404 entity.update(cx, |entity, cx| f(entity, window, cx)).ok();
7405 }
7406 }
7407
7408 pub fn on_window_should_close(
7411 &self,
7412 cx: &App,
7413 f: impl Fn(&mut Window, &mut App) -> bool + 'static,
7414 ) {
7415 let mut cx = self.to_async(cx);
7416 self.core.platform_window.on_should_close(Box::new(move || {
7417 cx.update(|window, cx| f(window, cx)).unwrap_or(true)
7418 }))
7419 }
7420
7421 pub fn on_action(
7430 &mut self,
7431 action_type: TypeId,
7432 listener: impl Fn(&dyn Any, DispatchPhase, &mut Window, &mut App) + 'static,
7433 ) {
7434 self.core.invalidator.debug_assert_paint();
7435
7436 self.frame_state
7437 .next_frame
7438 .dispatch_tree
7439 .on_action(action_type, Rc::new(listener));
7440 }
7441
7442 pub fn on_action_when(
7451 &mut self,
7452 condition: bool,
7453 action_type: TypeId,
7454 listener: impl Fn(&dyn Any, DispatchPhase, &mut Window, &mut App) + 'static,
7455 ) {
7456 self.core.invalidator.debug_assert_paint();
7457
7458 if condition {
7459 self.frame_state
7460 .next_frame
7461 .dispatch_tree
7462 .on_action(action_type, Rc::new(listener));
7463 }
7464 }
7465
7466 pub fn gpu_specs(&self) -> Option<GpuSpecs> {
7469 self.core.platform_window.gpu_specs()
7470 }
7471
7472 pub fn titlebar_double_click(&self) {
7475 self.core
7476 .platform_window
7477 .titlebar_double_click(self.core.is_resizable, self.core.is_minimizable);
7478 }
7479
7480 pub fn window_title(&self) -> String {
7483 self.core.platform_window.get_title()
7484 }
7485
7486 pub fn tabbed_windows(&self) -> Option<Vec<SystemWindowTab>> {
7489 self.core.platform_window.tabbed_windows()
7490 }
7491
7492 pub fn tab_bar_visible(&self) -> bool {
7495 self.core.platform_window.tab_bar_visible()
7496 }
7497
7498 pub fn merge_all_windows(&self) {
7501 self.core.platform_window.merge_all_windows()
7502 }
7503
7504 pub fn move_tab_to_new_window(&self) {
7507 self.core.platform_window.move_tab_to_new_window()
7508 }
7509
7510 pub fn toggle_window_tab_overview(&self) {
7513 self.core.platform_window.toggle_window_tab_overview()
7514 }
7515
7516 pub fn set_tabbing_identifier(&self, tabbing_identifier: Option<String>) {
7519 self.core
7520 .platform_window
7521 .set_tabbing_identifier(tabbing_identifier)
7522 }
7523
7524 pub fn play_system_bell(&self) {
7527 self.core.platform_window.play_system_bell()
7528 }
7529
7530 pub fn is_a11y_enabled(&self) -> bool {
7537 self.core.a11y.is_enabled()
7538 }
7539
7540 pub fn is_a11y_active(&self) -> bool {
7551 self.core.a11y.is_active()
7552 }
7553
7554 pub fn debug_a11y_tree_json(&self) -> Option<String> {
7556 self.core.a11y.debug_tree_json()
7557 }
7558
7559 pub fn on_a11y_action(
7565 &mut self,
7566 node_id: accesskit::NodeId,
7567 action: accesskit::Action,
7568 listener: impl FnMut(Option<&accesskit::ActionData>, &mut Window, &mut App) + 'static,
7569 ) {
7570 self.core
7571 .a11y
7572 .action_listeners
7573 .entry(node_id)
7574 .or_default()
7575 .push((action, Box::new(listener)));
7576 }
7577
7578 #[cfg(not(target_family = "wasm"))]
7579 pub(crate) fn handle_a11y_action(&mut self, request: accesskit::ActionRequest, cx: &mut App) {
7580 if let Some(mut listeners) = self.core.a11y.action_listeners.remove(&request.target_node) {
7583 let extra_data = request.data.as_ref();
7584 let mut matched = false;
7585 for (action, listener) in &mut listeners {
7586 if *action == request.action {
7587 listener(extra_data, self, cx);
7588 matched = true;
7589 }
7590 }
7591 self.core
7592 .a11y
7593 .action_listeners
7594 .insert(request.target_node, listeners);
7595 if matched {
7596 return;
7597 }
7598 }
7599
7600 match request.action {
7602 accesskit::Action::Click => {
7603 if let Some(bounds) = self
7604 .core
7605 .a11y
7606 .node_bounds
7607 .get(&request.target_node)
7608 .copied()
7609 {
7610 let center = bounds.center();
7611 let mouse_down = PlatformInput::MouseDown(crate::MouseDownEvent {
7612 button: MouseButton::Left,
7613 position: center,
7614 modifiers: Modifiers::default(),
7615 click_count: 1,
7616 first_mouse: false,
7617 });
7618 let mouse_up = PlatformInput::MouseUp(MouseUpEvent {
7619 button: MouseButton::Left,
7620 position: center,
7621 modifiers: Modifiers::default(),
7622 click_count: 1,
7623 });
7624 self.dispatch_event(mouse_down, cx);
7625 self.dispatch_event(mouse_up, cx);
7626 }
7627 }
7628 accesskit::Action::Focus => {
7629 if let Some(focus_id) = self.core.a11y.focus_ids.get(&request.target_node).copied()
7630 && let Some(handle) = FocusHandle::for_id(focus_id, &cx.focus_handles)
7631 {
7632 self.focus(&handle, cx);
7633 }
7634 }
7635 accesskit::Action::Blur => {
7636 self.blur(cx);
7637 }
7638 _ => {
7639 log::debug!(
7640 "Unhandled a11y action: {:?} on {:?}",
7641 request.action,
7642 request.target_node
7643 );
7644 }
7645 }
7646 }
7647
7648 #[cfg(any(feature = "inspector", debug_assertions))]
7650 pub fn toggle_inspector(&mut self, cx: &mut App) {
7651 self.core.inspector = match self.core.inspector {
7652 None => Some(cx.new(|_| Inspector::new())),
7653 Some(_) => {
7654 self.frame_state.rendered_frame.next_inspector_instance_ids = FxHashMap::default();
7655 self.frame_state.rendered_frame.inspector_hitboxes = FxHashMap::default();
7656 self.frame_state.next_frame.next_inspector_instance_ids = FxHashMap::default();
7657 self.frame_state.next_frame.inspector_hitboxes = FxHashMap::default();
7658 None
7659 }
7660 };
7661 self.refresh();
7662 }
7663
7664 pub fn is_inspector_picking(&self, _cx: &App) -> bool {
7666 #[cfg(any(feature = "inspector", debug_assertions))]
7667 {
7668 if let Some(inspector) = &self.core.inspector {
7669 return inspector.read(_cx).is_picking();
7670 }
7671 }
7672 false
7673 }
7674
7675 pub(crate) fn set_inspector_element_id(
7679 &mut self,
7680 inspector_element_id: Option<crate::InspectorElementId>,
7681 ) -> Option<crate::InspectorElementId> {
7682 std::mem::replace(
7683 &mut self.frame_state.inspector_element_id,
7684 inspector_element_id,
7685 )
7686 }
7687
7688 #[cfg(any(feature = "inspector", debug_assertions))]
7693 pub fn with_inspector_state<T: 'static, R>(
7694 &mut self,
7695 inspector_id: Option<&crate::InspectorElementId>,
7696 cx: &mut App,
7697 f: impl FnOnce(&mut Option<T>, &mut Self) -> R,
7698 ) -> Option<R> {
7699 let inspector_id = inspector_id?;
7700 let inspector = self.core.inspector.as_ref()?;
7701 if inspector.read(cx).active_element_id() != Some(inspector_id) {
7702 return None;
7703 }
7704 let inspector = inspector.clone();
7705 Some(inspector.update(cx, |inspector, _cx| {
7706 inspector.with_active_element_state(self, f)
7707 }))
7708 }
7709
7710 #[cfg(any(feature = "inspector", debug_assertions))]
7711 pub(crate) fn inspector_enabled(&self) -> bool {
7712 self.core.inspector.is_some()
7713 }
7714
7715 #[cfg(any(feature = "inspector", debug_assertions))]
7721 pub fn with_current_inspector_state<T: 'static, R>(
7722 &mut self,
7723 cx: &mut App,
7724 f: impl FnOnce(&mut Option<T>, &mut Self) -> R,
7725 ) -> Option<R> {
7726 let inspector_id = self.frame_state.inspector_element_id.clone();
7727 self.with_inspector_state(inspector_id.as_ref(), cx, f)
7728 }
7729
7730 #[cfg(any(feature = "inspector", debug_assertions))]
7731 pub(crate) fn build_inspector_element_id(
7732 &mut self,
7733 path: crate::InspectorElementPath,
7734 ) -> crate::InspectorElementId {
7735 self.core.invalidator.debug_assert_paint_or_prepaint();
7736 let path = Rc::new(path);
7737 let next_instance_id = self
7738 .frame_state
7739 .next_frame
7740 .next_inspector_instance_ids
7741 .entry(path.clone())
7742 .or_insert(0);
7743 let instance_id = *next_instance_id;
7744 *next_instance_id += 1;
7745 crate::InspectorElementId { path, instance_id }
7746 }
7747
7748 #[cfg(any(feature = "inspector", debug_assertions))]
7749 fn prepaint_inspector(&mut self, inspector_width: Pixels, cx: &mut App) -> Option<AnyElement> {
7750 if let Some(inspector) = self.core.inspector.take() {
7751 let mut inspector_element = AnyView::from(inspector.clone()).into_any_element();
7752 inspector_element.prepaint_as_root(
7753 point(self.core.viewport_size.width - inspector_width, px(0.0)),
7754 size(inspector_width, self.core.viewport_size.height).into(),
7755 self,
7756 cx,
7757 );
7758 self.core.inspector = Some(inspector);
7759 Some(inspector_element)
7760 } else {
7761 None
7762 }
7763 }
7764
7765 #[cfg(any(feature = "inspector", debug_assertions))]
7766 fn paint_inspector(&mut self, mut inspector_element: Option<AnyElement>, cx: &mut App) {
7767 if let Some(mut inspector_element) = inspector_element {
7768 inspector_element.paint(self, cx);
7769 };
7770 }
7771
7772 #[cfg(any(feature = "inspector", debug_assertions))]
7778 pub(crate) fn insert_inspector_hitbox(&mut self, hitbox_id: HitboxId, cx: &App) {
7779 self.core.invalidator.debug_assert_paint_or_prepaint();
7780 if !self.is_inspector_picking(cx) {
7781 return;
7782 }
7783 if let Some(inspector_id) = self.frame_state.inspector_element_id.clone() {
7784 self.frame_state
7785 .next_frame
7786 .inspector_hitboxes
7787 .insert(hitbox_id, inspector_id);
7788 }
7789 }
7790
7791 #[cfg(any(feature = "inspector", debug_assertions))]
7792 fn paint_inspector_hitbox(&mut self, cx: &App) {
7793 if let Some(inspector) = self.core.inspector.as_ref() {
7794 let inspector = inspector.read(cx);
7795 if let Some((hitbox_id, _)) =
7796 self.hovered_inspector_hitbox(inspector, &self.frame_state.next_frame)
7797 && let Some(hitbox) = self
7798 .frame_state
7799 .next_frame
7800 .hitboxes
7801 .iter()
7802 .find(|hitbox| hitbox.id == hitbox_id)
7803 {
7804 self.paint_quad(crate::fill(hitbox.bounds, crate::rgba(0x61afef4d)));
7805 }
7806 }
7807 }
7808
7809 #[cfg(any(feature = "inspector", debug_assertions))]
7810 fn handle_inspector_mouse_event(&mut self, event: &dyn Any, cx: &mut App) {
7811 let Some(inspector) = self.core.inspector.clone() else {
7812 return;
7813 };
7814 if event.downcast_ref::<MouseMoveEvent>().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.hover(inspector_id, self);
7820 }
7821 });
7822 } else if event.downcast_ref::<crate::MouseDownEvent>().is_some() {
7823 inspector.update(cx, |inspector, _cx| {
7824 if let Some((_, inspector_id)) =
7825 self.hovered_inspector_hitbox(inspector, &self.frame_state.rendered_frame)
7826 {
7827 inspector.select(inspector_id, self);
7828 }
7829 });
7830 } else if let Some(event) = event.downcast_ref::<crate::ScrollWheelEvent>() {
7831 const SCROLL_LINES: f32 = 3.0;
7833 const SCROLL_PIXELS_PER_LAYER: f32 = 36.0;
7834 let delta_y = event
7835 .delta
7836 .pixel_delta(px(SCROLL_PIXELS_PER_LAYER / SCROLL_LINES))
7837 .y;
7838 if let Some(inspector) = self.core.inspector.clone() {
7839 inspector.update(cx, |inspector, _cx| {
7840 if let Some(depth) = inspector.pick_depth.as_mut() {
7841 *depth += f32::from(delta_y) / SCROLL_PIXELS_PER_LAYER;
7842 let max_depth = self.core.mouse_hit_test.ids.len() as f32 - 0.5;
7843 if *depth < 0.0 {
7844 *depth = 0.0;
7845 } else if *depth > max_depth {
7846 *depth = max_depth;
7847 }
7848 if let Some((_, inspector_id)) = self
7849 .hovered_inspector_hitbox(inspector, &self.frame_state.rendered_frame)
7850 {
7851 inspector.set_active_element_id(inspector_id, self);
7852 }
7853 }
7854 });
7855 }
7856 }
7857 }
7858
7859 #[cfg(any(feature = "inspector", debug_assertions))]
7860 fn hovered_inspector_hitbox(
7861 &self,
7862 inspector: &Inspector,
7863 frame: &Frame,
7864 ) -> Option<(HitboxId, crate::InspectorElementId)> {
7865 if let Some(pick_depth) = inspector.pick_depth {
7866 let depth = (pick_depth as i64).try_into().unwrap_or(0);
7867 let max_skipped = self.core.mouse_hit_test.ids.len().saturating_sub(1);
7868 let skip_count = (depth as usize).min(max_skipped);
7869 for hitbox_id in self.core.mouse_hit_test.ids.iter().skip(skip_count) {
7870 if let Some(inspector_id) = frame.inspector_hitboxes.get(hitbox_id) {
7871 return Some((*hitbox_id, inspector_id.clone()));
7872 }
7873 }
7874 }
7875 None
7876 }
7877
7878 #[cfg(any(test, feature = "test-support"))]
7881 pub fn set_modifiers(&mut self, modifiers: Modifiers) {
7882 self.core.modifiers = modifiers;
7883 }
7884
7885 #[cfg(any(test, feature = "test-support"))]
7889 pub fn simulate_mouse_move(&mut self, position: Point<Pixels>, cx: &mut App) {
7890 let event = PlatformInput::MouseMove(MouseMoveEvent {
7891 position,
7892 modifiers: self.core.modifiers,
7893 pressed_button: None,
7894 });
7895 let _ = self.dispatch_event(event, cx);
7896 }
7897
7898 #[inline(never)]
7899 fn take_element_state(
7900 &mut self,
7901 global_id: &GlobalElementId,
7902 state_type: TypeId,
7903 ) -> ((GlobalElementId, TypeId), Option<ElementStateBox>) {
7904 let key = (global_id.clone(), state_type);
7905 self.frame_state
7906 .next_frame
7907 .accessed_element_states
7908 .push(key.clone());
7909 let state = self
7910 .frame_state
7911 .next_frame
7912 .element_states
7913 .remove(&key)
7914 .or_else(|| self.frame_state.rendered_frame.element_states.remove(&key));
7915 (key, state)
7916 }
7917
7918 #[inline(never)]
7919 fn insert_element_state(
7920 &mut self,
7921 key: (GlobalElementId, TypeId),
7922 state: ElementStateBox,
7923 ) -> Option<ElementStateBox> {
7924 self.frame_state
7925 .next_frame
7926 .element_states
7927 .insert(key, state)
7928 }
7929
7930 #[inline(never)]
7931 fn observe_keyed_state<S: 'static>(state: &Entity<S>, current_view: EntityId, cx: &mut App) {
7932 cx.observe(state, move |_, cx| {
7933 cx.notify(current_view);
7934 })
7935 .detach();
7936 }
7937}
7938
7939#[derive(Deref, DerefMut)]
7942pub struct WindowHandle<V> {
7943 #[deref]
7944 #[deref_mut]
7945 pub(crate) any_handle: AnyWindowHandle,
7946 state_type: PhantomData<fn(V) -> V>,
7947}
7948
7949impl<V> Debug for WindowHandle<V> {
7950 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
7951 f.debug_struct("WindowHandle")
7952 .field("any_handle", &self.any_handle.id.as_u64())
7953 .finish()
7954 }
7955}
7956
7957impl<V: 'static + Render> WindowHandle<V> {
7958 pub fn new(id: WindowId) -> Self {
7961 WindowHandle {
7962 any_handle: AnyWindowHandle {
7963 id,
7964 state_type: TypeId::of::<V>(),
7965 root_entity_type_name: std::any::type_name::<V>(),
7966 },
7967 state_type: PhantomData,
7968 }
7969 }
7970
7971 #[cfg(any(test, feature = "test-support"))]
7975 pub fn root<C>(&self, cx: &mut C) -> Result<Entity<V>>
7976 where
7977 C: AppContext,
7978 {
7979 cx.update_window(self.any_handle, |root_view, _, _| {
7980 root_view
7981 .downcast::<V>()
7982 .map_err(|_| anyhow!("the type of the window's root view has changed"))
7983 })?
7984 }
7985
7986 pub fn update<C, R>(
7990 &self,
7991 cx: &mut C,
7992 update: impl FnOnce(&mut V, &mut Window, &mut Context<V>) -> R,
7993 ) -> Result<R>
7994 where
7995 C: AppContext,
7996 {
7997 cx.update_window(self.any_handle, |root_view, window, cx| {
7998 let view = root_view
7999 .downcast::<V>()
8000 .map_err(|_| anyhow!("the type of the window's root view has changed"))?;
8001
8002 Ok(view.update(cx, |view, cx| update(view, window, cx)))
8003 })?
8004 }
8005
8006 pub fn read<'a>(&self, cx: &'a App) -> Result<&'a V> {
8010 let x = cx
8011 .windows
8012 .cell(self.id)
8013 .and_then(|cell| cell.borrow().core.root.clone())
8014 .map(|root_view| root_view.downcast::<V>())
8015 .context("window not found")?
8016 .map_err(|_| anyhow!("the type of the window's root view has changed"))?;
8017
8018 Ok(x.read(cx))
8019 }
8020
8021 pub fn read_with<C, R>(&self, cx: &C, read_with: impl FnOnce(&V, &App) -> R) -> Result<R>
8025 where
8026 C: AppContext,
8027 {
8028 cx.read_window(self, |root_view, cx| read_with(root_view.read(cx), cx))
8029 }
8030
8031 pub fn entity<C>(&self, cx: &C) -> Result<Entity<V>>
8035 where
8036 C: AppContext,
8037 {
8038 cx.read_window(self, |root_view, _cx| root_view)
8039 }
8040
8041 pub fn is_active(&self, cx: &mut App) -> Option<bool> {
8046 cx.update_window(self.any_handle, |_, window, _| window.is_window_active())
8047 .ok()
8048 }
8049}
8050
8051impl<V> Copy for WindowHandle<V> {}
8052
8053impl<V> Clone for WindowHandle<V> {
8054 fn clone(&self) -> Self {
8055 *self
8056 }
8057}
8058
8059impl<V> PartialEq for WindowHandle<V> {
8060 fn eq(&self, other: &Self) -> bool {
8061 self.any_handle == other.any_handle
8062 }
8063}
8064
8065impl<V> Eq for WindowHandle<V> {}
8066
8067impl<V> Hash for WindowHandle<V> {
8068 fn hash<H: Hasher>(&self, state: &mut H) {
8069 self.any_handle.hash(state);
8070 }
8071}
8072
8073impl<V: 'static> From<WindowHandle<V>> for AnyWindowHandle {
8074 fn from(val: WindowHandle<V>) -> Self {
8075 val.any_handle
8076 }
8077}
8078
8079#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
8081pub struct AnyWindowHandle {
8082 pub(crate) id: WindowId,
8083 state_type: TypeId,
8084 root_entity_type_name: &'static str,
8085}
8086
8087impl AnyWindowHandle {
8088 pub fn window_id(&self) -> WindowId {
8090 self.id
8091 }
8092
8093 pub fn metrics(&self, cx: &App) -> Option<WindowMetricsHandle> {
8100 cx.windows
8101 .cell(self.id)
8102 .map(|cell| cell.borrow().core.metrics_handle())
8103 }
8104
8105 pub fn root_entity_type_name(&self) -> &'static str {
8107 self.root_entity_type_name
8108 }
8109
8110 pub fn downcast<T: 'static>(&self) -> Option<WindowHandle<T>> {
8113 if TypeId::of::<T>() == self.state_type {
8114 Some(WindowHandle {
8115 any_handle: *self,
8116 state_type: PhantomData,
8117 })
8118 } else {
8119 None
8120 }
8121 }
8122
8123 pub fn update<C, R>(
8127 self,
8128 cx: &mut C,
8129 update: impl FnOnce(AnyView, &mut Window, &mut App) -> R,
8130 ) -> Result<R>
8131 where
8132 C: AppContext,
8133 {
8134 cx.update_window(self, update)
8135 }
8136
8137 pub fn read<T, C, R>(self, cx: &C, read: impl FnOnce(Entity<T>, &App) -> R) -> Result<R>
8141 where
8142 C: AppContext,
8143 T: 'static,
8144 {
8145 let view = self
8146 .downcast::<T>()
8147 .context("the type of the window's root view has changed")?;
8148
8149 cx.read_window(&view, read)
8150 }
8151}
8152
8153impl HasWindowHandle for Window<'_> {
8154 fn window_handle(&self) -> Result<raw_window_handle::WindowHandle<'_>, HandleError> {
8155 self.core.platform_window.window_handle()
8156 }
8157}
8158
8159impl HasDisplayHandle for Window<'_> {
8160 fn display_handle(
8161 &self,
8162 ) -> std::result::Result<raw_window_handle::DisplayHandle<'_>, HandleError> {
8163 self.core.platform_window.display_handle()
8164 }
8165}
8166
8167#[derive(Clone, Debug, Eq, PartialEq, Hash)]
8172pub enum ElementId {
8173 View(EntityId),
8175 Integer(u64),
8177 Name(SharedString),
8179 Uuid(Uuid),
8181 FocusHandle(FocusId),
8183 NamedInteger(SharedString, u64),
8185 Path(Arc<std::path::Path>),
8187 CodeLocation(core::panic::Location<'static>),
8189 NamedChild(Arc<ElementId>, SharedString),
8191 OpaqueId([u8; 20]),
8193}
8194
8195impl ElementId {
8196 pub fn named_usize(name: impl Into<SharedString>, integer: usize) -> ElementId {
8198 Self::NamedInteger(name.into(), integer as u64)
8199 }
8200}
8201
8202impl Display for ElementId {
8203 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
8204 match self {
8205 ElementId::View(entity_id) => write!(f, "view-{}", entity_id)?,
8206 ElementId::Integer(ix) => write!(f, "{}", ix)?,
8207 ElementId::Name(name) => write!(f, "{}", name)?,
8208 ElementId::FocusHandle(_) => write!(f, "FocusHandle")?,
8209 ElementId::NamedInteger(s, i) => write!(f, "{}-{}", s, i)?,
8210 ElementId::Uuid(uuid) => write!(f, "{}", uuid)?,
8211 ElementId::Path(path) => write!(f, "{}", path.display())?,
8212 ElementId::CodeLocation(location) => write!(f, "{}", location)?,
8213 ElementId::NamedChild(id, name) => write!(f, "{}-{}", id, name)?,
8214 ElementId::OpaqueId(opaque_id) => write!(f, "{:x?}", opaque_id)?,
8215 }
8216
8217 Ok(())
8218 }
8219}
8220
8221impl TryInto<SharedString> for ElementId {
8222 type Error = anyhow::Error;
8223
8224 fn try_into(self) -> anyhow::Result<SharedString> {
8225 if let ElementId::Name(name) = self {
8226 Ok(name)
8227 } else {
8228 anyhow::bail!("element id is not string")
8229 }
8230 }
8231}
8232
8233impl From<u64> for ElementId {
8234 fn from(id: u64) -> Self {
8235 ElementId::Integer(id)
8236 }
8237}
8238
8239impl From<usize> for ElementId {
8240 fn from(id: usize) -> Self {
8241 ElementId::Integer(id as u64)
8242 }
8243}
8244
8245impl From<i32> for ElementId {
8246 fn from(id: i32) -> Self {
8247 Self::Integer(id as u64)
8248 }
8249}
8250
8251impl From<SharedString> for ElementId {
8252 fn from(name: SharedString) -> Self {
8253 ElementId::Name(name)
8254 }
8255}
8256
8257impl From<String> for ElementId {
8258 fn from(name: String) -> Self {
8259 ElementId::Name(name.into())
8260 }
8261}
8262
8263impl From<Arc<str>> for ElementId {
8264 fn from(name: Arc<str>) -> Self {
8265 ElementId::Name(name.into())
8266 }
8267}
8268
8269impl From<Arc<std::path::Path>> for ElementId {
8270 fn from(path: Arc<std::path::Path>) -> Self {
8271 ElementId::Path(path)
8272 }
8273}
8274
8275impl From<&'static str> for ElementId {
8276 fn from(name: &'static str) -> Self {
8277 ElementId::Name(SharedString::new_static(name))
8278 }
8279}
8280
8281impl<'a> From<&'a FocusHandle> for ElementId {
8282 fn from(handle: &'a FocusHandle) -> Self {
8283 ElementId::FocusHandle(handle.id)
8284 }
8285}
8286
8287impl From<(&'static str, EntityId)> for ElementId {
8288 fn from((name, id): (&'static str, EntityId)) -> Self {
8289 ElementId::NamedInteger(SharedString::new_static(name), id.as_u64())
8290 }
8291}
8292
8293impl From<(&'static str, usize)> for ElementId {
8294 fn from((name, id): (&'static str, usize)) -> Self {
8295 ElementId::NamedInteger(SharedString::new_static(name), id as u64)
8296 }
8297}
8298
8299impl From<(SharedString, usize)> for ElementId {
8300 fn from((name, id): (SharedString, usize)) -> Self {
8301 ElementId::NamedInteger(name, id as u64)
8302 }
8303}
8304
8305impl From<(&'static str, u64)> for ElementId {
8306 fn from((name, id): (&'static str, u64)) -> Self {
8307 ElementId::NamedInteger(SharedString::new_static(name), id)
8308 }
8309}
8310
8311impl From<Uuid> for ElementId {
8312 fn from(value: Uuid) -> Self {
8313 Self::Uuid(value)
8314 }
8315}
8316
8317impl From<(&'static str, u32)> for ElementId {
8318 fn from((name, id): (&'static str, u32)) -> Self {
8319 ElementId::NamedInteger(SharedString::new_static(name), u64::from(id))
8320 }
8321}
8322
8323impl<T: Into<SharedString>> From<(ElementId, T)> for ElementId {
8324 fn from((id, name): (ElementId, T)) -> Self {
8325 ElementId::NamedChild(Arc::new(id), name.into())
8326 }
8327}
8328
8329impl From<&'static core::panic::Location<'static>> for ElementId {
8330 fn from(location: &'static core::panic::Location<'static>) -> Self {
8331 ElementId::CodeLocation(*location)
8332 }
8333}
8334
8335impl From<[u8; 20]> for ElementId {
8336 fn from(opaque_id: [u8; 20]) -> Self {
8337 ElementId::OpaqueId(opaque_id)
8338 }
8339}
8340
8341#[derive(Clone)]
8344pub struct PaintQuad {
8345 pub bounds: Bounds<Pixels>,
8347 pub corner_radii: Corners<Pixels>,
8349 pub background: Background,
8351 pub border_widths: Edges<Pixels>,
8353 pub border_color: Hsla,
8355 pub border_style: BorderStyle,
8357}
8358
8359impl PaintQuad {
8360 pub fn corner_radii(self, corner_radii: impl Into<Corners<Pixels>>) -> Self {
8362 PaintQuad {
8363 corner_radii: corner_radii.into(),
8364 ..self
8365 }
8366 }
8367
8368 pub fn border_widths(self, border_widths: impl Into<Edges<Pixels>>) -> Self {
8370 PaintQuad {
8371 border_widths: border_widths.into(),
8372 ..self
8373 }
8374 }
8375
8376 pub fn border_color(self, border_color: impl Into<Hsla>) -> Self {
8378 PaintQuad {
8379 border_color: border_color.into(),
8380 ..self
8381 }
8382 }
8383
8384 pub fn background(self, background: impl Into<Background>) -> Self {
8386 PaintQuad {
8387 background: background.into(),
8388 ..self
8389 }
8390 }
8391}
8392
8393pub fn quad(
8395 bounds: Bounds<Pixels>,
8396 corner_radii: impl Into<Corners<Pixels>>,
8397 background: impl Into<Background>,
8398 border_widths: impl Into<Edges<Pixels>>,
8399 border_color: impl Into<Hsla>,
8400 border_style: BorderStyle,
8401) -> PaintQuad {
8402 PaintQuad {
8403 bounds,
8404 corner_radii: corner_radii.into(),
8405 background: background.into(),
8406 border_widths: border_widths.into(),
8407 border_color: border_color.into(),
8408 border_style,
8409 }
8410}
8411
8412pub fn fill(bounds: impl Into<Bounds<Pixels>>, background: impl Into<Background>) -> PaintQuad {
8414 PaintQuad {
8415 bounds: bounds.into(),
8416 corner_radii: (0.).into(),
8417 background: background.into(),
8418 border_widths: (0.).into(),
8419 border_color: transparent_black(),
8420 border_style: BorderStyle::default(),
8421 }
8422}
8423
8424pub fn outline(
8426 bounds: impl Into<Bounds<Pixels>>,
8427 border_color: impl Into<Hsla>,
8428 border_style: BorderStyle,
8429) -> PaintQuad {
8430 PaintQuad {
8431 bounds: bounds.into(),
8432 corner_radii: (0.).into(),
8433 background: transparent_black().into(),
8434 border_widths: (1.).into(),
8435 border_color: border_color.into(),
8436 border_style,
8437 }
8438}
8439
8440#[inline(never)]
8441fn with_element_arena_erased(callback: &mut dyn FnMut(&mut Arena)) -> Result<(), AccessError> {
8442 if let Some(arena_pointer) = CURRENT_ELEMENT_ARENA.try_with(Cell::get)? {
8443 let arena_cell = unsafe { &*arena_pointer };
8446 callback(&mut arena_cell.borrow_mut());
8447 Ok(())
8448 } else {
8449 ELEMENT_ARENA.try_with(|arena| callback(&mut arena.borrow_mut()))
8450 }
8451}
8452
8453#[cfg(test)]
8454mod tests {
8455 use std::{
8456 cell::{Cell, RefCell},
8457 path::PathBuf,
8458 rc::Rc,
8459 time::Duration,
8460 };
8461
8462 use crate::{
8463 AnyWindowHandle, App, AppContext as _, ArenaClearNeeded, Bounds, ContentMask, Context,
8464 Corners, CustomRenderPrimitive, DispatchPhase, DragMoveEvent, Empty, ExternalDragPayload,
8465 ExternalPaths, FileDragPaths, FileDropEvent, FocusHandle, FocusId, FramePipeline,
8466 ImportedTextureHandle, InputEvent as _, InteractiveElement as _, IntoElement,
8467 KeyDownEvent, Keystroke, LongPressEvent, MouseButton, MouseDownEvent, MouseMoveEvent,
8468 ParentElement, Pixels, PlatformInput, Point, Render, RequestFrameOptions, ScaledPixels,
8469 StandardImmediatePipeline, StatefulInteractiveElement as _, Styled, TestAppContext,
8470 TouchDragEvent, TouchEvent, TouchId, TouchPhase, Underline, UnderlineStyle, Window,
8471 WindowAppearance, WindowMetrics, WindowOptions, canvas, div, gpu_canvas, hsla, point, px,
8472 size,
8473 };
8474
8475 #[gpui::test]
8479 fn test_window_visibility(cx: &mut TestAppContext) {
8480 use crate::WindowVisibility;
8481
8482 let window = cx.add_window(|_, _| EmptyView);
8483 let observed = Rc::new(RefCell::new(Vec::new()));
8484 let _subscription = window
8485 .update(cx, {
8486 let observed = observed.clone();
8487 move |_, window, _| {
8488 assert_eq!(window.visibility(), WindowVisibility::Visible);
8489 assert!(window.is_visible());
8490 window.observe_window_visibility(move |visibility, window, _| {
8491 assert_eq!(window.visibility(), visibility);
8492 observed.borrow_mut().push(visibility);
8493 })
8494 }
8495 })
8496 .unwrap();
8497 let test_window = cx.test_window(window.into());
8498 let frame_wake_count = test_window.frame_wake_count();
8499
8500 test_window.simulate_visibility_change(WindowVisibility::Hidden);
8501 assert_eq!(*observed.borrow(), [WindowVisibility::Hidden]);
8502 window
8503 .update(cx, |_, window, _| assert!(!window.is_visible()))
8504 .unwrap();
8505
8506 test_window.simulate_visibility_change(WindowVisibility::Hidden);
8508 assert_eq!(observed.borrow().len(), 1);
8509
8510 test_window.simulate_visibility_change(WindowVisibility::Visible);
8511 assert_eq!(
8512 *observed.borrow(),
8513 [WindowVisibility::Hidden, WindowVisibility::Visible]
8514 );
8515 window
8516 .update(cx, |_, window, _| assert!(window.is_visible()))
8517 .unwrap();
8518 assert_eq!(test_window.frame_wake_count(), frame_wake_count);
8519 }
8520
8521 #[gpui::test]
8524 fn a_window_draws_through_its_frame_pipeline(cx: &mut TestAppContext) {
8525 let recorded: Rc<RefCell<Vec<&'static str>>> = Rc::new(RefCell::new(Vec::new()));
8526 let recorder = recorded.clone();
8527 cx.update(move |cx| {
8528 cx.set_frame_pipeline_factory(Rc::new(move |_| {
8529 Box::new(RecordingPipeline(recorder.clone()))
8530 }));
8531 });
8532
8533 let window = cx.add_window(|_, _| EmptyView);
8534 recorded.borrow_mut().clear();
8535 cx.update_window(window.into(), |_, window, cx| window.draw(cx).clear(cx))
8536 .unwrap();
8537
8538 let phases = recorded.borrow();
8539 assert!(
8540 phases.starts_with(&[
8541 "begin_frame",
8542 "draw_roots",
8543 "finish_frame",
8544 "complete_frame",
8545 "end_frame"
8546 ]),
8547 "frame ran {phases:?}"
8548 );
8549 }
8550
8551 struct RecordingPipeline(Rc<RefCell<Vec<&'static str>>>);
8554
8555 impl FramePipeline for RecordingPipeline {
8556 fn begin_frame(&mut self, window: &mut Window, cx: &mut App) {
8557 self.0.borrow_mut().push("begin_frame");
8558 window.begin_frame(cx);
8559 }
8560
8561 fn draw_roots(&mut self, window: &mut Window, cx: &mut App) {
8562 self.0.borrow_mut().push("draw_roots");
8563 window.draw_roots(cx);
8564 }
8565
8566 fn finish_frame(&mut self, window: &mut Window, cx: &mut App) {
8567 self.0.borrow_mut().push("finish_frame");
8568 window.finish_frame(cx);
8569 }
8570
8571 fn complete_frame(&mut self, window: &mut Window, cx: &mut App) -> Option<FocusId> {
8572 self.0.borrow_mut().push("complete_frame");
8573 window.complete_frame(cx)
8574 }
8575
8576 fn end_frame(
8577 &mut self,
8578 window: &mut Window,
8579 cx: &mut App,
8580 focus_before_listeners: Option<FocusId>,
8581 ) {
8582 self.0.borrow_mut().push("end_frame");
8583 window.end_frame(cx, focus_before_listeners);
8584 }
8585 }
8586
8587 #[gpui::test]
8590 fn a_frame_that_skips_drawing_still_completes(cx: &mut TestAppContext) {
8591 cx.skip_drawing();
8592 let window = cx.add_window(|_, _| EmptyView);
8593
8594 cx.update_window(window.into(), |_, window, cx| window.draw(cx).clear(cx))
8595 .unwrap();
8596 }
8597
8598 #[cfg(debug_assertions)]
8601 #[gpui::test]
8602 #[should_panic(expected = "paint_roots ran while the frame was Evaluated")]
8603 fn a_frame_pass_out_of_order_is_rejected(cx: &mut TestAppContext) {
8604 let window = cx.add_window(|_, _| EmptyView);
8605
8606 cx.update_window(window.into(), |_, window, cx| {
8607 window.begin_frame(cx);
8608 let roots = window.evaluate_roots(cx);
8609 window.paint_roots(roots, cx);
8612 })
8613 .unwrap();
8614 }
8615
8616 struct PacingPipeline {
8618 frames: Rc<Cell<usize>>,
8619 asks: Rc<Cell<usize>>,
8620 allow: bool,
8621 }
8622
8623 impl FramePipeline for PacingPipeline {
8624 fn should_render(&mut self, is_dirty: bool, _metrics: &WindowMetrics) -> bool {
8625 self.asks.set(self.asks.get() + 1);
8626 self.allow && is_dirty
8627 }
8628
8629 fn draw(&mut self, window: &mut Window, cx: &mut App) -> ArenaClearNeeded {
8630 self.frames.set(self.frames.get() + 1);
8631 FramePipeline::draw(&mut StandardImmediatePipeline, window, cx)
8632 }
8633 }
8634
8635 fn install_pacing_pipeline(
8637 cx: &mut TestAppContext,
8638 frames: Rc<Cell<usize>>,
8639 asks: Rc<Cell<usize>>,
8640 allow: bool,
8641 ) {
8642 cx.update(move |cx| {
8643 cx.set_frame_pipeline_factory(Rc::new(move |_| {
8644 Box::new(PacingPipeline {
8645 frames: frames.clone(),
8646 asks: asks.clone(),
8647 allow,
8648 })
8649 }));
8650 });
8651 }
8652
8653 #[gpui::test]
8655 fn a_pipeline_can_defer_a_frame(cx: &mut TestAppContext) {
8656 let frames = Rc::new(Cell::new(0));
8657 let asks = Rc::new(Cell::new(0));
8658 install_pacing_pipeline(cx, frames.clone(), asks.clone(), false);
8659 let window = cx.add_window(|_, _| EmptyView);
8660 let test_window = cx.test_window(window.into());
8661 frames.set(0);
8662 asks.set(0);
8663
8664 window.update(cx, |_, _, cx| cx.notify()).unwrap();
8665 test_window.simulate_frame_request(RequestFrameOptions::default());
8666
8667 assert!(asks.get() > 0, "the pipeline was asked about the frame");
8668 assert_eq!(frames.get(), 0, "the deferred frame was never drawn");
8669 }
8670
8671 #[gpui::test]
8673 fn a_pipeline_can_allow_a_frame(cx: &mut TestAppContext) {
8674 let frames = Rc::new(Cell::new(0));
8675 let asks = Rc::new(Cell::new(0));
8676 install_pacing_pipeline(cx, frames.clone(), asks, true);
8677 let window = cx.add_window(|_, _| EmptyView);
8678 frames.set(0);
8679
8680 window.update(cx, |_, _, cx| cx.notify()).unwrap();
8681
8682 assert!(frames.get() > 0, "the allowed frame was drawn");
8683 }
8684
8685 #[gpui::test]
8686 fn test_fully_visible_bounds_preserve_layout_viewport(cx: &mut TestAppContext) {
8687 let window = cx.add_window(|_, _| EmptyView);
8688 let mut platform_window = cx.test_window(window.into());
8689 platform_window.simulate_resize(size(px(400.), px(800.)));
8690 window
8691 .update(cx, |_, window, _| {
8692 assert_eq!(
8693 window.visual_viewport_bounds(),
8694 Bounds::new(Point::default(), size(px(400.), px(800.)))
8695 );
8696 assert_eq!(
8697 window.fully_visible_bounds(),
8698 window.visual_viewport_bounds()
8699 );
8700 })
8701 .unwrap();
8702
8703 platform_window.simulate_frame_request(RequestFrameOptions::default());
8704 let wakes = platform_window.frame_wake_count();
8705 let visual_bounds = Bounds::new(point(px(10.), px(40.)), size(px(380.), px(460.)));
8706 platform_window.simulate_visual_viewport_change(visual_bounds);
8707 assert!(platform_window.frame_wake_count() > wakes);
8708 platform_window.simulate_frame_request(RequestFrameOptions::default());
8709 let wakes = platform_window.frame_wake_count();
8710 platform_window.simulate_insets_change(crate::WindowInsets {
8711 safe_area: crate::Edges {
8712 top: px(60.),
8713 right: px(20.),
8714 bottom: px(30.),
8715 left: px(-10.),
8716 },
8717 ime: crate::Edges {
8718 bottom: px(350.),
8719 ..Default::default()
8720 },
8721 });
8722 assert!(platform_window.frame_wake_count() > wakes);
8723 window
8724 .update(cx, |_, window, _| {
8725 assert_eq!(window.viewport_size(), size(px(400.), px(800.)));
8726 assert_eq!(window.visual_viewport_bounds(), visual_bounds);
8727 assert_eq!(
8728 window.fully_visible_bounds(),
8729 Bounds::new(point(px(10.), px(60.)), size(px(370.), px(390.)))
8730 );
8731 window.request_virtual_keyboard();
8732 window.dismiss_virtual_keyboard();
8733 assert_eq!(window.viewport_size(), size(px(400.), px(800.)));
8734 })
8735 .unwrap();
8736 assert_eq!(platform_window.virtual_keyboard_requests(), 1);
8737 assert_eq!(platform_window.virtual_keyboard_dismissals(), 1);
8738
8739 platform_window.simulate_insets_change(crate::WindowInsets {
8740 safe_area: crate::Edges {
8741 top: px(900.),
8742 left: px(500.),
8743 ..Default::default()
8744 },
8745 ..Default::default()
8746 });
8747 window
8748 .update(cx, |_, window, _| {
8749 assert_eq!(
8750 window.fully_visible_bounds().size,
8751 size(Pixels::ZERO, Pixels::ZERO)
8752 );
8753 })
8754 .unwrap();
8755 }
8756
8757 struct EmptyView;
8758
8759 impl Render for EmptyView {
8760 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
8761 div()
8762 }
8763 }
8764
8765 struct OpensWindowOnPaint {
8766 opened: Rc<Cell<bool>>,
8767 }
8768
8769 impl Render for OpensWindowOnPaint {
8770 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
8771 let opened = self.opened.clone();
8772 div()
8773 .size_full()
8774 .child(canvas(
8775 |_, _, _| {},
8776 move |_, _, _window, cx| {
8777 if !opened.replace(true) {
8778 cx.open_window(WindowOptions::default(), |_, cx| cx.new(|_| EmptyView))
8779 .unwrap();
8780 }
8781 },
8782 ))
8783 .child(div().child("after"))
8787 }
8788 }
8789
8790 #[test]
8796 fn test_window_opened_during_draw_defers_arena_clear() {
8797 let mut cx = TestAppContext::single();
8798
8799 let opened = Rc::new(Cell::new(false));
8800 let window = cx.add_window({
8802 let opened = opened.clone();
8803 move |_, _| OpensWindowOnPaint { opened }
8804 });
8805
8806 assert!(opened.get());
8807 assert_eq!(cx.windows().len(), 2);
8808
8809 cx.update_window(window.into(), |_, window, cx| window.draw(cx).clear(cx))
8812 .unwrap();
8813 }
8814
8815 #[test]
8816 fn test_scale_factor_change_preserves_bounds_and_survives_resize() {
8817 let mut cx = TestAppContext::single();
8818 let window = cx.add_window(|_, _| EmptyView);
8819 let handle: AnyWindowHandle = window.into();
8820 let window_state = |cx: &mut TestAppContext| {
8821 cx.update_window(handle, |_, window, _| {
8822 (
8823 window.scale_factor(),
8824 window.bounds(),
8825 window.viewport_size(),
8826 )
8827 })
8828 .unwrap()
8829 };
8830
8831 let (scale_factor, mut expected_bounds, _) = window_state(&mut cx);
8832 assert_eq!(scale_factor, 2.0);
8833
8834 for (scale_factor, resized_size) in [
8835 (1.0, size(px(800.), px(600.))),
8836 (1.25, size(px(640.), px(480.))),
8837 (2.0, size(px(1024.), px(768.))),
8838 ] {
8839 cx.simulate_window_scale_factor_change(handle, scale_factor);
8840 assert_eq!(
8841 window_state(&mut cx),
8842 (scale_factor, expected_bounds, expected_bounds.size)
8843 );
8844
8845 cx.simulate_window_resize(handle, resized_size);
8846 expected_bounds.size = resized_size;
8847 assert_eq!(
8848 window_state(&mut cx),
8849 (scale_factor, expected_bounds, resized_size)
8850 );
8851 }
8852 }
8853
8854 #[gpui::test]
8855 fn metrics_handle_agrees_with_the_window_and_tracks_resizes(cx: &mut TestAppContext) {
8856 let window = cx.add_window(|_, _| EmptyView);
8857 let handle: AnyWindowHandle = window.into();
8858
8859 let metrics = cx.read(|app| handle.metrics(app).expect("window is open"));
8860 assert_eq!(metrics.id(), handle.window_id());
8861 assert!(metrics.display_id().is_some());
8862
8863 let window_state = |cx: &mut TestAppContext| {
8865 cx.update_window(handle, |_, window, _| {
8866 assert_eq!(metrics.bounds(), window.bounds());
8867 assert_eq!(metrics.viewport(), window.visual_viewport_bounds());
8868 assert_eq!(metrics.content_size(), window.viewport_size());
8869 assert_eq!(metrics.scale_factor(), window.scale_factor());
8870 assert_eq!(metrics.is_active(), window.is_window_active());
8871 assert_eq!(metrics.is_fullscreen(), window.is_fullscreen());
8872 })
8873 .unwrap();
8874 };
8875 window_state(cx);
8876
8877 cx.simulate_window_resize(handle, size(px(800.), px(600.)));
8878 cx.simulate_window_scale_factor_change(handle, 1.25);
8879
8880 assert_eq!(metrics.content_size(), size(px(800.), px(600.)));
8883 assert_eq!(metrics.scale_factor(), 1.25);
8884 window_state(cx);
8885 }
8886
8887 #[gpui::test]
8891 fn metrics_track_viewport_changes_that_do_not_resize(cx: &mut TestAppContext) {
8892 let window = cx.add_window(|_, _| EmptyView);
8893 let handle: AnyWindowHandle = window.into();
8894 let metrics = cx.read(|app| handle.metrics(app).expect("window is open"));
8895
8896 let narrow = Bounds::new(point(px(0.), px(120.)), size(px(400.), px(680.)));
8897 assert_ne!(metrics.viewport(), narrow);
8898
8899 cx.simulate_window_visual_viewport_change(handle, narrow);
8900 assert_eq!(metrics.viewport(), narrow);
8901
8902 cx.update_window(handle, |_, window, _| {
8903 assert_eq!(metrics.viewport(), window.visual_viewport_bounds());
8904 assert_eq!(metrics.content_size(), window.viewport_size());
8906 })
8907 .unwrap();
8908 }
8909
8910 #[gpui::test]
8914 fn updating_a_window_from_within_its_own_update_reports_an_error(cx: &mut TestAppContext) {
8915 let window = cx.add_window(|_, _| EmptyView);
8916 let handle: AnyWindowHandle = window.into();
8917
8918 let nested_error = cx
8919 .update_window(handle, |_, _window, cx| {
8920 cx.update_window(handle, |_, _, _| ())
8921 .map_err(|error| error.to_string())
8922 .unwrap_err()
8923 })
8924 .expect("the outer update completes");
8925
8926 assert!(
8927 nested_error.contains("already being updated"),
8928 "unexpected error: {nested_error}"
8929 );
8930 }
8931
8932 #[gpui::test]
8935 fn a_window_can_be_updated_from_within_another_windows_update(cx: &mut TestAppContext) {
8936 let first: AnyWindowHandle = cx.add_window(|_, _| EmptyView).into();
8937 let second: AnyWindowHandle = cx.add_window(|_, _| EmptyView).into();
8938
8939 let expected = cx
8940 .update_window(second, |_, window, _| window.viewport_size())
8941 .expect("the window can be updated on its own");
8942
8943 let from_inner = cx
8944 .update_window(first, |_, _window, cx| {
8945 cx.update_window(second, |_, window, _| window.viewport_size())
8946 .expect("the inner update succeeds")
8947 })
8948 .expect("the outer update completes");
8949
8950 assert_eq!(from_inner, expected);
8951 }
8952
8953 #[gpui::test]
8956 fn metrics_handle_is_readable_from_another_thread(cx: &mut TestAppContext) {
8957 let window = cx.add_window(|_, _| EmptyView);
8958 let handle: AnyWindowHandle = window.into();
8959 cx.simulate_window_resize(handle, size(px(640.), px(480.)));
8960 let metrics = cx.read(|app| handle.metrics(app).expect("window is open"));
8961
8962 let (id, content_size, scale_factor) = std::thread::spawn(move || {
8963 (metrics.id(), metrics.content_size(), metrics.scale_factor())
8964 })
8965 .join()
8966 .expect("the metrics reader did not panic");
8967
8968 assert_eq!(id, handle.window_id());
8969 assert_eq!(content_size, size(px(640.), px(480.)));
8970 assert_eq!(scale_factor, 2.0);
8971 }
8972
8973 #[gpui::test]
8978 fn test_frame_waker_fires_on_frame_demand(cx: &mut TestAppContext) {
8979 let window = cx.add_window(|_, _| EmptyView);
8980 let test_window = cx.test_window(window.into());
8981
8982 assert!(
8986 test_window.frame_wake_count() >= 1,
8987 "opening a window must wake the frame source for the initial frame"
8988 );
8989
8990 test_window.simulate_frame_request(RequestFrameOptions::default());
8992
8993 let baseline = test_window.frame_wake_count();
8996 test_window.simulate_frame_request(RequestFrameOptions::default());
8997 window.update(cx, |_, _, _| {}).unwrap();
8998 assert_eq!(
8999 test_window.frame_wake_count(),
9000 baseline,
9001 "clean frames and non-notifying updates must not wake the frame source"
9002 );
9003
9004 window.update(cx, |_, _, cx| cx.notify()).unwrap();
9006 assert!(
9007 test_window.frame_wake_count() > baseline,
9008 "notifying a view in an idle window must wake the frame source"
9009 );
9010
9011 test_window.simulate_frame_request(RequestFrameOptions::default());
9013 let baseline = test_window.frame_wake_count();
9014 test_window.simulate_frame_request(RequestFrameOptions::default());
9015 assert_eq!(
9016 test_window.frame_wake_count(),
9017 baseline,
9018 "serving demand must return the window to idle"
9019 );
9020
9021 window
9023 .update(cx, |_, window, _| window.on_next_frame(|_, _| {}))
9024 .unwrap();
9025 assert!(
9026 test_window.frame_wake_count() > baseline,
9027 "scheduling a next-frame callback in an idle window must wake the frame source"
9028 );
9029 }
9030
9031 #[gpui::test]
9036 fn test_pending_next_frame_callbacks_are_not_stranded(cx: &mut TestAppContext) {
9037 let window = cx.add_window(|_, _| EmptyView);
9038 let test_window = cx.test_window(window.into());
9039 test_window.simulate_frame_request(RequestFrameOptions::default());
9042
9043 let callback_ran = Rc::new(Cell::new(false));
9044 window
9045 .update(cx, {
9046 let callback_ran = callback_ran.clone();
9047 move |_, window, _| {
9048 window.on_next_frame(move |_, _| callback_ran.set(true));
9049 }
9050 })
9051 .unwrap();
9052
9053 let baseline = test_window.frame_wake_count();
9054 test_window.simulate_frame_request(RequestFrameOptions::default());
9055 assert!(
9062 test_window.frame_wake_count() > baseline || callback_ran.get(),
9063 "a frame request with pending next-frame callbacks must either run them or re-arm the frame source"
9064 );
9065 }
9066
9067 #[gpui::test]
9068 fn test_window_reports_no_raw_handle_instead_of_panicking(cx: &mut TestAppContext) {
9069 use raw_window_handle::{HandleError, HasDisplayHandle as _, HasWindowHandle as _};
9070
9071 let window = cx.add_window(|_, _| EmptyView);
9072 window
9073 .update(cx, |_, window, _| {
9074 assert!(matches!(
9075 window.window_handle(),
9076 Err(HandleError::NotSupported)
9077 ));
9078 assert!(matches!(
9079 window.display_handle(),
9080 Err(HandleError::NotSupported)
9081 ));
9082 })
9083 .unwrap();
9084 }
9085
9086 #[gpui::test]
9087 fn test_appearance_change_runs_after_app_update(cx: &mut TestAppContext) {
9088 let window = cx.add_window(|_, _| EmptyView);
9089 let observed_appearance = Rc::new(Cell::new(None));
9090 let _subscription = window
9091 .update(cx, {
9092 let observed_appearance = observed_appearance.clone();
9093 move |_, window, _| {
9094 window.observe_window_appearance(move |window, _| {
9095 observed_appearance.set(Some(window.appearance()));
9096 })
9097 }
9098 })
9099 .unwrap();
9100 let test_window = cx.test_window(window.into());
9101
9102 cx.update(|_| {
9103 test_window.simulate_appearance_change(WindowAppearance::Dark);
9104 assert_eq!(observed_appearance.get(), None);
9105 });
9106 cx.run_until_parked();
9107
9108 assert_eq!(observed_appearance.get(), Some(WindowAppearance::Dark));
9109 }
9110
9111 #[gpui::test]
9112 fn queued_frame_callback_wakes_a_parked_render_loop(cx: &mut TestAppContext) {
9113 let window = cx.add_window(|_, _| Empty);
9114 let test_window = cx.test_window(window.into());
9115
9116 assert!(test_window.simulate_scheduled_frame());
9117 assert!(test_window.simulate_scheduled_frame());
9118 assert!(!test_window.frame_scheduled());
9119
9120 cx.update_window(window.into(), |_, window, _| {
9121 window.core.active.set(true);
9122 window.on_next_frame(|_, _| {});
9123 })
9124 .unwrap();
9125 assert!(
9126 test_window.frame_scheduled(),
9127 "queuing work on a parked window must wake the render loop"
9128 );
9129
9130 assert!(test_window.simulate_scheduled_frame());
9131 assert!(
9132 test_window.frame_scheduled(),
9133 "presenting the frame must await one compositor callback"
9134 );
9135 assert!(test_window.simulate_scheduled_frame());
9136 assert!(!test_window.frame_scheduled());
9137 }
9138
9139 #[gpui::test]
9140 fn pending_presentation_wakes_a_parked_render_loop(cx: &mut TestAppContext) {
9141 let window = cx.add_window(|_, _| Empty);
9142 let test_window = cx.test_window(window.into());
9143
9144 assert!(test_window.simulate_scheduled_frame());
9145 assert!(test_window.simulate_scheduled_frame());
9146 assert!(!test_window.frame_scheduled());
9147
9148 cx.update_window(window.into(), |_, window, cx| window.draw(cx).clear(cx))
9149 .unwrap();
9150
9151 assert!(
9152 test_window.frame_scheduled(),
9153 "a rendered scene awaiting presentation must wake the render loop"
9154 );
9155 }
9156
9157 #[gpui::test]
9158 fn callback_queued_during_a_frame_requests_a_follow_up(cx: &mut TestAppContext) {
9159 let window = cx.add_window(|_, _| Empty);
9160 let test_window = cx.test_window(window.into());
9161
9162 let callback_ran = Rc::new(Cell::new(false));
9163 cx.update_window(window.into(), |_, window, _| {
9164 window.core.active.set(true);
9167 let callback_ran = callback_ran.clone();
9168 window.on_next_frame(move |window, _| {
9169 window.on_next_frame(move |_, _| callback_ran.set(true));
9170 });
9171 })
9172 .unwrap();
9173
9174 assert!(test_window.simulate_scheduled_frame());
9175 assert!(!callback_ran.get());
9176 assert!(
9177 test_window.frame_scheduled(),
9178 "a callback queued mid-frame must schedule a follow-up before the loop parks"
9179 );
9180
9181 assert!(test_window.simulate_scheduled_frame());
9182 assert!(callback_ran.get());
9183 }
9184
9185 struct RootView {
9186 explicit_size: bool,
9187 child_bounds: Rc<Cell<Bounds<Pixels>>>,
9188 }
9189
9190 impl Render for RootView {
9191 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
9192 let child_bounds = self.child_bounds.clone();
9193 let root = div().flex().flex_col().child(
9194 canvas(
9195 move |bounds, _, _| child_bounds.set(bounds),
9196 |_, _, _, _| {},
9197 )
9198 .size_full(),
9199 );
9200 if self.explicit_size {
9201 root.w(px(300.)).h(px(200.))
9202 } else {
9203 root
9204 }
9205 }
9206 }
9207
9208 #[test]
9209 fn auto_sized_window_root_fills_the_window() {
9210 let mut cx = TestAppContext::single();
9211 let child_bounds = Rc::new(Cell::new(Bounds::default()));
9212 let window = cx.add_window({
9213 let child_bounds = child_bounds.clone();
9214 move |_, _| RootView {
9215 explicit_size: false,
9216 child_bounds,
9217 }
9218 });
9219
9220 let viewport_size = cx
9221 .update_window(window.into(), |_, window, cx| {
9222 window.draw(cx).clear(cx);
9223 window.viewport_size()
9224 })
9225 .unwrap();
9226
9227 assert_eq!(child_bounds.get().size, viewport_size);
9228 }
9229
9230 #[test]
9231 fn explicitly_sized_window_root_keeps_its_size() {
9232 let mut cx = TestAppContext::single();
9233 let child_bounds = Rc::new(Cell::new(Bounds::default()));
9234 let window = cx.add_window({
9235 let child_bounds = child_bounds.clone();
9236 move |_, _| RootView {
9237 explicit_size: true,
9238 child_bounds,
9239 }
9240 });
9241
9242 cx.update_window(window.into(), |_, window, cx| {
9243 window.draw(cx).clear(cx);
9244 })
9245 .unwrap();
9246
9247 assert_eq!(child_bounds.get().size, size(px(300.), px(200.)));
9248 }
9249
9250 struct FileDragView {
9251 path: PathBuf,
9252 observed_drag_moves: Rc<RefCell<Vec<Point<Pixels>>>>,
9253 observed_drops: Rc<RefCell<Vec<PathBuf>>>,
9254 }
9255
9256 struct FileDropExitView(Rc<Cell<usize>>);
9257
9258 impl Render for FileDropExitView {
9259 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
9260 div().size_full().on_file_drop_exit({
9261 let observed_file_drop_exit = self.0.clone();
9262 move |_, _, _| observed_file_drop_exit.set(observed_file_drop_exit.get() + 1)
9263 })
9264 }
9265 }
9266
9267 impl Render for FileDragView {
9268 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
9269 div()
9270 .id("file-drag")
9271 .size_full()
9272 .on_drag(self.path.clone(), |_, _, _, cx| cx.new(|_| Empty))
9273 .external_drag_payload(|path: &PathBuf, _, _| {
9274 Some(ExternalDragPayload::Files(FileDragPaths::new([(
9275 path.clone(),
9276 true,
9277 )])))
9278 })
9279 .on_drag_move({
9280 let observed_drag_moves = self.observed_drag_moves.clone();
9281 move |event: &DragMoveEvent<PathBuf>, _, _| {
9282 observed_drag_moves.borrow_mut().push(event.event.position);
9283 }
9284 })
9285 .on_drop({
9286 let observed_drops = self.observed_drops.clone();
9287 move |path: &PathBuf, _, _| observed_drops.borrow_mut().push(path.clone())
9288 })
9289 }
9290 }
9291
9292 #[gpui::test]
9293 fn file_drag_is_promoted_once_and_restored_in_source_window(cx: &mut TestAppContext) {
9294 struct Drag {
9295 window: AnyWindowHandle,
9296 observed_drag_moves: Rc<RefCell<Vec<Point<Pixels>>>>,
9297 observed_drops: Rc<RefCell<Vec<PathBuf>>>,
9298 }
9299
9300 fn start_drag(cx: &mut TestAppContext, path: PathBuf, platform_result: bool) -> Drag {
9301 let observed_drag_moves = Rc::new(RefCell::new(Vec::new()));
9302 let observed_drops = Rc::new(RefCell::new(Vec::new()));
9303 let window: AnyWindowHandle = cx
9304 .add_window({
9305 let observed_drag_moves = observed_drag_moves.clone();
9306 let observed_drops = observed_drops.clone();
9307 move |_, _| FileDragView {
9308 path,
9309 observed_drag_moves,
9310 observed_drops,
9311 }
9312 })
9313 .into();
9314 cx.test_window(window)
9315 .set_start_external_drag_result(platform_result);
9316
9317 let update_result = cx.update_window(window, |_, window, cx| {
9318 window.draw(cx).clear(cx);
9319 window.dispatch_event(
9320 MouseDownEvent {
9321 position: point(px(10.), px(10.)),
9322 button: MouseButton::Left,
9323 modifiers: Default::default(),
9324 click_count: 1,
9325 first_mouse: false,
9326 }
9327 .to_platform_input(),
9328 cx,
9329 );
9330 window.dispatch_event(
9331 MouseMoveEvent {
9332 position: point(px(20.), px(20.)),
9333 pressed_button: Some(MouseButton::Left),
9334 modifiers: Default::default(),
9335 }
9336 .to_platform_input(),
9337 cx,
9338 );
9339 assert!(cx.active_drag.is_some());
9340 });
9341 assert!(
9342 update_result.is_ok(),
9343 "failed to start drag: {update_result:?}"
9344 );
9345
9346 assert!(cx.test_window(window).external_drag_files().is_empty());
9347 Drag {
9348 window,
9349 observed_drag_moves,
9350 observed_drops,
9351 }
9352 }
9353
9354 let successful_path = PathBuf::from("/tmp/successful-drag");
9355 let successful = start_drag(cx, successful_path.clone(), true);
9356 let outside_position = point(px(-1.), px(20.));
9357 let update_result = cx.update_window(successful.window, |_, window, cx| {
9358 window.dispatch_event(
9359 MouseMoveEvent {
9360 position: outside_position,
9361 pressed_button: Some(MouseButton::Left),
9362 modifiers: Default::default(),
9363 }
9364 .to_platform_input(),
9365 cx,
9366 );
9367 assert!(cx.active_drag.is_none());
9368 });
9369 assert!(
9370 update_result.is_ok(),
9371 "failed to promote drag: {update_result:?}"
9372 );
9373 assert_eq!(
9374 cx.test_window(successful.window).external_drag_files(),
9375 [(successful_path.clone(), true)]
9376 );
9377 assert_eq!(
9380 successful.observed_drag_moves.borrow().last(),
9381 Some(&outside_position)
9382 );
9383
9384 let first_destination_exit_count = Rc::new(Cell::new(0));
9385 let first_destination: AnyWindowHandle = cx
9386 .add_window({
9387 let first_destination_exit_count = first_destination_exit_count.clone();
9388 move |_, _| FileDropExitView(first_destination_exit_count)
9389 })
9390 .into();
9391 let second_destination_exit_count = Rc::new(Cell::new(0));
9392 let second_destination: AnyWindowHandle = cx
9393 .add_window({
9394 let second_destination_exit_count = second_destination_exit_count.clone();
9395 move |_, _| FileDropExitView(second_destination_exit_count)
9396 })
9397 .into();
9398 let reentry_position = point(px(30.), px(30.));
9399 let external_paths = || ExternalPaths([successful_path.clone()].into_iter().collect());
9400 let update_result = cx.update_window(first_destination, |_, window, cx| {
9401 window.draw(cx).clear(cx);
9402 window.dispatch_event(
9403 FileDropEvent::Entered {
9404 position: reentry_position,
9405 paths: external_paths(),
9406 }
9407 .to_platform_input(),
9408 cx,
9409 );
9410 assert!(
9411 cx.active_drag
9412 .as_ref()
9413 .is_some_and(|drag| drag.value.downcast_ref::<ExternalPaths>().is_some())
9414 );
9415 window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx);
9416 assert!(cx.active_drag.is_none());
9417 assert_eq!(first_destination_exit_count.get(), 1);
9418 assert_eq!(second_destination_exit_count.get(), 0);
9419 });
9420 assert!(
9421 update_result.is_ok(),
9422 "failed to handle drag in first destination window: {update_result:?}"
9423 );
9424
9425 let update_result = cx.update_window(second_destination, |_, window, cx| {
9426 window.draw(cx).clear(cx);
9427 window.dispatch_event(
9428 PlatformInput::KeyDown(KeyDownEvent {
9429 keystroke: Keystroke::parse("down").expect("valid keystroke"),
9430 is_held: false,
9431 prefer_character_input: false,
9432 }),
9433 cx,
9434 );
9435 window.dispatch_event(
9436 FileDropEvent::Entered {
9437 position: reentry_position,
9438 paths: external_paths(),
9439 }
9440 .to_platform_input(),
9441 cx,
9442 );
9443 assert!(
9444 cx.active_drag
9445 .as_ref()
9446 .is_some_and(|drag| drag.value.downcast_ref::<ExternalPaths>().is_some())
9447 );
9448 assert_eq!(first_destination_exit_count.get(), 1);
9449 assert_eq!(second_destination_exit_count.get(), 0);
9450
9451 window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx);
9452 assert!(cx.active_drag.is_none());
9453 assert_eq!(first_destination_exit_count.get(), 1);
9454 assert_eq!(second_destination_exit_count.get(), 1);
9455 });
9456 assert!(
9457 update_result.is_ok(),
9458 "failed to handle drag in second destination window: {update_result:?}"
9459 );
9460
9461 let update_result = cx.update_window(successful.window, |_, window, cx| {
9462 window.dispatch_event(
9463 FileDropEvent::Entered {
9464 position: reentry_position,
9465 paths: external_paths(),
9466 }
9467 .to_platform_input(),
9468 cx,
9469 );
9470 assert!(
9471 cx.active_drag
9472 .as_ref()
9473 .is_some_and(|drag| drag.value.downcast_ref::<PathBuf>().is_some())
9474 );
9475 assert_eq!(
9476 successful.observed_drag_moves.borrow().last(),
9477 Some(&reentry_position)
9478 );
9479
9480 window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx);
9481 assert!(cx.active_drag.is_none());
9482
9483 window.dispatch_event(
9484 FileDropEvent::Entered {
9485 position: reentry_position,
9486 paths: external_paths(),
9487 }
9488 .to_platform_input(),
9489 cx,
9490 );
9491 assert!(
9492 cx.active_drag
9493 .as_ref()
9494 .is_some_and(|drag| drag.value.downcast_ref::<PathBuf>().is_some())
9495 );
9496
9497 window.dispatch_event(
9498 FileDropEvent::Submit {
9499 position: reentry_position,
9500 }
9501 .to_platform_input(),
9502 cx,
9503 );
9504 assert_eq!(
9505 successful.observed_drops.borrow().as_slice(),
9506 std::slice::from_ref(&successful_path)
9507 );
9508 assert!(cx.active_drag.is_none());
9509
9510 window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx);
9511 assert!(cx.active_drag.is_none());
9512 window.dispatch_event(FileDropEvent::Ended.to_platform_input(), cx);
9513 assert!(cx.active_drag.is_none());
9514
9515 window.dispatch_event(
9516 FileDropEvent::Entered {
9517 position: reentry_position,
9518 paths: external_paths(),
9519 }
9520 .to_platform_input(),
9521 cx,
9522 );
9523 assert!(
9524 cx.active_drag
9525 .as_ref()
9526 .is_some_and(|drag| drag.value.downcast_ref::<ExternalPaths>().is_some())
9527 );
9528 window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx);
9529 });
9530 assert!(
9531 update_result.is_ok(),
9532 "failed to restore drag in source window: {update_result:?}"
9533 );
9534
9535 let cancelled_path = PathBuf::from("/tmp/cancelled-drag");
9536 let cancelled = start_drag(cx, cancelled_path.clone(), true);
9537 let update_result = cx.update_window(cancelled.window, |_, window, cx| {
9538 window.dispatch_event(
9539 MouseMoveEvent {
9540 position: outside_position,
9541 pressed_button: Some(MouseButton::Left),
9542 modifiers: Default::default(),
9543 }
9544 .to_platform_input(),
9545 cx,
9546 );
9547 assert!(cx.active_drag.is_none());
9548
9549 window.dispatch_event(
9550 FileDropEvent::Entered {
9551 position: reentry_position,
9552 paths: ExternalPaths([cancelled_path].into_iter().collect()),
9553 }
9554 .to_platform_input(),
9555 cx,
9556 );
9557 assert!(
9558 cx.active_drag
9559 .as_ref()
9560 .is_some_and(|drag| drag.value.downcast_ref::<PathBuf>().is_some())
9561 );
9562 assert!(cx.stop_active_drag(window));
9563 assert!(cx.active_drag.is_none());
9564 });
9565 assert!(
9566 update_result.is_ok(),
9567 "failed to cancel restored drag: {update_result:?}"
9568 );
9569 assert!(!cx.update(|cx| cx.end_platform_drag(cancelled.window.window_id())));
9570
9571 let removed_path = PathBuf::from("/tmp/removed-window-drag");
9572 let removed = start_drag(cx, removed_path, true);
9573 let removed_window_id = removed.window.window_id();
9574 let update_result = cx.update_window(removed.window, |_, window, cx| {
9575 window.dispatch_event(
9576 MouseMoveEvent {
9577 position: outside_position,
9578 pressed_button: Some(MouseButton::Left),
9579 modifiers: Default::default(),
9580 }
9581 .to_platform_input(),
9582 cx,
9583 );
9584 assert!(cx.active_drag.is_none());
9585 window.remove_window();
9586 });
9587 assert!(
9588 update_result.is_ok(),
9589 "failed to remove drag source window: {update_result:?}"
9590 );
9591 assert!(!cx.update(|cx| cx.end_platform_drag(removed_window_id)));
9592
9593 let failed_path = PathBuf::from("/tmp/failed-drag");
9594 let failed = start_drag(cx, failed_path.clone(), false);
9595 let update_result = cx.update_window(failed.window, |_, window, cx| {
9596 for x_position in [-1., -2.] {
9597 window.dispatch_event(
9598 MouseMoveEvent {
9599 position: point(px(x_position), px(20.)),
9600 pressed_button: Some(MouseButton::Left),
9601 modifiers: Default::default(),
9602 }
9603 .to_platform_input(),
9604 cx,
9605 );
9606 }
9607 assert!(cx.active_drag.is_some());
9608 });
9609 assert!(
9610 update_result.is_ok(),
9611 "failed to retain drag after platform failure: {update_result:?}"
9612 );
9613 assert_eq!(
9614 cx.test_window(failed.window).external_drag_files(),
9615 [(failed_path, true)]
9616 );
9617 }
9618
9619 struct FocusForwarder {
9620 a: FocusHandle,
9621 b: FocusHandle,
9622 }
9623
9624 impl Render for FocusForwarder {
9625 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
9626 div()
9627 .size_full()
9628 .child(div().w(px(50.)).h(px(50.)).track_focus(&self.a))
9629 .child(div().w(px(50.)).h(px(50.)).track_focus(&self.b))
9630 }
9631 }
9632
9633 #[gpui::test]
9637 fn test_focus_moved_by_focus_listener_is_dispatched(cx: &mut TestAppContext) {
9638 let b_focus_count = Rc::new(Cell::new(0));
9639 let window = cx.add_window({
9640 let b_focus_count = b_focus_count.clone();
9641 move |window, cx| {
9642 let a = cx.focus_handle();
9643 let b = cx.focus_handle();
9644 cx.on_focus(&a, window, |this: &mut FocusForwarder, window, cx| {
9645 let b = this.b.clone();
9646 window.focus(&b, cx);
9647 })
9648 .detach();
9649 cx.on_focus(&b, window, move |_, _, _| {
9650 b_focus_count.set(b_focus_count.get() + 1);
9651 })
9652 .detach();
9653 FocusForwarder { a, b }
9654 }
9655 });
9656
9657 window
9658 .update(cx, |_, window, _| window.activate_window())
9659 .unwrap();
9660 cx.executor().run_until_parked();
9661
9662 window
9663 .update(cx, |this, window, cx| {
9664 let a = this.a.clone();
9665 window.focus(&a, cx);
9666 })
9667 .unwrap();
9668 cx.executor().run_until_parked();
9669
9670 window
9671 .update(cx, |this, window, _| {
9672 assert!(this.b.is_focused(window));
9673 })
9674 .unwrap();
9675 assert_eq!(b_focus_count.get(), 1);
9676 }
9677
9678 #[gpui::test]
9679 fn claimed_touch_drag_receives_movement_and_release(cx: &mut TestAppContext) {
9680 let events = Rc::new(RefCell::new(Vec::new()));
9681 let window = cx.add_window({
9682 let events = events.clone();
9683 move |_, _| TouchDragListener { events }
9684 });
9685 let touch = TouchId(1);
9686
9687 dispatch_touch(window, cx, touch, TouchPhase::Started, 10.);
9688 dispatch_touch(window, cx, touch, TouchPhase::Moved, 30.);
9689 dispatch_touch(window, cx, touch, TouchPhase::Ended, 40.);
9690
9691 assert_eq!(
9692 events.borrow().as_slice(),
9693 [
9694 (TouchPhase::Started, px(10.)),
9695 (TouchPhase::Moved, px(30.)),
9696 (TouchPhase::Ended, px(40.)),
9697 ]
9698 );
9699 }
9700
9701 struct TouchDragListener {
9702 events: Rc<RefCell<Vec<(TouchPhase, Pixels)>>>,
9703 }
9704
9705 impl Render for TouchDragListener {
9706 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
9707 let events = self.events.clone();
9708 canvas(
9709 |_, _, _| {},
9710 move |_, _, window, _| {
9711 window.on_mouse_event(move |event: &TouchDragEvent, phase, window, _cx| {
9712 if phase != DispatchPhase::Bubble {
9713 return;
9714 }
9715 events.borrow_mut().push((event.phase, event.position.x));
9716 if event.phase == TouchPhase::Started {
9717 window.prevent_default();
9718 }
9719 });
9720 },
9721 )
9722 }
9723 }
9724
9725 #[gpui::test]
9726 fn long_press_is_claimed_only_when_started_prevents_default(cx: &mut TestAppContext) {
9727 for response in [
9728 LongPressResponse::PreventDefault,
9729 LongPressResponse::StopPropagation,
9730 LongPressResponse::None,
9731 ] {
9732 let phases = Rc::new(RefCell::new(Vec::new()));
9733 let window = cx.add_window({
9734 let phases = phases.clone();
9735 move |_, _| LongPressListener { phases, response }
9736 });
9737 dispatch_touch(window, cx, TouchId(1), TouchPhase::Started, 0.);
9738 cx.executor().advance_clock(Duration::from_millis(501));
9739 cx.executor().run_until_parked();
9740 window
9741 .update(cx, |_, window, _| {
9742 assert_eq!(
9743 window.core.long_press_capture.is_some(),
9744 response == LongPressResponse::PreventDefault
9745 );
9746 })
9747 .unwrap();
9748 dispatch_touch(window, cx, TouchId(1), TouchPhase::Moved, 2.);
9749 dispatch_touch(window, cx, TouchId(1), TouchPhase::Ended, 2.);
9750 window
9751 .update(cx, |_, window, _| {
9752 assert!(window.core.long_press_capture.is_none());
9753 })
9754 .unwrap();
9755
9756 let phases = phases.borrow();
9757 if response == LongPressResponse::PreventDefault {
9758 assert_eq!(
9759 phases.as_slice(),
9760 [TouchPhase::Started, TouchPhase::Moved, TouchPhase::Ended]
9761 );
9762 } else {
9763 assert_eq!(phases.as_slice(), [TouchPhase::Started]);
9764 }
9765 }
9766 }
9767
9768 #[gpui::test]
9769 fn stale_default_prevention_does_not_claim_long_press(cx: &mut TestAppContext) {
9770 let phases = Rc::new(RefCell::new(Vec::new()));
9771 let window = cx.add_window({
9772 let phases = phases.clone();
9773 move |_, _| LongPressListener {
9774 phases,
9775 response: LongPressResponse::None,
9776 }
9777 });
9778 window
9779 .update(cx, |_, window, _| {
9780 window.prevent_default();
9781 })
9782 .unwrap();
9783
9784 dispatch_touch(window, cx, TouchId(1), TouchPhase::Started, 0.);
9785 cx.executor().advance_clock(Duration::from_millis(501));
9786 cx.executor().run_until_parked();
9787 dispatch_touch(window, cx, TouchId(1), TouchPhase::Moved, 2.);
9788
9789 assert_eq!(phases.borrow().as_slice(), [TouchPhase::Started]);
9790 }
9791
9792 #[gpui::test]
9793 fn resolved_touch_cancels_scheduled_long_press(cx: &mut TestAppContext) {
9794 for (phase, position) in [
9795 (TouchPhase::Ended, 0.),
9796 (TouchPhase::Cancelled, 0.),
9797 (TouchPhase::Moved, 20.),
9798 ] {
9799 let phases = Rc::new(RefCell::new(Vec::new()));
9800 let window = cx.add_window({
9801 let phases = phases.clone();
9802 move |_, _| LongPressListener {
9803 phases,
9804 response: LongPressResponse::PreventDefault,
9805 }
9806 });
9807 dispatch_touch(window, cx, TouchId(1), TouchPhase::Started, 0.);
9808 dispatch_touch(window, cx, TouchId(1), phase, position);
9809 cx.executor().advance_clock(Duration::from_millis(501));
9810 cx.executor().run_until_parked();
9811
9812 assert!(phases.borrow().is_empty(), "{phase:?} allowed long press");
9813 }
9814 }
9815
9816 #[gpui::test]
9817 fn stale_long_press_timer_cannot_affect_replacement_touch(cx: &mut TestAppContext) {
9818 let phases = Rc::new(RefCell::new(Vec::new()));
9819 let window = cx.add_window({
9820 let phases = phases.clone();
9821 move |_, _| LongPressListener {
9822 phases,
9823 response: LongPressResponse::PreventDefault,
9824 }
9825 });
9826 let first_touch = TouchId(1);
9827 dispatch_touch(window, cx, first_touch, TouchPhase::Started, 0.);
9828 cx.executor().advance_clock(Duration::from_millis(250));
9829 dispatch_touch(window, cx, first_touch, TouchPhase::Cancelled, 0.);
9830 dispatch_touch(window, cx, TouchId(2), TouchPhase::Started, 10.);
9831
9832 cx.executor().advance_clock(Duration::from_millis(251));
9833 cx.executor().run_until_parked();
9834 assert!(phases.borrow().is_empty());
9835
9836 cx.executor().advance_clock(Duration::from_millis(250));
9837 cx.executor().run_until_parked();
9838 assert_eq!(phases.borrow().as_slice(), [TouchPhase::Started]);
9839 }
9840
9841 #[gpui::test]
9842 fn test_underline_exclusions_preserve_device_geometry(cx: &mut TestAppContext) {
9843 test_underline_paint_at_scales(cx, |window| {
9844 let (numerator, denominator) = match window.scale_factor() {
9845 1.0 => (1, 1),
9846 1.25 => (5, 4),
9847 1.5 => (3, 2),
9848 2.0 => (2, 1),
9849 3.0 => (3, 1),
9850 scale => panic!("unexpected scale {scale}"),
9851 };
9852 let snap = |quarters: i32| {
9853 let scaled = quarters * numerator;
9854 let divisor = 4 * denominator;
9855 let rounded =
9856 scaled.abs() / divisor + i32::from(2 * (scaled.abs() % divisor) > divisor);
9857 ScaledPixels((scaled.signum() * rounded) as f32)
9858 };
9859 let stroke = |quarters| {
9860 if quarters == 0 {
9861 ScaledPixels(0.)
9862 } else {
9863 snap(quarters).max(ScaledPixels(1.))
9864 }
9865 };
9866
9867 for origin in [-101, -3, -2, -1, 0, 1, 2, 3, 101] {
9868 for width in [0, 1, 3, 84] {
9869 for thickness in [0, 1, 4, 7] {
9870 for wavy in [false, true] {
9871 let style = UnderlineStyle {
9872 thickness: px(thickness as f32 / 4.),
9873 color: Some(hsla(0.25, 0.5, 0.75, 0.5)),
9874 wavy,
9875 };
9876 let bounds = Bounds::new(
9877 point(snap(origin), snap(-origin)),
9878 size(
9879 stroke(width),
9880 stroke(thickness) * if wavy { 3. } else { 1. },
9881 ),
9882 );
9883 let origin = point(px(origin as f32 / 4.), px(-origin as f32 / 4.));
9884 let width = px(width as f32 / 4.);
9885 assert_eq!(window.underline_bounds(origin, width, &style), bounds);
9886 let original = paint_test_underlines(window, |window| {
9887 window.paint_underline(origin, width, &style);
9888 })
9889 .to_vec();
9890 assert_eq!(original.len(), usize::from(!bounds.is_empty()));
9891 if let Some(underline) = original.first() {
9892 assert_eq!(underline.bounds, bounds);
9893 assert_eq!(underline.thickness, stroke(thickness));
9894 assert_eq!(underline.wavy, wavy.into());
9895 assert_eq!(underline.color, hsla(0.25, 0.5, 0.75, 0.5));
9896 }
9897
9898 let underlines = paint_test_underlines(window, |window| {
9899 window.paint_underline_with_exclusions(origin, width, &style, &[]);
9900 });
9901 assert_eq!(underlines.len(), original.len());
9902 for (actual, original) in underlines.iter().zip(&original) {
9903 assert_same_underline_geometry(actual, original);
9904 assert_eq!(actual.content_mask, original.content_mask);
9905 }
9906
9907 let left = bounds.left().0 as i32;
9908 let right = bounds.right().0 as i32;
9909 let exclusions = [
9910 (15, 25),
9911 (3, 7),
9912 (5, 10),
9913 (10, 12),
9914 (0, 0),
9915 (4, 2),
9916 (-50, -1),
9917 (70, 100),
9918 ]
9919 .map(|(start, end)| {
9920 ScaledPixels((left + start) as f32)
9921 ..ScaledPixels((left + end) as f32)
9922 });
9923 let underlines = paint_test_underlines(window, |window| {
9924 window.paint_underline_with_exclusions(
9925 origin,
9926 width,
9927 &style,
9928 &exclusions,
9929 );
9930 });
9931 let expected = (left..right)
9932 .filter(|column| {
9933 !bounds.is_empty()
9934 && !exclusions.iter().any(|span| {
9935 span.contains(&ScaledPixels(*column as f32))
9936 })
9937 })
9938 .collect::<Vec<_>>();
9939 assert_eq!(underline_device_columns(underlines), expected);
9940 for underline in underlines {
9941 assert_same_underline_geometry(underline, &original[0]);
9942 }
9943 }
9944 }
9945 }
9946 }
9947 });
9948 }
9949
9950 #[gpui::test]
9951 fn test_underline_exclusions_preserve_helvetica_warning_edge(cx: &mut TestAppContext) {
9952 test_underline_paint_at_scales(cx, |window| {
9953 if window.scale_factor() != 2.0 {
9954 return;
9955 }
9956 let style = UnderlineStyle {
9957 thickness: px(1.),
9958 color: None,
9959 wavy: true,
9960 };
9961 let origin = point(px(2.888_183_8), px(15.357_917));
9962 let width = px(23.848_145) - origin.x;
9963 let original = paint_test_underlines(window, |window| {
9964 window.paint_underline(origin, width, &style);
9965 })[0];
9966 assert_eq!(original.bounds.left(), ScaledPixels(6.));
9967 assert_eq!(original.bounds.right(), ScaledPixels(48.));
9968 let point_bounds = window.underline_bounds(origin, px(10.829_102), &style);
9969 assert_eq!(point_bounds.left(), ScaledPixels(6.));
9970 assert_eq!(point_bounds.right(), ScaledPixels(28.));
9971
9972 let underlines = paint_test_underlines(window, |window| {
9973 window.paint_underline_with_exclusions(
9974 origin,
9975 width,
9976 &style,
9977 &[point_bounds.left()..point_bounds.right()],
9978 );
9979 });
9980 assert_eq!(underlines.len(), 1);
9981 assert_same_underline_geometry(&underlines[0], &original);
9982 assert_eq!(
9983 underline_device_columns(underlines),
9984 (28..48).collect::<Vec<_>>()
9985 );
9986 });
9987 }
9988
9989 #[gpui::test]
9990 fn test_underline_exclusions_preserve_content_mask_and_opacity(cx: &mut TestAppContext) {
9991 test_underline_paint_at_scales(cx, |window| {
9992 let scale = window.scale_factor();
9993 let original_mask = window.content_mask();
9994 let original_opacity = window.element_opacity();
9995 let mask = ContentMask {
9996 bounds: Bounds::from_corners(point(px(4.), px(10.)), point(px(16.), px(12.))),
9997 };
9998 let style = UnderlineStyle {
9999 thickness: px(2.),
10000 color: Some(hsla(0.25, 0.5, 0.75, 0.5)),
10001 wavy: true,
10002 };
10003 let origin = point(px(-1.25), px(9.25));
10004 let width = px(24.);
10005 window.with_content_mask(Some(mask), |window| {
10006 window.with_element_opacity(Some(0.5), |window| {
10007 window.with_element_opacity(Some(0.5), |window| {
10008 let original = paint_test_underlines(window, |window| {
10009 window.paint_underline(origin, width, &style);
10010 })[0];
10011 assert_eq!(original.color, hsla(0.25, 0.5, 0.75, 0.125));
10012 let underlines = paint_test_underlines(window, |window| {
10013 window.paint_underline_with_exclusions(
10014 origin,
10015 width,
10016 &style,
10017 &[ScaledPixels(6. * scale)..ScaledPixels(8. * scale)],
10018 );
10019 });
10020 assert_eq!(underlines.len(), 2);
10021 let expected = [(4. * scale, 6. * scale), (8. * scale, 16. * scale)].map(
10022 |(left, right)| {
10023 Bounds::from_corners(
10024 point(ScaledPixels(left), ScaledPixels((10. * scale).floor())),
10025 point(ScaledPixels(right), ScaledPixels((12. * scale).ceil())),
10026 )
10027 },
10028 );
10029 assert_eq!(
10030 underlines
10031 .iter()
10032 .map(|underline| underline.content_mask.bounds)
10033 .collect::<Vec<_>>(),
10034 expected
10035 );
10036 for underline in underlines {
10037 assert_same_underline_geometry(underline, &original);
10038 }
10039 });
10040 });
10041 });
10042 assert_eq!(window.content_mask(), original_mask);
10043 assert_eq!(window.element_opacity(), original_opacity);
10044 });
10045 }
10046
10047 #[gpui::test]
10048 fn test_underline_exclusions_adversarial_permutations(cx: &mut TestAppContext) {
10049 test_underline_paint_at_scales(cx, |window| {
10050 let style = UnderlineStyle {
10051 thickness: px(1.),
10052 color: None,
10053 wavy: true,
10054 };
10055 let origin = point(px(-4.), px(-1.));
10056 let width = px(8.);
10057 let left = (-4. * window.scale_factor()) as i32;
10058 let right = (4. * window.scale_factor()) as i32;
10059 let endpoints = [-16, -8, -1, 0, 1, 8, 16];
10060 for first_start in endpoints {
10061 for first_end in endpoints {
10062 for second_start in endpoints {
10063 for second_end in endpoints {
10064 let exclusions = [first_start..first_end, second_start..second_end];
10065 let expected = (left..right)
10066 .filter(|column| {
10067 !exclusions.iter().any(|span| span.contains(column))
10068 })
10069 .collect::<Vec<_>>();
10070 let exclusions = exclusions.map(|span| {
10071 ScaledPixels(span.start as f32)..ScaledPixels(span.end as f32)
10072 });
10073 let underlines = paint_test_underlines(window, |window| {
10074 window.paint_underline_with_exclusions(
10075 origin,
10076 width,
10077 &style,
10078 &exclusions,
10079 );
10080 });
10081 assert_eq!(
10082 underline_device_columns(underlines),
10083 expected,
10084 "{exclusions:?}"
10085 );
10086 }
10087 }
10088 }
10089 }
10090 });
10091 }
10092
10093 #[derive(Clone, Copy, PartialEq)]
10094 enum LongPressResponse {
10095 PreventDefault,
10096 StopPropagation,
10097 None,
10098 }
10099
10100 struct LongPressListener {
10101 phases: Rc<RefCell<Vec<TouchPhase>>>,
10102 response: LongPressResponse,
10103 }
10104
10105 impl Render for LongPressListener {
10106 fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
10107 let entity = cx.entity();
10108 let phases = self.phases.clone();
10109 let response = self.response;
10110 canvas(
10111 |_, _, _| {},
10112 move |_, _, window, _| {
10113 window.on_mouse_event(move |event: &LongPressEvent, phase, window, cx| {
10114 if phase != DispatchPhase::Bubble {
10115 return;
10116 }
10117 phases.borrow_mut().push(event.phase);
10118 match response {
10119 LongPressResponse::PreventDefault => {
10120 window.capture_long_press(&entity);
10121 window.prevent_default();
10122 }
10123 LongPressResponse::StopPropagation => cx.stop_propagation(),
10124 LongPressResponse::None => {}
10125 }
10126 });
10127 },
10128 )
10129 }
10130 }
10131
10132 struct UnderlineTestView(Rc<dyn Fn(&mut Window)>);
10133
10134 impl Render for UnderlineTestView {
10135 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
10136 let paint = self.0.clone();
10137 canvas(
10138 |_, _, _| {},
10139 move |_, _, window, _| {
10140 window.frame_state.content_mask_stack.push(ContentMask {
10141 bounds: Bounds::from_corners(
10142 point(px(-1000.), px(-1000.)),
10143 point(px(1000.), px(1000.)),
10144 ),
10145 });
10146 paint(window);
10147 window.frame_state.content_mask_stack.pop();
10148 },
10149 )
10150 .size_full()
10151 }
10152 }
10153
10154 fn test_underline_paint_at_scales(
10155 cx: &mut TestAppContext,
10156 paint: impl Fn(&mut Window) + 'static,
10157 ) {
10158 let window = cx.add_window(move |_, _| UnderlineTestView(Rc::new(paint)));
10159 for scale in [1., 1.25, 1.5, 2., 3.] {
10160 cx.simulate_window_scale_factor_change(window.into(), scale);
10161 cx.update_window(window.into(), |_, window, cx| window.draw(cx).clear(cx))
10162 .unwrap();
10163 }
10164 }
10165
10166 fn paint_test_underlines<'a>(
10167 window: &'a mut Window<'_>,
10168 paint: impl FnOnce(&mut Window),
10169 ) -> &'a [Underline] {
10170 window.frame_state.next_frame.scene.clear();
10171 paint(window);
10172 &window.frame_state.next_frame.scene.underlines
10173 }
10174
10175 fn assert_same_underline_geometry(actual: &Underline, expected: &Underline) {
10176 assert_eq!(actual.bounds, expected.bounds);
10177 assert_eq!(actual.thickness, expected.thickness);
10178 assert_eq!(actual.wavy, expected.wavy);
10179 assert_eq!(actual.color, expected.color);
10180 assert_eq!(actual.pad, expected.pad);
10181 }
10182
10183 fn underline_device_columns(underlines: &[Underline]) -> Vec<i32> {
10184 underlines
10185 .iter()
10186 .flat_map(|underline| {
10187 let bounds = underline.bounds.intersect(&underline.content_mask.bounds);
10188 bounds.left().0 as i32..bounds.right().0 as i32
10189 })
10190 .collect()
10191 }
10192
10193 fn dispatch_touch<T: 'static>(
10194 window: crate::WindowHandle<T>,
10195 cx: &mut TestAppContext,
10196 id: TouchId,
10197 phase: TouchPhase,
10198 x: f32,
10199 ) {
10200 window
10201 .update(cx, |_, window, cx| {
10202 window.dispatch_event(
10203 TouchEvent {
10204 id,
10205 phase,
10206 position: point(px(x), px(0.)),
10207 predicted_position: None,
10208 force: None,
10209 }
10210 .to_platform_input(),
10211 cx,
10212 );
10213 })
10214 .unwrap();
10215 }
10216
10217 #[gpui::test]
10222 fn a_painted_imported_texture_converts_its_radii_at_paint_time(cx: &mut TestAppContext) {
10223 struct PaintedTexture {
10224 handle: ImportedTextureHandle,
10225 bounds: Bounds<Pixels>,
10226 corner_radii: Corners<Pixels>,
10227 opacity: f32,
10228 flip_v: bool,
10229 }
10230
10231 impl Render for PaintedTexture {
10232 fn render(
10233 &mut self,
10234 _window: &mut Window,
10235 _cx: &mut Context<Self>,
10236 ) -> impl IntoElement {
10237 let handle = self.handle.clone();
10238 let bounds = self.bounds;
10239 let corner_radii = self.corner_radii;
10240 let opacity = self.opacity;
10241 let flip_v = self.flip_v;
10242 gpu_canvas(move |ctx| {
10243 ctx.paint_texture(handle.clone(), corner_radii, opacity, flip_v);
10244 })
10245 .w(bounds.size.width)
10246 .h(bounds.size.height)
10247 }
10248 }
10249
10250 let corner_radii = Corners {
10251 top_left: px(4.),
10252 top_right: px(0.),
10253 bottom_right: px(0.),
10254 bottom_left: px(0.),
10255 };
10256 let window = cx.add_window(|_, _| PaintedTexture {
10257 handle: ImportedTextureHandle::new(()),
10258 bounds: Bounds::new(point(px(10.), px(20.)), size(px(30.), px(40.))),
10259 corner_radii,
10260 opacity: 0.5,
10261 flip_v: true,
10262 });
10263 cx.update_window(window.into(), |_, window, cx| window.draw(cx).clear(cx))
10264 .unwrap();
10265
10266 cx.update_window(window.into(), |_, window, _| {
10267 let painted = window.painted_imported_textures();
10268 assert_eq!(painted.len(), 1, "the window should have painted the texture");
10269
10270 let CustomRenderPrimitive::Texture {
10271 radii,
10272 opacity,
10273 flip_v,
10274 bounds,
10275 ..
10276 } = &painted[0];
10277 assert_eq!(
10278 *radii,
10279 corner_radii.scale(window.scale_factor()),
10280 "the radii must be converted to scaled pixels at paint time"
10281 );
10282 assert_eq!(*opacity, 0.5);
10283 assert!(*flip_v);
10284 assert_eq!(bounds.size.width, ScaledPixels(30. * window.scale_factor()));
10285 assert_eq!(bounds.size.height, ScaledPixels(40. * window.scale_factor()));
10286 })
10287 .unwrap();
10288 }
10289}
10290
10291#[cfg(all(test, any(feature = "inspector", debug_assertions)))]
10292mod inspector_tests {
10293 use super::*;
10294 use crate::{
10295 DivInspectorState, InspectorElementId, MouseDownEvent, StyleRefinement, TestAppContext, div,
10296 };
10297
10298 #[gpui::test]
10299 fn inspector_only_tracks_its_open_window(cx: &mut TestAppContext) {
10300 let windows = [
10301 cx.add_window(|_, cx| InspectorTestRoot {
10302 child: cx.new(|_| InspectorTestView::default()),
10303 }),
10304 cx.add_window(|_, cx| InspectorTestRoot {
10305 child: cx.new(|_| InspectorTestView::default()),
10306 }),
10307 ];
10308 for window in windows {
10309 assert_closed_inspector(window.into(), cx);
10310 }
10311 for _ in 0..2 {
10312 cx.update_window(windows[0].into(), |root, window, cx| {
10313 let root = root.downcast::<InspectorTestRoot>().expect("test root");
10314 let child_widths = root.read(cx).child.read(cx).child_widths.clone();
10315 window.toggle_inspector(cx);
10316 window.draw(cx).clear(cx);
10317 assert_eq!(child_widths.borrow().as_slice(), &[px(10.); 3]);
10318 let path = window
10319 .frame_state
10320 .rendered_frame
10321 .next_inspector_instance_ids
10322 .iter()
10323 .find_map(|(path, count)| (*count == 3).then(|| path.clone()))
10324 .expect("anonymous siblings share an inspector path");
10325 let selected_id = InspectorElementId {
10326 path,
10327 instance_id: 1,
10328 };
10329 let position = window
10330 .frame_state
10331 .rendered_frame
10332 .hitboxes
10333 .iter()
10334 .find(|hitbox| {
10335 window
10336 .frame_state
10337 .rendered_frame
10338 .inspector_hitboxes
10339 .get(&hitbox.id)
10340 == Some(&selected_id)
10341 })
10342 .expect("middle sibling is pickable")
10343 .bounds
10344 .center();
10345 window.simulate_mouse_move(position, cx);
10346 window.dispatch_event(
10347 PlatformInput::MouseDown(MouseDownEvent {
10348 position,
10349 button: MouseButton::Left,
10350 modifiers: Modifiers::default(),
10351 click_count: 1,
10352 first_mouse: false,
10353 }),
10354 cx,
10355 );
10356 window.dispatch_event(
10357 PlatformInput::MouseUp(MouseUpEvent {
10358 position,
10359 button: MouseButton::Left,
10360 modifiers: Modifiers::default(),
10361 click_count: 1,
10362 }),
10363 cx,
10364 );
10365 assert!(!window.is_inspector_picking(cx));
10366 assert_eq!(
10367 window
10368 .core
10369 .inspector
10370 .as_ref()
10371 .expect("open inspector")
10372 .read(cx)
10373 .active_element_id(),
10374 Some(&selected_id)
10375 );
10376 window.draw(cx).clear(cx);
10377 window.with_inspector_state::<DivInspectorState, _>(
10378 Some(&selected_id),
10379 cx,
10380 |state, _| {
10381 let state = state.as_mut().expect("selected div has style state");
10382 assert_eq!(
10383 state.base_style.size.width,
10384 Some(crate::Length::from(px(10.)))
10385 );
10386 state.base_style.size.width = Some(crate::Length::from(px(25.)));
10387 },
10388 );
10389 window.refresh();
10390 window.draw(cx).clear(cx);
10391 assert_eq!(
10392 child_widths.borrow().as_slice(),
10393 &[px(10.), px(25.), px(10.)]
10394 );
10395 })
10396 .expect("pick and edit a cached child");
10397 assert_closed_inspector(windows[1].into(), cx);
10398 windows[0]
10399 .update(cx, |_, window, cx| window.toggle_inspector(cx))
10400 .expect("close inspector");
10401 assert_closed_inspector(windows[0].into(), cx);
10402 }
10403 }
10404
10405 struct InspectorTestRoot {
10406 child: Entity<InspectorTestView>,
10407 }
10408
10409 impl Render for InspectorTestRoot {
10410 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
10411 self.child
10412 .clone()
10413 .cached(StyleRefinement::default().size(px(100.)))
10414 }
10415 }
10416
10417 #[derive(Default)]
10418 struct InspectorTestView {
10419 child_widths: Rc<RefCell<Vec<Pixels>>>,
10420 }
10421
10422 impl Render for InspectorTestView {
10423 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
10424 let child_widths = self.child_widths.clone();
10425 div()
10426 .on_children_prepainted(move |bounds, _, _| {
10427 *child_widths.borrow_mut() =
10428 bounds.iter().map(|bounds| bounds.size.width).collect();
10429 })
10430 .with_dynamic_prepaint_order(|_, _| SmallVec::from_iter([2, 0, 1]))
10431 .id("inspector-root")
10432 .flex()
10433 .children((0..3).map(|_| div().size(px(10.)).flex_shrink_0()))
10434 }
10435 }
10436
10437 fn assert_closed_inspector(window: AnyWindowHandle, cx: &mut TestAppContext) {
10438 cx.update_window(window, |root, window, cx| {
10439 window.draw(cx).clear(cx);
10440 window.draw(cx).clear(cx);
10441 let root = root.downcast::<InspectorTestRoot>().expect("test root");
10442 assert_eq!(
10443 root.read(cx)
10444 .child
10445 .read(cx)
10446 .child_widths
10447 .borrow()
10448 .as_slice(),
10449 &[px(10.); 3]
10450 );
10451 for frame in [
10452 &window.frame_state.rendered_frame,
10453 &window.frame_state.next_frame,
10454 ] {
10455 assert_eq!(frame.next_inspector_instance_ids.capacity(), 0);
10456 assert_eq!(frame.inspector_hitboxes.capacity(), 0);
10457 }
10458 })
10459 .expect("closed inspector has no bookkeeping and no style overrides");
10460 }
10461}