1use crate::{
2 Action, AnyDrag, AnyElement, AnyImageCache, AnyTooltip, AnyView, App, AppContext, Arena, Asset,
3 AsyncWindowContext, AtlasTile, AvailableSpace, BackdropFilter, Background, BorderStyle, Bounds,
4 BoxShadow, Capslock, Context, Corners, CursorHideMode, CursorStyle, Decorations, DevicePixels,
5 DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity,
6 EntityId, EventEmitter, FileDropEvent, Filter, FilterBoundary, FontId, Global, GlobalElementId,
7 GlyphId, GlyphRenderMode, GpuSpecs, InputHandler, InputPreference, IntoElement, IsZero,
8 KeyBinding, KeyContext, KeyDownEvent, KeyEvent, Keystroke, KeystrokeEvent, LayoutId, Lerp,
9 LineLayoutIndex, Modifiers, ModifiersChangedEvent, MonochromeSprite, Motion, MouseButton,
10 MouseEvent, MouseMoveEvent, MouseUpEvent, PaintSurface, Path, Pixels, PlatformAtlas,
11 PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point, PolychromeSprite,
12 Priority, PromptButton, PromptLevel, Quad, RasterizedGlyphFormat, Render, RenderGlyphParams,
13 RenderImage, RenderImageParams, RenderSvgParams, Replay, ResizeEdge, ResolvedDirection,
14 SMOOTH_SVG_SCALE_FACTOR, SUBPIXEL_VARIANTS_X, SUBPIXEL_VARIANTS_Y, ScaledFilter, ScaledPixels,
15 Scene, Shadow, SharedString, Size, StrikethroughStyle, Style, SubpixelSprite, SubscriberSet,
16 Subscription, SystemWindowTab, SystemWindowTabController, TabStopMap, TaffyLayoutEngine, Task,
17 TextInputConfiguration, TextInputStateChange, TextRenderingMode, TextStyle,
18 TextStyleRefinement, ThermalState, TransformationMatrix, Transition, TransitionState,
19 Underline, UnderlineStyle, UnicodeBidi, WindowAppearance, WindowBackgroundAppearance,
20 WindowBounds, WindowControls, WindowDecorations, WindowInsets, WindowOptions, WindowParams,
21 WindowTextSystem, WindowVisibility,
22 gestures::{GestureTuning, RecognizedTouchGesture, TouchGestureRecognizer},
23 interactive::TouchEvent,
24 point, px, rems, size, transparent_black,
25 util::{
26 atomic_incr_if_not_zero, ceil_to_device_pixel, floor_to_device_pixel,
27 round_half_toward_zero, round_half_toward_zero_f64, round_stroke_to_device_pixel,
28 round_to_device_pixel,
29 },
30 white,
31};
32use anyhow::{Context as _, Result, anyhow};
33use collections::{FxHashMap, FxHashSet};
34use derive_more::{Deref, DerefMut};
35use futures::channel::oneshot;
36use gpui_util::post_inc;
37use gpui_util::{ResultExt, measure};
38use itertools::Itertools;
39use palette::Hsla;
40use parking_lot::{RwLock, RwLockUpgradableReadGuard};
41use raw_window_handle::{HandleError, HasDisplayHandle, HasWindowHandle};
42use refineable::Refineable;
43use scheduler::Instant;
44use slotmap::SlotMap;
45use smallvec::SmallVec;
46use std::{
47 any::{Any, TypeId},
48 borrow::Cow,
49 cell::{Cell, RefCell},
50 cmp,
51 fmt::{Debug, Display},
52 hash::{Hash, Hasher},
53 marker::PhantomData,
54 mem,
55 ops::{DerefMut, Range},
56 rc::Rc,
57 sync::{
58 Arc, Weak,
59 atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst},
60 },
61 thread::AccessError,
62 time::Duration,
63};
64use uuid::Uuid;
65
66#[cfg(not(target_family = "wasm"))]
67use self::a11y::ROOT_NODE_ID;
68#[cfg(feature = "profiler")]
69use crate::DebugFrameOverlayMode;
70#[cfg(any(feature = "inspector", debug_assertions))]
71use crate::Inspector;
72#[cfg(feature = "profiler")]
73use crate::profiler;
74
75pub(crate) mod a11y;
76mod prompts;
77
78use a11y::A11y;
79pub use a11y::A11ySubtreeBuilder;
80pub use prompts::*;
81
82fn quantize_glyph_origin(origin: Point<ScaledPixels>) -> (Point<ScaledPixels>, Point<u8>) {
83 fn axis(value: ScaledPixels, variants: u8) -> (ScaledPixels, u8) {
84 let quantized = round_half_toward_zero(value.0 * variants as f32) / variants as f32;
85 let integer = quantized.floor();
86 let variant = ((quantized - integer) * variants as f32).round() as u8;
87 (ScaledPixels(integer), variant.min(variants - 1))
88 }
89
90 let (integer_x, variant_x) = axis(origin.x, SUBPIXEL_VARIANTS_X);
91 let (integer_y, variant_y) = axis(origin.y, SUBPIXEL_VARIANTS_Y);
92 (point(integer_x, integer_y), point(variant_x, variant_y))
93}
94
95fn quantize_color_glyph_origin(origin: Point<ScaledPixels>) -> (Point<ScaledPixels>, Point<u8>) {
96 (
97 origin.map(|coordinate| ScaledPixels(round_half_toward_zero(coordinate.0))),
98 Point::default(),
99 )
100}
101
102pub const DEFAULT_WINDOW_SIZE: Size<Pixels> = size(px(1536.), px(1095.));
107
108pub const DEFAULT_ADDITIONAL_WINDOW_SIZE: Size<Pixels> = Size {
111 width: Pixels(900.),
112 height: Pixels(750.),
113};
114
115#[derive(Default, Copy, Clone, Debug, Eq, PartialEq)]
121pub enum DispatchPhase {
122 #[default]
128 Bubble,
129 Capture,
136}
137
138impl DispatchPhase {
139 #[inline]
141 pub fn bubble(self) -> bool {
142 self == DispatchPhase::Bubble
143 }
144
145 #[inline]
147 pub fn capture(self) -> bool {
148 self == DispatchPhase::Capture
149 }
150}
151
152struct WindowInvalidatorInner {
153 #[cfg(feature = "profiler")]
154 pub window_id: WindowId,
155 pub dirty: bool,
156 pub draw_phase: DrawPhase,
157 pub dirty_views: FxHashSet<EntityId>,
158 pub update_count: usize,
159 #[cfg(feature = "profiler")]
160 pub frame_dirty: FrameDirtyAccumulator,
161 pub platform_waker: Option<Rc<dyn Fn()>>,
162}
163
164#[cfg(feature = "profiler")]
170#[derive(Default)]
171struct FrameDirtyAccumulator {
172 dirty_at: Option<Instant>,
173 invalidations: u64,
174}
175
176#[derive(Clone, Copy)]
178enum FrameRateLimit {
179 Inactive,
181 Thermal,
183}
184
185#[derive(Clone)]
186pub(crate) struct WindowInvalidator {
187 inner: Rc<RefCell<WindowInvalidatorInner>>,
188}
189
190impl WindowInvalidator {
191 pub fn new(#[allow(unused_variables)] window_id: WindowId) -> Self {
192 WindowInvalidator {
193 inner: Rc::new(RefCell::new(WindowInvalidatorInner {
194 #[cfg(feature = "profiler")]
195 window_id,
196 dirty: true,
197 draw_phase: DrawPhase::None,
198 dirty_views: FxHashSet::default(),
199 update_count: 0,
200 #[cfg(feature = "profiler")]
201 frame_dirty: FrameDirtyAccumulator::default(),
202 platform_waker: None,
203 })),
204 }
205 }
206
207 pub fn invalidate_view(&self, entity: EntityId, cx: &mut App) -> bool {
208 let mut inner = self.inner.borrow_mut();
209 inner.update_count += 1;
210 inner.dirty_views.insert(entity);
211 if inner.draw_phase == DrawPhase::None {
212 #[cfg(feature = "profiler")]
213 let dirty_at = Self::record_frame_dirty(&mut inner);
214 let became_dirty = !inner.dirty;
215 inner.dirty = true;
216 let waker = became_dirty.then(|| inner.platform_waker.clone()).flatten();
217 #[cfg(feature = "profiler")]
218 let window_id = inner.window_id;
219 drop(inner);
220 #[cfg(feature = "profiler")]
221 if became_dirty {
222 profiler::journal::record_frame_pending(window_id, dirty_at);
223 }
224 cx.push_effect(Effect::Notify { emitter: entity });
225 if let Some(waker) = waker {
226 waker();
227 }
228 true
229 } else {
230 false
231 }
232 }
233
234 pub fn is_dirty(&self) -> bool {
235 self.inner.borrow().dirty
236 }
237
238 pub fn set_dirty(&self, dirty: bool) {
239 let mut inner = self.inner.borrow_mut();
240 let became_dirty = dirty && !inner.dirty;
241 inner.dirty = dirty;
242 if dirty {
243 inner.update_count += 1;
244 }
245 #[cfg(feature = "profiler")]
246 let dirty_at = dirty.then(|| Self::record_frame_dirty(&mut inner));
247 let waker = became_dirty.then(|| inner.platform_waker.clone()).flatten();
248 #[cfg(feature = "profiler")]
249 let window_id = inner.window_id;
250 drop(inner);
251 #[cfg(feature = "profiler")]
252 if became_dirty && let Some(dirty_at) = dirty_at {
253 profiler::journal::record_frame_pending(window_id, dirty_at);
254 }
255 if let Some(waker) = waker {
256 waker();
257 }
258 }
259
260 pub fn set_platform_waker(&self, waker: Option<Rc<dyn Fn()>>) {
261 let mut inner = self.inner.borrow_mut();
262 inner.platform_waker = waker;
263 let waker = inner.dirty.then(|| inner.platform_waker.clone()).flatten();
264 drop(inner);
265 if let Some(waker) = waker {
266 waker();
267 }
268 }
269
270 pub fn wake_platform(&self) {
274 let waker = self.inner.borrow().platform_waker.clone();
275 if let Some(waker) = waker {
276 waker();
277 }
278 }
279
280 pub fn set_phase(&self, phase: DrawPhase) {
281 self.inner.borrow_mut().draw_phase = phase
282 }
283
284 pub fn update_count(&self) -> usize {
285 self.inner.borrow().update_count
286 }
287
288 #[cfg(feature = "profiler")]
289 fn record_frame_dirty(inner: &mut WindowInvalidatorInner) -> Instant {
290 let dirty_at = *inner.frame_dirty.dirty_at.get_or_insert_with(Instant::now);
291 inner.frame_dirty.invalidations += 1;
292 dirty_at
293 }
294
295 #[cfg(feature = "profiler")]
296 fn take_frame_dirty(&self) -> FrameDirtyAccumulator {
297 mem::take(&mut self.inner.borrow_mut().frame_dirty)
298 }
299
300 fn mark_dirty_for_next_draw(&self, entity: EntityId) {
302 self.inner.borrow_mut().dirty_views.insert(entity);
303 }
304
305 pub fn take_views(&self) -> FxHashSet<EntityId> {
306 mem::take(&mut self.inner.borrow_mut().dirty_views)
307 }
308
309 pub fn replace_views(&self, views: FxHashSet<EntityId>) {
310 self.inner.borrow_mut().dirty_views = views;
311 }
312
313 pub fn not_drawing(&self) -> bool {
314 self.inner.borrow().draw_phase == DrawPhase::None
315 }
316
317 #[track_caller]
318 pub fn debug_assert_paint(&self) {
319 debug_assert!(
320 matches!(self.inner.borrow().draw_phase, DrawPhase::Paint),
321 "this method can only be called during paint"
322 );
323 }
324
325 #[track_caller]
326 pub fn debug_assert_prepaint(&self) {
327 debug_assert!(
328 matches!(self.inner.borrow().draw_phase, DrawPhase::Prepaint),
329 "this method can only be called during request_layout, or prepaint"
330 );
331 }
332
333 #[track_caller]
334 pub fn debug_assert_paint_or_prepaint(&self) {
335 debug_assert!(
336 matches!(
337 self.inner.borrow().draw_phase,
338 DrawPhase::Paint | DrawPhase::Prepaint
339 ),
340 "this method can only be called during request_layout, prepaint, or paint"
341 );
342 }
343}
344
345type AnyObserver = Box<dyn FnMut(&mut Window, &mut App) -> bool + 'static>;
346
347pub(crate) type AnyWindowFocusListener =
348 Box<dyn FnMut(&WindowFocusEvent, &mut Window, &mut App) -> bool + 'static>;
349
350pub(crate) struct WindowFocusEvent {
351 pub(crate) previous_focus_path: SmallVec<[FocusId; 8]>,
352 pub(crate) current_focus_path: SmallVec<[FocusId; 8]>,
353}
354
355impl WindowFocusEvent {
356 pub fn is_focus_in(&self, focus_id: FocusId) -> bool {
357 !self.previous_focus_path.contains(&focus_id) && self.current_focus_path.contains(&focus_id)
358 }
359
360 pub fn is_focus_out(&self, focus_id: FocusId) -> bool {
361 self.previous_focus_path.contains(&focus_id) && !self.current_focus_path.contains(&focus_id)
362 }
363}
364
365pub struct FocusOutEvent {
367 pub blurred: WeakFocusHandle,
371}
372
373slotmap::new_key_type! {
374 pub struct FocusId;
378}
379
380thread_local! {
381 pub(crate) static ELEMENT_ARENA: RefCell<Arena> = RefCell::new(Arena::new(1024 * 1024));
384
385 static CURRENT_ELEMENT_ARENA: Cell<Option<*const RefCell<Arena>>> = const { Cell::new(None) };
389}
390
391fn draw_in_progress() -> bool {
402 CURRENT_ELEMENT_ARENA.with(|current| current.get().is_some())
403}
404
405#[inline(always)]
408pub(crate) fn with_element_arena<R>(callback: impl FnOnce(&mut Arena) -> R) -> R {
409 let mut callback = Some(callback);
410 let mut result = None;
411 let access = with_element_arena_erased(&mut |arena| {
412 result = Some(callback.take().expect("arena callback runs once")(arena));
413 });
414 drop(callback);
415 access.expect("cannot access a Thread Local Storage value during or after destruction");
416 result.expect("arena callback produces a result")
417}
418
419pub(crate) struct ElementArenaScope {
434 entered: *const RefCell<Arena>,
437 previous: Option<*const RefCell<Arena>>,
438 exited: bool,
439}
440
441impl ElementArenaScope {
442 pub(crate) fn enter(arena: &RefCell<Arena>) -> Self {
444 arena.borrow_mut().begin_scope();
445 let previous = CURRENT_ELEMENT_ARENA.with(|current| {
446 let prev = current.get();
447 current.set(Some(arena as *const RefCell<Arena>));
448 prev
449 });
450 Self {
451 entered: arena as *const RefCell<Arena>,
452 previous,
453 exited: false,
454 }
455 }
456
457 pub(crate) fn exit(mut self, arena: &RefCell<Arena>) -> ArenaClearNeeded {
466 assert!(
467 std::ptr::eq(self.entered, arena),
468 "ElementArenaScope::exit called with a different arena than was entered"
469 );
470 self.exited = true;
471 ArenaClearNeeded::new(arena)
476 }
477}
478
479impl Drop for ElementArenaScope {
480 fn drop(&mut self) {
481 CURRENT_ELEMENT_ARENA.with(|current| {
488 current.set(self.previous);
489 });
490 unsafe { &*self.entered }.borrow_mut().end_scope();
494 if !self.exited && !std::thread::panicking() {
495 debug_assert!(false, "ElementArenaScope dropped without calling exit()");
496 log::error!(
497 "ElementArenaScope dropped without calling exit(); \
498 the arena clear for this draw was never requested"
499 );
500 }
501 }
502}
503
504#[must_use]
506pub struct ArenaClearNeeded {
507 arena: *const RefCell<Arena>,
510}
511
512impl ArenaClearNeeded {
513 fn new(arena: &RefCell<Arena>) -> Self {
516 Self {
517 arena: arena as *const RefCell<Arena>,
518 }
519 }
520
521 pub fn clear(self, cx: &mut App) {
530 assert!(
531 std::ptr::eq(self.arena, &cx.element_arena),
532 "ArenaClearNeeded::clear called with a different App than the draw ran against"
533 );
534 cx.element_arena.borrow_mut().clear();
535 }
536}
537
538#[derive(Default)]
539pub(crate) struct FocusMap {
540 pub(crate) handles: SlotMap<FocusId, FocusRef>,
541 pub(crate) dropped_handles: Vec<FocusId>,
542}
543
544pub(crate) struct FocusRef {
545 pub(crate) ref_count: AtomicUsize,
546 pub(crate) tab_index: isize,
547 pub(crate) tab_stop: bool,
548}
549
550impl FocusId {
551 pub fn is_focused(&self, window: &Window) -> bool {
553 window.focus == Some(*self)
554 }
555
556 pub fn contains_focused(&self, window: &Window, cx: &App) -> bool {
558 window
559 .focused(cx)
560 .is_some_and(|focused| self.contains(focused.id, window))
561 }
562
563 pub fn within_focused(&self, window: &Window, cx: &App) -> bool {
565 let focused = window.focused(cx);
566 focused.is_some_and(|focused| focused.id.contains(*self, window))
567 }
568
569 pub(crate) fn contains(&self, other: Self, window: &Window) -> bool {
571 window
572 .rendered_frame
573 .dispatch_tree
574 .focus_contains(*self, other)
575 }
576}
577
578pub struct FocusHandle {
611 pub(crate) id: FocusId,
612 handles: Arc<RwLock<FocusMap>>,
613 pub tab_index: isize,
615 pub tab_stop: bool,
617}
618
619impl std::fmt::Debug for FocusHandle {
620 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
621 f.write_fmt(format_args!("FocusHandle({:?})", self.id))
622 }
623}
624
625impl FocusHandle {
626 pub(crate) fn new(handles: &Arc<RwLock<FocusMap>>) -> Self {
627 let id = handles.write().handles.insert(FocusRef {
628 ref_count: AtomicUsize::new(1),
629 tab_index: 0,
630 tab_stop: false,
631 });
632
633 Self {
634 id,
635 tab_index: 0,
636 tab_stop: false,
637 handles: handles.clone(),
638 }
639 }
640
641 pub(crate) fn for_id(id: FocusId, handles: &Arc<RwLock<FocusMap>>) -> Option<Self> {
642 let lock = handles.read();
643 let focus = lock.handles.get(id)?;
644 if atomic_incr_if_not_zero(&focus.ref_count) == 0 {
645 return None;
646 }
647 Some(Self {
648 id,
649 tab_index: focus.tab_index,
650 tab_stop: focus.tab_stop,
651 handles: handles.clone(),
652 })
653 }
654
655 pub fn tab_index(mut self, index: isize) -> Self {
673 self.tab_index = index;
674 if let Some(focus) = self.handles.write().handles.get_mut(self.id) {
675 focus.tab_index = index;
676 }
677 self
678 }
679
680 pub fn tab_stop(mut self, tab_stop: bool) -> Self {
685 self.tab_stop = tab_stop;
686 if let Some(focus) = self.handles.write().handles.get_mut(self.id) {
687 focus.tab_stop = tab_stop;
688 }
689 self
690 }
691
692 pub fn downgrade(&self) -> WeakFocusHandle {
694 WeakFocusHandle {
695 id: self.id,
696 handles: Arc::downgrade(&self.handles),
697 }
698 }
699
700 pub fn focus(&self, window: &mut Window, cx: &mut App) {
702 window.focus(self, cx)
703 }
704
705 pub fn is_focused(&self, window: &Window) -> bool {
707 self.id.is_focused(window)
708 }
709
710 pub fn contains_focused(&self, window: &Window, cx: &App) -> bool {
712 self.id.contains_focused(window, cx)
713 }
714
715 pub fn within_focused(&self, window: &Window, cx: &mut App) -> bool {
717 self.id.within_focused(window, cx)
718 }
719
720 pub fn contains(&self, other: &Self, window: &Window) -> bool {
722 self.id.contains(other.id, window)
723 }
724
725 pub fn dispatch_action(&self, action: &dyn Action, window: &mut Window, cx: &mut App) {
731 if let Some(node_id) = window
732 .rendered_frame
733 .dispatch_tree
734 .focusable_node_id(self.id)
735 {
736 window.dispatch_action_on_node(node_id, action, cx)
737 }
738 }
739}
740
741impl Clone for FocusHandle {
742 fn clone(&self) -> Self {
743 Self::for_id(self.id, &self.handles).unwrap()
744 }
745}
746
747impl PartialEq for FocusHandle {
748 fn eq(&self, other: &Self) -> bool {
749 self.id == other.id
750 }
751}
752
753impl Eq for FocusHandle {}
754
755impl Drop for FocusHandle {
756 fn drop(&mut self) {
757 let handles = self.handles.upgradable_read();
758 let focus = handles.handles.get(self.id).expect("focus handle missing");
759 let previous_count = focus.ref_count.fetch_sub(1, SeqCst);
760 assert_ne!(previous_count, 0, "over-released focus handle");
761 if previous_count == 1 {
762 RwLockUpgradableReadGuard::upgrade(handles)
763 .dropped_handles
764 .push(self.id);
765 }
766 }
767}
768
769#[derive(Clone, Debug)]
774pub struct WeakFocusHandle {
775 pub(crate) id: FocusId,
776 pub(crate) handles: Weak<RwLock<FocusMap>>,
777}
778
779impl WeakFocusHandle {
780 pub fn upgrade(&self) -> Option<FocusHandle> {
782 let handles = self.handles.upgrade()?;
783 FocusHandle::for_id(self.id, &handles)
784 }
785}
786
787impl PartialEq for WeakFocusHandle {
788 fn eq(&self, other: &WeakFocusHandle) -> bool {
789 self.id == other.id
790 }
791}
792
793impl Eq for WeakFocusHandle {}
794
795impl PartialEq<FocusHandle> for WeakFocusHandle {
796 fn eq(&self, other: &FocusHandle) -> bool {
797 self.id == other.id
798 }
799}
800
801impl PartialEq<WeakFocusHandle> for FocusHandle {
802 fn eq(&self, other: &WeakFocusHandle) -> bool {
803 self.id == other.id
804 }
805}
806
807pub trait Focusable: 'static {
838 fn focus_handle(&self, cx: &App) -> FocusHandle;
843}
844
845impl<V: Focusable> Focusable for Entity<V> {
846 fn focus_handle(&self, cx: &App) -> FocusHandle {
847 self.read(cx).focus_handle(cx)
848 }
849}
850
851pub trait ManagedView: Focusable + EventEmitter<DismissEvent> + Render {}
857
858impl<M: Focusable + EventEmitter<DismissEvent> + Render> ManagedView for M {}
859
860pub struct DismissEvent;
866
867type FrameCallback = Box<dyn FnOnce(&mut Window, &mut App)>;
868
869pub(crate) type AnyMouseListener =
870 Box<dyn FnMut(&dyn Any, DispatchPhase, &mut Window, &mut App) + 'static>;
871
872#[derive(Clone, Copy)]
873pub(crate) struct PointerChange {
874 pub(crate) position: Option<Point<Pixels>>,
875 pub(crate) cause: PointerChangeCause,
876}
877
878#[derive(Clone, Copy, PartialEq, Eq)]
879pub(crate) enum PointerChangeCause {
880 Input,
881 Layout,
882}
883
884type PointerObserver = Rc<dyn Fn(PointerChange, &mut Window, &mut App)>;
885
886#[derive(Clone)]
887enum CursorStyleRequest {
888 Window(CursorStyleSource),
889 Hitbox {
890 id: HitboxId,
891 style: CursorStyleSource,
892 },
893}
894
895#[derive(Clone)]
896enum CursorStyleSource {
897 Fixed(CursorStyle),
898 Position(Rc<dyn Fn(Point<Pixels>) -> Option<CursorStyle>>),
899}
900
901impl CursorStyleSource {
902 fn resolve(&self, position: Point<Pixels>) -> Option<CursorStyle> {
903 match self {
904 Self::Fixed(style) => Some(*style),
905 Self::Position(resolve) => resolve(position),
906 }
907 }
908}
909
910#[derive(Default, Eq, PartialEq)]
915pub struct HitTest {
916 ordered_ids: SmallVec<[HitboxId; 8]>,
918 entries: FxHashMap<HitboxId, HitTestEntry>,
920 hover_hitbox_count: usize,
922}
923
924impl HitTest {
925 fn new<'a>(
927 hitboxes: impl ExactSizeIterator<Item = &'a Hitbox>,
928 position: Point<Pixels>,
929 ) -> Self {
930 let mut num_until_mouse_blocked = None::<usize>;
931 let mut ids_until_occlusion = SmallVec::default();
932 let mut entries = FxHashMap::with_capacity_and_hasher(hitboxes.len(), Default::default());
933 let mut found_hit_occlusion = false;
934 for (index, hitbox) in hitboxes.enumerate() {
936 entries.insert(
937 hitbox.id,
938 HitTestEntry {
939 depth: index,
940 tags: hitbox.tags.clone(),
941 },
942 );
943
944 if found_hit_occlusion {
945 continue;
946 }
947
948 if !hitbox.contains(&position) {
949 continue;
950 }
951
952 ids_until_occlusion.push(hitbox.id);
953
954 if num_until_mouse_blocked.is_none() && hitbox.behavior.blocks_mouse() {
956 num_until_mouse_blocked = Some(ids_until_occlusion.len());
957 }
958
959 if hitbox.behavior.occludes() {
961 found_hit_occlusion = true;
962 }
963 }
964 Self {
965 hover_hitbox_count: num_until_mouse_blocked.unwrap_or(ids_until_occlusion.len()),
966 ordered_ids: ids_until_occlusion,
967 entries,
968 }
969 }
970
971 pub fn iter_hovered(&self) -> impl Iterator<Item = &HitboxId> {
976 self.ordered_ids.iter().take(self.hover_hitbox_count)
977 }
978
979 pub fn iter_scrollable(&self) -> impl Iterator<Item = &HitboxId> {
985 self.ordered_ids.iter()
986 }
987
988 pub fn entry(&self, id: &HitboxId) -> Option<&HitTestEntry> {
990 self.entries.get(id)
991 }
992}
993
994#[derive(Debug, PartialEq, Eq)]
997pub struct HitTestEntry {
998 depth: usize,
1001 tags: Vec<SharedString>,
1002}
1003
1004impl HitTestEntry {
1005 pub fn depth(&self) -> usize {
1009 self.depth
1010 }
1011
1012 pub fn tags(&self) -> &Vec<SharedString> {
1014 &self.tags
1015 }
1016}
1017
1018#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1023pub enum WindowControlArea {
1024 Drag,
1026 Close,
1028 Max,
1030 Min,
1032}
1033
1034#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
1039pub struct HitboxId(u64);
1040
1041#[cfg(feature = "test-support")]
1042impl HitboxId {
1043 pub const fn placeholder() -> Self {
1048 Self(0)
1049 }
1050}
1051
1052impl HitboxId {
1053 pub fn is_hovered(self, window: &Window) -> bool {
1059 if window.captured_hitbox == Some(self) {
1061 return true;
1062 }
1063 if window.last_input_was_keyboard() {
1064 return false;
1065 }
1066 self.hit_test(window)
1067 }
1068
1069 pub(crate) fn is_hovered_ignoring_last_input(self, window: &Window) -> bool {
1074 if window.captured_hitbox == Some(self) {
1076 return true;
1077 }
1078 self.hit_test(window)
1079 }
1080
1081 fn hit_test(self, window: &Window) -> bool {
1082 window.mouse_hit_test().iter_hovered().contains(&self)
1083 }
1084
1085 pub fn should_handle_scroll(self, window: &Window) -> bool {
1090 window.mouse_hit_test().iter_scrollable().contains(&self)
1091 }
1092
1093 fn next(mut self) -> HitboxId {
1094 HitboxId(self.0.wrapping_add(1))
1095 }
1096}
1097
1098#[derive(Clone, Debug, Deref)]
1111pub struct Hitbox {
1112 pub id: HitboxId,
1114 #[deref]
1116 pub bounds: Bounds<Pixels>,
1117 pub content_mask: ContentMask<Pixels>,
1122 pub behavior: HitboxBehavior,
1124 pub fragments: Option<Arc<[Bounds<Pixels>]>>,
1129 pub tags: Vec<SharedString>,
1131}
1132
1133impl Hitbox {
1134 pub fn contains(&self, point: &Point<Pixels>) -> bool {
1140 self.content_mask.bounds.contains(point)
1141 && self.fragments.as_ref().map_or_else(
1142 || self.bounds.contains(point),
1143 |fragments| fragments.iter().any(|bounds| bounds.contains(point)),
1144 )
1145 }
1146
1147 pub fn is_hovered(&self, window: &Window) -> bool {
1156 self.id.is_hovered(window)
1157 }
1158
1159 pub fn is_hovered_at(&self, position: Point<Pixels>, window: &Window) -> bool {
1162 let hit_test = window.rendered_frame.hit_test(position);
1163 hit_test.iter_hovered().any(|id| self.id == *id)
1164 }
1165
1166 pub fn should_handle_scroll(&self, window: &Window) -> bool {
1172 self.id.should_handle_scroll(window)
1173 }
1174}
1175
1176#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
1183pub enum HitboxBehavior {
1184 #[default]
1189 Normal,
1190
1191 BlockMouse,
1197
1198 BlockMouseExceptScroll,
1205}
1206
1207impl HitboxBehavior {
1208 pub fn blocks_mouse(&self) -> bool {
1210 matches!(self, Self::BlockMouse | Self::BlockMouseExceptScroll)
1211 }
1212
1213 pub fn occludes(&self) -> bool {
1215 matches!(self, Self::BlockMouse)
1216 }
1217}
1218
1219#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
1221pub struct TooltipId(usize);
1222
1223impl TooltipId {
1224 pub fn is_hovered(&self, window: &Window) -> bool {
1226 window
1227 .tooltip_bounds
1228 .as_ref()
1229 .is_some_and(|tooltip_bounds| {
1230 tooltip_bounds.id == *self
1231 && tooltip_bounds.bounds.contains(&window.mouse_position())
1232 })
1233 }
1234}
1235
1236pub(crate) struct TooltipBounds {
1237 id: TooltipId,
1238 bounds: Bounds<Pixels>,
1239}
1240
1241#[derive(Clone)]
1242pub(crate) struct TooltipRequest {
1243 id: TooltipId,
1244 tooltip: AnyTooltip,
1245}
1246
1247#[derive(Clone, Default)]
1253pub(crate) struct ElementIdStack {
1254 ids: SmallVec<[ElementId; 32]>,
1255 hashes: SmallVec<[u64; 32]>,
1256}
1257
1258impl ElementIdStack {
1259 pub(crate) fn push(&mut self, id: ElementId) {
1260 self.hashes.push(crate::extend_path_hash(self.hash(), &id));
1261 self.ids.push(id);
1262 }
1263
1264 pub(crate) fn pop(&mut self) -> Option<ElementId> {
1265 self.hashes.pop();
1266 self.ids.pop()
1267 }
1268
1269 pub(crate) fn clear(&mut self) {
1270 self.ids.clear();
1271 self.hashes.clear();
1272 }
1273
1274 fn hash(&self) -> u64 {
1276 self.hashes
1277 .last()
1278 .copied()
1279 .unwrap_or(crate::EMPTY_PATH_HASH)
1280 }
1281
1282 pub(crate) fn global_id(&self) -> GlobalElementId {
1283 GlobalElementId::with_hash(&self.ids, self.hash())
1284 }
1285}
1286
1287impl std::ops::Deref for ElementIdStack {
1288 type Target = [ElementId];
1289
1290 fn deref(&self) -> &Self::Target {
1291 &self.ids
1292 }
1293}
1294
1295pub(crate) struct DeferredDraw {
1296 current_view: EntityId,
1297 priority: usize,
1298 parent_node: DispatchNodeId,
1299 element_id_stack: ElementIdStack,
1300 text_style_stack: Vec<TextStyleRefinement>,
1301 content_mask: Option<ContentMask<Pixels>>,
1302 rem_size: Pixels,
1303 element: Option<AnyElement>,
1304 absolute_offset: Point<Pixels>,
1305 prepaint_range: Range<PrepaintStateIndex>,
1306 paint_range: Range<PaintIndex>,
1307}
1308
1309pub(crate) struct Frame {
1310 pub(crate) focus: Option<FocusId>,
1311 pub(crate) window_active: bool,
1312 pub(crate) element_states: FxHashMap<(GlobalElementId, TypeId), ElementStateBox>,
1313 accessed_element_states: Vec<(GlobalElementId, TypeId)>,
1314 pub(crate) mouse_listeners: Vec<Option<AnyMouseListener>>,
1315 pointer_observers: Rc<Vec<PointerObserver>>,
1316 pub(crate) dispatch_tree: DispatchTree,
1317 pub(crate) scene: Scene,
1318 pub(crate) hitboxes: Vec<Hitbox>,
1319 pub(crate) window_control_hitboxes: Vec<(WindowControlArea, Hitbox)>,
1320 pub(crate) deferred_draws: Vec<DeferredDraw>,
1321 pub(crate) input_handlers: Vec<Option<PlatformInputHandler>>,
1322 pub(crate) tooltip_requests: Vec<Option<TooltipRequest>>,
1323 cursor_styles: Vec<CursorStyleRequest>,
1324 #[cfg(any(test, feature = "test-support"))]
1325 pub(crate) debug_bounds: FxHashMap<String, Bounds<Pixels>>,
1326 #[cfg(any(test, feature = "test-support"))]
1327 debug_bounds_records: Vec<(String, Bounds<Pixels>)>,
1328 #[cfg(any(feature = "inspector", debug_assertions))]
1329 pub(crate) next_inspector_instance_ids: FxHashMap<Rc<crate::InspectorElementPath>, usize>,
1330 #[cfg(any(feature = "inspector", debug_assertions))]
1331 pub(crate) inspector_hitboxes: FxHashMap<HitboxId, crate::InspectorElementId>,
1332 pub(crate) tab_stops: TabStopMap,
1333}
1334
1335#[derive(Clone, Default)]
1336pub(crate) struct PrepaintStateIndex {
1337 hitboxes_index: usize,
1338 tooltips_index: usize,
1339 deferred_draws_index: usize,
1340 dispatch_tree_index: usize,
1341 accessed_element_states_index: usize,
1342 line_layout_index: LineLayoutIndex,
1343}
1344
1345#[derive(Clone, Default)]
1346pub(crate) struct PaintIndex {
1347 scene_index: usize,
1348 #[cfg(any(test, feature = "test-support"))]
1349 debug_bounds_index: usize,
1350 mouse_listeners_index: usize,
1351 pointer_observers_index: usize,
1352 input_handlers_index: usize,
1353 cursor_styles_index: usize,
1354 accessed_element_states_index: usize,
1355 tab_handle_index: usize,
1356 line_layout_index: LineLayoutIndex,
1357}
1358
1359impl PrepaintStateIndex {
1360 pub(crate) fn within(inner: &Range<Self>, outer: &Range<Self>) -> bool {
1362 let lanes = |index: &Self| {
1363 [
1364 index.hitboxes_index,
1365 index.tooltips_index,
1366 index.deferred_draws_index,
1367 index.dispatch_tree_index,
1368 index.accessed_element_states_index,
1369 ]
1370 };
1371 within_lanes(
1372 lanes(&inner.start),
1373 lanes(&inner.end),
1374 lanes(&outer.start),
1375 lanes(&outer.end),
1376 )
1377 }
1378
1379 pub(crate) fn rebased(&self, source: &Range<Self>, destination: &Self) -> Self {
1381 let shift = |index: usize, start: usize, to: usize| index - start + to;
1382 let (from, to) = (&source.start, destination);
1383 Self {
1384 hitboxes_index: shift(self.hitboxes_index, from.hitboxes_index, to.hitboxes_index),
1385 tooltips_index: shift(self.tooltips_index, from.tooltips_index, to.tooltips_index),
1386 deferred_draws_index: shift(
1387 self.deferred_draws_index,
1388 from.deferred_draws_index,
1389 to.deferred_draws_index,
1390 ),
1391 dispatch_tree_index: shift(
1392 self.dispatch_tree_index,
1393 from.dispatch_tree_index,
1394 to.dispatch_tree_index,
1395 ),
1396 accessed_element_states_index: shift(
1397 self.accessed_element_states_index,
1398 from.accessed_element_states_index,
1399 to.accessed_element_states_index,
1400 ),
1401 line_layout_index: self
1402 .line_layout_index
1403 .rebased(&from.line_layout_index, &to.line_layout_index),
1404 }
1405 }
1406}
1407
1408fn within_lanes<const N: usize>(
1409 inner_start: [usize; N],
1410 inner_end: [usize; N],
1411 outer_start: [usize; N],
1412 outer_end: [usize; N],
1413) -> bool {
1414 (0..N).all(|lane| {
1415 outer_start[lane] <= inner_start[lane]
1416 && inner_start[lane] <= inner_end[lane]
1417 && inner_end[lane] <= outer_end[lane]
1418 })
1419}
1420
1421impl PaintIndex {
1422 pub(crate) fn within(inner: &Range<Self>, outer: &Range<Self>) -> bool {
1424 let lanes = |index: &Self| {
1425 [
1426 index.scene_index,
1427 index.mouse_listeners_index,
1428 index.pointer_observers_index,
1429 index.input_handlers_index,
1430 index.cursor_styles_index,
1431 index.accessed_element_states_index,
1432 index.tab_handle_index,
1433 ]
1434 };
1435 within_lanes(
1436 lanes(&inner.start),
1437 lanes(&inner.end),
1438 lanes(&outer.start),
1439 lanes(&outer.end),
1440 )
1441 }
1442
1443 pub(crate) fn rebased(&self, source: &Range<Self>, destination: &Self) -> Self {
1445 let shift = |index: usize, start: usize, to: usize| index - start + to;
1446 let (from, to) = (&source.start, destination);
1447 Self {
1448 scene_index: shift(self.scene_index, from.scene_index, to.scene_index),
1449 #[cfg(any(test, feature = "test-support"))]
1450 debug_bounds_index: shift(
1451 self.debug_bounds_index,
1452 from.debug_bounds_index,
1453 to.debug_bounds_index,
1454 ),
1455 mouse_listeners_index: shift(
1456 self.mouse_listeners_index,
1457 from.mouse_listeners_index,
1458 to.mouse_listeners_index,
1459 ),
1460 pointer_observers_index: shift(
1461 self.pointer_observers_index,
1462 from.pointer_observers_index,
1463 to.pointer_observers_index,
1464 ),
1465 input_handlers_index: shift(
1466 self.input_handlers_index,
1467 from.input_handlers_index,
1468 to.input_handlers_index,
1469 ),
1470 cursor_styles_index: shift(
1471 self.cursor_styles_index,
1472 from.cursor_styles_index,
1473 to.cursor_styles_index,
1474 ),
1475 accessed_element_states_index: shift(
1476 self.accessed_element_states_index,
1477 from.accessed_element_states_index,
1478 to.accessed_element_states_index,
1479 ),
1480 tab_handle_index: shift(
1481 self.tab_handle_index,
1482 from.tab_handle_index,
1483 to.tab_handle_index,
1484 ),
1485 line_layout_index: self
1486 .line_layout_index
1487 .rebased(&from.line_layout_index, &to.line_layout_index),
1488 }
1489 }
1490}
1491
1492impl Frame {
1493 #[cfg(any(test, feature = "test-support"))]
1494 pub(crate) fn record_debug_bounds(&mut self, selector: String, bounds: Bounds<Pixels>) {
1495 self.debug_bounds.insert(selector.clone(), bounds);
1496 self.debug_bounds_records.push((selector, bounds));
1497 }
1498
1499 pub(crate) fn new(dispatch_tree: DispatchTree) -> Self {
1500 Frame {
1501 focus: None,
1502 window_active: false,
1503 element_states: FxHashMap::default(),
1504 accessed_element_states: Vec::new(),
1505 mouse_listeners: Vec::new(),
1506 pointer_observers: Rc::default(),
1507 dispatch_tree,
1508 scene: Scene::default(),
1509 hitboxes: Vec::new(),
1510 window_control_hitboxes: Vec::new(),
1511 deferred_draws: Vec::new(),
1512 input_handlers: Vec::new(),
1513 tooltip_requests: Vec::new(),
1514 cursor_styles: Vec::new(),
1515
1516 #[cfg(any(test, feature = "test-support"))]
1517 debug_bounds: FxHashMap::default(),
1518 #[cfg(any(test, feature = "test-support"))]
1519 debug_bounds_records: Vec::new(),
1520
1521 #[cfg(any(feature = "inspector", debug_assertions))]
1522 next_inspector_instance_ids: FxHashMap::default(),
1523
1524 #[cfg(any(feature = "inspector", debug_assertions))]
1525 inspector_hitboxes: FxHashMap::default(),
1526 tab_stops: TabStopMap::default(),
1527 }
1528 }
1529
1530 pub(crate) fn clear(&mut self) {
1531 self.element_states.clear();
1532 self.accessed_element_states.clear();
1533 self.mouse_listeners.clear();
1534 Rc::make_mut(&mut self.pointer_observers).clear();
1535 self.dispatch_tree.clear();
1536 self.scene.clear();
1537 self.input_handlers.clear();
1538 self.tooltip_requests.clear();
1539 self.cursor_styles.clear();
1540 self.hitboxes.clear();
1541 self.window_control_hitboxes.clear();
1542 self.deferred_draws.clear();
1543 self.tab_stops.clear();
1544 self.focus = None;
1545
1546 #[cfg(any(test, feature = "test-support"))]
1547 {
1548 self.debug_bounds.clear();
1549 self.debug_bounds_records.clear();
1550 }
1551
1552 #[cfg(any(feature = "inspector", debug_assertions))]
1553 {
1554 self.next_inspector_instance_ids.clear();
1555 self.inspector_hitboxes.clear();
1556 }
1557 }
1558
1559 pub(crate) fn cursor_style(&self, window: &Window) -> Option<CursorStyle> {
1560 self.cursor_styles
1562 .iter()
1563 .rev()
1564 .find_map(|request| match request {
1565 CursorStyleRequest::Window(style) => style.resolve(window.mouse_position()),
1566 CursorStyleRequest::Hitbox { .. } => None,
1567 })
1568 .or_else(|| {
1569 if !window.pointer_inside_window {
1570 return None;
1571 }
1572 self.cursor_styles
1573 .iter()
1574 .rev()
1575 .find_map(|request| match request {
1576 CursorStyleRequest::Hitbox { id, style }
1577 if id.is_hovered_ignoring_last_input(window) =>
1578 {
1579 style.resolve(window.mouse_position())
1580 }
1581 _ => None,
1582 })
1583 })
1584 }
1585
1586 pub(crate) fn hit_test(&self, position: Point<Pixels>) -> HitTest {
1587 HitTest::new(self.hitboxes.iter().rev(), position)
1588 }
1589
1590 pub(crate) fn focus_path(&self) -> SmallVec<[FocusId; 8]> {
1591 self.focus
1592 .map(|focus_id| self.dispatch_tree.focus_path(focus_id))
1593 .unwrap_or_default()
1594 }
1595
1596 pub(crate) fn finish(&mut self, prev_frame: &mut Self) {
1597 for element_state_key in &self.accessed_element_states {
1598 if let Some((element_state_key, element_state)) =
1599 prev_frame.element_states.remove_entry(element_state_key)
1600 {
1601 self.element_states.insert(element_state_key, element_state);
1602 }
1603 }
1604
1605 self.scene.finish();
1606 }
1607}
1608
1609#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
1610pub(crate) enum InputModality {
1611 Mouse,
1612 Keyboard,
1613 Touch,
1614}
1615
1616bitflags::bitflags! {
1617 #[derive(Clone, Copy, Default)]
1619 pub(crate) struct InputReads: u8 {
1620 const POINTER = 1;
1621 const MODIFIERS = 1 << 1;
1622 const MODALITY = 1 << 2;
1623 }
1624}
1625
1626pub struct Window {
1681 pub(crate) handle: AnyWindowHandle,
1682 pub(crate) invalidator: WindowInvalidator,
1683 pub(crate) removed: bool,
1684 pub(crate) platform_window: Box<dyn PlatformWindow>,
1685 pub(crate) display_id: Option<DisplayId>,
1686 pub(crate) display_observers: SubscriberSet<(), AnyObserver>,
1687 is_resizable: bool,
1688 is_minimizable: bool,
1689 sprite_atlas: Arc<dyn PlatformAtlas>,
1690 text_system: Arc<WindowTextSystem>,
1691 text_rendering_mode: Rc<Cell<TextRenderingMode>>,
1692 rem_size: Pixels,
1693 rem_size_override_stack: SmallVec<[Pixels; 8]>,
1698 pub(crate) viewport_size: Size<Pixels>,
1699 layout_engine: Option<TaffyLayoutEngine>,
1700 pub(crate) collecting_inline: bool,
1701 pub(crate) current_inline_fragments: Option<Arc<[Bounds<Pixels>]>>,
1702 pub(crate) root: Option<AnyView>,
1703 pub(crate) element_id_stack: ElementIdStack,
1704 pub(crate) text_style_stack: Vec<TextStyleRefinement>,
1705 pub(crate) measurement_direction: ResolvedDirection,
1706 pub(crate) measurement_unicode_bidi: UnicodeBidi,
1707 pub(crate) rendered_entity_stack: Vec<EntityId>,
1708 pub(crate) element_offset_stack: Vec<Point<Pixels>>,
1709 style_transition_containing_bounds: Option<Bounds<Pixels>>,
1712 pub(crate) content_mask_stack: Vec<ContentMask<Pixels>>,
1713 pub(crate) requested_autoscroll: Option<Bounds<Pixels>>,
1714 last_text_input_configuration: Option<TextInputConfiguration>,
1718 focused_text_input: Option<FocusId>,
1719 pub(crate) image_cache_stack: Vec<AnyImageCache>,
1720 pub(crate) rendered_frame: Frame,
1721 pub(crate) next_frame: Frame,
1722 next_hitbox_id: HitboxId,
1723 pub(crate) next_tooltip_id: TooltipId,
1724 pub(crate) tooltip_bounds: Option<TooltipBounds>,
1725 pub(crate) next_frame_callbacks: Rc<RefCell<Vec<FrameCallback>>>,
1726 pub(crate) dirty_views: FxHashSet<EntityId>,
1727 focus_listeners: SubscriberSet<(), AnyWindowFocusListener>,
1728 pub(crate) focus_lost_listeners: SubscriberSet<(), AnyObserver>,
1729 focus_lost_path: SmallVec<[FocusId; 8]>,
1730 default_prevented: bool,
1731 pub(crate) mouse_position: Point<Pixels>,
1732 pointer_inside_window: bool,
1733 mouse_hit_test: HitTest,
1734 pub(crate) modifiers: Modifiers,
1735 capslock: Capslock,
1736 pub(crate) input_reads: Cell<InputReads>,
1738 deferred_priority_stack: Vec<usize>,
1739 pub(crate) frame_number: u64,
1741 scale_factor: f32,
1742 pub(crate) bounds_observers: SubscriberSet<(), AnyObserver>,
1743 appearance: WindowAppearance,
1744 pub(crate) appearance_observers: SubscriberSet<(), AnyObserver>,
1745 pub(crate) button_layout_observers: SubscriberSet<(), AnyObserver>,
1746 active: Rc<Cell<bool>>,
1747 visibility: WindowVisibility,
1748 pub(crate) visibility_observers:
1749 SubscriberSet<(), Box<dyn FnMut(WindowVisibility, &mut Window, &mut App) -> bool>>,
1750 hovered: Rc<Cell<bool>>,
1751 pub(crate) needs_present: Rc<Cell<bool>>,
1752 pub(crate) input_rate_tracker: Rc<RefCell<InputRateTracker>>,
1755 #[cfg(feature = "profiler")]
1756 pub(crate) window_profiler: profiler::WindowProfiler,
1757 pub(crate) last_input_modality: InputModality,
1758 touch_gestures: TouchGestureRecognizer,
1759 touch_prediction_enabled: bool,
1760 long_press_timer: Option<Task<()>>,
1761 long_press_capture: Option<EntityId>,
1762 pub(crate) refreshing: bool,
1763 pub(crate) activation_observers: SubscriberSet<(), AnyObserver>,
1764 pub(crate) focus: Option<FocusId>,
1765 focus_enabled: bool,
1766 pub(crate) focus_generation: u64,
1769 pending_input: Option<PendingInput>,
1770 pending_modifier: ModifierState,
1771 pub(crate) pending_input_observers: SubscriberSet<(), AnyObserver>,
1772 prompt: Option<RenderablePromptHandle>,
1773 pub(crate) client_inset: Option<Pixels>,
1774 captured_hitbox: Option<HitboxId>,
1777 #[cfg(any(feature = "inspector", debug_assertions))]
1778 inspector: Option<Entity<Inspector>>,
1779 #[cfg(feature = "profiler")]
1780 debug_frame_overlay: crate::debug_overlay::DebugFrameOverlay,
1781 pub(crate) a11y: A11y,
1782}
1783
1784#[derive(Clone, Debug, Default)]
1785struct ModifierState {
1786 modifiers: Modifiers,
1787 saw_other_input: bool,
1788}
1789
1790#[derive(Clone, Debug)]
1793pub(crate) struct InputRateTracker {
1794 timestamps: Vec<Instant>,
1795 window: Duration,
1796 inputs_per_second: u32,
1797 sustain_until: Instant,
1798 sustain_duration: Duration,
1799}
1800
1801impl Default for InputRateTracker {
1802 fn default() -> Self {
1803 Self {
1804 timestamps: Vec::new(),
1805 window: Duration::from_millis(100),
1806 inputs_per_second: 60,
1807 sustain_until: Instant::now(),
1808 sustain_duration: Duration::from_secs(1),
1809 }
1810 }
1811}
1812
1813impl InputRateTracker {
1814 pub fn record_input(&mut self) {
1815 let now = Instant::now();
1816 self.timestamps.push(now);
1817 self.prune_old_timestamps(now);
1818
1819 let min_events = self.inputs_per_second as u128 * self.window.as_millis() / 1000;
1820 if self.timestamps.len() as u128 >= min_events {
1821 self.sustain_until = now + self.sustain_duration;
1822 }
1823 }
1824
1825 pub fn is_high_rate(&self) -> bool {
1826 Instant::now() < self.sustain_until
1827 }
1828
1829 fn prune_old_timestamps(&mut self, now: Instant) {
1830 self.timestamps
1831 .retain(|&t| now.duration_since(t) <= self.window);
1832 }
1833}
1834
1835#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1836pub(crate) enum DrawPhase {
1837 None,
1838 Prepaint,
1839 Paint,
1840 Focus,
1841}
1842
1843pub(crate) const PENDING_INPUT_TIMEOUT: Duration = Duration::from_secs(1);
1844
1845pub struct PendingInputStatus<'a> {
1847 keystrokes: &'a [Keystroke],
1848 timeout: Option<PendingInputTimeoutStatus>,
1849}
1850
1851impl<'a> PendingInputStatus<'a> {
1852 pub fn keystrokes(&self) -> &'a [Keystroke] {
1854 self.keystrokes
1855 }
1856
1857 pub fn timeout(&self) -> Option<PendingInputTimeoutStatus> {
1859 self.timeout
1860 }
1861}
1862
1863#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1865pub struct PendingInputTimeoutStatus {
1866 duration: Duration,
1867 remaining: Duration,
1868 started_at: Option<Instant>,
1869 paused: bool,
1870}
1871
1872impl PendingInputTimeoutStatus {
1873 pub fn duration(&self) -> Duration {
1875 self.duration
1876 }
1877
1878 pub fn remaining(&self, cx: &App) -> Duration {
1880 self.started_at
1881 .map(|started_at| {
1882 self.remaining
1883 .saturating_sub(cx.background_executor().now() - started_at)
1884 })
1885 .unwrap_or(self.remaining)
1886 }
1887
1888 pub fn is_paused(&self) -> bool {
1890 self.paused
1891 }
1892}
1893
1894#[derive(Debug)]
1895struct PendingInputTimeout {
1896 duration: Duration,
1897 remaining: Duration,
1898 state: PendingInputTimeoutState,
1899}
1900
1901#[derive(Debug)]
1902enum PendingInputTimeoutState {
1903 Running { started_at: Instant, task: Task<()> },
1904 Paused { pause: PendingInputTimeoutPause },
1905}
1906
1907#[derive(Debug)]
1908struct PendingInputTimeoutPause {
1909 owner_id: EntityId,
1910 _release_subscription: Subscription,
1911}
1912
1913impl PendingInputTimeout {
1914 fn is_paused(&self) -> bool {
1915 matches!(&self.state, PendingInputTimeoutState::Paused { .. })
1916 }
1917
1918 fn pause(&mut self, pause: PendingInputTimeoutPause, now: Instant) -> bool {
1919 match std::mem::replace(&mut self.state, PendingInputTimeoutState::Paused { pause }) {
1920 PendingInputTimeoutState::Running { started_at, task } => {
1921 self.remaining = self.remaining.saturating_sub(now - started_at);
1922 drop(task);
1923 true
1924 }
1925 previous_state @ PendingInputTimeoutState::Paused { .. } => {
1926 self.state = previous_state;
1927 false
1928 }
1929 }
1930 }
1931
1932 fn pause_owner_id(&self) -> Option<EntityId> {
1933 match &self.state {
1934 PendingInputTimeoutState::Running { .. } => None,
1935 PendingInputTimeoutState::Paused { pause } => Some(pause.owner_id),
1936 }
1937 }
1938
1939 fn resume(&mut self, owner_id: EntityId, started_at: Instant, task: Task<()>) -> bool {
1940 match std::mem::replace(
1941 &mut self.state,
1942 PendingInputTimeoutState::Running { started_at, task },
1943 ) {
1944 PendingInputTimeoutState::Paused { pause } if pause.owner_id == owner_id => true,
1945 previous_state => {
1946 self.state = previous_state;
1947 false
1948 }
1949 }
1950 }
1951
1952 fn reset_duration(&mut self, duration: Duration) {
1953 self.duration = duration;
1954 self.remaining = duration;
1955 }
1956
1957 fn status(&self) -> PendingInputTimeoutStatus {
1958 let (started_at, paused) = match &self.state {
1959 PendingInputTimeoutState::Running { started_at, .. } => (Some(*started_at), false),
1960 PendingInputTimeoutState::Paused { .. } => (None, true),
1961 };
1962 PendingInputTimeoutStatus {
1963 duration: self.duration,
1964 remaining: self.remaining,
1965 started_at,
1966 paused,
1967 }
1968 }
1969}
1970
1971#[derive(Default, Debug)]
1972struct PendingInput {
1973 keystrokes: SmallVec<[Keystroke; 1]>,
1974 focus: Option<FocusId>,
1975 timeout: Option<PendingInputTimeout>,
1976}
1977
1978pub(crate) struct ElementStateBox {
1979 pub(crate) inner: Box<dyn Any>,
1980 #[cfg(debug_assertions)]
1981 pub(crate) type_name: &'static str,
1982}
1983
1984fn default_bounds(display_id: Option<DisplayId>, cx: &mut App) -> WindowBounds {
1985 let active_window_bounds = cx
1990 .active_window()
1991 .and_then(|w| w.update(cx, |_, window, _| window.window_bounds()).ok());
1992
1993 const CASCADE_OFFSET: f32 = 25.0;
1994
1995 let display = display_id
1996 .map(|id| cx.find_display(id))
1997 .unwrap_or_else(|| cx.primary_display());
1998
1999 let default_placement = || Bounds::new(point(px(0.), px(0.)), DEFAULT_WINDOW_SIZE);
2000
2001 let display_bounds = display
2003 .as_ref()
2004 .map(|d| d.visible_bounds())
2005 .unwrap_or_else(default_placement);
2006
2007 let (
2008 Bounds {
2009 origin: base_origin,
2010 size: base_size,
2011 },
2012 window_bounds_ctor,
2013 ): (_, fn(Bounds<Pixels>) -> WindowBounds) = match active_window_bounds {
2014 Some(bounds) => match bounds {
2015 WindowBounds::Windowed(bounds) => (bounds, WindowBounds::Windowed),
2016 WindowBounds::Maximized(bounds) => (bounds, WindowBounds::Maximized),
2017 WindowBounds::Fullscreen(bounds) => (bounds, WindowBounds::Fullscreen),
2018 },
2019 None => (
2020 display
2021 .as_ref()
2022 .map(|d| d.default_bounds())
2023 .unwrap_or_else(default_placement),
2024 WindowBounds::Windowed,
2025 ),
2026 };
2027
2028 let cascade_offset = point(px(CASCADE_OFFSET), px(CASCADE_OFFSET));
2029 let proposed_origin = base_origin + cascade_offset;
2030 let proposed_bounds = Bounds::new(proposed_origin, base_size);
2031
2032 let display_right = display_bounds.origin.x + display_bounds.size.width;
2033 let display_bottom = display_bounds.origin.y + display_bounds.size.height;
2034 let window_right = proposed_bounds.origin.x + proposed_bounds.size.width;
2035 let window_bottom = proposed_bounds.origin.y + proposed_bounds.size.height;
2036
2037 let fits_horizontally = window_right <= display_right;
2038 let fits_vertically = window_bottom <= display_bottom;
2039
2040 let final_origin = match (fits_horizontally, fits_vertically) {
2041 (true, true) => proposed_origin,
2042 (false, true) => point(display_bounds.origin.x, base_origin.y),
2043 (true, false) => point(base_origin.x, display_bounds.origin.y),
2044 (false, false) => display_bounds.origin,
2045 };
2046 window_bounds_ctor(Bounds::new(final_origin, base_size))
2047}
2048
2049impl Window {
2050 pub(crate) fn new(
2051 handle: AnyWindowHandle,
2052 options: WindowOptions,
2053 cx: &mut App,
2054 ) -> Result<Self> {
2055 let background_appearance = options.background_appearance();
2056 let WindowOptions {
2057 window_bounds,
2058 titlebar,
2059 focus,
2060 show,
2061 kind,
2062 is_movable,
2063 app_owns_titlebar_drag,
2064 inactive_frame_interval,
2065 is_resizable,
2066 is_minimizable,
2067 display_id,
2068 app_id,
2069 window_min_size,
2070 window_decorations,
2071 #[cfg_attr(
2072 not(any(target_os = "linux", target_os = "freebsd")),
2073 allow(unused_variables)
2074 )]
2075 icon,
2076 #[cfg_attr(not(target_os = "macos"), allow(unused_variables))]
2077 tabbing_identifier,
2078 #[cfg_attr(not(target_os = "macos"), allow(unused_variables))]
2079 use_display_p3,
2080 ..
2081 } = options;
2082
2083 let initial_window_title = titlebar
2084 .as_ref()
2085 .and_then(|titlebar| titlebar.title.clone());
2086
2087 let window_bounds = window_bounds.unwrap_or_else(|| default_bounds(display_id, cx));
2088 let mut platform_window = cx.platform.open_window(
2089 handle,
2090 WindowParams {
2091 bounds: window_bounds.get_bounds(),
2092 titlebar,
2093 kind,
2094 is_movable,
2095 app_owns_titlebar_drag,
2096 is_resizable,
2097 is_minimizable,
2098 focus,
2099 show,
2100 display_id,
2101 window_min_size,
2102 app_id: app_id.clone(),
2103 icon,
2104 #[cfg(target_os = "macos")]
2105 tabbing_identifier,
2106 #[cfg(target_os = "macos")]
2107 use_display_p3,
2108 },
2109 )?;
2110
2111 let tab_bar_visible = platform_window.tab_bar_visible();
2112 SystemWindowTabController::init_visible(cx, tab_bar_visible);
2113 if let Some(tabs) = platform_window.tabbed_windows() {
2114 SystemWindowTabController::add_tab(cx, handle.window_id(), tabs);
2115 }
2116
2117 let display_id = platform_window.display().map(|display| display.id());
2118 let sprite_atlas = platform_window.sprite_atlas();
2119 let mouse_position = platform_window.mouse_position();
2120 let modifiers = platform_window.modifiers();
2121 let capslock = platform_window.capslock();
2122 let content_size = platform_window.content_size();
2123 let scale_factor = platform_window.scale_factor();
2124 let appearance = platform_window.appearance();
2125 let text_system = Arc::new(WindowTextSystem::new(cx.text_system().clone()));
2126 let invalidator = WindowInvalidator::new(handle.window_id());
2127 let active = Rc::new(Cell::new(platform_window.is_active()));
2128 let visibility = platform_window.visibility();
2129 #[cfg(feature = "profiler")]
2130 profiler::journal::record_window_visibility(handle.window_id(), visibility);
2131 let hovered = Rc::new(Cell::new(platform_window.is_hovered()));
2132 let needs_present = Rc::new(Cell::new(false));
2133 let next_frame_callbacks: Rc<RefCell<Vec<FrameCallback>>> = Default::default();
2134 let input_rate_tracker = Rc::new(RefCell::new(InputRateTracker::default()));
2135 let last_frame_time = Rc::new(Cell::new(None));
2136
2137 platform_window
2138 .request_decorations(window_decorations.unwrap_or(WindowDecorations::Server));
2139 platform_window.set_background_appearance(background_appearance);
2140
2141 match window_bounds {
2142 WindowBounds::Fullscreen(_) => platform_window.toggle_fullscreen(),
2143 WindowBounds::Maximized(_) => platform_window.zoom(),
2144 WindowBounds::Windowed(_) => {}
2145 }
2146
2147 let accessibility_force_disabled = cx.accessibility_force_disabled;
2148 let accessibility_forced = cx.accessibility_forced;
2149 let a11y_active_flag = Arc::new(AtomicBool::new(false));
2150
2151 #[cfg(not(target_family = "wasm"))]
2152 if !accessibility_force_disabled {
2153 let mut initial_root_node = accesskit::Node::new(accesskit::Role::Window);
2154 if let Some(title) = &initial_window_title {
2155 initial_root_node.set_label(title.to_string());
2156 }
2157 let initial_tree = accesskit::TreeUpdate {
2158 nodes: vec![(ROOT_NODE_ID, initial_root_node)],
2159 tree: Some(accesskit::Tree::new(ROOT_NODE_ID)),
2160 tree_id: accesskit::TreeId::ROOT,
2161 focus: ROOT_NODE_ID,
2162 };
2163 let (activation_sender, activation_receiver) = async_channel::unbounded::<()>();
2164 let (deactivation_sender, deactivation_receiver) = async_channel::unbounded::<()>();
2165 let (action_sender, action_receiver) =
2166 async_channel::unbounded::<accesskit::ActionRequest>();
2167
2168 platform_window.a11y_init(crate::A11yCallbacks {
2169 activation: {
2170 let active_flag = a11y_active_flag.clone();
2171 Box::new(move || {
2172 log::info!("Accessibility activated");
2173 active_flag.store(true, SeqCst);
2174 activation_sender.send_blocking(()).log_err();
2175 Some(initial_tree.clone())
2176 })
2177 },
2178 action: Box::new(move |request| {
2179 action_sender.send_blocking(request).log_err();
2180 }),
2181 deactivation: {
2182 let active_flag = a11y_active_flag.clone();
2183 Box::new(move || {
2184 log::info!("Accessibility deactivated");
2185 active_flag.store(false, SeqCst);
2186 deactivation_sender.send_blocking(()).log_err();
2187 })
2188 },
2189 });
2190
2191 let mut async_cx = cx.to_async();
2197 cx.foreground_executor()
2198 .spawn(async move {
2199 while activation_receiver.recv().await.is_ok() {
2200 handle
2201 .update(&mut async_cx, |_, window, _| window.refresh())
2202 .log_err();
2203 }
2204 })
2205 .detach();
2206
2207 let mut async_cx = cx.to_async();
2208 cx.foreground_executor()
2209 .spawn(async move {
2210 while deactivation_receiver.recv().await.is_ok() {
2211 handle
2212 .update(&mut async_cx, |_, window, _| window.refresh())
2213 .log_err();
2214 }
2215 })
2216 .detach();
2217
2218 let mut async_cx = cx.to_async();
2219 cx.foreground_executor()
2220 .spawn(async move {
2221 while let Ok(request) = action_receiver.recv().await {
2222 handle
2223 .update(&mut async_cx, |_, window, cx| {
2224 window.handle_a11y_action(request, cx);
2225 })
2226 .log_err();
2227 }
2228 })
2229 .detach();
2230 }
2231
2232 #[cfg(all(
2233 feature = "hot-patching",
2234 debug_assertions,
2235 not(target_family = "wasm")
2236 ))]
2237 let (hot_patch_sender, hot_patch_receiver) = async_channel::bounded(1);
2238
2239 platform_window.on_close(Box::new({
2240 let window_id = handle.window_id();
2241 let mut cx = cx.to_async();
2242 #[cfg(all(
2243 feature = "hot-patching",
2244 debug_assertions,
2245 not(target_family = "wasm")
2246 ))]
2247 let hot_patch_sender = hot_patch_sender.clone();
2248 move || {
2249 #[cfg(all(
2250 feature = "hot-patching",
2251 debug_assertions,
2252 not(target_family = "wasm")
2253 ))]
2254 hot_patch_sender.close();
2255 let _ = handle.update(&mut cx, |_, window, _| window.remove_window());
2256 let _ = cx.update(|cx| {
2257 SystemWindowTabController::remove_tab(cx, window_id);
2258 });
2259 }
2260 }));
2261 #[cfg(all(
2262 feature = "hot-patching",
2263 debug_assertions,
2264 not(target_family = "wasm")
2265 ))]
2266 {
2267 subsecond::register_handler(Arc::new(move || {
2270 let _ = hot_patch_sender.try_send(());
2271 }));
2272 let mut cx = cx.to_async();
2273 let foreground_executor = cx.foreground_executor().clone();
2274 foreground_executor
2275 .spawn(async move {
2276 while hot_patch_receiver.recv().await.is_ok() {
2277 if handle
2278 .update(&mut cx, |_, window, _| window.refresh())
2279 .is_err()
2280 {
2281 break;
2282 }
2283 }
2284 })
2285 .detach();
2286 }
2287 platform_window.on_request_frame(Box::new({
2288 let mut cx = cx.to_async();
2289 let invalidator = invalidator.clone();
2290 let active = active.clone();
2291 let needs_present = needs_present.clone();
2292 let next_frame_callbacks = next_frame_callbacks.clone();
2293 let input_rate_tracker = input_rate_tracker.clone();
2294 let mut deferred_force_render = false;
2295 move |request_frame_options| {
2296 #[cfg(feature = "profiler")]
2297 let (_foreground_turn, window_id) = {
2298 let foreground_turn = profiler::journal::foreground_turn();
2299 let window_id = handle.window_id();
2300 profiler::journal::record_platform_signal(profiler::journal::PlatformSignal {
2301 window_id,
2302 signal_at: request_frame_options.signal_at,
2303 handled_at: Instant::now(),
2304 source: request_frame_options.signal_source,
2305 });
2306 (foreground_turn, window_id)
2307 };
2308 if draw_in_progress() {
2324 log::debug!("deferring re-entrant window draw request");
2325 deferred_force_render |= request_frame_options.force_render;
2326 #[cfg(feature = "profiler")]
2327 profiler::journal::record_reentrant_frame_skipped(window_id, Instant::now());
2328 return;
2329 }
2330 let force_render =
2334 mem::take(&mut deferred_force_render) || request_frame_options.force_render;
2335
2336 let thermal_state = handle
2337 .update(&mut cx, |_, _, cx| cx.thermal_state())
2338 .log_err();
2339
2340 let min_frame_interval = if request_frame_options.require_presentation
2344 || (!request_frame_options.force_render
2345 && next_frame_callbacks.borrow().is_empty())
2346 {
2347 None
2348 } else if !active.get() && !input_rate_tracker.borrow_mut().is_high_rate() {
2349 inactive_frame_interval.map(|interval| (interval, FrameRateLimit::Inactive))
2350 } else if let Some(ThermalState::Critical | ThermalState::Serious) = thermal_state {
2351 Some((Duration::from_micros(16667), FrameRateLimit::Thermal))
2352 } else {
2353 None
2354 };
2355
2356 let now = Instant::now();
2357 #[cfg_attr(not(feature = "profiler"), expect(unused_variables))]
2358 if let Some((min_interval, limit)) = min_frame_interval {
2359 if let Some(last_frame) = last_frame_time.get()
2360 && now.duration_since(last_frame) < min_interval
2361 {
2362 deferred_force_render |= force_render;
2364 handle
2366 .update(&mut cx, |_, window, _| {
2367 window.platform_window.schedule_frame();
2368 })
2369 .log_err();
2370 invalidator.wake_platform();
2375 #[cfg(feature = "profiler")]
2376 profiler::journal::record_frame_skipped(
2377 window_id,
2378 Instant::now(),
2379 match limit {
2380 FrameRateLimit::Inactive => {
2381 profiler::journal::FrameSkipReason::InactiveFrameRateLimit
2382 }
2383 FrameRateLimit::Thermal => {
2384 profiler::journal::FrameSkipReason::ThermalFrameRateLimit
2385 }
2386 },
2387 );
2388 return;
2389 }
2390 }
2391 last_frame_time.set(Some(now));
2392
2393 let pending_next_frame_callbacks = next_frame_callbacks.take();
2394 if !pending_next_frame_callbacks.is_empty() {
2395 handle
2396 .update(&mut cx, |_, window, cx| {
2397 for callback in pending_next_frame_callbacks {
2398 callback(window, cx);
2399 }
2400 })
2401 .log_err();
2402 }
2403
2404 let needs_present = request_frame_options.require_presentation
2408 || needs_present.get()
2409 || input_rate_tracker.borrow_mut().is_high_rate();
2410
2411 let should_draw = invalidator.is_dirty() || force_render;
2412 if should_draw {
2413 measure("frame duration", || {
2414 handle
2415 .update(&mut cx, |_, window, cx| {
2416 #[cfg(feature = "profiler")]
2417 window
2418 .window_profiler
2419 .record_frame_signal(request_frame_options.signal_at);
2420 if force_render {
2421 window.refresh();
2424 }
2425 let arena_clear_needed = window.draw(cx);
2426 window.present();
2427 arena_clear_needed.clear(cx);
2428 })
2429 .log_err();
2430 })
2431 } else if needs_present {
2432 handle
2433 .update(&mut cx, |_, window, _| window.present())
2434 .log_err();
2435 }
2436
2437 handle
2438 .update(&mut cx, |_, window, _| {
2439 if window.invalidator.is_dirty()
2440 || !window.next_frame_callbacks.borrow().is_empty()
2441 {
2442 window.platform_window.schedule_frame();
2443 }
2444 })
2445 .log_err();
2446
2447 if invalidator.is_dirty() || !next_frame_callbacks.borrow().is_empty() {
2453 invalidator.wake_platform();
2454 }
2455 #[cfg(feature = "profiler")]
2456 if !should_draw {
2457 profiler::journal::record_frame_skipped(
2458 window_id,
2459 Instant::now(),
2460 profiler::journal::FrameSkipReason::NoRenderNeeded,
2461 );
2462 }
2463 }
2464 }));
2465 invalidator.set_platform_waker(platform_window.frame_waker());
2466 platform_window.on_visual_viewport_changed(Box::new({
2467 let mut cx = cx.to_async();
2468 move || {
2469 handle
2470 .update(&mut cx, |_, window, _| window.refresh())
2471 .log_err();
2472 }
2473 }));
2474 platform_window.on_insets_changed(Box::new({
2475 let mut cx = cx.to_async();
2476 move |_| {
2477 handle
2478 .update(&mut cx, |_, window, _| window.refresh())
2479 .log_err();
2480 }
2481 }));
2482 platform_window.on_resize(Box::new({
2483 let mut cx = cx.to_async();
2484 move |_, _| {
2485 handle
2486 .update(&mut cx, |_, window, cx| window.bounds_changed(cx))
2487 .log_err();
2488 }
2489 }));
2490 platform_window.on_moved(Box::new({
2491 let mut cx = cx.to_async();
2492 move || {
2493 handle
2494 .update(&mut cx, |_, window, cx| window.bounds_changed(cx))
2495 .log_err();
2496 }
2497 }));
2498 platform_window.on_display_changed(Box::new({
2499 let cx = cx.to_async();
2500 move || cx.update(|cx| Window::display_changed(handle, cx))
2501 }));
2502 platform_window.on_appearance_changed(Box::new({
2503 let cx = cx.to_async();
2504 let foreground_executor = cx.foreground_executor().clone();
2505 move || {
2506 let mut cx = cx.clone();
2507 foreground_executor
2510 .spawn(async move {
2511 handle
2512 .update(&mut cx, |_, window, cx| window.appearance_changed(cx))
2513 .log_err();
2514 })
2515 .detach();
2516 }
2517 }));
2518 platform_window.on_button_layout_changed(Box::new({
2519 let mut cx = cx.to_async();
2520 move || {
2521 handle
2522 .update(&mut cx, |_, window, cx| window.button_layout_changed(cx))
2523 .log_err();
2524 }
2525 }));
2526 platform_window.on_active_status_change(Box::new({
2527 let mut cx = cx.to_async();
2528 move |active| {
2529 handle
2530 .update(&mut cx, |_, window, cx| {
2531 window.active.set(active);
2532 window.modifiers = window.platform_window.modifiers();
2533 window.capslock = window.platform_window.capslock();
2534 window
2535 .activation_observers
2536 .clone()
2537 .retain(&(), |callback| callback(window, cx));
2538
2539 window.bounds_changed(cx);
2540 window.refresh();
2541
2542 SystemWindowTabController::update_last_active(cx, window.handle.id);
2543 })
2544 .log_err();
2545 }
2546 }));
2547 platform_window.on_visibility_change(Box::new({
2548 let mut cx = cx.to_async();
2549 move |_| {
2550 handle
2551 .update(&mut cx, |_, window, cx| {
2552 window.refresh_visibility(cx);
2553 })
2554 .log_err();
2555 }
2556 }));
2557 platform_window.on_hover_status_change(Box::new({
2558 let mut cx = cx.to_async();
2559 move |active| {
2560 handle
2561 .update(&mut cx, |_, window, _| {
2562 window.hovered.set(active);
2563 window.pointer_inside_window = active;
2564 window.refresh();
2565 })
2566 .log_err();
2567 }
2568 }));
2569 platform_window.on_input({
2570 let mut cx = cx.to_async();
2571 Box::new(move |event| {
2572 handle
2573 .update(&mut cx, |_, window, cx| window.dispatch_event(event, cx))
2574 .log_err()
2575 .unwrap_or(DispatchEventResult::default())
2576 })
2577 });
2578 platform_window.on_hit_test_window_control({
2579 let mut cx = cx.to_async();
2580 Box::new(move || {
2581 handle
2582 .update(&mut cx, |_, window, _cx| {
2583 for (area, hitbox) in &window.rendered_frame.window_control_hitboxes {
2584 let mut scrollable_ids = window.mouse_hit_test().iter_scrollable();
2585 if scrollable_ids.contains(&hitbox.id) {
2586 return Some(*area);
2587 }
2588 }
2589 None
2590 })
2591 .log_err()
2592 .unwrap_or(None)
2593 })
2594 });
2595 platform_window.on_move_tab_to_new_window({
2596 let mut cx = cx.to_async();
2597 Box::new(move || {
2598 handle
2599 .update(&mut cx, |_, _window, cx| {
2600 SystemWindowTabController::move_tab_to_new_window(cx, handle.window_id());
2601 })
2602 .log_err();
2603 })
2604 });
2605 platform_window.on_merge_all_windows({
2606 let mut cx = cx.to_async();
2607 Box::new(move || {
2608 handle
2609 .update(&mut cx, |_, _window, cx| {
2610 SystemWindowTabController::merge_all_windows(cx, handle.window_id());
2611 })
2612 .log_err();
2613 })
2614 });
2615 platform_window.on_select_next_tab({
2616 let mut cx = cx.to_async();
2617 Box::new(move || {
2618 handle
2619 .update(&mut cx, |_, _window, cx| {
2620 SystemWindowTabController::select_next_tab(cx, handle.window_id());
2621 })
2622 .log_err();
2623 })
2624 });
2625 platform_window.on_select_previous_tab({
2626 let mut cx = cx.to_async();
2627 Box::new(move || {
2628 handle
2629 .update(&mut cx, |_, _window, cx| {
2630 SystemWindowTabController::select_previous_tab(cx, handle.window_id())
2631 })
2632 .log_err();
2633 })
2634 });
2635 platform_window.on_toggle_tab_bar({
2636 let mut cx = cx.to_async();
2637 Box::new(move || {
2638 handle
2639 .update(&mut cx, |_, window, cx| {
2640 let tab_bar_visible = window.platform_window.tab_bar_visible();
2641 SystemWindowTabController::set_visible(cx, tab_bar_visible);
2642 })
2643 .log_err();
2644 })
2645 });
2646
2647 if let Some(app_id) = app_id {
2648 platform_window.set_app_id(&app_id);
2649 }
2650
2651 platform_window.map_window().unwrap();
2652
2653 Ok(Window {
2654 handle,
2655 invalidator,
2656 removed: false,
2657 platform_window,
2658 display_id,
2659 display_observers: SubscriberSet::new(),
2660 is_resizable,
2661 is_minimizable,
2662 sprite_atlas,
2663 text_system,
2664 text_rendering_mode: cx.text_rendering_mode.clone(),
2665 rem_size: px(16.),
2666 rem_size_override_stack: SmallVec::new(),
2667 viewport_size: content_size,
2668 layout_engine: Some(TaffyLayoutEngine::new()),
2669 collecting_inline: false,
2670 current_inline_fragments: None,
2671 root: None,
2672 element_id_stack: ElementIdStack::default(),
2673 text_style_stack: Vec::new(),
2674 measurement_direction: ResolvedDirection::LeftToRight,
2675 measurement_unicode_bidi: UnicodeBidi::Normal,
2676 rendered_entity_stack: Vec::new(),
2677 element_offset_stack: Vec::new(),
2678 style_transition_containing_bounds: None,
2679 content_mask_stack: Vec::new(),
2680 requested_autoscroll: None,
2681 last_text_input_configuration: None,
2682 focused_text_input: None,
2683 rendered_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())),
2684 next_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())),
2685 next_frame_callbacks,
2686 next_hitbox_id: HitboxId(0),
2687 next_tooltip_id: TooltipId::default(),
2688 tooltip_bounds: None,
2689 dirty_views: FxHashSet::default(),
2690 focus_listeners: SubscriberSet::new(),
2691 focus_lost_listeners: SubscriberSet::new(),
2692 focus_lost_path: SmallVec::new(),
2693 default_prevented: true,
2694 mouse_position,
2695 pointer_inside_window: true,
2696 mouse_hit_test: HitTest::default(),
2697 modifiers,
2698 capslock,
2699 scale_factor,
2700 bounds_observers: SubscriberSet::new(),
2701 appearance,
2702 appearance_observers: SubscriberSet::new(),
2703 button_layout_observers: SubscriberSet::new(),
2704 active,
2705 visibility,
2706 visibility_observers: SubscriberSet::new(),
2707 hovered,
2708 needs_present,
2709 input_rate_tracker,
2710 #[cfg(feature = "profiler")]
2711 window_profiler: profiler::WindowProfiler::new(handle.window_id())?,
2712 last_input_modality: InputModality::Mouse,
2713 input_reads: Cell::default(),
2714 deferred_priority_stack: Vec::new(),
2715 frame_number: 0,
2716 touch_gestures: TouchGestureRecognizer::new(
2717 cx.platform
2718 .gestures()
2719 .map_or_else(GestureTuning::default, |gestures| gestures.tuning()),
2720 ),
2721 touch_prediction_enabled: true,
2722 long_press_timer: None,
2723 long_press_capture: None,
2724 refreshing: false,
2725 activation_observers: SubscriberSet::new(),
2726 focus: None,
2727 focus_enabled: true,
2728 focus_generation: 0,
2729 pending_input: None,
2730 pending_modifier: ModifierState::default(),
2731 pending_input_observers: SubscriberSet::new(),
2732 prompt: None,
2733 client_inset: None,
2734 image_cache_stack: Vec::new(),
2735 captured_hitbox: None,
2736 #[cfg(any(feature = "inspector", debug_assertions))]
2737 inspector: None,
2738 #[cfg(feature = "profiler")]
2739 debug_frame_overlay: crate::debug_overlay::DebugFrameOverlay::new(),
2740 a11y: A11y::new(
2741 a11y_active_flag,
2742 accessibility_force_disabled,
2743 accessibility_forced,
2744 initial_window_title,
2745 ),
2746 })
2747 }
2748
2749 pub(crate) fn new_focus_listener(
2750 &self,
2751 value: AnyWindowFocusListener,
2752 ) -> (Subscription, impl FnOnce() + use<>) {
2753 self.focus_listeners.insert((), value)
2754 }
2755}
2756
2757#[derive(Clone, Debug, Default, PartialEq, Eq)]
2758pub struct DispatchEventResult {
2763 pub propagate: bool,
2765 pub default_prevented: bool,
2767}
2768
2769#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
2779#[repr(C)]
2780pub struct ContentMask<P: Clone + Debug + Default + PartialEq> {
2781 pub bounds: Bounds<P>,
2783 pub fade_out: Edges<P>,
2787}
2788
2789impl ContentMask<Pixels> {
2790 pub fn scale(&self, factor: f32) -> ContentMask<ScaledPixels> {
2792 ContentMask {
2793 bounds: self.bounds.scale(factor),
2794 fade_out: self.fade_out.scale(factor),
2795 }
2796 }
2797
2798 pub fn intersect(&self, other: &Self) -> Self {
2803 fn edge_fade(
2804 a: Pixels,
2805 b: Pixels,
2806 a_fade: Pixels,
2807 b_fade: Pixels,
2808 a_clips_closer: impl Fn(Pixels, Pixels) -> bool,
2809 ) -> Pixels {
2810 if a_clips_closer(a, b) {
2811 a_fade
2812 } else if a_clips_closer(b, a) {
2813 b_fade
2814 } else {
2815 a_fade.max(b_fade)
2816 }
2817 }
2818
2819 ContentMask {
2820 bounds: self.bounds.intersect(&other.bounds),
2821 fade_out: Edges {
2822 top: edge_fade(
2823 self.bounds.top(),
2824 other.bounds.top(),
2825 self.fade_out.top,
2826 other.fade_out.top,
2827 |a, b| a > b,
2828 ),
2829 right: edge_fade(
2830 self.bounds.right(),
2831 other.bounds.right(),
2832 self.fade_out.right,
2833 other.fade_out.right,
2834 |a, b| a < b,
2835 ),
2836 bottom: edge_fade(
2837 self.bounds.bottom(),
2838 other.bounds.bottom(),
2839 self.fade_out.bottom,
2840 other.fade_out.bottom,
2841 |a, b| a < b,
2842 ),
2843 left: edge_fade(
2844 self.bounds.left(),
2845 other.bounds.left(),
2846 self.fade_out.left,
2847 other.fade_out.left,
2848 |a, b| a > b,
2849 ),
2850 },
2851 }
2852 }
2853}
2854
2855#[cfg(test)]
2856mod content_mask_tests {
2857 use super::*;
2858
2859 fn mask(left: f32, width: f32, fade_left: f32) -> ContentMask<Pixels> {
2860 ContentMask {
2861 bounds: Bounds::new(point(px(left), px(0.)), size(px(width), px(10.))),
2862 fade_out: Edges {
2863 left: px(fade_left),
2864 ..Default::default()
2865 },
2866 }
2867 }
2868
2869 #[test]
2870 fn intersect_keeps_only_the_fade_at_the_visible_edge() {
2871 let outer = mask(0., 100., 20.);
2872 let inner = mask(30., 40., 0.);
2873 assert_eq!(outer.intersect(&inner).fade_out.left, px(0.));
2874
2875 let child = mask(0., 40., 0.);
2876 assert_eq!(outer.intersect(&child).fade_out.left, px(20.));
2877 }
2878}
2879
2880impl Window {
2881 fn mark_view_dirty(&mut self, view_id: EntityId) {
2882 for ancestor_id in self
2885 .rendered_frame
2886 .dispatch_tree
2887 .view_path_reversed(view_id)
2888 {
2889 if !self.dirty_views.insert(ancestor_id) {
2890 break;
2891 }
2892 }
2893 self.dirty_views.insert(view_id);
2896 }
2897
2898 pub(crate) fn refresh_visibility(&mut self, cx: &mut App) {
2899 let visibility = self.platform_window.visibility();
2900 if self.visibility != visibility {
2901 self.visibility = visibility;
2902 #[cfg(feature = "profiler")]
2903 profiler::journal::record_window_visibility(self.handle.window_id(), visibility);
2904 self.visibility_observers
2905 .clone()
2906 .retain(&(), |callback| callback(visibility, self, cx));
2907 }
2908 #[cfg(feature = "profiler")]
2909 if self.invalidator.is_dirty() || self.needs_present.get() {
2910 profiler::journal::record_frame_pending(self.handle.window_id(), Instant::now());
2911 }
2912 }
2913
2914 pub fn visibility(&self) -> WindowVisibility {
2917 self.visibility
2918 }
2919
2920 pub fn is_visible(&self) -> bool {
2926 self.visibility.is_visible()
2927 }
2928
2929 pub fn observe_window_visibility(
2931 &self,
2932 mut callback: impl FnMut(WindowVisibility, &mut Window, &mut App) + 'static,
2933 ) -> Subscription {
2934 let (subscription, activate) = self.visibility_observers.insert(
2935 (),
2936 Box::new(move |visibility, window, cx| {
2937 callback(visibility, window, cx);
2938 true
2939 }),
2940 );
2941 activate();
2942 subscription
2943 }
2944
2945 pub fn observe_window_appearance(
2952 &self,
2953 mut callback: impl FnMut(&mut Window, &mut App) + 'static,
2954 ) -> Subscription {
2955 let (subscription, activate) = self.appearance_observers.insert(
2956 (),
2957 Box::new(move |window, cx| {
2958 callback(window, cx);
2959 true
2960 }),
2961 );
2962 activate();
2963 subscription
2964 }
2965
2966 pub fn observe_button_layout_changed(
2972 &self,
2973 mut callback: impl FnMut(&mut Window, &mut App) + 'static,
2974 ) -> Subscription {
2975 let (subscription, activate) = self.button_layout_observers.insert(
2976 (),
2977 Box::new(move |window, cx| {
2978 callback(window, cx);
2979 true
2980 }),
2981 );
2982 activate();
2983 subscription
2984 }
2985
2986 pub fn replace_root<E>(
2992 &mut self,
2993 cx: &mut App,
2994 build_view: impl FnOnce(&mut Window, &mut Context<E>) -> E,
2995 ) -> Entity<E>
2996 where
2997 E: 'static + Render,
2998 {
2999 let view = cx.new(|cx| build_view(self, cx));
3000 self.root = Some(view.clone().into());
3001 self.refresh();
3002 view
3003 }
3004
3005 pub fn root<E>(&self) -> Option<Option<Entity<E>>>
3027 where
3028 E: 'static + Render,
3029 {
3030 self.root
3031 .as_ref()
3032 .map(|view| view.clone().downcast::<E>().ok())
3033 }
3034
3035 pub fn window_handle(&self) -> AnyWindowHandle {
3037 self.handle
3038 }
3039
3040 pub fn refresh(&mut self) {
3062 if self.invalidator.not_drawing() {
3063 self.refreshing = true;
3064 self.invalidator.set_dirty(true);
3065 }
3066 }
3067
3068 pub fn remove_window(&mut self) {
3073 self.removed = true;
3074 }
3075
3076 pub fn focused(&self, cx: &App) -> Option<FocusHandle> {
3080 self.focus
3081 .and_then(|id| FocusHandle::for_id(id, &cx.focus_handles))
3082 }
3083
3084 pub fn focus_lost_restore_target(&self, cx: &App) -> Option<FocusHandle> {
3088 let (_leaf, ancestors) = self.focus_lost_path.split_last()?;
3089 ancestors.iter().rev().find_map(|id| {
3090 self.rendered_frame.dispatch_tree.focusable_node_id(*id)?;
3091 FocusHandle::for_id(*id, &cx.focus_handles)
3092 })
3093 }
3094
3095 pub fn focus(&mut self, handle: &FocusHandle, cx: &mut App) {
3120 if !self.focus_enabled || self.focus == Some(handle.id) {
3121 return;
3122 }
3123
3124 self.focus = Some(handle.id);
3125 self.focus_generation = self.focus_generation.wrapping_add(1);
3126 self.clear_pending_keystrokes(cx);
3127
3128 self.refresh();
3129 }
3130
3131 pub fn blur(&mut self, cx: &mut App) {
3136 self.clear_pending_keystrokes(cx);
3137
3138 if !self.focus_enabled {
3139 return;
3140 }
3141
3142 if self.focus.is_some() {
3143 self.focus_generation = self.focus_generation.wrapping_add(1);
3144 }
3145 self.focus = None;
3146 self.refresh();
3147 }
3148
3149 pub fn disable_focus(&mut self, cx: &mut App) {
3154 self.blur(cx);
3155 self.focus_enabled = false;
3156 }
3157
3158 pub fn focus_next(&mut self, cx: &mut App) {
3167 if !self.focus_enabled {
3168 return;
3169 }
3170
3171 if let Some(handle) = self.rendered_frame.tab_stops.next(self.focus.as_ref()) {
3172 self.focus(&handle, cx)
3173 }
3174 }
3175
3176 pub fn focus_prev(&mut self, cx: &mut App) {
3181 if !self.focus_enabled {
3182 return;
3183 }
3184
3185 if let Some(handle) = self.rendered_frame.tab_stops.prev(self.focus.as_ref()) {
3186 self.focus(&handle, cx)
3187 }
3188 }
3189
3190 pub fn text_system(&self) -> &Arc<WindowTextSystem> {
3192 &self.text_system
3193 }
3194
3195 pub fn text_style(&self) -> TextStyle {
3197 let mut style = TextStyle::default();
3198 for refinement in &self.text_style_stack {
3199 style.refine(refinement);
3200 }
3201 style
3202 }
3203
3204 pub fn resolved_direction(&self) -> ResolvedDirection {
3209 self.measurement_direction
3210 }
3211
3212 #[doc(hidden)]
3214 pub fn resolved_unicode_bidi(&self) -> UnicodeBidi {
3215 self.measurement_unicode_bidi
3216 }
3217
3218 pub fn is_maximized(&self) -> bool {
3223 self.platform_window.is_maximized()
3224 }
3225
3226 pub fn request_decorations(&self, decorations: WindowDecorations) {
3230 self.platform_window.request_decorations(decorations);
3231 }
3232
3233 pub fn set_exclusive_zone(&self, zone: Pixels) {
3239 self.platform_window.set_exclusive_zone(zone);
3240 }
3241
3242 #[cfg(all(target_os = "linux", feature = "wayland"))]
3247 pub fn set_exclusive_edge(&self, edge: crate::layer_shell::Anchor) {
3248 self.platform_window.set_exclusive_edge(edge);
3249 }
3250
3251 pub fn start_window_resize(&self, edge: ResizeEdge) {
3253 if self.is_resizable {
3254 self.platform_window.start_window_resize(edge);
3255 }
3256 }
3257
3258 pub fn set_input_region(&self, region: Option<&[Bounds<Pixels>]>) {
3265 self.platform_window.set_input_region(region);
3266 }
3267
3268 pub fn window_bounds(&self) -> WindowBounds {
3273 self.platform_window.window_bounds()
3274 }
3275
3276 pub fn inner_window_bounds(&self) -> WindowBounds {
3279 self.platform_window.inner_window_bounds()
3280 }
3281
3282 pub fn native_window_state(&self) -> Option<Vec<u8>> {
3285 self.platform_window.native_window_state()
3286 }
3287
3288 pub fn restore_native_window_state(&self, state: &[u8]) {
3291 self.platform_window.restore_native_window_state(state);
3292 }
3293
3294 pub fn dispatch_action(&mut self, action: Box<dyn Action>, cx: &mut App) {
3300 let focus_id = self.focused(cx).map(|handle| handle.id);
3301
3302 let window = self.handle;
3303 cx.defer(move |cx| {
3304 window
3305 .update(cx, |_, window, cx| {
3306 let node_id = window.focus_node_id_in_rendered_frame(focus_id);
3307 window.dispatch_action_on_node(node_id, action.as_ref(), cx);
3308 })
3309 .log_err();
3310 })
3311 }
3312
3313 pub(crate) fn dispatch_keystroke_observers(
3314 &mut self,
3315 keystroke: &Keystroke,
3316 input_preference: InputPreference,
3317 action: Option<&dyn Action>,
3318 context_stack: Vec<KeyContext>,
3319 cx: &mut App,
3320 ) {
3321 cx.keystroke_observers.clone().retain(&(), move |callback| {
3322 (callback)(
3323 &KeystrokeEvent {
3324 keystroke: keystroke.clone(),
3325 input_preference,
3326 action: action.map(|action| action.boxed_clone()),
3327 context_stack: context_stack.clone(),
3328 },
3329 self,
3330 cx,
3331 )
3332 });
3333 }
3334
3335 pub(crate) fn dispatch_keystroke_interceptors(
3336 &mut self,
3337 keystroke: &Keystroke,
3338 input_preference: InputPreference,
3339 context_stack: Vec<KeyContext>,
3340 cx: &mut App,
3341 ) {
3342 cx.keystroke_interceptors
3343 .clone()
3344 .retain(&(), move |callback| {
3345 (callback)(
3346 &KeystrokeEvent {
3347 keystroke: keystroke.clone(),
3348 input_preference,
3349 action: None,
3350 context_stack: context_stack.clone(),
3351 },
3352 self,
3353 cx,
3354 )
3355 });
3356 }
3357
3358 pub fn defer(&self, cx: &mut App, f: impl FnOnce(&mut Window, &mut App) + 'static) {
3363 let handle = self.handle;
3364 cx.defer(move |cx| {
3365 handle.update(cx, |_, window, cx| f(window, cx)).ok();
3366 });
3367 }
3368
3369 pub fn observe<T: 'static>(
3376 &mut self,
3377 observed: &Entity<T>,
3378 cx: &mut App,
3379 mut on_notify: impl FnMut(Entity<T>, &mut Window, &mut App) + 'static,
3380 ) -> Subscription {
3381 let entity_id = observed.entity_id();
3382 let observed = observed.downgrade();
3383 let window_handle = self.handle;
3384 cx.new_observer(
3385 entity_id,
3386 Box::new(move |cx| {
3387 window_handle
3388 .update(cx, |_, window, cx| {
3389 if let Some(handle) = observed.upgrade() {
3390 on_notify(handle, window, cx);
3391 true
3392 } else {
3393 false
3394 }
3395 })
3396 .unwrap_or(false)
3397 }),
3398 )
3399 }
3400
3401 pub fn subscribe<Emitter, Evt>(
3408 &mut self,
3409 entity: &Entity<Emitter>,
3410 cx: &mut App,
3411 mut on_event: impl FnMut(Entity<Emitter>, &Evt, &mut Window, &mut App) + 'static,
3412 ) -> Subscription
3413 where
3414 Emitter: EventEmitter<Evt>,
3415 Evt: 'static,
3416 {
3417 let entity_id = entity.entity_id();
3418 let handle = entity.downgrade();
3419 let window_handle = self.handle;
3420 cx.new_subscription(
3421 entity_id,
3422 (
3423 TypeId::of::<Evt>(),
3424 Box::new(move |event, cx| {
3425 window_handle
3426 .update(cx, |_, window, cx| {
3427 if let Some(entity) = handle.upgrade() {
3428 let event = event.downcast_ref().expect("invalid event type");
3429 on_event(entity, event, window, cx);
3430 true
3431 } else {
3432 false
3433 }
3434 })
3435 .unwrap_or(false)
3436 }),
3437 ),
3438 )
3439 }
3440
3441 pub fn observe_release<T>(
3447 &self,
3448 entity: &Entity<T>,
3449 cx: &mut App,
3450 mut on_release: impl FnOnce(&mut T, &mut Window, &mut App) + 'static,
3451 ) -> Subscription
3452 where
3453 T: 'static,
3454 {
3455 let entity_id = entity.entity_id();
3456 let window_handle = self.handle;
3457 let (subscription, activate) = cx.release_listeners.insert(
3458 entity_id,
3459 Box::new(move |entity, cx| {
3460 let entity = entity.downcast_mut().expect("invalid entity type");
3461 let _ = window_handle.update(cx, |_, window, cx| on_release(entity, window, cx));
3462 }),
3463 );
3464 activate();
3465 subscription
3466 }
3467
3468 pub fn to_async(&self, cx: &App) -> AsyncWindowContext {
3473 AsyncWindowContext::new_context(cx.to_async(), self.handle)
3474 }
3475
3476 pub fn on_next_frame(&self, callback: impl FnOnce(&mut Window, &mut App) + 'static) {
3506 RefCell::borrow_mut(&self.next_frame_callbacks).push(Box::new(callback));
3507 self.platform_window.schedule_frame();
3508 self.invalidator.wake_platform();
3511 }
3512
3513 pub fn request_animation_frame(&self) {
3530 let entity = self.current_view();
3531 self.invalidator.mark_dirty_for_next_draw(entity);
3533 self.on_next_frame(move |_, cx| cx.notify(entity));
3534 }
3535
3536 #[cfg(any(test, feature = "test-support"))]
3541 pub fn simulate_next_frame(&mut self, cx: &mut App) -> usize {
3542 let callbacks = self.next_frame_callbacks.take();
3543 let count = callbacks.len();
3544 for callback in callbacks {
3545 callback(self, cx);
3546 }
3547 count
3548 }
3549
3550 #[track_caller]
3556 pub fn spawn<AsyncFn, R>(&self, cx: &App, f: AsyncFn) -> Task<R>
3557 where
3558 R: 'static,
3559 AsyncFn: AsyncFnOnce(&mut AsyncWindowContext) -> R + 'static,
3560 {
3561 let handle = self.handle;
3562 cx.spawn(async move |app| {
3563 let mut async_window_cx = AsyncWindowContext::new_context(app.clone(), handle);
3564 f(&mut async_window_cx).await
3565 })
3566 }
3567
3568 #[track_caller]
3570 pub fn spawn_with_priority<AsyncFn, R>(
3571 &self,
3572 priority: Priority,
3573 cx: &App,
3574 f: AsyncFn,
3575 ) -> Task<R>
3576 where
3577 R: 'static,
3578 AsyncFn: AsyncFnOnce(&mut AsyncWindowContext) -> R + 'static,
3579 {
3580 let handle = self.handle;
3581 cx.spawn_with_priority(priority, async move |app| {
3582 let mut async_window_cx = AsyncWindowContext::new_context(app.clone(), handle);
3583 f(&mut async_window_cx).await
3584 })
3585 }
3586
3587 pub fn bounds_changed(&mut self, cx: &mut App) {
3595 self.scale_factor = self.platform_window.scale_factor();
3596 self.viewport_size = self.platform_window.content_size();
3597 self.mouse_position = self.platform_window.mouse_position();
3598
3599 self.refresh();
3600
3601 self.bounds_observers
3602 .clone()
3603 .retain(&(), |callback| callback(self, cx));
3604 }
3605
3606 fn display_changed(handle: AnyWindowHandle, cx: &mut App) {
3607 let moved_to = handle
3608 .update(cx, |_, window, _| {
3609 let display_id = window.platform_window.display().map(|display| display.id());
3610 let moved = display_id != window.display_id;
3611 window.display_id = display_id;
3612 moved.then_some(display_id)
3613 })
3614 .log_err()
3615 .flatten();
3616 let Some(display_id) = moved_to else {
3617 return;
3618 };
3619 if let Some(display_id) = display_id
3623 && !cx.knows_display(display_id)
3624 {
3625 cx.displays_changed();
3626 }
3627 handle
3628 .update(cx, |_, window, cx| window.notify_display_observers(cx))
3629 .log_err();
3630 }
3631
3632 pub(crate) fn notify_display_observers(&mut self, cx: &mut App) {
3633 self.display_observers
3634 .clone()
3635 .retain(&(), |callback| callback(self, cx));
3636 }
3637
3638 pub fn refresh_interval(&self, cx: &App) -> Option<Duration> {
3643 cx.display_refresh_interval(self.display_id?)
3644 }
3645
3646 pub fn observe_window_display(
3651 &self,
3652 mut callback: impl FnMut(&mut Window, &mut App) + 'static,
3653 ) -> Subscription {
3654 let (subscription, activate) = self.display_observers.insert(
3655 (),
3656 Box::new(move |window, cx| {
3657 callback(window, cx);
3658 true
3659 }),
3660 );
3661 activate();
3662 subscription
3663 }
3664
3665 pub fn bounds(&self) -> Bounds<Pixels> {
3670 self.platform_window.bounds()
3671 }
3672
3673 pub fn insets(&self) -> WindowInsets {
3675 self.platform_window.insets()
3676 }
3677
3678 #[cfg(any(test, feature = "test-support"))]
3683 pub fn render_to_image(&self) -> anyhow::Result<image::RgbaImage> {
3684 self.platform_window
3685 .render_to_image(&self.rendered_frame.scene)
3686 }
3687
3688 #[cfg(any(test, feature = "test-support"))]
3693 pub fn rendered_primitive_counts(&self) -> (usize, usize, usize, usize) {
3694 let scene = &self.rendered_frame.scene;
3695 (
3696 scene.quads.len(),
3697 scene.monochrome_sprites.len(),
3698 scene.subpixel_sprites.len(),
3699 scene.polychrome_sprites.len(),
3700 )
3701 }
3702
3703 #[cfg(any(test, feature = "test-support"))]
3728 pub fn painted_quads(&self) -> Vec<Quad> {
3729 self.rendered_frame.scene.quads.clone()
3730 }
3731
3732 #[cfg(any(test, feature = "test-support"))]
3734 pub fn painted_underlines(&self) -> Vec<Underline> {
3735 self.rendered_frame.scene.underlines.clone()
3736 }
3737
3738 pub fn resize(&mut self, size: Size<Pixels>) {
3740 self.platform_window.resize(size);
3741 }
3742
3743 pub fn is_fullscreen(&self) -> bool {
3745 self.platform_window.is_fullscreen()
3746 }
3747
3748 pub fn is_simple_fullscreen(&self) -> bool {
3753 self.platform_window.is_simple_fullscreen()
3754 }
3755
3756 pub(crate) fn appearance_changed(&mut self, cx: &mut App) {
3757 self.appearance = self.platform_window.appearance();
3758
3759 self.appearance_observers
3760 .clone()
3761 .retain(&(), |callback| callback(self, cx));
3762 }
3763
3764 pub(crate) fn button_layout_changed(&mut self, cx: &mut App) {
3765 self.button_layout_observers
3766 .clone()
3767 .retain(&(), |callback| callback(self, cx));
3768 }
3769
3770 pub fn appearance(&self) -> WindowAppearance {
3774 self.appearance
3775 }
3776
3777 pub fn viewport_size(&self) -> Size<Pixels> {
3781 self.viewport_size
3782 }
3783
3784 pub fn visual_viewport_bounds(&self) -> Bounds<Pixels> {
3790 self.platform_window.visual_viewport_bounds()
3791 }
3792
3793 pub fn fully_visible_bounds(&self) -> Bounds<Pixels> {
3798 let insets = self.platform_window.insets().effective();
3799 let viewport = self.viewport_size();
3800 let left = insets.left.max(Pixels::ZERO).min(viewport.width);
3801 let top = insets.top.max(Pixels::ZERO).min(viewport.height);
3802 let right = (viewport.width - insets.right.max(Pixels::ZERO)).max(left);
3803 let bottom = (viewport.height - insets.bottom.max(Pixels::ZERO)).max(top);
3804 let safe_bounds = Bounds::from_corners(point(left, top), point(right, bottom));
3805 let mut visible = safe_bounds.intersect(&self.visual_viewport_bounds());
3806 visible.size.width = visible.size.width.max(Pixels::ZERO);
3807 visible.size.height = visible.size.height.max(Pixels::ZERO);
3808 visible
3809 }
3810
3811 pub fn request_virtual_keyboard(&self) {
3816 self.platform_window.show_soft_keyboard();
3817 }
3818
3819 pub fn dismiss_virtual_keyboard(&self) {
3821 self.platform_window.hide_soft_keyboard();
3822 }
3823
3824 pub fn is_window_active(&self) -> bool {
3828 self.active.get()
3829 }
3830
3831 pub fn is_window_hovered(&self) -> bool {
3835 if cfg!(any(
3836 target_os = "windows",
3837 target_os = "linux",
3838 target_os = "freebsd"
3839 )) {
3840 self.hovered.get()
3841 } else {
3842 self.is_window_active()
3843 }
3844 }
3845
3846 pub fn zoom_window(&self) {
3848 self.platform_window.zoom();
3849 }
3850
3851 pub fn show_window_menu(&self, position: Point<Pixels>) {
3855 self.platform_window.show_window_menu(position)
3856 }
3857
3858 pub fn start_window_move(&self) {
3863 self.platform_window.start_window_move()
3864 }
3865
3866 pub fn set_client_inset(&mut self, inset: Pixels) {
3871 self.client_inset = Some(inset);
3872 self.platform_window.set_client_inset(inset);
3873 }
3874
3875 pub fn client_inset(&self) -> Option<Pixels> {
3877 self.client_inset
3878 }
3879
3880 pub fn window_decorations(&self) -> Decorations {
3885 self.platform_window.window_decorations()
3886 }
3887
3888 pub fn is_resizable(&self) -> bool {
3890 self.is_resizable
3891 }
3892
3893 pub fn is_minimizable(&self) -> bool {
3895 self.is_minimizable
3896 }
3897
3898 pub fn window_controls(&self) -> WindowControls {
3900 self.platform_window.window_controls()
3901 }
3902
3903 pub fn set_window_title(&mut self, title: &str) {
3905 self.platform_window.set_title(title);
3906 self.a11y.set_window_title(title.to_string());
3907 }
3908
3909 #[cfg(target_os = "macos")]
3911 pub fn set_traffic_light_position(&self, position: Point<Pixels>) {
3912 self.platform_window.set_traffic_light_position(position);
3913 }
3914
3915 pub fn set_app_id(&mut self, app_id: &str) {
3917 self.platform_window.set_app_id(app_id);
3918 }
3919
3920 pub fn set_background_appearance(&self, background_appearance: WindowBackgroundAppearance) {
3922 self.platform_window
3923 .set_background_appearance(background_appearance);
3924 }
3925
3926 pub fn set_window_edited(&mut self, edited: bool) {
3928 self.platform_window.set_edited(edited);
3929 }
3930
3931 pub fn set_document_path(&self, path: Option<&std::path::Path>) {
3934 self.platform_window.set_document_path(path);
3935 }
3936
3937 pub fn display(&self, cx: &App) -> Option<Rc<dyn PlatformDisplay>> {
3939 cx.platform
3940 .displays()
3941 .into_iter()
3942 .find(|display| Some(display.id()) == self.display_id)
3943 }
3944
3945 pub fn show_character_palette(&self) {
3952 self.platform_window.show_character_palette();
3953 }
3954
3955 pub fn scale_factor(&self) -> f32 {
3961 self.scale_factor
3962 }
3963
3964 #[cfg(any(test, feature = "test-support"))]
3966 pub fn set_scale_factor(&mut self, scale_factor: f32) {
3967 self.scale_factor = scale_factor;
3968 self.refresh();
3969 }
3970
3971 pub fn rem_size(&self) -> Pixels {
3977 self.rem_size_override_stack
3978 .last()
3979 .copied()
3980 .unwrap_or(self.rem_size)
3981 }
3982
3983 pub fn set_rem_size(&mut self, rem_size: impl Into<Pixels>) {
3989 self.rem_size = rem_size.into();
3990 }
3991
3992 pub fn with_global_id<R>(
3998 &mut self,
3999 element_id: ElementId,
4000 f: impl FnOnce(&GlobalElementId, &mut Self) -> R,
4001 ) -> R {
4002 self.with_id(element_id, |this| {
4003 let global_id = this.element_id_stack.global_id();
4004
4005 f(&global_id, this)
4006 })
4007 }
4008
4009 #[inline]
4014 pub fn with_id<R>(
4015 &mut self,
4016 element_id: impl Into<ElementId>,
4017 f: impl FnOnce(&mut Self) -> R,
4018 ) -> R {
4019 self.element_id_stack.push(element_id.into());
4020 let result = f(self);
4021 self.element_id_stack.pop();
4022 result
4023 }
4024
4025 #[inline]
4036 pub fn with_rem_size<F, R>(&mut self, rem_size: Option<impl Into<Pixels>>, f: F) -> R
4037 where
4038 F: FnOnce(&mut Self) -> R,
4039 {
4040 self.invalidator.debug_assert_paint_or_prepaint();
4041
4042 if let Some(rem_size) = rem_size {
4043 self.rem_size_override_stack.push(rem_size.into());
4044 let result = f(self);
4045 self.rem_size_override_stack.pop();
4046 result
4047 } else {
4048 f(self)
4049 }
4050 }
4051
4052 pub fn line_height(&self) -> Pixels {
4054 self.text_style().line_height_in_pixels(self.rem_size())
4055 }
4056
4057 #[inline]
4061 pub fn pixel_snap(&self, value: Pixels) -> Pixels {
4062 px(round_to_device_pixel(value.0, self.scale_factor()) / self.scale_factor())
4063 }
4064
4065 #[inline]
4067 pub fn pixel_snap_f64(&self, value: f64) -> f64 {
4068 let scale_factor = f64::from(self.scale_factor());
4069 round_half_toward_zero_f64(value * scale_factor) / scale_factor
4070 }
4071
4072 #[inline]
4074 pub fn pixel_snap_bounds(&self, bounds: Bounds<Pixels>) -> Bounds<Pixels> {
4075 bounds.map(|c| self.pixel_snap(c))
4076 }
4077
4078 #[inline]
4080 pub fn pixel_snap_point(&self, position: Point<Pixels>) -> Point<Pixels> {
4081 position.map(|c| self.pixel_snap(c))
4082 }
4083
4084 pub fn underline_bounds(
4086 &self,
4087 origin: Point<Pixels>,
4088 width: Pixels,
4089 style: &UnderlineStyle,
4090 ) -> Bounds<ScaledPixels> {
4091 let scale_factor = self.scale_factor();
4092 let thickness = self.snap_stroke(style.thickness);
4093 let height = if style.wavy {
4094 ScaledPixels(thickness.0 * 3.)
4095 } else {
4096 thickness
4097 };
4098 Bounds {
4099 origin: origin.map(|c| ScaledPixels(round_to_device_pixel(c.0, scale_factor))),
4100 size: size(self.snap_stroke(width), height),
4101 }
4102 }
4103
4104 pub fn paint_underline_with_exclusions(
4106 &mut self,
4107 origin: Point<Pixels>,
4108 width: Pixels,
4109 style: &UnderlineStyle,
4110 exclusions: &[Range<ScaledPixels>],
4111 ) {
4112 self.invalidator.debug_assert_paint();
4113 let underline = self.underline(origin, width, style);
4114 if exclusions.is_empty() {
4115 self.next_frame.scene.insert_primitive(underline);
4116 return;
4117 }
4118
4119 let bounds = underline.bounds.intersect(&underline.content_mask.bounds);
4120 if bounds.is_empty() {
4121 return;
4122 }
4123
4124 let mut exclusions = exclusions
4125 .iter()
4126 .filter_map(|span| {
4127 let start = span.start.max(bounds.left());
4128 let end = span.end.min(bounds.right());
4129 (start < end).then_some(start..end)
4130 })
4131 .collect::<SmallVec<[_; 4]>>();
4132 if exclusions.is_empty() {
4133 self.next_frame.scene.insert_primitive(underline);
4134 return;
4135 }
4136 exclusions.sort_unstable_by_key(|span| span.start);
4137
4138 let mut paint_span = |start, end| {
4139 let clip_bounds =
4140 Bounds::from_corners(point(start, bounds.top()), point(end, bounds.bottom()));
4141 self.next_frame.scene.insert_primitive(Underline {
4142 clip_bounds,
4143 ..underline
4144 });
4145 };
4146 let mut start = bounds.left();
4147 for exclusion in exclusions {
4148 if start < exclusion.start {
4149 paint_span(start, exclusion.start);
4150 }
4151 start = start.max(exclusion.end);
4152 }
4153 if start < bounds.right() {
4154 paint_span(start, bounds.right());
4155 }
4156 }
4157
4158 fn underline(&self, origin: Point<Pixels>, width: Pixels, style: &UnderlineStyle) -> Underline {
4159 let content_mask = self.snapped_content_mask();
4160 Underline {
4161 order: 0,
4162 padding: 0,
4163 bounds: self.underline_bounds(origin, width, style),
4164 clip_bounds: content_mask.bounds,
4165 content_mask,
4166 color: style.color.unwrap_or_default().into(),
4167 thickness: self.snap_stroke(style.thickness),
4168 wavy: style.wavy.into(),
4169 }
4170 }
4171
4172 #[inline]
4173 fn snap_bounds(&self, bounds: Bounds<Pixels>) -> Bounds<ScaledPixels> {
4174 let scale_factor = self.scale_factor();
4175 let left = round_to_device_pixel(bounds.left().0, scale_factor);
4176 let top = round_to_device_pixel(bounds.top().0, scale_factor);
4177 let right = round_to_device_pixel(bounds.right().0, scale_factor).max(left);
4178 let bottom = round_to_device_pixel(bounds.bottom().0, scale_factor).max(top);
4179 Bounds::from_corners(
4180 point(ScaledPixels(left), ScaledPixels(top)),
4181 point(ScaledPixels(right), ScaledPixels(bottom)),
4182 )
4183 }
4184
4185 #[inline]
4187 fn snap_stroke(&self, value: Pixels) -> ScaledPixels {
4188 ScaledPixels(round_stroke_to_device_pixel(value.0, self.scale_factor()))
4189 }
4190
4191 #[inline]
4192 fn snap_border_widths(&self, edges: Edges<Pixels>) -> Edges<ScaledPixels> {
4193 edges.map(|e| self.snap_stroke(*e))
4194 }
4195
4196 #[inline]
4198 fn cover_bounds(&self, bounds: Bounds<Pixels>) -> Bounds<ScaledPixels> {
4199 let scale_factor = self.scale_factor();
4200 let left = floor_to_device_pixel(bounds.left().0, scale_factor);
4201 let top = floor_to_device_pixel(bounds.top().0, scale_factor);
4202 let right = ceil_to_device_pixel(bounds.right().0, scale_factor).max(left);
4203 let bottom = ceil_to_device_pixel(bounds.bottom().0, scale_factor).max(top);
4204 Bounds::from_corners(
4205 point(ScaledPixels(left), ScaledPixels(top)),
4206 point(ScaledPixels(right), ScaledPixels(bottom)),
4207 )
4208 }
4209
4210 #[inline]
4211 fn snapped_content_mask(&self) -> ContentMask<ScaledPixels> {
4212 let mask = self.content_mask();
4213 ContentMask {
4214 bounds: self.cover_bounds(mask.bounds),
4215 fade_out: mask.fade_out.scale(self.scale_factor()),
4216 }
4217 }
4218
4219 pub fn prevent_default(&mut self) {
4224 self.default_prevented = true;
4225 }
4226
4227 pub fn default_prevented(&self) -> bool {
4229 self.default_prevented
4230 }
4231
4232 pub fn is_action_available(&self, action: &dyn Action, cx: &App) -> bool {
4237 let node_id =
4238 self.focus_node_id_in_rendered_frame(self.focused(cx).map(|handle| handle.id));
4239 self.rendered_frame
4240 .dispatch_tree
4241 .is_action_available(action, node_id)
4242 }
4243
4244 pub fn is_action_available_in(&self, action: &dyn Action, focus_handle: &FocusHandle) -> bool {
4248 let node_id = self.focus_node_id_in_rendered_frame(Some(focus_handle.id));
4249 self.rendered_frame
4250 .dispatch_tree
4251 .is_action_available(action, node_id)
4252 }
4253
4254 pub fn mouse_position(&self) -> Point<Pixels> {
4258 self.note_input_read(InputReads::POINTER);
4259 self.mouse_position
4260 }
4261
4262 pub fn capture_pointer(&mut self, hitbox_id: HitboxId) {
4269 self.captured_hitbox = Some(hitbox_id);
4270 }
4271
4272 pub fn release_pointer(&mut self) {
4274 self.captured_hitbox = None;
4275 }
4276
4277 pub fn captured_hitbox(&self) -> Option<HitboxId> {
4279 self.captured_hitbox
4280 }
4281
4282 pub fn capture_long_press<T: 'static>(&mut self, entity: &Entity<T>) {
4288 self.long_press_capture = Some(entity.entity_id());
4289 }
4290
4291 pub fn has_long_press_capture<T: 'static>(&self, entity: &Entity<T>) -> bool {
4293 self.long_press_capture == Some(entity.entity_id())
4294 }
4295
4296 pub fn modifiers(&self) -> Modifiers {
4298 self.note_input_read(InputReads::MODIFIERS);
4299 self.modifiers
4300 }
4301
4302 pub fn last_input_was_keyboard(&self) -> bool {
4307 self.note_input_read(InputReads::MODALITY);
4308 self.last_input_modality == InputModality::Keyboard
4309 }
4310
4311 pub fn last_input_was_touch(&self) -> bool {
4313 self.note_input_read(InputReads::MODALITY);
4314 self.last_input_modality == InputModality::Touch
4315 }
4316
4317 pub fn capslock(&self) -> Capslock {
4319 self.note_input_read(InputReads::MODIFIERS);
4320 self.capslock
4321 }
4322
4323 fn note_input_read(&self, reads: InputReads) {
4324 self.input_reads.set(self.input_reads.get() | reads);
4325 }
4326
4327 #[profiling::function]
4335 pub fn draw(&mut self, cx: &mut App) -> ArenaClearNeeded {
4336 #[cfg(feature = "profiler")]
4339 let frame_dirty = self.invalidator.take_frame_dirty();
4340 #[cfg(feature = "profiler")]
4341 self.window_profiler.begin_draw();
4342
4343 let arena_scope = ElementArenaScope::enter(&cx.element_arena);
4346 self.frame_number += 1;
4347
4348 if self.platform_window.prepare_frame() {
4349 self.refresh();
4350 }
4351 self.invalidate_entities();
4352 cx.entities.clear_accessed();
4353 debug_assert!(self.rendered_entity_stack.is_empty());
4354 self.invalidator.set_dirty(false);
4355 self.requested_autoscroll = None;
4356
4357 if let Some(input_handler) = self.platform_window.take_input_handler() {
4362 let stale_focus = input_handler
4363 .focus_id()
4364 .is_some_and(|focus_id| Some(focus_id) != self.focus);
4365 if stale_focus {
4366 self.refreshing = true;
4369 } else if let Some(slot) = self
4370 .rendered_frame
4371 .input_handlers
4372 .iter_mut()
4373 .rev()
4374 .find(|handler| handler.is_none())
4375 {
4376 *slot = Some(input_handler);
4377 } else {
4378 self.rendered_frame.input_handlers.push(Some(input_handler));
4379 }
4380 }
4381 if !cx.mode.skip_drawing() {
4382 self.draw_roots(cx);
4383 #[cfg(feature = "profiler")]
4384 {
4385 let viewport_size = self.viewport_size;
4386 let scale_factor = self.scale_factor();
4387 self.debug_frame_overlay.paint(
4388 &mut self.next_frame.scene,
4389 viewport_size,
4390 scale_factor,
4391 );
4392 }
4393 }
4394 self.dirty_views.clear();
4395 self.next_frame.window_active = self.active.get();
4396
4397 let focused_text_input = if let Some(mut input_handler) = self
4403 .next_frame
4404 .input_handlers
4405 .iter_mut()
4406 .rev()
4407 .find_map(|h| h.take())
4408 {
4409 let accepts_text_input = input_handler.accepts_text_input(self, cx);
4410 self.platform_window.set_input_handler(input_handler);
4411 self.focus.filter(|_| accepts_text_input)
4412 } else {
4413 None
4414 };
4415 self.apply_text_input_configuration(cx);
4416 let previous = mem::replace(&mut self.focused_text_input, focused_text_input);
4417 if previous.is_some() != focused_text_input.is_some() {
4418 self.platform_window
4419 .text_input_state_changed(if focused_text_input.is_some() {
4420 TextInputStateChange::FocusGained
4421 } else {
4422 TextInputStateChange::FocusLost
4423 });
4424 }
4425
4426 self.layout_engine.as_mut().unwrap().clear();
4427 self.text_system().finish_frame();
4428 self.next_frame.finish(&mut self.rendered_frame);
4429
4430 self.invalidator.set_phase(DrawPhase::Focus);
4431 let previous_focus_path = self.rendered_frame.focus_path();
4432 let previous_window_active = self.rendered_frame.window_active;
4433 mem::swap(&mut self.rendered_frame, &mut self.next_frame);
4434 self.next_frame.clear();
4435 let current_focus_path = self.rendered_frame.focus_path();
4436 let current_window_active = self.rendered_frame.window_active;
4437 let mut focus_before_listeners = self.focus;
4438
4439 if previous_focus_path != current_focus_path
4440 || previous_window_active != current_window_active
4441 {
4442 if !previous_focus_path.is_empty() && current_focus_path.is_empty() {
4443 self.focus_lost_path = previous_focus_path.clone();
4444 self.focus_lost_listeners
4445 .clone()
4446 .retain(&(), |listener| listener(self, cx));
4447 self.focus_lost_path = SmallVec::new();
4448 focus_before_listeners = self.focus;
4453 }
4454
4455 let event = WindowFocusEvent {
4456 previous_focus_path: if previous_window_active {
4457 previous_focus_path
4458 } else {
4459 Default::default()
4460 },
4461 current_focus_path: if current_window_active {
4462 current_focus_path
4463 } else {
4464 Default::default()
4465 },
4466 };
4467 self.focus_listeners
4468 .clone()
4469 .retain(&(), |listener| listener(&event, self, cx));
4470 }
4471
4472 debug_assert!(self.rendered_entity_stack.is_empty());
4473 self.record_entities_accessed(cx);
4474 self.reset_cursor_style(cx);
4475 self.refreshing = false;
4476 self.invalidator.set_phase(DrawPhase::None);
4477 self.notify_pointer_observers(PointerChangeCause::Layout, cx);
4478 if self.focus != focus_before_listeners {
4483 self.refresh();
4484 }
4485 self.needs_present.set(true);
4486
4487 #[cfg(feature = "profiler")]
4488 {
4489 self.window_profiler
4490 .set_refresh_interval(self.refresh_interval(cx));
4491 let draw_duration = self
4492 .window_profiler
4493 .end_draw(frame_dirty.dirty_at, frame_dirty.invalidations);
4494 self.debug_frame_overlay.record_frame(draw_duration);
4495 }
4496
4497 arena_scope.exit(&cx.element_arena)
4500 }
4501
4502 fn record_entities_accessed(&mut self, cx: &mut App) {
4503 let mut entities_ref = cx.entities.accessed_entities.get_mut();
4504 let mut entities = mem::take(entities_ref.deref_mut());
4505 let handle = self.handle;
4506 cx.record_entities_accessed(
4507 handle,
4508 self.invalidator.clone(),
4510 &entities,
4511 );
4512 let mut entities_ref = cx.entities.accessed_entities.get_mut();
4513 mem::swap(&mut entities, entities_ref.deref_mut());
4514 }
4515
4516 fn invalidate_entities(&mut self) {
4517 let mut views = self.invalidator.take_views();
4518 for entity in views.drain() {
4519 self.mark_view_dirty(entity);
4520 }
4521 self.invalidator.replace_views(views);
4522 }
4523
4524 #[profiling::function]
4525 fn present(&mut self) {
4526 #[cfg(feature = "profiler")]
4527 let _foreground_turn = profiler::journal::foreground_turn();
4528 #[cfg(feature = "profiler")]
4529 let present_start = Instant::now();
4530 self.platform_window.draw(&self.rendered_frame.scene);
4531 #[cfg(feature = "profiler")]
4532 self.window_profiler.record_present(
4533 present_start,
4534 Instant::now(),
4535 self.active.get(),
4536 !self.next_frame_callbacks.borrow().is_empty(),
4537 );
4538 self.needs_present.set(false);
4539 profiling::finish_frame!();
4540 }
4541
4542 #[cfg(all(test, feature = "profiler"))]
4544 pub fn present_if_needed(&mut self) {
4545 if self.needs_present.get() {
4546 self.present();
4547 }
4548 }
4549
4550 #[cfg(feature = "profiler")]
4552 pub fn input_latency_snapshot(&self) -> profiler::InputLatencySnapshot {
4553 self.window_profiler.input_latency_snapshot()
4554 }
4555
4556 #[cfg(feature = "profiler")]
4558 pub fn frame_duration_snapshot(&self) -> profiler::FrameDurationSnapshot {
4559 self.window_profiler.frame_duration_snapshot()
4560 }
4561
4562 #[cfg(feature = "profiler")]
4564 pub fn debug_frame_overlay_mode(&self) -> DebugFrameOverlayMode {
4565 self.debug_frame_overlay.mode()
4566 }
4567
4568 #[cfg(feature = "profiler")]
4570 pub fn set_debug_frame_overlay_mode(&mut self, mode: DebugFrameOverlayMode) {
4571 self.debug_frame_overlay.set_mode(mode);
4572 self.refresh();
4573 }
4574
4575 #[cfg(feature = "profiler")]
4578 pub fn cycle_debug_frame_overlay_mode(&mut self) {
4579 self.set_debug_frame_overlay_mode(self.debug_frame_overlay.mode().next());
4580 }
4581
4582 #[cfg(feature = "profiler")]
4585 pub fn reset_debug_frame_overlay_stats(&mut self) {
4586 self.debug_frame_overlay.reset_stats();
4587 self.refresh();
4588 }
4589
4590 fn draw_roots(&mut self, cx: &mut App) {
4591 self.invalidator.set_phase(DrawPhase::Prepaint);
4592 self.tooltip_bounds.take();
4593
4594 self.a11y.sync_active_flag();
4595 if self.a11y.is_active() {
4596 self.a11y.begin_frame();
4597 }
4598
4599 let _inspector_width: Pixels = rems(30.0).to_pixels(self.rem_size());
4600 let root_size = {
4601 #[cfg(any(feature = "inspector", debug_assertions))]
4602 {
4603 if self.inspector.is_some() {
4604 let mut size = self.viewport_size;
4605 size.width = (size.width - _inspector_width).max(px(0.0));
4606 size
4607 } else {
4608 self.viewport_size
4609 }
4610 }
4611 #[cfg(not(any(feature = "inspector", debug_assertions)))]
4612 {
4613 self.viewport_size
4614 }
4615 };
4616
4617 let scale_factor = self.scale_factor();
4621 #[cfg(feature = "profiler")]
4622 let request_layout_start = Instant::now();
4623 let mut root_element = self.root.as_ref().unwrap().clone().into_any_element();
4624 let root_layout_id = root_element.request_layout(self, cx);
4625 #[cfg(feature = "profiler")]
4626 let prepaint_start = Instant::now();
4627 self.layout_engine
4628 .as_mut()
4629 .unwrap()
4630 .stretch_auto_size_to_fill(root_layout_id, root_size, scale_factor);
4631 root_element.prepaint_as_root(Point::default(), root_size.into(), self, cx);
4632
4633 #[cfg(any(feature = "inspector", debug_assertions))]
4634 let inspector_element = self.prepaint_inspector(_inspector_width, cx);
4635
4636 self.prepaint_deferred_draws(cx);
4637
4638 let mut prompt_element = None;
4639 let mut active_drag_element = None;
4640 let mut tooltip_element = None;
4641 if let Some(prompt) = self.prompt.take() {
4642 let mut element = prompt.view.any_view().into_any_element();
4643 let prompt_layout_id = element.request_layout(self, cx);
4644 self.layout_engine
4645 .as_mut()
4646 .unwrap()
4647 .stretch_auto_size_to_fill(prompt_layout_id, root_size, scale_factor);
4648 element.prepaint_as_root(Point::default(), root_size.into(), self, cx);
4649 prompt_element = Some(element);
4650 self.prompt = Some(prompt);
4651 } else if let Some(active_drag) = cx.active_drag.take() {
4652 let mut element = active_drag.view.clone().into_any_element();
4653 let offset = self.mouse_position() - active_drag.cursor_offset;
4654 element.prepaint_as_root(offset, AvailableSpace::min_size(), self, cx);
4655 active_drag_element = Some(element);
4656 cx.active_drag = Some(active_drag);
4657 } else {
4658 tooltip_element = self.prepaint_tooltip(cx);
4659 }
4660
4661 self.mouse_hit_test = self.next_frame.hit_test(self.mouse_position);
4662
4663 #[cfg(feature = "profiler")]
4665 let paint_start = Instant::now();
4666 self.invalidator.set_phase(DrawPhase::Paint);
4667 root_element.paint(self, cx);
4668
4669 #[cfg(any(feature = "inspector", debug_assertions))]
4670 self.paint_inspector(inspector_element, cx);
4671
4672 self.paint_deferred_draws(cx);
4673
4674 if let Some(mut prompt_element) = prompt_element {
4675 prompt_element.paint(self, cx);
4676 } else if let Some(mut drag_element) = active_drag_element {
4677 drag_element.paint(self, cx);
4678 } else if let Some(mut tooltip_element) = tooltip_element {
4679 tooltip_element.paint(self, cx);
4680 }
4681
4682 #[cfg(any(feature = "inspector", debug_assertions))]
4683 self.paint_inspector_hitbox(cx);
4684
4685 #[cfg(feature = "profiler")]
4686 self.window_profiler
4687 .record_draw_phases(profiler::DrawPhases {
4688 request_layout: prepaint_start.duration_since(request_layout_start),
4689 prepaint: paint_start.duration_since(prepaint_start),
4690 paint: Instant::now().duration_since(paint_start),
4691 });
4692
4693 let a11y_active_start_of_frame = self.a11y.is_active();
4695 self.a11y.sync_active_flag();
4696 let a11y_active_end_of_frame = self.a11y.is_active();
4697
4698 let should_send_a11y_update = a11y_active_start_of_frame && a11y_active_end_of_frame;
4699
4700 if a11y_active_start_of_frame {
4701 let frame_info = crate::window::a11y::debug::FrameDebugInfo {
4704 viewport_size: self.viewport_size,
4705 scale_factor: self.scale_factor,
4706 tab_stop_count: self.next_frame.tab_stops.tab_stop_count(),
4707 };
4708 let tree_update = self.a11y.end_frame(frame_info);
4710
4711 if should_send_a11y_update {
4712 log::debug!(
4713 "Sending a11y tree update: {} nodes",
4714 tree_update.nodes.len()
4715 );
4716 self.platform_window.a11y_tree_update(tree_update);
4717 }
4718 }
4719 }
4720
4721 fn prepaint_tooltip(&mut self, cx: &mut App) -> Option<AnyElement> {
4722 for tooltip_request_index in (0..self.next_frame.tooltip_requests.len()).rev() {
4724 let Some(Some(tooltip_request)) = self
4725 .next_frame
4726 .tooltip_requests
4727 .get(tooltip_request_index)
4728 .cloned()
4729 else {
4730 log::error!("Unexpectedly absent TooltipRequest");
4731 continue;
4732 };
4733 let mut element = tooltip_request.tooltip.view.clone().into_any_element();
4734 let mouse_position = tooltip_request.tooltip.mouse_position;
4735 let tooltip_size = element.layout_as_root(AvailableSpace::min_size(), self, cx);
4736
4737 let mut tooltip_bounds =
4738 Bounds::new(mouse_position + point(px(1.), px(1.)), tooltip_size);
4739 let window_bounds = Bounds {
4740 origin: Point::default(),
4741 size: self.viewport_size(),
4742 };
4743
4744 if tooltip_bounds.right() > window_bounds.right() {
4745 let new_x = mouse_position.x - tooltip_bounds.size.width - px(1.);
4746 if new_x >= Pixels::ZERO {
4747 tooltip_bounds.origin.x = new_x;
4748 } else {
4749 tooltip_bounds.origin.x = cmp::max(
4750 Pixels::ZERO,
4751 tooltip_bounds.origin.x - tooltip_bounds.right() - window_bounds.right(),
4752 );
4753 }
4754 }
4755
4756 if tooltip_bounds.bottom() > window_bounds.bottom() {
4757 let new_y = mouse_position.y - tooltip_bounds.size.height - px(1.);
4758 if new_y >= Pixels::ZERO {
4759 tooltip_bounds.origin.y = new_y;
4760 } else {
4761 tooltip_bounds.origin.y = cmp::max(
4762 Pixels::ZERO,
4763 tooltip_bounds.origin.y - tooltip_bounds.bottom() - window_bounds.bottom(),
4764 );
4765 }
4766 }
4767
4768 let is_visible =
4772 (tooltip_request.tooltip.check_visible_and_update)(tooltip_bounds, self, cx);
4773 if !is_visible {
4774 continue;
4775 }
4776
4777 self.with_absolute_element_offset(tooltip_bounds.origin, |window| {
4778 element.prepaint(window, cx)
4779 });
4780
4781 self.tooltip_bounds = Some(TooltipBounds {
4782 id: tooltip_request.id,
4783 bounds: tooltip_bounds,
4784 });
4785 return Some(element);
4786 }
4787 None
4788 }
4789
4790 fn prepaint_deferred_draws(&mut self, cx: &mut App) {
4791 assert_eq!(self.element_id_stack.len(), 0);
4792
4793 let mut round_start = 0;
4804 let mut depth = 0;
4805 loop {
4806 let round_end = self.next_frame.deferred_draws.len();
4807 if round_start == round_end {
4808 break;
4809 }
4810 assert!(depth < 10, "Exceeded maximum (10) deferred depth");
4812 depth += 1;
4813
4814 let mut traversal_order = (round_start..round_end).collect::<SmallVec<[usize; 8]>>();
4816 traversal_order.sort_by_key(|ix| self.next_frame.deferred_draws[*ix].priority);
4817
4818 for deferred_draw_ix in traversal_order {
4819 let (
4820 priority,
4821 element,
4822 parent_node,
4823 current_view,
4824 rem_size,
4825 absolute_offset,
4826 content_mask,
4827 prepaint_range,
4828 ) = {
4829 let deferred_draw = &mut self.next_frame.deferred_draws[deferred_draw_ix];
4830 self.element_id_stack
4831 .clone_from(&deferred_draw.element_id_stack);
4832 self.text_style_stack
4833 .clone_from(&deferred_draw.text_style_stack);
4834 (
4835 deferred_draw.priority,
4836 deferred_draw.element.take(),
4837 deferred_draw.parent_node,
4838 deferred_draw.current_view,
4839 deferred_draw.rem_size,
4840 deferred_draw.absolute_offset,
4841 deferred_draw.content_mask,
4842 deferred_draw.prepaint_range.clone(),
4843 )
4844 };
4845 self.next_frame.dispatch_tree.set_active_node(parent_node);
4846
4847 let prepaint_start = self.prepaint_index();
4848 if let Some(mut element) = element {
4849 self.with_rendered_view(current_view, |window| {
4850 window.with_content_mask(content_mask, |window| {
4851 window.with_rem_size(Some(rem_size), |window| {
4852 window.with_absolute_element_offset(absolute_offset, |window| {
4853 window.push_deferred_priority(priority);
4854 element.prepaint(window, cx);
4855 window.pop_deferred_priority();
4856 });
4857 });
4858 });
4859 });
4860 self.next_frame.deferred_draws[deferred_draw_ix].element = Some(element);
4861 } else {
4862 self.reuse_prepaint(prepaint_range);
4863 }
4864 let prepaint_end = self.prepaint_index();
4865 self.next_frame.deferred_draws[deferred_draw_ix].prepaint_range =
4866 prepaint_start..prepaint_end;
4867 }
4868
4869 self.element_id_stack.clear();
4870 self.text_style_stack.clear();
4871 round_start = round_end;
4872 }
4873 }
4874
4875 fn paint_deferred_draws(&mut self, cx: &mut App) {
4876 assert_eq!(self.element_id_stack.len(), 0);
4877
4878 if self.next_frame.deferred_draws.len() == 0 {
4881 return;
4882 }
4883
4884 self.next_frame.scene.raise_order_floor();
4888
4889 let traversal_order = self.deferred_draw_traversal_order();
4890 let mut deferred_draws = mem::take(&mut self.next_frame.deferred_draws);
4891 for deferred_draw_ix in traversal_order {
4892 let mut deferred_draw = &mut deferred_draws[deferred_draw_ix];
4893 self.element_id_stack
4894 .clone_from(&deferred_draw.element_id_stack);
4895 self.next_frame
4896 .dispatch_tree
4897 .set_active_node(deferred_draw.parent_node);
4898
4899 let paint_start = self.paint_index();
4900 let content_mask = deferred_draw.content_mask;
4901 if let Some(element) = deferred_draw.element.as_mut() {
4902 self.with_rendered_view(deferred_draw.current_view, |window| {
4903 window.with_content_mask(content_mask, |window| {
4904 window.with_rem_size(Some(deferred_draw.rem_size), |window| {
4905 element.paint(window, cx);
4906 });
4907 })
4908 })
4909 } else {
4910 self.reuse_paint(deferred_draw.paint_range.clone(), &mut None);
4911 }
4912 let paint_end = self.paint_index();
4913 deferred_draw.paint_range = paint_start..paint_end;
4914 }
4915 self.next_frame.deferred_draws = deferred_draws;
4916 self.element_id_stack.clear();
4917 }
4918
4919 fn deferred_draw_traversal_order(&mut self) -> SmallVec<[usize; 8]> {
4920 let deferred_count = self.next_frame.deferred_draws.len();
4921 let mut sorted_indices = (0..deferred_count).collect::<SmallVec<[_; 8]>>();
4922 sorted_indices.sort_by_key(|ix| self.next_frame.deferred_draws[*ix].priority);
4923 sorted_indices
4924 }
4925
4926 pub(crate) fn prepaint_index(&self) -> PrepaintStateIndex {
4927 PrepaintStateIndex {
4928 hitboxes_index: self.next_frame.hitboxes.len(),
4929 tooltips_index: self.next_frame.tooltip_requests.len(),
4930 deferred_draws_index: self.next_frame.deferred_draws.len(),
4931 dispatch_tree_index: self.next_frame.dispatch_tree.len(),
4932 accessed_element_states_index: self.next_frame.accessed_element_states.len(),
4933 line_layout_index: self.text_system.layout_index(),
4934 }
4935 }
4936
4937 pub(crate) fn element_states_mark(&self) -> usize {
4939 self.next_frame.accessed_element_states.len()
4940 }
4941
4942 pub(crate) fn element_states_since(&self, mark: usize) -> Vec<(GlobalElementId, TypeId)> {
4944 self.next_frame.accessed_element_states[mark..].to_vec()
4945 }
4946
4947 pub(crate) fn keep_element_states(&mut self, states: &[(GlobalElementId, TypeId)]) {
4949 self.next_frame
4950 .accessed_element_states
4951 .extend_from_slice(states);
4952 }
4953
4954 pub(crate) fn reuse_prepaint(&mut self, range: Range<PrepaintStateIndex>) {
4955 let destination = self.prepaint_index();
4956 self.rebase_nested_views(
4957 range.start.accessed_element_states_index..range.end.accessed_element_states_index,
4958 |state, frame| state.rebase_prepaint(&range, &destination, frame),
4959 );
4960 self.next_frame.hitboxes.extend(
4961 self.rendered_frame.hitboxes[range.start.hitboxes_index..range.end.hitboxes_index]
4962 .iter()
4963 .cloned(),
4964 );
4965 self.next_frame.tooltip_requests.extend(
4966 self.rendered_frame.tooltip_requests
4967 [range.start.tooltips_index..range.end.tooltips_index]
4968 .iter_mut()
4969 .map(|request| request.take()),
4970 );
4971 self.next_frame.accessed_element_states.extend(
4972 self.rendered_frame.accessed_element_states[range.start.accessed_element_states_index
4973 ..range.end.accessed_element_states_index]
4974 .iter()
4975 .map(|(id, type_id)| (id.clone(), *type_id)),
4976 );
4977 self.text_system
4978 .reuse_layouts(range.start.line_layout_index..range.end.line_layout_index);
4979
4980 let reused_subtree = self.next_frame.dispatch_tree.reuse_subtree(
4981 range.start.dispatch_tree_index..range.end.dispatch_tree_index,
4982 &mut self.rendered_frame.dispatch_tree,
4983 self.focus,
4984 );
4985
4986 if reused_subtree.contains_focus() {
4987 self.next_frame.focus = self.focus;
4988 }
4989
4990 self.next_frame.deferred_draws.extend(
4991 self.rendered_frame.deferred_draws
4992 [range.start.deferred_draws_index..range.end.deferred_draws_index]
4993 .iter()
4994 .map(|deferred_draw| DeferredDraw {
4995 current_view: deferred_draw.current_view,
4996 parent_node: reused_subtree.refresh_node_id(deferred_draw.parent_node),
4997 element_id_stack: deferred_draw.element_id_stack.clone(),
4998 text_style_stack: deferred_draw.text_style_stack.clone(),
4999 content_mask: deferred_draw.content_mask,
5000 rem_size: deferred_draw.rem_size,
5001 priority: deferred_draw.priority,
5002 element: None,
5003 absolute_offset: deferred_draw.absolute_offset,
5004 prepaint_range: deferred_draw.prepaint_range.clone(),
5005 paint_range: deferred_draw.paint_range.clone(),
5006 }),
5007 );
5008 }
5009
5010 pub(crate) fn paint_index(&self) -> PaintIndex {
5011 PaintIndex {
5012 scene_index: self.next_frame.scene.len(),
5013 #[cfg(any(test, feature = "test-support"))]
5014 debug_bounds_index: self.next_frame.debug_bounds_records.len(),
5015 mouse_listeners_index: self.next_frame.mouse_listeners.len(),
5016 pointer_observers_index: self.next_frame.pointer_observers.len(),
5017 input_handlers_index: self.next_frame.input_handlers.len(),
5018 cursor_styles_index: self.next_frame.cursor_styles.len(),
5019 accessed_element_states_index: self.next_frame.accessed_element_states.len(),
5020 tab_handle_index: self.next_frame.tab_stops.paint_index(),
5021 line_layout_index: self.text_system.layout_index(),
5022 }
5023 }
5024
5025 pub(crate) fn reuse_paint(
5026 &mut self,
5027 range: Range<PaintIndex>,
5028 summary: &mut Option<crate::scene::ReplaySummary>,
5029 ) {
5030 let destination = self.paint_index();
5031 self.rebase_nested_views(
5032 range.start.accessed_element_states_index..range.end.accessed_element_states_index,
5033 |state, frame| state.rebase_paint(&range, &destination, frame),
5034 );
5035 #[cfg(any(test, feature = "test-support"))]
5037 for (selector, bounds) in &self.rendered_frame.debug_bounds_records
5038 [range.start.debug_bounds_index..range.end.debug_bounds_index]
5039 {
5040 self.next_frame
5041 .record_debug_bounds(selector.clone(), *bounds);
5042 }
5043 Rc::make_mut(&mut self.next_frame.pointer_observers).extend(
5044 self.rendered_frame.pointer_observers
5045 [range.start.pointer_observers_index..range.end.pointer_observers_index]
5046 .iter()
5047 .cloned(),
5048 );
5049 self.next_frame.cursor_styles.extend(
5050 self.rendered_frame.cursor_styles
5051 [range.start.cursor_styles_index..range.end.cursor_styles_index]
5052 .iter()
5053 .cloned(),
5054 );
5055 self.next_frame.input_handlers.extend(
5056 self.rendered_frame.input_handlers
5057 [range.start.input_handlers_index..range.end.input_handlers_index]
5058 .iter_mut()
5059 .map(|handler| handler.take()),
5060 );
5061 self.next_frame.mouse_listeners.extend(
5062 self.rendered_frame.mouse_listeners
5063 [range.start.mouse_listeners_index..range.end.mouse_listeners_index]
5064 .iter_mut()
5065 .map(|listener| listener.take()),
5066 );
5067 self.next_frame.accessed_element_states.extend(
5068 self.rendered_frame.accessed_element_states[range.start.accessed_element_states_index
5069 ..range.end.accessed_element_states_index]
5070 .iter()
5071 .map(|(id, type_id)| (id.clone(), *type_id)),
5072 );
5073 self.next_frame.tab_stops.replay(
5074 &self.rendered_frame.tab_stops.insertion_history
5075 [range.start.tab_handle_index..range.end.tab_handle_index],
5076 );
5077
5078 self.text_system
5079 .reuse_layouts(range.start.line_layout_index..range.end.line_layout_index);
5080 self.next_frame.scene.replay_with(
5081 range.start.scene_index..range.end.scene_index,
5082 &self.rendered_frame.scene,
5083 summary,
5084 );
5085 }
5086
5087 fn rebase_nested_views(
5090 &mut self,
5091 accessed: Range<usize>,
5092 mut rebase: impl FnMut(&mut crate::view::ViewState, u64),
5093 ) {
5094 let view_state = TypeId::of::<crate::view::ViewState>();
5095 let frame_number = self.frame_number;
5096 let frame = &mut self.rendered_frame;
5097 for (id, type_id) in &frame.accessed_element_states[accessed] {
5098 if *type_id != view_state {
5099 continue;
5100 }
5101 if let Some(Some(state)) = frame
5102 .element_states
5103 .get_mut(&(id.clone(), view_state))
5104 .and_then(|state| state.inner.downcast_mut::<Option<crate::view::ViewState>>())
5105 {
5106 rebase(state, frame_number);
5107 }
5108 }
5109 }
5110
5111 pub(crate) fn push_deferred_priority(&mut self, priority: usize) {
5112 self.deferred_priority_stack.push(priority);
5113 }
5114
5115 pub(crate) fn pop_deferred_priority(&mut self) {
5116 self.deferred_priority_stack
5117 .pop()
5118 .expect("deferred priority stack must have been pushed");
5119 }
5120
5121 pub(crate) fn deferred_priority(&self) -> Option<usize> {
5122 self.deferred_priority_stack.last().copied()
5123 }
5124
5125 #[cfg(any(test, feature = "test-support"))]
5126 pub(crate) fn record_debug_bounds(
5127 &mut self,
5128 selector: impl Into<String>,
5129 bounds: Bounds<Pixels>,
5130 ) {
5131 self.next_frame.record_debug_bounds(selector.into(), bounds);
5132 }
5133
5134 pub(crate) fn create_layout_island(&mut self) -> crate::taffy::LayoutIsland {
5135 self.layout_engine.as_mut().unwrap().create_island()
5136 }
5137
5138 pub(crate) fn with_layout_island<R>(
5140 &mut self,
5141 island: crate::taffy::IslandId,
5142 f: impl FnOnce(&mut Self) -> R,
5143 ) -> R {
5144 self.layout_engine.as_mut().unwrap().begin_island(island);
5145 let result = f(self);
5146 self.layout_engine.as_mut().unwrap().end_island();
5147 result
5148 }
5149
5150 pub(crate) fn set_layout_island_root(
5151 &mut self,
5152 island: crate::taffy::IslandId,
5153 root: LayoutId,
5154 ) {
5155 self.layout_engine
5156 .as_mut()
5157 .unwrap()
5158 .set_island_root(island, root);
5159 }
5160
5161 pub(crate) fn request_layout_island(&mut self, island: crate::taffy::IslandId) -> LayoutId {
5163 self.layout_engine.as_mut().unwrap().request_island(island)
5164 }
5165
5166 pub(crate) fn anchor_layout_island(
5167 &mut self,
5168 island: crate::taffy::IslandId,
5169 anchor: LayoutId,
5170 ) {
5171 self.layout_engine
5172 .as_mut()
5173 .unwrap()
5174 .anchor_island(island, anchor);
5175 }
5176
5177 pub(crate) fn layout_island(
5179 &mut self,
5180 island: crate::taffy::IslandId,
5181 size: Size<Pixels>,
5182 cx: &mut App,
5183 ) {
5184 let mut layout_engine = self.layout_engine.take().unwrap();
5185 layout_engine.layout_island(island, size, self.measurement_direction, self, cx);
5186 self.layout_engine = Some(layout_engine);
5187 }
5188
5189 pub fn with_text_style<F, R>(&mut self, style: Option<TextStyleRefinement>, f: F) -> R
5195 where
5196 F: FnOnce(&mut Self) -> R,
5197 {
5198 self.invalidator.debug_assert_paint_or_prepaint();
5199 if let Some(style) = style {
5200 self.text_style_stack.push(style);
5201 let result = f(self);
5202 self.text_style_stack.pop();
5203 result
5204 } else {
5205 f(self)
5206 }
5207 }
5208
5209 pub fn set_cursor_style(&mut self, style: CursorStyle, hitbox: &Hitbox) {
5215 self.invalidator.debug_assert_paint();
5216 self.next_frame
5217 .cursor_styles
5218 .push(CursorStyleRequest::Hitbox {
5219 id: hitbox.id,
5220 style: CursorStyleSource::Fixed(style),
5221 });
5222 }
5223
5224 pub fn set_cursor_style_with(
5227 &mut self,
5228 hitbox: &Hitbox,
5229 resolve: impl Fn(Point<Pixels>) -> Option<CursorStyle> + 'static,
5230 ) {
5231 self.invalidator.debug_assert_paint();
5232 self.next_frame
5233 .cursor_styles
5234 .push(CursorStyleRequest::Hitbox {
5235 id: hitbox.id,
5236 style: CursorStyleSource::Position(Rc::new(resolve)),
5237 });
5238 }
5239
5240 pub fn set_window_cursor_style(&mut self, style: CursorStyle) {
5244 self.invalidator.debug_assert_paint();
5245 self.next_frame
5246 .cursor_styles
5247 .push(CursorStyleRequest::Window(CursorStyleSource::Fixed(style)));
5248 }
5249
5250 pub fn set_window_cursor_style_with(
5253 &mut self,
5254 resolve: impl Fn(Point<Pixels>) -> Option<CursorStyle> + 'static,
5255 ) {
5256 self.invalidator.debug_assert_paint();
5257 self.next_frame
5258 .cursor_styles
5259 .push(CursorStyleRequest::Window(CursorStyleSource::Position(
5260 Rc::new(resolve),
5261 )));
5262 }
5263
5264 pub fn set_tooltip(&mut self, tooltip: AnyTooltip) -> TooltipId {
5269 self.invalidator.debug_assert_prepaint();
5270 let id = TooltipId(post_inc(&mut self.next_tooltip_id.0));
5271 self.next_frame
5272 .tooltip_requests
5273 .push(Some(TooltipRequest { id, tooltip }));
5274 id
5275 }
5276
5277 #[inline]
5285 pub fn with_content_mask<R>(
5286 &mut self,
5287 mask: Option<ContentMask<Pixels>>,
5288 f: impl FnOnce(&mut Self) -> R,
5289 ) -> R {
5290 self.invalidator.debug_assert_paint_or_prepaint();
5291 if let Some(mask) = mask {
5292 let mask = mask.intersect(&self.content_mask());
5293 self.content_mask_stack.push(mask);
5294 let result = f(self);
5295 self.content_mask_stack.pop();
5296 result
5297 } else {
5298 f(self)
5299 }
5300 }
5301
5302 pub fn with_element_offset<R>(
5307 &mut self,
5308 offset: Point<Pixels>,
5309 f: impl FnOnce(&mut Self) -> R,
5310 ) -> R {
5311 self.invalidator.debug_assert_prepaint();
5312
5313 if offset.is_zero() {
5314 return f(self);
5315 };
5316
5317 let abs_offset = self.element_offset() + offset;
5318 self.with_absolute_element_offset(abs_offset, f)
5319 }
5320
5321 pub(crate) fn with_style_transition_containing_bounds<R>(
5322 &mut self,
5323 bounds: Bounds<Pixels>,
5324 f: impl FnOnce(&mut Self) -> R,
5325 ) -> R {
5326 self.invalidator.debug_assert_prepaint();
5327 let previous_bounds = self.style_transition_containing_bounds.replace(bounds);
5328 let result = f(self);
5329 self.style_transition_containing_bounds = previous_bounds;
5330 result
5331 }
5332
5333 pub(crate) fn style_transition_containing_bounds(&self) -> Option<Bounds<Pixels>> {
5334 self.style_transition_containing_bounds
5335 }
5336
5337 pub fn with_absolute_element_offset<R>(
5342 &mut self,
5343 offset: Point<Pixels>,
5344 f: impl FnOnce(&mut Self) -> R,
5345 ) -> R {
5346 self.invalidator.debug_assert_prepaint();
5347 self.element_offset_stack.push(offset);
5348 let result = f(self);
5349 self.element_offset_stack.pop();
5350 result
5351 }
5352
5353 pub(crate) fn with_element_opacity<R>(
5354 &mut self,
5355 opacity: Option<f32>,
5356 paint: impl FnOnce(&mut Self) -> R,
5357 ) -> R {
5358 self.invalidator.debug_assert_paint();
5359 let opacity = opacity.unwrap_or(1.0).clamp(0.0, 1.0);
5360
5361 if opacity == 1.0 {
5362 return paint(self);
5363 }
5364
5365 let bounds = self.cover_bounds(Bounds::new(Point::default(), self.viewport_size()));
5368 let boundary = FilterBoundary {
5369 order: 0,
5370 bounds,
5371 content_mask: ContentMask {
5372 bounds,
5373 fade_out: Edges::default(),
5374 },
5375 corner_radii: Corners::default(),
5376 corner_smoothing: 0.0,
5377 filters: SmallVec::new(),
5378 opacity,
5379 is_start: true,
5380 };
5381
5382 self.with_filter_boundary(boundary, paint)
5383 }
5384
5385 fn with_filter_boundary<R>(
5386 &mut self,
5387 boundary: FilterBoundary,
5388 paint: impl FnOnce(&mut Self) -> R,
5389 ) -> R {
5390 self.next_frame.scene.insert_primitive(boundary.clone());
5391 let result = paint(self);
5392 self.next_frame.scene.insert_primitive(FilterBoundary {
5393 is_start: false,
5394 ..boundary
5395 });
5396
5397 result
5398 }
5399
5400 pub fn transact<T, U>(&mut self, f: impl FnOnce(&mut Self) -> Result<T, U>) -> Result<T, U> {
5410 self.invalidator.debug_assert_prepaint();
5411 let index = self.prepaint_index();
5412 let result = f(self);
5413 if result.is_err() {
5414 self.next_frame.hitboxes.truncate(index.hitboxes_index);
5415 self.next_frame
5416 .tooltip_requests
5417 .truncate(index.tooltips_index);
5418 self.next_frame
5419 .deferred_draws
5420 .truncate(index.deferred_draws_index);
5421 self.next_frame
5422 .dispatch_tree
5423 .truncate(index.dispatch_tree_index);
5424 self.next_frame
5425 .accessed_element_states
5426 .truncate(index.accessed_element_states_index);
5427 self.text_system.truncate_layouts(index.line_layout_index);
5428 }
5429 result
5430 }
5431
5432 pub fn request_autoscroll(&mut self, bounds: Bounds<Pixels>) {
5437 self.invalidator.debug_assert_prepaint();
5438 self.requested_autoscroll = Some(bounds);
5439 }
5440
5441 pub fn take_autoscroll(&mut self) -> Option<Bounds<Pixels>> {
5445 self.invalidator.debug_assert_prepaint();
5446 self.requested_autoscroll.take()
5447 }
5448
5449 pub fn use_asset<A: Asset>(&mut self, source: &A::Source, cx: &mut App) -> Option<A::Output> {
5456 cx.asset_entry::<A>(source).use_by(self.current_view())
5457 }
5458
5459 pub fn get_asset<A: Asset>(&mut self, source: &A::Source, cx: &mut App) -> Option<A::Output> {
5465 cx.fetch_asset::<A>(source)
5466 }
5467 pub fn element_offset(&self) -> Point<Pixels> {
5471 self.invalidator.debug_assert_prepaint();
5472 self.element_offset_stack
5473 .last()
5474 .copied()
5475 .unwrap_or_default()
5476 }
5477
5478 pub fn content_mask(&self) -> ContentMask<Pixels> {
5482 self.invalidator.debug_assert_paint_or_prepaint();
5483 self.content_mask_stack
5484 .last()
5485 .cloned()
5486 .unwrap_or_else(|| ContentMask {
5487 bounds: Bounds {
5488 origin: Point::default(),
5489 size: self.viewport_size,
5490 },
5491 ..Default::default()
5492 })
5493 }
5494
5495 pub fn with_element_namespace<R>(
5500 &mut self,
5501 element_id: impl Into<ElementId>,
5502 f: impl FnOnce(&mut Self) -> R,
5503 ) -> R {
5504 self.element_id_stack.push(element_id.into());
5505 let result = f(self);
5506 self.element_id_stack.pop();
5507 result
5508 }
5509
5510 pub fn use_keyed_state<S: 'static>(
5565 &mut self,
5566 key: impl Into<ElementId>,
5567 cx: &mut App,
5568 init: impl FnOnce(&mut Self, &mut Context<S>) -> S,
5569 ) -> Entity<S> {
5570 let current_view = self.current_view();
5571 self.with_global_id(key.into(), |global_id, window| {
5572 window.with_element_state(global_id, |state: Option<Entity<S>>, window| {
5573 if let Some(state) = state {
5574 (state.clone(), state)
5575 } else {
5576 let new_state = cx.new(|cx| init(window, cx));
5577 Self::observe_keyed_state(&new_state, current_view, cx);
5578 (new_state.clone(), new_state)
5579 }
5580 })
5581 })
5582 }
5583
5584 #[track_caller]
5590 pub fn use_state<S: 'static>(
5591 &mut self,
5592 cx: &mut App,
5593 init: impl FnOnce(&mut Self, &mut Context<S>) -> S,
5594 ) -> Entity<S> {
5595 self.use_keyed_state(
5596 ElementId::CodeLocation(*core::panic::Location::caller()),
5597 cx,
5598 init,
5599 )
5600 }
5601
5602 pub fn with_element_state<S, R>(
5614 &mut self,
5615 global_id: &GlobalElementId,
5616 f: impl FnOnce(Option<S>, &mut Self) -> (R, S),
5617 ) -> R
5618 where
5619 S: 'static,
5620 {
5621 self.invalidator.debug_assert_paint_or_prepaint();
5622
5623 let (key, state) = self.take_element_state(global_id, TypeId::of::<S>());
5624
5625 if let Some(any) = state {
5626 let ElementStateBox {
5627 inner,
5628 #[cfg(debug_assertions)]
5629 type_name,
5630 } = any;
5631 let mut state_box = inner
5633 .downcast::<Option<S>>()
5634 .map_err(|_| {
5635 #[cfg(debug_assertions)]
5636 {
5637 anyhow::anyhow!(
5638 "invalid element state type for id, requested {:?}, actual: {:?}",
5639 std::any::type_name::<S>(),
5640 type_name
5641 )
5642 }
5643
5644 #[cfg(not(debug_assertions))]
5645 {
5646 anyhow::anyhow!(
5647 "invalid element state type for id, requested {:?}",
5648 std::any::type_name::<S>(),
5649 )
5650 }
5651 })
5652 .unwrap();
5653
5654 let state = state_box.take().expect(
5655 "reentrant call to with_element_state for the same state type and element id",
5656 );
5657 let (result, state) = f(Some(state), self);
5658 state_box.replace(state);
5659 self.insert_element_state(
5660 key,
5661 ElementStateBox {
5662 inner: state_box,
5663 #[cfg(debug_assertions)]
5664 type_name,
5665 },
5666 );
5667 result
5668 } else {
5669 let (result, state) = f(None, self);
5670 self.insert_element_state(
5671 key,
5672 ElementStateBox {
5673 inner: Box::new(Some(state)),
5674 #[cfg(debug_assertions)]
5675 type_name: std::any::type_name::<S>(),
5676 },
5677 );
5678 result
5679 }
5680 }
5681
5682 pub fn with_optional_element_state<S, R>(
5690 &mut self,
5691 global_id: Option<&GlobalElementId>,
5692 f: impl FnOnce(Option<Option<S>>, &mut Self) -> (R, Option<S>),
5693 ) -> R
5694 where
5695 S: 'static,
5696 {
5697 self.invalidator.debug_assert_paint_or_prepaint();
5698
5699 if let Some(global_id) = global_id {
5700 self.with_element_state(global_id, |state, cx| {
5701 let (result, state) = f(Some(state), cx);
5702 let state =
5703 state.expect("you must return some state when you pass some element id");
5704 (result, state)
5705 })
5706 } else {
5707 let (result, state) = f(None, self);
5708 debug_assert!(
5709 state.is_none(),
5710 "you must not return an element state when passing None for the global id"
5711 );
5712 result
5713 }
5714 }
5715
5716 #[track_caller]
5722 pub fn use_transition<T: Lerp + Clone + PartialEq + 'static>(
5723 &mut self,
5724 cx: &mut App,
5725 motion: impl Into<Motion>,
5726 init: impl Fn(&mut Window, &mut Context<TransitionState<T>>) -> T,
5727 ) -> Transition<T> {
5728 let motion = motion.into();
5729 let state = self.use_state(cx, |window, cx| {
5730 TransitionState::new(init(window, cx), motion.clone())
5731 });
5732
5733 Transition::new(state, motion)
5734 }
5735
5736 pub fn use_keyed_transition<T: Lerp + Clone + PartialEq + 'static>(
5745 &mut self,
5746 key: impl Into<ElementId>,
5747 cx: &mut App,
5748 motion: impl Into<Motion>,
5749 init: impl Fn(&mut Window, &mut Context<TransitionState<T>>) -> T,
5750 ) -> Transition<T> {
5751 let motion = motion.into();
5752 let state = self.use_keyed_state(key, cx, |window, cx| {
5753 TransitionState::new(init(window, cx), motion.clone())
5754 });
5755
5756 Transition::new(state, motion)
5757 }
5758
5759 #[inline]
5764 pub fn with_tab_group<R>(&mut self, index: Option<isize>, f: impl FnOnce(&mut Self) -> R) -> R {
5765 if let Some(index) = index {
5766 self.next_frame.tab_stops.begin_group(index);
5767 let result = f(self);
5768 self.next_frame.tab_stops.end_group();
5769 result
5770 } else {
5771 f(self)
5772 }
5773 }
5774
5775 pub fn defer_draw(
5789 &mut self,
5790 element: AnyElement,
5791 absolute_offset: Point<Pixels>,
5792 priority: usize,
5793 content_mask: Option<ContentMask<Pixels>>,
5794 ) {
5795 self.invalidator.debug_assert_prepaint();
5796 let parent_node = self.next_frame.dispatch_tree.active_node_id().unwrap();
5797 self.next_frame.deferred_draws.push(DeferredDraw {
5798 current_view: self.current_view(),
5799 parent_node,
5800 element_id_stack: self.element_id_stack.clone(),
5801 text_style_stack: self.text_style_stack.clone(),
5802 content_mask,
5803 rem_size: self.rem_size(),
5804 priority,
5805 element: Some(element),
5806 absolute_offset,
5807 prepaint_range: PrepaintStateIndex::default()..PrepaintStateIndex::default(),
5808 paint_range: PaintIndex::default()..PaintIndex::default(),
5809 });
5810 }
5811
5812 pub fn paint_layer<R>(&mut self, bounds: Bounds<Pixels>, f: impl FnOnce(&mut Self) -> R) -> R {
5819 self.invalidator.debug_assert_paint();
5820
5821 let content_mask = self.content_mask();
5822 let clipped_bounds = bounds.intersect(&content_mask.bounds);
5823 if !clipped_bounds.is_empty() {
5824 self.next_frame
5825 .scene
5826 .push_layer(self.cover_bounds(clipped_bounds));
5827 }
5828
5829 let result = f(self);
5830
5831 if !clipped_bounds.is_empty() {
5832 self.next_frame.scene.pop_layer();
5833 }
5834
5835 result
5836 }
5837
5838 pub fn paint_drop_shadows(
5844 &mut self,
5845 bounds: Bounds<Pixels>,
5846 corner_radii: Corners<Pixels>,
5847 corner_smoothing: f32,
5848 shadows: &[BoxShadow],
5849 ) {
5850 self.paint_shadows(bounds, corner_radii, corner_smoothing, shadows, false);
5851 }
5852
5853 pub fn paint_inset_shadows(
5859 &mut self,
5860 bounds: Bounds<Pixels>,
5861 corner_radii: Corners<Pixels>,
5862 corner_smoothing: f32,
5863 shadows: &[BoxShadow],
5864 ) {
5865 self.paint_shadows(bounds, corner_radii, corner_smoothing, shadows, true);
5866 }
5867
5868 fn paint_shadows(
5869 &mut self,
5870 bounds: Bounds<Pixels>,
5871 corner_radii: Corners<Pixels>,
5872 corner_smoothing: f32,
5873 shadows: &[BoxShadow],
5874 inset: bool,
5875 ) {
5876 self.invalidator.debug_assert_paint();
5877
5878 let scale_factor = self.scale_factor();
5879 let content_mask = self.snapped_content_mask();
5880 let element_bounds = self.cover_bounds(bounds);
5881 let element_corner_radii = corner_radii.scale(scale_factor);
5882 let corner_smoothing = corner_smoothing.clamp(0.0, 1.0);
5883
5884 for shadow in shadows.iter().filter(|shadow| shadow.inset == inset) {
5885 let spread = if inset {
5886 -shadow.spread_radius
5887 } else {
5888 shadow.spread_radius
5889 };
5890
5891 let shadow_bounds = (bounds + shadow.offset).dilate(spread);
5892
5893 let shadow_radii = if inset {
5894 corner_radii.map(|radius| (*radius - shadow.spread_radius).max(Pixels::ZERO))
5896 } else {
5897 corner_radii
5898 };
5899
5900 self.next_frame.scene.insert_primitive(Shadow {
5901 order: 0,
5902 blur_radius: shadow.blur_radius.scale(scale_factor),
5903 bounds: self.cover_bounds(shadow_bounds),
5904 content_mask,
5905 corner_radii: shadow_radii.scale(scale_factor),
5906 color: shadow.color,
5907 element_bounds,
5908 element_corner_radii,
5909 inset: inset.into(),
5910 corner_smoothing,
5911 });
5912 }
5913 }
5914
5915 pub fn paint_backdrop_filter(
5926 &mut self,
5927 bounds: Bounds<Pixels>,
5928 corner_radii: Corners<Pixels>,
5929 corner_smoothing: f32,
5930 filters: &[Filter],
5931 ) {
5932 self.invalidator.debug_assert_paint();
5933
5934 let scale_factor = self.scale_factor();
5935 let filters: SmallVec<[ScaledFilter; 4]> = filters
5936 .iter()
5937 .filter(|filter| !filter.is_identity())
5938 .map(|filter| filter.scale(scale_factor))
5939 .collect();
5940 if filters.is_empty() {
5941 return;
5942 }
5943
5944 self.next_frame.scene.insert_primitive(BackdropFilter {
5945 order: 0,
5946 bounds: self.snap_bounds(bounds),
5947 content_mask: self.snapped_content_mask(),
5948 corner_radii: corner_radii.scale(scale_factor),
5949 corner_smoothing: corner_smoothing.clamp(0.0, 1.0),
5950 filters,
5951 opacity: 1.0,
5952 });
5953 }
5954
5955 pub fn with_filter_layer<R>(
5966 &mut self,
5967 bounds: Bounds<Pixels>,
5968 corner_radii: Corners<Pixels>,
5969 corner_smoothing: f32,
5970 filters: &[Filter],
5971 f: impl FnOnce(&mut Self) -> R,
5972 ) -> R {
5973 self.invalidator.debug_assert_paint();
5974
5975 let scale_factor = self.scale_factor();
5976 let filters: SmallVec<[ScaledFilter; 4]> = filters
5977 .iter()
5978 .filter(|filter| !filter.is_identity())
5979 .map(|filter| filter.scale(scale_factor))
5980 .collect();
5981 if filters.is_empty() {
5982 return f(self);
5983 }
5984
5985 let boundary = FilterBoundary {
5986 order: 0,
5987 bounds: self.snap_bounds(bounds),
5988 content_mask: self.snapped_content_mask(),
5989 corner_radii: corner_radii.scale(scale_factor),
5990 corner_smoothing: corner_smoothing.clamp(0.0, 1.0),
5991 filters,
5992 opacity: 1.0,
5993 is_start: true,
5994 };
5995
5996 self.with_filter_boundary(boundary, f)
5997 }
5998
5999 fn largest_border_interior(quad: &Quad) -> Bounds<ScaledPixels> {
6000 let radii = &quad.corner_radii;
6001 let widths = &quad.border_widths;
6002 let reach_factor = 1.0 + quad.corner_smoothing;
6003 let edge_reaches = Edges {
6004 top: radii.top_left.max(radii.top_right) * reach_factor,
6005 right: radii.top_right.max(radii.bottom_right) * reach_factor,
6006 bottom: radii.bottom_left.max(radii.bottom_right) * reach_factor,
6007 left: radii.top_left.max(radii.bottom_left) * reach_factor,
6008 };
6009
6010 let antialias_inset = point(ScaledPixels(1.0), ScaledPixels(1.0));
6011 let inset_bounds = |top_left_inset, bottom_right_inset| {
6012 Bounds::from_corners(
6013 quad.bounds.origin + top_left_inset + antialias_inset,
6014 quad.bounds.bottom_right() - bottom_right_inset - antialias_inset,
6015 )
6016 };
6017
6018 let horizontal_band = inset_bounds(
6021 point(widths.left, widths.top.max(edge_reaches.top)),
6022 point(widths.right, widths.bottom.max(edge_reaches.bottom)),
6023 );
6024 let vertical_band = inset_bounds(
6025 point(widths.left.max(edge_reaches.left), widths.top),
6026 point(widths.right.max(edge_reaches.right), widths.bottom),
6027 );
6028
6029 let area = |bounds: &Bounds<ScaledPixels>| {
6030 bounds.size.width.0.max(0.) * bounds.size.height.0.max(0.)
6031 };
6032 if area(&horizontal_band) >= area(&vertical_band) {
6033 horizontal_band
6034 } else {
6035 vertical_band
6036 }
6037 }
6038
6039 pub fn paint_quad(&mut self, quad: PaintQuad) {
6050 self.invalidator.debug_assert_paint();
6051
6052 let snapped_bounds = self.snap_bounds(quad.bounds);
6053 let snapped_border_widths = self.snap_border_widths(quad.border_widths);
6054 let quad = Quad {
6055 order: 0,
6056 bounds: snapped_bounds,
6057 content_mask: self.snapped_content_mask(),
6058 background: quad.background,
6059 border_color: quad.border_color,
6060 corner_radii: quad.corner_radii.scale(self.scale_factor()),
6061 border_widths: snapped_border_widths,
6062 border_style: quad.border_style,
6063 border_dashed_length: quad.border_dashed_length,
6064 border_dashed_gap: quad.border_dashed_gap,
6065 corner_smoothing: quad.corner_smoothing.clamp(0.0, 1.0),
6066 padding: 0,
6067 };
6068
6069 if !quad.background.is_transparent() {
6070 self.next_frame.scene.insert_primitive(quad);
6071 return;
6072 }
6073
6074 let outer_bounds = quad.bounds;
6077 let inner_bounds = Self::largest_border_interior(&quad);
6078
6079 if inner_bounds.is_empty() {
6080 self.next_frame.scene.insert_primitive(quad);
6081 return;
6082 }
6083
6084 let strips = [
6085 Bounds::from_corners(
6087 outer_bounds.origin,
6088 point(outer_bounds.right(), inner_bounds.top()),
6089 ),
6090 Bounds::from_corners(
6092 point(outer_bounds.left(), inner_bounds.bottom()),
6093 outer_bounds.bottom_right(),
6094 ),
6095 Bounds::from_corners(
6097 point(outer_bounds.left(), inner_bounds.top()),
6098 inner_bounds.bottom_left(),
6099 ),
6100 Bounds::from_corners(
6102 inner_bounds.top_right(),
6103 point(outer_bounds.right(), inner_bounds.bottom()),
6104 ),
6105 ];
6106
6107 for strip in strips {
6108 let content_mask_bounds = quad.content_mask.bounds.intersect(&strip);
6109 if !content_mask_bounds.is_empty() {
6110 self.next_frame.scene.insert_primitive(Quad {
6111 content_mask: ContentMask {
6112 bounds: content_mask_bounds,
6113 ..Default::default()
6114 },
6115 ..quad
6116 });
6117 }
6118 }
6119 }
6120
6121 pub fn paint_path(&mut self, mut path: Path<Pixels>, color: impl Into<Background>) {
6126 self.invalidator.debug_assert_paint();
6127
6128 let scale_factor = self.scale_factor();
6129 let content_mask = self.content_mask();
6130 path.content_mask = content_mask;
6131 let color: Background = color.into();
6132 path.color = color;
6133 self.next_frame
6134 .scene
6135 .insert_primitive(path.scale(scale_factor));
6136 }
6137
6138 pub fn paint_underline(
6143 &mut self,
6144 origin: Point<Pixels>,
6145 width: Pixels,
6146 style: &UnderlineStyle,
6147 ) {
6148 self.invalidator.debug_assert_paint();
6149
6150 let scale_factor = self.scale_factor();
6151 let thickness = self.snap_stroke(style.thickness);
6152 let height = if style.wavy {
6153 ScaledPixels(thickness.0 * 3.)
6154 } else {
6155 thickness
6156 };
6157 let bounds = Bounds {
6158 origin: origin.map(|c| ScaledPixels(round_to_device_pixel(c.0, scale_factor))),
6159 size: size(self.snap_stroke(width), height),
6160 };
6161 let content_mask = self.snapped_content_mask();
6162
6163 self.next_frame.scene.insert_primitive(Underline {
6164 order: 0,
6165 padding: 0,
6166 bounds,
6167 clip_bounds: content_mask.bounds,
6168 content_mask,
6169 color: style.color.unwrap_or_default().into(),
6170 thickness,
6171 wavy: style.wavy.into(),
6172 });
6173 }
6174
6175 pub fn paint_strikethrough(
6180 &mut self,
6181 origin: Point<Pixels>,
6182 width: Pixels,
6183 style: &StrikethroughStyle,
6184 ) {
6185 self.invalidator.debug_assert_paint();
6186
6187 let scale_factor = self.scale_factor();
6188 let height = style.thickness;
6189 let bounds = Bounds {
6190 origin: origin.map(|c| ScaledPixels(round_to_device_pixel(c.0, scale_factor))),
6191 size: size(self.snap_stroke(width), self.snap_stroke(height)),
6192 };
6193 let content_mask = self.snapped_content_mask();
6194 let thickness = self.snap_stroke(style.thickness);
6195
6196 self.next_frame.scene.insert_primitive(Underline {
6197 order: 0,
6198 padding: 0,
6199 bounds,
6200 clip_bounds: content_mask.bounds,
6201 content_mask,
6202 thickness,
6203 color: style.color.unwrap_or_default().into(),
6204 wavy: false.into(),
6205 });
6206 }
6207
6208 pub fn paint_glyph(
6217 &mut self,
6218 origin: Point<Pixels>,
6219 font_id: FontId,
6220 glyph_id: GlyphId,
6221 font_size: Pixels,
6222 color: Hsla,
6223 ) -> Result<()> {
6224 self.invalidator.debug_assert_paint();
6225
6226 let scale_factor = self.scale_factor();
6227 let glyph_origin = origin.scale(scale_factor);
6228
6229 let (integer_origin, subpixel_variant) = quantize_glyph_origin(glyph_origin);
6230 let requested_mode = if self.should_use_subpixel_rendering(font_id, font_size) {
6231 GlyphRenderMode::Subpixel
6232 } else {
6233 GlyphRenderMode::Grayscale
6234 };
6235 let raster_style =
6236 self.text_system()
6237 .prepare_raster_style(font_id, glyph_id, color, requested_mode);
6238 let params = RenderGlyphParams {
6239 font_id,
6240 glyph_id,
6241 font_size,
6242 subpixel_variant,
6243 scale_factor,
6244 raster_style,
6245 };
6246
6247 self.paint_glyph_from_atlas(integer_origin, params, color)
6248 }
6249
6250 fn paint_glyph_from_atlas(
6251 &mut self,
6252 integer_origin: Point<ScaledPixels>,
6253 params: RenderGlyphParams,
6254 mask_color: Hsla,
6255 ) -> Result<()> {
6256 let text_system = self.text_system().clone();
6257 let entry = self
6258 .sprite_atlas
6259 .get_or_insert_glyph_with(¶ms, &mut || text_system.rasterize_glyph(¶ms))?;
6260 let Some(tile) = entry.tile else {
6261 return Ok(());
6262 };
6263
6264 debug_assert_eq!(entry.bounds.size, tile.bounds.size.map(Into::into));
6265 let bounds = Bounds {
6266 origin: integer_origin + entry.bounds.origin.map(Into::into),
6267 size: tile.bounds.size.map(Into::into),
6268 };
6269 let content_mask = self.snapped_content_mask();
6270
6271 match entry.format {
6272 RasterizedGlyphFormat::AlphaMask => {
6273 self.next_frame.scene.insert_primitive(MonochromeSprite {
6274 order: 0,
6275 padding: 0,
6276 bounds,
6277 content_mask,
6278 color: mask_color.into(),
6279 tile,
6280 transformation: TransformationMatrix::unit(),
6281 });
6282 }
6283 RasterizedGlyphFormat::BgraSubpixelMask => {
6284 self.next_frame.scene.insert_primitive(SubpixelSprite {
6285 order: 0,
6286 padding: 0,
6287 bounds,
6288 content_mask,
6289 color: mask_color.into(),
6290 tile,
6291 transformation: TransformationMatrix::unit(),
6292 });
6293 }
6294 RasterizedGlyphFormat::BgraColor => {
6295 self.next_frame.scene.insert_primitive(PolychromeSprite {
6296 order: 0,
6297 grayscale: false.into(),
6298 corner_smoothing: 0.0,
6299 bounds,
6300 corner_radii: Default::default(),
6301 content_mask,
6302 tile,
6303 opacity: 1.0,
6304 });
6305 }
6306 }
6307 Ok(())
6308 }
6309
6310 fn should_use_subpixel_rendering(&self, font_id: FontId, font_size: Pixels) -> bool {
6311 if self.next_frame.scene.is_in_filter_group() {
6312 return false;
6313 }
6314
6315 if self
6316 .platform_window
6317 .background_appearance()
6318 .is_transparent()
6319 {
6320 return false;
6321 }
6322
6323 if !self.platform_window.is_subpixel_rendering_supported() {
6324 return false;
6325 }
6326
6327 let mode = match self.text_rendering_mode.get() {
6328 TextRenderingMode::PlatformDefault => self
6329 .text_system()
6330 .recommended_rendering_mode(font_id, font_size),
6331 mode => mode,
6332 };
6333
6334 mode == TextRenderingMode::Subpixel
6335 }
6336
6337 pub fn paint_emoji(
6346 &mut self,
6347 origin: Point<Pixels>,
6348 font_id: FontId,
6349 glyph_id: GlyphId,
6350 font_size: Pixels,
6351 ) -> Result<()> {
6352 self.paint_emoji_with_color(origin, font_id, glyph_id, font_size, white())
6353 }
6354
6355 pub fn paint_emoji_with_color(
6357 &mut self,
6358 origin: Point<Pixels>,
6359 font_id: FontId,
6360 glyph_id: GlyphId,
6361 font_size: Pixels,
6362 color: Hsla,
6363 ) -> Result<()> {
6364 self.invalidator.debug_assert_paint();
6365
6366 let scale_factor = self.scale_factor();
6367 let glyph_origin = origin.scale(scale_factor);
6368 let (integer_origin, subpixel_variant) = quantize_color_glyph_origin(glyph_origin);
6369 let raster_style = self.text_system().prepare_raster_style(
6370 font_id,
6371 glyph_id,
6372 color,
6373 GlyphRenderMode::Color,
6374 );
6375 let params = RenderGlyphParams {
6376 font_id,
6377 glyph_id,
6378 font_size,
6379 subpixel_variant,
6380 scale_factor,
6381 raster_style,
6382 };
6383
6384 self.paint_glyph_from_atlas(integer_origin, params, color)
6385 }
6386
6387 pub fn paint_svg(
6394 &mut self,
6395 bounds: Bounds<Pixels>,
6396 path: SharedString,
6397 mut data: Option<&[u8]>,
6398 transformation: TransformationMatrix,
6399 color: Hsla,
6400 cx: &App,
6401 ) -> Result<()> {
6402 self.invalidator.debug_assert_paint();
6403
6404 let bounds = self.snap_bounds(bounds);
6405
6406 let params = RenderSvgParams {
6407 path,
6408 size: bounds.size.map(|pixels| {
6409 DevicePixels::from((pixels.0 * SMOOTH_SVG_SCALE_FACTOR).ceil() as i32)
6410 }),
6411 };
6412
6413 let Some(tile) =
6414 self.sprite_atlas
6415 .get_or_insert_with(params.clone().into(), &mut || {
6416 let Some((size, bytes)) = cx.svg_renderer.render_alpha_mask(¶ms, data)?
6417 else {
6418 return Ok(None);
6419 };
6420 Ok(Some((size, Cow::Owned(bytes))))
6421 })?
6422 else {
6423 return Ok(());
6424 };
6425 let content_mask = self.snapped_content_mask();
6426 let svg_bounds = Bounds {
6427 origin: bounds.center()
6428 - Point::new(
6429 ScaledPixels(tile.bounds.size.width.0 as f32 / SMOOTH_SVG_SCALE_FACTOR / 2.),
6430 ScaledPixels(tile.bounds.size.height.0 as f32 / SMOOTH_SVG_SCALE_FACTOR / 2.),
6431 ),
6432 size: tile
6433 .bounds
6434 .size
6435 .map(|value| ScaledPixels(value.0 as f32 / SMOOTH_SVG_SCALE_FACTOR)),
6436 };
6437 let final_bounds = svg_bounds
6438 .map_origin(|value| ScaledPixels(round_half_toward_zero(value.0)))
6439 .map_size(|size| size.ceil());
6440
6441 self.next_frame.scene.insert_primitive(MonochromeSprite {
6442 order: 0,
6443 padding: 0,
6444 bounds: final_bounds,
6445 content_mask,
6446 color: color.into(),
6447 tile,
6448 transformation,
6449 });
6450
6451 Ok(())
6452 }
6453
6454 pub fn paint_image(
6461 &mut self,
6462 bounds: Bounds<Pixels>,
6463 image_bounds: Bounds<Pixels>,
6464 corner_radii: Corners<Pixels>,
6465 corner_smoothing: f32,
6466 data: Arc<RenderImage>,
6467 frame_index: usize,
6468 grayscale: bool,
6469 ) -> Result<()> {
6470 self.invalidator.debug_assert_paint();
6471
6472 let visible_bounds = bounds.intersect(&image_bounds);
6473 if visible_bounds.size.width <= Pixels::ZERO || visible_bounds.size.height <= Pixels::ZERO {
6474 return Ok(());
6475 }
6476 if image_bounds.size.width <= Pixels::ZERO || image_bounds.size.height <= Pixels::ZERO {
6477 return Ok(());
6478 }
6479
6480 let params = RenderImageParams {
6481 image_id: data.id,
6482 frame_index,
6483 };
6484
6485 let tile = self
6486 .sprite_atlas
6487 .get_or_insert_with(params.into(), &mut || {
6488 Ok(Some((
6489 data.size(frame_index),
6490 Cow::Borrowed(
6491 data.as_bytes(frame_index)
6492 .expect("It's the caller's job to pass a valid frame index"),
6493 ),
6494 )))
6495 })?
6496 .expect("Callback above only returns Some");
6497
6498 let visible_bounds_snapped = self.snap_bounds(visible_bounds);
6499
6500 let sub_tile = if visible_bounds == image_bounds {
6501 tile
6502 } else {
6503 let x_offset_ratio =
6504 (visible_bounds.origin.x - image_bounds.origin.x) / image_bounds.size.width;
6505 let y_offset_ratio =
6506 (visible_bounds.origin.y - image_bounds.origin.y) / image_bounds.size.height;
6507 let width_ratio = visible_bounds.size.width / image_bounds.size.width;
6508 let height_ratio = visible_bounds.size.height / image_bounds.size.height;
6509
6510 let tile_origin_x = tile.bounds.origin.x.0;
6511 let tile_origin_y = tile.bounds.origin.y.0;
6512 let tile_width = tile.bounds.size.width.0;
6513 let tile_height = tile.bounds.size.height.0;
6514
6515 let sub_origin_x = tile_origin_x + (x_offset_ratio * tile_width as f32).round() as i32;
6516 let sub_origin_y = tile_origin_y + (y_offset_ratio * tile_height as f32).round() as i32;
6517 let sub_width = (width_ratio * tile_width as f32).round() as i32;
6518 let sub_height = (height_ratio * tile_height as f32).round() as i32;
6519
6520 let max_x = tile_origin_x + tile_width;
6521 let max_y = tile_origin_y + tile_height;
6522
6523 let clamped_origin_x = sub_origin_x.clamp(tile_origin_x, max_x);
6524 let clamped_origin_y = sub_origin_y.clamp(tile_origin_y, max_y);
6525 let clamped_width = sub_width.min(max_x - clamped_origin_x).max(0);
6526 let clamped_height = sub_height.min(max_y - clamped_origin_y).max(0);
6527
6528 AtlasTile {
6529 bounds: Bounds {
6530 origin: point(
6531 DevicePixels(clamped_origin_x),
6532 DevicePixels(clamped_origin_y),
6533 ),
6534 size: size(DevicePixels(clamped_width), DevicePixels(clamped_height)),
6535 },
6536 ..tile
6537 }
6538 };
6539
6540 let content_mask = self.snapped_content_mask();
6541 let corner_radii = corner_radii
6542 .clamp_radii_for_quad_size(visible_bounds.size)
6543 .scale(self.scale_factor());
6544
6545 self.next_frame.scene.insert_primitive(PolychromeSprite {
6546 order: 0,
6547 grayscale: grayscale.into(),
6548 corner_smoothing: corner_smoothing.clamp(0.0, 1.0),
6549 bounds: visible_bounds_snapped,
6550 content_mask,
6551 corner_radii,
6552 tile: sub_tile,
6553 opacity: 1.0,
6554 });
6555 Ok(())
6556 }
6557
6558 pub fn paint_surface(
6562 &mut self,
6563 bounds: Bounds<Pixels>,
6564 source: impl Into<crate::SurfaceSource>,
6565 ) {
6566 self.invalidator.debug_assert_paint();
6567
6568 let bounds = self.snap_bounds(bounds);
6569 let content_mask = self.snapped_content_mask();
6570 self.next_frame.scene.insert_primitive(PaintSurface {
6571 order: 0,
6572 bounds,
6573 content_mask,
6574 source: source.into(),
6575 });
6576 }
6577
6578 pub fn drop_image(&mut self, data: Arc<RenderImage>) -> Result<()> {
6583 for frame_index in 0..data.frame_count() {
6584 let params = RenderImageParams {
6585 image_id: data.id,
6586 frame_index,
6587 };
6588
6589 self.sprite_atlas.remove(¶ms.clone().into());
6590 }
6591
6592 Ok(())
6593 }
6594
6595 #[cfg(any(test, feature = "test-support"))]
6600 pub fn has_image_atlas_entry(&self, data: &RenderImage) -> bool {
6601 data.frame_count() > 0
6602 && (0..data.frame_count()).all(|frame_index| {
6603 self.sprite_atlas.contains(
6604 &RenderImageParams {
6605 image_id: data.id,
6606 frame_index,
6607 }
6608 .into(),
6609 )
6610 })
6611 }
6612
6613 #[must_use]
6622 pub fn request_layout(
6623 &mut self,
6624 style: Style,
6625 children: impl IntoIterator<Item = LayoutId>,
6626 cx: &mut App,
6627 ) -> LayoutId {
6628 self.invalidator.debug_assert_prepaint();
6629
6630 cx.layout_id_buffer.clear();
6631 cx.layout_id_buffer.extend(children);
6632 let rem_size = self.rem_size();
6633 let scale_factor = self.scale_factor();
6634
6635 self.layout_engine.as_mut().unwrap().request_layout(
6636 style,
6637 rem_size,
6638 scale_factor,
6639 &cx.layout_id_buffer,
6640 )
6641 }
6642
6643 pub fn request_measured_layout<F>(&mut self, style: Style, measure: F) -> LayoutId
6652 where
6653 F: Fn(Size<Option<Pixels>>, Size<AvailableSpace>, &mut Window, &mut App) -> Size<Pixels>
6654 + 'static,
6655 {
6656 self.invalidator.debug_assert_prepaint();
6657
6658 let rem_size = self.rem_size();
6659 let scale_factor = self.scale_factor();
6660 self.layout_engine
6661 .as_mut()
6662 .unwrap()
6663 .request_measured_layout(style, rem_size, scale_factor, measure)
6664 }
6665
6666 pub(crate) fn request_keyed_measured_layout<F>(
6670 &mut self,
6671 style: Style,
6672 key: impl crate::taffy::MeasureKey,
6673 measure: F,
6674 ) -> LayoutId
6675 where
6676 F: Fn(Size<Option<Pixels>>, Size<AvailableSpace>, &mut Window, &mut App) -> Size<Pixels>
6677 + 'static,
6678 {
6679 self.invalidator.debug_assert_prepaint();
6680
6681 let rem_size = self.rem_size();
6682 let scale_factor = self.scale_factor();
6683 self.layout_engine
6684 .as_mut()
6685 .unwrap()
6686 .request_keyed_measured_layout(
6687 style,
6688 rem_size,
6689 scale_factor,
6690 Some(Box::new(key)),
6691 measure,
6692 )
6693 }
6694
6695 pub fn compute_layout(
6700 &mut self,
6701 layout_id: LayoutId,
6702 available_space: Size<AvailableSpace>,
6703 cx: &mut App,
6704 ) {
6705 self.invalidator.debug_assert_prepaint();
6706
6707 let mut layout_engine = self.layout_engine.take().unwrap();
6708 layout_engine.compute_layout(
6709 layout_id,
6710 available_space,
6711 self.measurement_direction,
6712 self,
6713 cx,
6714 );
6715 self.layout_engine = Some(layout_engine);
6716 }
6717
6718 pub fn layout_bounds(&mut self, layout_id: LayoutId) -> Bounds<Pixels> {
6724 self.invalidator.debug_assert_prepaint();
6725
6726 let scale_factor = self.scale_factor();
6727 let mut bounds = self
6728 .layout_engine
6729 .as_mut()
6730 .unwrap()
6731 .layout_bounds(layout_id, scale_factor)
6732 .map(Into::into);
6733 let snapped_offset = self.pixel_snap_point(self.element_offset());
6734 bounds.origin += snapped_offset;
6735 bounds.origin = self.pixel_snap_point(bounds.origin);
6739 bounds
6740 }
6741
6742 pub fn parent_relative_layout_bounds(&mut self, layout_id: LayoutId) -> Bounds<Pixels> {
6748 self.invalidator.debug_assert_prepaint();
6749
6750 let scale_factor = self.scale_factor();
6751 let mut bounds = self
6752 .layout_engine
6753 .as_mut()
6754 .unwrap()
6755 .parent_relative_layout_bounds(layout_id, scale_factor);
6756 bounds.origin += self.pixel_snap_point(self.element_offset());
6757 bounds
6758 }
6759
6760 pub(crate) fn publish_inline_content(
6761 &mut self,
6762 node_id: LayoutId,
6763 content: crate::InlineContent,
6764 ) -> bool {
6765 let Some(layout_engine) = self.layout_engine.as_mut() else {
6766 return false;
6767 };
6768
6769 if let crate::InlineContent::Text { text, .. } = &content {
6770 layout_engine.set_direction_text(node_id, text.clone());
6771 }
6772
6773 layout_engine.set_inline_content(node_id, content);
6774 true
6775 }
6776
6777 pub(crate) fn set_layout_logical_children(&mut self, node_id: LayoutId, children: &[LayoutId]) {
6778 self.layout_engine
6779 .as_mut()
6780 .unwrap()
6781 .set_logical_children(node_id, children);
6782 }
6783
6784 pub(crate) fn set_layout_auto_direction_hint(
6785 &mut self,
6786 node_id: LayoutId,
6787 direction: Option<ResolvedDirection>,
6788 ) {
6789 self.layout_engine
6790 .as_mut()
6791 .unwrap()
6792 .set_auto_direction_hint(node_id, direction);
6793 }
6794
6795 pub(crate) fn layout_island_direction_contribution(
6796 &self,
6797 island: crate::taffy::IslandId,
6798 ) -> Option<ResolvedDirection> {
6799 self.layout_engine
6800 .as_ref()
6801 .unwrap()
6802 .island_direction_contribution(island)
6803 }
6804
6805 pub(crate) fn layout_direction_handle(
6806 &self,
6807 node_id: LayoutId,
6808 ) -> crate::taffy::LayoutDirectionHandle {
6809 self.layout_engine
6810 .as_ref()
6811 .unwrap()
6812 .direction_handle(node_id)
6813 }
6814
6815 pub(crate) fn layout_directionality(
6816 &self,
6817 node_id: LayoutId,
6818 ) -> (ResolvedDirection, UnicodeBidi) {
6819 self.layout_engine.as_ref().unwrap().directionality(node_id)
6820 }
6821
6822 pub(crate) fn with_layout_direction_context<R>(
6823 &mut self,
6824 node_id: LayoutId,
6825 f: impl FnOnce(&mut Self) -> R,
6826 ) -> R {
6827 let (direction, unicode_bidi) = self.layout_directionality(node_id);
6828 let previous_direction = std::mem::replace(&mut self.measurement_direction, direction);
6829 let previous_unicode_bidi =
6830 std::mem::replace(&mut self.measurement_unicode_bidi, unicode_bidi);
6831 let result = f(self);
6832 self.measurement_direction = previous_direction;
6833 self.measurement_unicode_bidi = previous_unicode_bidi;
6834 result
6835 }
6836
6837 pub fn set_layout_direction_text(&mut self, node_id: LayoutId, text: impl Into<SharedString>) {
6842 self.layout_engine
6843 .as_mut()
6844 .unwrap()
6845 .set_direction_text(node_id, text.into());
6846 }
6847
6848 pub(crate) fn inline_content(&self, node_id: LayoutId) -> Option<Arc<crate::InlineContent>> {
6849 self.layout_engine.as_ref()?.inline_content(node_id)
6850 }
6851
6852 pub(crate) fn layout_inherited(&self, node_id: LayoutId) -> bool {
6854 self.layout_engine
6855 .as_ref()
6856 .is_some_and(|layout_engine| layout_engine.inherited(node_id))
6857 }
6858
6859 pub(crate) fn layout_display_and_position(
6860 &self,
6861 node_id: LayoutId,
6862 ) -> Option<(crate::Display, crate::Position)> {
6863 self.layout_engine
6864 .as_ref()
6865 .map(|layout_engine| layout_engine.display_and_position(node_id))
6866 }
6867
6868 pub(crate) fn inline_fragments(&self, node_id: LayoutId) -> Option<Arc<[Bounds<Pixels>]>> {
6869 self.layout_engine
6870 .as_ref()?
6871 .inline_fragments(node_id)
6872 .map(|fragments| {
6873 let offset = self.pixel_snap_point(self.element_offset());
6874 if offset == Point::default() {
6875 return fragments;
6876 }
6877
6878 fragments
6879 .iter()
6880 .map(|bounds| Bounds::new(bounds.origin + offset, bounds.size))
6881 .collect()
6882 })
6883 }
6884
6885 pub(crate) fn place_inline(
6886 &mut self,
6887 node_id: LayoutId,
6888 mut bounds: Bounds<Pixels>,
6889 fragments: Option<Vec<Bounds<Pixels>>>,
6890 ) -> bool {
6891 if self.layout_engine.is_none() {
6892 return false;
6893 }
6894
6895 let offset = self.pixel_snap_point(self.element_offset());
6896 bounds.origin -= offset;
6897
6898 let scale = self.scale_factor();
6899 let Some(engine) = self.layout_engine.as_mut() else {
6900 return false;
6901 };
6902
6903 engine.place_inline(node_id, bounds, scale);
6904
6905 if let Some(mut fragments) = fragments {
6906 if offset != Point::default() {
6908 for fragment in &mut fragments {
6909 fragment.origin -= offset;
6910 }
6911 }
6912
6913 engine.set_inline_fragments(node_id, fragments.into());
6914 }
6915
6916 true
6917 }
6918
6919 pub(crate) fn layout_vertical_align(
6920 &self,
6921 layout_id: LayoutId,
6922 ) -> Option<crate::VerticalAlign> {
6923 self.layout_engine
6924 .as_ref()
6925 .map(|layout_engine| layout_engine.vertical_align(layout_id))
6926 }
6927
6928 pub fn insert_hitbox(&mut self, bounds: Bounds<Pixels>, behavior: HitboxBehavior) -> Hitbox {
6966 self.insert_hitbox_mut(bounds, behavior).clone()
6967 }
6968
6969 pub fn insert_hitbox_mut(
6975 &mut self,
6976 bounds: Bounds<Pixels>,
6977 behavior: HitboxBehavior,
6978 ) -> &mut Hitbox {
6979 self.invalidator.debug_assert_prepaint();
6980
6981 let content_mask = self.content_mask();
6982 let hitbox_id = self.next_hitbox_id;
6983 self.next_hitbox_id = self.next_hitbox_id.next();
6984 let hitbox = Hitbox {
6985 id: hitbox_id,
6986 bounds,
6987 content_mask,
6988 behavior,
6989 fragments: self.current_inline_fragments.clone(),
6990 tags: Vec::default(),
6991 };
6992
6993 self.next_frame.hitboxes.push_mut(hitbox)
6994 }
6995
6996 pub fn insert_window_control_hitbox(&mut self, area: WindowControlArea, hitbox: Hitbox) {
7001 self.invalidator.debug_assert_paint();
7002 self.next_frame.window_control_hitboxes.push((area, hitbox));
7003 }
7004
7005 pub fn set_key_context(&mut self, context: KeyContext) {
7010 self.invalidator.debug_assert_paint();
7011 self.next_frame.dispatch_tree.set_key_context(context);
7012 }
7013
7014 pub fn set_focus_handle(&mut self, focus_handle: &FocusHandle, _: &App) {
7019 self.invalidator.debug_assert_prepaint();
7020 if focus_handle.is_focused(self) {
7021 self.next_frame.focus = Some(focus_handle.id);
7022 }
7023 self.next_frame.dispatch_tree.set_focus_id(focus_handle.id);
7024 }
7025
7026 pub fn set_view_id(&mut self, view_id: EntityId) {
7031 self.invalidator.debug_assert_prepaint();
7032 self.next_frame.dispatch_tree.set_view_id(view_id);
7033 }
7034
7035 pub fn current_view(&self) -> EntityId {
7045 self.invalidator.debug_assert_paint_or_prepaint();
7046 self.rendered_entity_stack.last().copied().unwrap()
7047 }
7048
7049 #[inline]
7050 pub(crate) fn with_rendered_view<R>(
7051 &mut self,
7052 id: EntityId,
7053 f: impl FnOnce(&mut Self) -> R,
7054 ) -> R {
7055 self.rendered_entity_stack.push(id);
7056 let result = f(self);
7057 self.rendered_entity_stack.pop();
7058 result
7059 }
7060
7061 pub fn with_image_cache<F, R>(&mut self, image_cache: Option<AnyImageCache>, f: F) -> R
7065 where
7066 F: FnOnce(&mut Self) -> R,
7067 {
7068 if let Some(image_cache) = image_cache {
7069 self.image_cache_stack.push(image_cache);
7070 let result = f(self);
7071 self.image_cache_stack.pop();
7072 result
7073 } else {
7074 f(self)
7075 }
7076 }
7077
7078 pub fn handle_input(
7085 &mut self,
7086 focus_handle: &FocusHandle,
7087 input_handler: impl InputHandler,
7088 cx: &App,
7089 ) {
7090 self.invalidator.debug_assert_paint();
7091
7092 if focus_handle.is_focused(self) {
7093 let cx = self.to_async(cx);
7094 self.next_frame.input_handlers.push(Some(
7095 PlatformInputHandler::new(cx, Box::new(input_handler)).with_focus(focus_handle.id),
7096 ));
7097 }
7098 }
7099
7100 pub fn notify_text_input_changed(&self, focus_handle: &FocusHandle) {
7105 if self.focused_text_input == Some(focus_handle.id) && focus_handle.is_focused(self) {
7106 self.platform_window
7107 .text_input_state_changed(TextInputStateChange::ContentChanged);
7108 }
7109 }
7110
7111 pub fn notify_text_selection_changed(&self, focus_handle: &FocusHandle) {
7116 if self.focused_text_input == Some(focus_handle.id) && focus_handle.is_focused(self) {
7117 self.platform_window
7118 .text_input_state_changed(TextInputStateChange::SelectionChanged);
7119 }
7120 }
7121
7122 fn apply_text_input_configuration(&mut self, cx: &mut App) {
7127 let configuration = match self.platform_window.take_input_handler() {
7128 Some(mut input_handler) => {
7129 let configuration = input_handler.text_input_configuration(self, cx);
7130 self.platform_window.set_input_handler(input_handler);
7131 configuration
7132 }
7133 None => TextInputConfiguration::default(),
7134 };
7135 if self.last_text_input_configuration.as_ref() != Some(&configuration) {
7136 self.platform_window
7137 .set_text_input_configuration(configuration.clone());
7138 self.last_text_input_configuration = Some(configuration);
7139 }
7140 }
7141
7142 pub fn on_mouse_event<Event: MouseEvent>(
7184 &mut self,
7185 mut listener: impl FnMut(&Event, DispatchPhase, &mut Window, &mut App) + 'static,
7186 ) {
7187 self.invalidator.debug_assert_paint();
7188
7189 self.next_frame.mouse_listeners.push(Some(Box::new(
7190 move |event: &dyn Any, phase: DispatchPhase, window: &mut Window, cx: &mut App| {
7191 if let Some(event) = event.downcast_ref() {
7192 listener(event, phase, window, cx)
7193 }
7194 },
7195 )));
7196 }
7197
7198 pub(crate) fn on_pointer_change(
7200 &mut self,
7201 observer: impl Fn(PointerChange, &mut Window, &mut App) + 'static,
7202 ) {
7203 self.invalidator.debug_assert_paint();
7204 Rc::make_mut(&mut self.next_frame.pointer_observers).push(Rc::new(observer));
7205 }
7206
7207 fn notify_pointer_observers(&mut self, cause: PointerChangeCause, cx: &mut App) {
7208 let change = PointerChange {
7209 position: self.pointer_inside_window.then_some(self.mouse_position),
7210 cause,
7211 };
7212 let observers = self.rendered_frame.pointer_observers.clone();
7214 let propagate_event = cx.propagate_event;
7215 let default_prevented = self.default_prevented;
7216 for observer in observers.iter() {
7217 observer(change, self, cx);
7218 cx.propagate_event = propagate_event;
7220 self.default_prevented = default_prevented;
7221 }
7222 }
7223
7224 pub fn on_key_event<Event: KeyEvent>(
7231 &mut self,
7232 listener: impl Fn(&Event, DispatchPhase, &mut Window, &mut App) + 'static,
7233 ) {
7234 self.invalidator.debug_assert_paint();
7235
7236 self.next_frame.dispatch_tree.on_key_event(Rc::new(
7237 move |event: &dyn Any, phase, window: &mut Window, cx: &mut App| {
7238 if let Some(event) = event.downcast_ref::<Event>() {
7239 listener(event, phase, window, cx)
7240 }
7241 },
7242 ));
7243 }
7244
7245 pub fn on_modifiers_changed(
7250 &mut self,
7251 listener: impl Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static,
7252 ) {
7253 self.invalidator.debug_assert_paint();
7254
7255 self.next_frame.dispatch_tree.on_modifiers_changed(Rc::new(
7256 move |event: &ModifiersChangedEvent, window: &mut Window, cx: &mut App| {
7257 listener(event, window, cx)
7258 },
7259 ));
7260 }
7261
7262 pub fn on_focus_in(
7269 &mut self,
7270 handle: &FocusHandle,
7271 cx: &mut App,
7272 mut listener: impl FnMut(&mut Window, &mut App) + 'static,
7273 ) -> Subscription {
7274 let focus_id = handle.id;
7275 let (subscription, activate) =
7276 self.new_focus_listener(Box::new(move |event, window, cx| {
7277 if event.is_focus_in(focus_id) {
7278 listener(window, cx);
7279 }
7280 true
7281 }));
7282 cx.defer(move |_| activate());
7283 subscription
7284 }
7285
7286 pub fn on_focus_out(
7293 &mut self,
7294 handle: &FocusHandle,
7295 cx: &mut App,
7296 mut listener: impl FnMut(FocusOutEvent, &mut Window, &mut App) + 'static,
7297 ) -> Subscription {
7298 let focus_id = handle.id;
7299 let (subscription, activate) =
7300 self.new_focus_listener(Box::new(move |event, window, cx| {
7301 if let Some(blurred_id) = event.previous_focus_path.last().copied()
7302 && event.is_focus_out(focus_id)
7303 {
7304 let event = FocusOutEvent {
7305 blurred: WeakFocusHandle {
7306 id: blurred_id,
7307 handles: Arc::downgrade(&cx.focus_handles),
7308 },
7309 };
7310 listener(event, window, cx)
7311 }
7312 true
7313 }));
7314 cx.defer(move |_| activate());
7315 subscription
7316 }
7317
7318 fn reset_cursor_style(&self, cx: &mut App) {
7319 if self.is_window_hovered() {
7321 let style = self
7322 .rendered_frame
7323 .cursor_style(self)
7324 .unwrap_or(CursorStyle::Arrow);
7325 cx.platform.set_cursor_style(style);
7326 }
7327 }
7328
7329 pub fn dispatch_keystroke(&mut self, keystroke: Keystroke, cx: &mut App) -> bool {
7336 let keystroke = keystroke.with_simulated_ime();
7337 let result = self.dispatch_event(
7338 PlatformInput::KeyDown(KeyDownEvent {
7339 keystroke: keystroke.clone(),
7340 is_held: false,
7341 prefer_character_input: false,
7342 }),
7343 cx,
7344 );
7345 if !result.propagate {
7346 return true;
7347 }
7348
7349 if let Some(input) = keystroke.key_char
7350 && let Some(mut input_handler) = self.platform_window.take_input_handler()
7351 {
7352 input_handler.dispatch_input(&input, self, cx);
7353 self.platform_window.set_input_handler(input_handler);
7354 return true;
7355 }
7356
7357 false
7358 }
7359
7360 pub fn keystroke_text_for(&self, action: &dyn Action) -> String {
7365 self.highest_precedence_binding_for_action(action)
7366 .map(|binding| {
7367 binding
7368 .keystrokes()
7369 .iter()
7370 .map(ToString::to_string)
7371 .collect::<Vec<_>>()
7372 .join(" ")
7373 })
7374 .unwrap_or_else(|| action.name().to_string())
7375 }
7376
7377 #[profiling::function]
7383 pub fn dispatch_event(&mut self, event: PlatformInput, cx: &mut App) -> DispatchEventResult {
7384 #[cfg(feature = "profiler")]
7385 self.window_profiler.begin_input(event.kind_name());
7386 let update_count_before = self.invalidator.update_count();
7387 let old_modality = self.last_input_modality;
7391 self.last_input_modality = match &event {
7392 PlatformInput::KeyDown(_) => InputModality::Keyboard,
7393 PlatformInput::MouseMove(_) | PlatformInput::MouseDown(_) => InputModality::Mouse,
7394 PlatformInput::Touch(_) => InputModality::Touch,
7395 _ => self.last_input_modality,
7396 };
7397 if self.last_input_modality != old_modality {
7398 self.refresh();
7399 }
7400
7401 cx.propagate_event = true;
7403 self.default_prevented = false;
7405
7406 let event = match event {
7407 PlatformInput::MouseMove(mouse_move) => {
7410 self.mouse_position = mouse_move.position;
7411 self.modifiers = mouse_move.modifiers;
7412 PlatformInput::MouseMove(mouse_move)
7413 }
7414 PlatformInput::MouseDown(mouse_down) => {
7415 self.mouse_position = mouse_down.position;
7416 self.modifiers = mouse_down.modifiers;
7417 PlatformInput::MouseDown(mouse_down)
7418 }
7419 PlatformInput::MouseUp(mouse_up) => {
7420 self.mouse_position = mouse_up.position;
7421 self.modifiers = mouse_up.modifiers;
7422 PlatformInput::MouseUp(mouse_up)
7423 }
7424 PlatformInput::MousePressure(mouse_pressure) => {
7425 PlatformInput::MousePressure(mouse_pressure)
7426 }
7427 PlatformInput::MouseExited(mouse_exited) => {
7428 self.modifiers = mouse_exited.modifiers;
7429 PlatformInput::MouseExited(mouse_exited)
7430 }
7431 PlatformInput::ModifiersChanged(modifiers_changed) => {
7432 self.modifiers = modifiers_changed.modifiers;
7433 self.capslock = modifiers_changed.capslock;
7434 PlatformInput::ModifiersChanged(modifiers_changed)
7435 }
7436 PlatformInput::ScrollWheel(scroll_wheel) => {
7437 self.mouse_position = scroll_wheel.position;
7438 self.modifiers = scroll_wheel.modifiers;
7439 PlatformInput::ScrollWheel(scroll_wheel)
7440 }
7441 PlatformInput::Pinch(pinch) => {
7442 self.mouse_position = pinch.position;
7443 self.modifiers = pinch.modifiers;
7444 PlatformInput::Pinch(pinch)
7445 }
7446 PlatformInput::FileDrop(file_drop) => match file_drop {
7449 FileDropEvent::Entered { position, paths } => {
7450 self.mouse_position = position;
7451 let source_window = self.handle.window_id();
7452 if !cx.restore_platform_drag(source_window) && cx.active_drag.is_none() {
7453 cx.active_drag = Some(AnyDrag {
7454 value: Arc::new(paths.clone()),
7455 view: cx.new(|_| paths).into(),
7456 cursor_offset: position,
7457 cursor_style: None,
7458 external_payload_source: None,
7459 });
7460 }
7461 PlatformInput::MouseMove(MouseMoveEvent {
7462 position,
7463 pressed_button: Some(MouseButton::Left),
7464 modifiers: Modifiers::default(),
7465 })
7466 }
7467 FileDropEvent::Pending { position } => {
7468 self.mouse_position = position;
7469 PlatformInput::MouseMove(MouseMoveEvent {
7470 position,
7471 pressed_button: Some(MouseButton::Left),
7472 modifiers: Modifiers::default(),
7473 })
7474 }
7475 FileDropEvent::Submit { position } => {
7476 cx.activate(true);
7477 self.mouse_position = position;
7478 PlatformInput::MouseUp(MouseUpEvent {
7479 button: MouseButton::Left,
7480 position,
7481 modifiers: Modifiers::default(),
7482 click_count: 1,
7483 })
7484 }
7485 FileDropEvent::Exited => {
7486 if !cx.hand_restored_drag_to_platform(self.handle.window_id()) {
7487 cx.active_drag.take();
7488 }
7489 self.refresh();
7490 PlatformInput::FileDrop(FileDropEvent::Exited)
7491 }
7492 FileDropEvent::Ended => {
7493 cx.end_platform_drag(self.handle.window_id());
7494 self.refresh();
7495 PlatformInput::FileDrop(FileDropEvent::Ended)
7496 }
7497 },
7498 PlatformInput::Touch(touch) => PlatformInput::Touch(touch),
7499 PlatformInput::LongPress(long_press) => {
7500 self.mouse_position = if long_press.phase == crate::TouchPhase::Started {
7501 long_press.start_position
7502 } else {
7503 long_press.position
7504 };
7505 if long_press.phase == crate::TouchPhase::Started {
7506 self.long_press_capture = None;
7507 }
7508 PlatformInput::LongPress(long_press)
7509 }
7510 PlatformInput::TouchDrag(touch_drag) => {
7511 self.mouse_position = touch_drag.start_position;
7512 PlatformInput::TouchDrag(touch_drag)
7513 }
7514 PlatformInput::KeyDown(_) | PlatformInput::KeyUp(_) => event,
7515 };
7516
7517 if let Some(any_mouse_event) = event.mouse_event() {
7518 self.dispatch_mouse_event(any_mouse_event, cx);
7519 } else if let Some(any_key_event) = event.keyboard_event() {
7520 self.dispatch_key_event(any_key_event, cx);
7521 } else if let Some(touch_event) = event.touch_event() {
7522 self.dispatch_touch_event(touch_event, cx);
7523 }
7524 if let PlatformInput::LongPress(long_press) = &event {
7525 match long_press.phase {
7526 crate::TouchPhase::Started if !self.default_prevented => {
7527 self.long_press_capture = None;
7528 }
7529 crate::TouchPhase::Ended | crate::TouchPhase::Cancelled => {
7530 self.long_press_capture = None;
7531 }
7532 crate::TouchPhase::Started | crate::TouchPhase::Moved => {}
7533 }
7534 }
7535
7536 self.promote_external_drag_to_platform(&event, cx);
7539
7540 let caused_invalidation = self.invalidator.update_count() > update_count_before;
7541 if caused_invalidation {
7542 self.input_rate_tracker.borrow_mut().record_input();
7543 }
7544 #[cfg(feature = "profiler")]
7545 self.window_profiler.end_input(caused_invalidation);
7546
7547 DispatchEventResult {
7548 propagate: cx.propagate_event,
7549 default_prevented: self.default_prevented,
7550 }
7551 }
7552
7553 fn promote_external_drag_to_platform(&mut self, event: &PlatformInput, cx: &mut App) {
7554 let PlatformInput::MouseMove(mouse_move) = event else {
7555 return;
7556 };
7557 if mouse_move.pressed_button != Some(MouseButton::Left) {
7558 return;
7559 }
7560 if Bounds::new(Point::default(), self.viewport_size).contains(&mouse_move.position) {
7561 return;
7562 }
7563 if !self.platform_window.can_start_external_drag() {
7564 return;
7565 }
7566 let Some(payload_source) = cx
7567 .active_drag
7568 .as_mut()
7569 .and_then(|drag| drag.external_payload_source.take())
7570 else {
7571 return;
7572 };
7573 let Some(payload) = payload_source(self, cx) else {
7574 return;
7575 };
7576 if self.platform_window.start_external_drag(&payload)
7577 && cx.hand_active_drag_to_platform(self.handle.window_id())
7578 {
7579 self.refresh();
7580 }
7581 }
7582
7583 pub fn touch_prediction_enabled(&self) -> bool {
7587 self.touch_prediction_enabled
7588 }
7589
7590 pub fn set_touch_prediction_enabled(&mut self, enabled: bool) {
7594 self.touch_prediction_enabled = enabled;
7595 }
7596
7597 fn dispatch_touch_event(&mut self, event: &TouchEvent, cx: &mut App) {
7601 let mut event = event.clone();
7602 if !self.touch_prediction_enabled {
7603 event.predicted_position = None;
7604 }
7605 let recognized_gestures = self.touch_gestures.handle_event(&event);
7606 if event.phase == crate::TouchPhase::Started
7607 && let Some(touch_drag) = self.touch_gestures.offer_touch_drag(event.id)
7608 {
7609 self.dispatch_recognized_touch_gesture(touch_drag, cx);
7610 }
7611 if event.phase == crate::TouchPhase::Started
7612 && self.touch_gestures.pending_long_press().is_some()
7613 {
7614 self.long_press_capture = None;
7615 }
7616 let mut tapped = false;
7617 for gesture in recognized_gestures {
7618 tapped |= matches!(gesture, RecognizedTouchGesture::Tap { .. });
7619 self.dispatch_recognized_touch_gesture(gesture, cx);
7620 }
7621 if event.phase == crate::TouchPhase::Started {
7622 self.schedule_long_press_timer(cx);
7623 } else if self.touch_gestures.pending_long_press().is_none() {
7624 self.long_press_timer.take();
7625 }
7626 if tapped && self.invalidator.is_dirty() {
7632 self.draw(cx).clear(cx);
7633 }
7634 if self.touch_gestures.has_momentum() {
7635 self.schedule_touch_momentum_tick();
7636 }
7637 }
7638
7639 fn dispatch_recognized_touch_gesture(&mut self, gesture: RecognizedTouchGesture, cx: &mut App) {
7640 match gesture {
7641 RecognizedTouchGesture::Scroll(scroll_wheel) => {
7642 self.mouse_position = scroll_wheel.position;
7643 cx.propagate_event = true;
7644 self.dispatch_mouse_event(&scroll_wheel, cx);
7645 }
7646 RecognizedTouchGesture::Tap { down, up } => {
7647 self.mouse_position = up.position;
7648 cx.propagate_event = true;
7649 self.dispatch_mouse_event(&down, cx);
7650 cx.propagate_event = true;
7651 self.dispatch_mouse_event(&up, cx);
7652 }
7653 RecognizedTouchGesture::TouchDrag(touch_drag) => {
7654 self.mouse_position = touch_drag.start_position;
7655 cx.propagate_event = true;
7656 self.default_prevented = false;
7657 let started = touch_drag.phase == crate::TouchPhase::Started;
7658 self.dispatch_mouse_event(&touch_drag, cx);
7659 if started {
7660 self.touch_gestures
7661 .resolve_touch_drag(self.default_prevented);
7662 }
7663 }
7664 RecognizedTouchGesture::LongPress(long_press) => {
7665 self.mouse_position = if long_press.phase == crate::TouchPhase::Started {
7666 long_press.start_position
7667 } else {
7668 long_press.position
7669 };
7670 cx.propagate_event = true;
7671 self.default_prevented = false;
7672 let started = long_press.phase == crate::TouchPhase::Started;
7673 let ended = matches!(
7674 long_press.phase,
7675 crate::TouchPhase::Ended | crate::TouchPhase::Cancelled
7676 );
7677 self.dispatch_mouse_event(&long_press, cx);
7678 if started {
7679 let claimed = self.default_prevented;
7680 self.touch_gestures.resolve_long_press(claimed);
7681 if !claimed {
7682 self.long_press_capture = None;
7683 }
7684 }
7685 if ended {
7686 self.long_press_capture = None;
7687 }
7688 }
7689 }
7690 }
7691
7692 fn schedule_long_press_timer(&mut self, cx: &mut App) {
7693 self.long_press_timer.take();
7694 let Some((touch_id, duration)) = self.touch_gestures.pending_long_press() else {
7695 return;
7696 };
7697 self.long_press_timer = Some(self.spawn(cx, async move |cx| {
7698 cx.background_executor.timer(duration).await;
7699 cx.update(move |window, cx| {
7700 window.long_press_timer.take();
7701 if let Some(gesture) = window.touch_gestures.offer_long_press(touch_id) {
7702 window.dispatch_recognized_touch_gesture(gesture, cx);
7703 }
7704 })
7705 .log_err();
7706 }));
7707 }
7708
7709 fn schedule_touch_momentum_tick(&mut self) {
7710 self.on_next_frame(|window, cx| {
7711 if let Some(gesture) = window.touch_gestures.tick_momentum() {
7712 window.dispatch_recognized_touch_gesture(gesture, cx);
7713 }
7714 if window.touch_gestures.has_momentum() {
7715 window.schedule_touch_momentum_tick();
7716 }
7717 });
7718 }
7719
7720 pub fn mouse_hit_test(&self) -> &HitTest {
7725 &self.mouse_hit_test
7726 }
7727
7728 fn dispatch_mouse_event(&mut self, event: &dyn Any, cx: &mut App) {
7729 if event.is::<crate::MouseExitEvent>() {
7730 self.pointer_inside_window = false;
7731 } else if event.is::<MouseMoveEvent>()
7732 || event.is::<crate::MouseDownEvent>()
7733 || event.is::<MouseUpEvent>()
7734 {
7735 self.pointer_inside_window =
7736 Bounds::new(Point::default(), self.viewport_size).contains(&self.mouse_position);
7737 }
7738 let hit_test = self.rendered_frame.hit_test(self.mouse_position());
7739 if hit_test != self.mouse_hit_test {
7740 self.mouse_hit_test = hit_test;
7741 }
7742 self.reset_cursor_style(cx);
7744
7745 #[cfg(any(feature = "inspector", debug_assertions))]
7746 if self.is_inspector_picking(cx) {
7747 self.handle_inspector_mouse_event(event, cx);
7748 return;
7750 }
7751
7752 let mut mouse_listeners = mem::take(&mut self.rendered_frame.mouse_listeners);
7753
7754 for listener in &mut mouse_listeners {
7757 let listener = listener.as_mut().unwrap();
7758 listener(event, DispatchPhase::Capture, self, cx);
7759 if !cx.propagate_event {
7760 break;
7761 }
7762 }
7763
7764 if cx.propagate_event {
7766 for listener in mouse_listeners.iter_mut().rev() {
7767 let listener = listener.as_mut().unwrap();
7768 listener(event, DispatchPhase::Bubble, self, cx);
7769 if !cx.propagate_event {
7770 break;
7771 }
7772 }
7773 }
7774
7775 self.rendered_frame.mouse_listeners = mouse_listeners;
7776
7777 if cx.has_active_drag() {
7778 if event.is::<MouseMoveEvent>() {
7779 self.refresh();
7782 } else if event.is::<MouseUpEvent>() {
7783 cx.active_drag = None;
7786 self.refresh();
7787 }
7788 }
7789
7790 if event.is::<MouseUpEvent>() && self.captured_hitbox.is_some() {
7792 self.captured_hitbox = None;
7793 }
7794 if event.is::<MouseMoveEvent>()
7795 || event.is::<crate::MouseExitEvent>()
7796 || event.is::<MouseUpEvent>()
7797 {
7798 self.notify_pointer_observers(PointerChangeCause::Input, cx);
7799 }
7800 }
7801
7802 fn input_preference(&mut self, event: &dyn Any, cx: &mut App) -> InputPreference {
7805 let prefer_character_input = event
7806 .downcast_ref::<KeyDownEvent>()
7807 .is_some_and(|key_down_event| key_down_event.prefer_character_input);
7808 if !prefer_character_input {
7809 return InputPreference::KeyBindings;
7810 }
7811 let Some(mut input_handler) = self.platform_window.take_input_handler() else {
7812 return InputPreference::KeyBindings;
7813 };
7814 let accepts_text_input = input_handler.accepts_text_input(self, cx);
7815 self.platform_window.set_input_handler(input_handler);
7816 if accepts_text_input {
7817 InputPreference::CharacterInput
7818 } else {
7819 InputPreference::KeyBindings
7820 }
7821 }
7822
7823 fn dispatch_key_event(&mut self, event: &dyn Any, cx: &mut App) {
7824 if self.invalidator.is_dirty() {
7825 self.draw(cx).clear(cx);
7826 }
7827
7828 let node_id = self.focus_node_id_in_rendered_frame(self.focus);
7829 let dispatch_path = self.rendered_frame.dispatch_tree.dispatch_path(node_id);
7830
7831 let mut keystroke: Option<Keystroke> = None;
7832
7833 if let Some(event) = event.downcast_ref::<ModifiersChangedEvent>() {
7834 if event.modifiers.number_of_modifiers() == 0
7835 && self.pending_modifier.modifiers.number_of_modifiers() == 1
7836 && !self.pending_modifier.saw_other_input
7837 {
7838 let key = match self.pending_modifier.modifiers {
7839 modifiers if modifiers.shift => Some("shift"),
7840 modifiers if modifiers.control => Some("control"),
7841 modifiers if modifiers.alt => Some("alt"),
7842 modifiers if modifiers.platform => Some("platform"),
7843 modifiers if modifiers.function => Some("function"),
7844 _ => None,
7845 };
7846 if let Some(key) = key {
7847 keystroke = Some(Keystroke {
7848 key: key.to_string(),
7849 key_char: None,
7850 modifiers: Modifiers::default(),
7851 });
7852 }
7853 }
7854
7855 if self.pending_modifier.modifiers.number_of_modifiers() == 0
7856 && event.modifiers.number_of_modifiers() == 1
7857 {
7858 self.pending_modifier.saw_other_input = false
7859 } else if event.modifiers.number_of_modifiers() > 1 {
7860 self.pending_modifier.saw_other_input = true
7861 }
7862 self.pending_modifier.modifiers = event.modifiers
7863 } else if let Some(key_down_event) = event.downcast_ref::<KeyDownEvent>() {
7864 self.pending_modifier.saw_other_input = true;
7865 keystroke = Some(key_down_event.keystroke.clone());
7866 if key_down_event.keystroke.key_char.is_some()
7867 && matches!(
7868 cx.cursor_hide_mode,
7869 CursorHideMode::OnTyping | CursorHideMode::OnTypingAndAction
7870 )
7871 {
7872 cx.platform.hide_cursor_until_mouse_moves();
7873 }
7874 }
7875
7876 let Some(keystroke) = keystroke else {
7877 self.finish_dispatch_key_event(
7878 event,
7879 None,
7880 InputPreference::KeyBindings,
7881 dispatch_path,
7882 self.context_stack(),
7883 cx,
7884 );
7885 return;
7886 };
7887
7888 let input_preference = self.input_preference(event, cx);
7889
7890 cx.propagate_event = true;
7891 self.dispatch_keystroke_interceptors(
7892 &keystroke,
7893 input_preference,
7894 self.context_stack(),
7895 cx,
7896 );
7897 if !cx.propagate_event {
7898 self.finish_dispatch_key_event(
7899 event,
7900 Some(&keystroke),
7901 input_preference,
7902 dispatch_path,
7903 self.context_stack(),
7904 cx,
7905 );
7906 return;
7907 }
7908
7909 let mut currently_pending = self.pending_input.take().unwrap_or_default();
7910 if currently_pending.focus.is_some() && currently_pending.focus != self.focus {
7911 currently_pending = PendingInput::default();
7912 }
7913
7914 let match_result = self.rendered_frame.dispatch_tree.dispatch_key(
7915 currently_pending.keystrokes,
7916 keystroke.clone(),
7917 &dispatch_path,
7918 );
7919
7920 if !match_result.to_replay.is_empty() {
7921 self.replay_pending_input(match_result.to_replay, cx);
7922 cx.propagate_event = true;
7923 }
7924
7925 if !match_result.pending.is_empty() {
7926 let previous_timeout = currently_pending.timeout.take();
7927 currently_pending.keystrokes = match_result.pending;
7928 currently_pending.focus = self.focus;
7929
7930 let text_input_requires_timeout = event
7931 .downcast_ref::<KeyDownEvent>()
7932 .filter(|key_down| key_down.keystroke.key_char.is_some())
7933 .and_then(|_| self.platform_window.take_input_handler())
7934 .map_or(false, |mut input_handler| {
7935 let accepts = input_handler.accepts_text_input(self, cx);
7936 self.platform_window.set_input_handler(input_handler);
7937 accepts
7938 });
7939
7940 let needs_timeout = previous_timeout.is_some()
7941 || match_result.pending_has_binding
7942 || text_input_requires_timeout;
7943 currently_pending.timeout = if needs_timeout {
7944 match previous_timeout {
7945 Some(mut timeout) if timeout.is_paused() => {
7946 timeout.reset_duration(PENDING_INPUT_TIMEOUT);
7947 Some(timeout)
7948 }
7949 previous_timeout => {
7950 drop(previous_timeout);
7951 Some(self.new_pending_input_timeout(PENDING_INPUT_TIMEOUT, cx))
7952 }
7953 }
7954 } else {
7955 None
7956 };
7957 self.pending_input = Some(currently_pending);
7958 self.pending_input_changed(cx);
7959 cx.propagate_event = false;
7960 return;
7961 }
7962
7963 let input_preference = self.input_preference(event, cx);
7965 if input_preference == InputPreference::KeyBindings {
7966 for binding in match_result.bindings {
7967 self.dispatch_action_on_node(node_id, binding.action.as_ref(), cx);
7968 if !cx.propagate_event {
7969 self.dispatch_keystroke_observers(
7970 &keystroke,
7971 input_preference,
7972 Some(binding.action.as_ref()),
7973 match_result.context_stack,
7974 cx,
7975 );
7976 self.pending_input_changed(cx);
7977 return;
7978 }
7979 }
7980 }
7981
7982 self.finish_dispatch_key_event(
7983 event,
7984 Some(&keystroke),
7985 input_preference,
7986 dispatch_path,
7987 match_result.context_stack,
7988 cx,
7989 );
7990 self.pending_input_changed(cx);
7991 }
7992
7993 fn new_pending_input_timeout(&self, duration: Duration, cx: &App) -> PendingInputTimeout {
7994 let (started_at, task) = self.start_pending_input_timeout(duration, cx);
7995 PendingInputTimeout {
7996 duration,
7997 remaining: duration,
7998 state: PendingInputTimeoutState::Running { started_at, task },
7999 }
8000 }
8001
8002 fn start_pending_input_timeout(&self, remaining: Duration, cx: &App) -> (Instant, Task<()>) {
8003 let started_at = cx.background_executor().now();
8004 let task = self.spawn(cx, async move |cx| {
8005 cx.background_executor.timer(remaining).await;
8006 cx.update(move |window, cx| {
8007 let Some(currently_pending) = window
8008 .pending_input
8009 .take()
8010 .filter(|pending| pending.focus == window.focus)
8011 else {
8012 return;
8013 };
8014
8015 let node_id = window.focus_node_id_in_rendered_frame(window.focus);
8016 let dispatch_path = window.rendered_frame.dispatch_tree.dispatch_path(node_id);
8017
8018 let to_replay = window
8019 .rendered_frame
8020 .dispatch_tree
8021 .flush_dispatch(currently_pending.keystrokes, &dispatch_path);
8022
8023 window.pending_input_changed(cx);
8024 window.replay_pending_input(to_replay, cx)
8025 })
8026 .log_err();
8027 });
8028 (started_at, task)
8029 }
8030
8031 fn finish_dispatch_key_event(
8032 &mut self,
8033 event: &dyn Any,
8034 recognized_keystroke: Option<&Keystroke>,
8035 input_preference: InputPreference,
8036 dispatch_path: SmallVec<[DispatchNodeId; 32]>,
8037 context_stack: Vec<KeyContext>,
8038 cx: &mut App,
8039 ) {
8040 self.dispatch_key_down_up_event(event, &dispatch_path, cx);
8041 if !cx.propagate_event {
8042 return;
8043 }
8044
8045 self.dispatch_modifiers_changed_event(event, &dispatch_path, cx);
8046 if !cx.propagate_event {
8047 return;
8048 }
8049
8050 if let Some(keystroke) = recognized_keystroke {
8051 self.dispatch_keystroke_observers(keystroke, input_preference, None, context_stack, cx);
8052 }
8053 }
8054
8055 pub(crate) fn pending_input_changed(&mut self, cx: &mut App) {
8056 self.pending_input_observers
8057 .clone()
8058 .retain(&(), |callback| callback(self, cx));
8059 }
8060
8061 fn defer_pending_input_changed(&self, cx: &mut App) {
8062 let window_handle = self.handle;
8065 cx.defer(move |cx| {
8066 window_handle
8067 .update(cx, |_, window, cx| {
8068 window.pending_input_changed(cx);
8069 })
8070 .ok();
8071 });
8072 }
8073
8074 fn dispatch_key_down_up_event(
8075 &mut self,
8076 event: &dyn Any,
8077 dispatch_path: &SmallVec<[DispatchNodeId; 32]>,
8078 cx: &mut App,
8079 ) {
8080 for node_id in dispatch_path {
8082 let node = self.rendered_frame.dispatch_tree.node(*node_id);
8083
8084 for key_listener in node.key_listeners.clone() {
8085 key_listener(event, DispatchPhase::Capture, self, cx);
8086 if !cx.propagate_event {
8087 return;
8088 }
8089 }
8090 }
8091
8092 for node_id in dispatch_path.iter().rev() {
8094 let node = self.rendered_frame.dispatch_tree.node(*node_id);
8096 for key_listener in node.key_listeners.clone() {
8097 key_listener(event, DispatchPhase::Bubble, self, cx);
8098 if !cx.propagate_event {
8099 return;
8100 }
8101 }
8102 }
8103 }
8104
8105 fn dispatch_modifiers_changed_event(
8106 &mut self,
8107 event: &dyn Any,
8108 dispatch_path: &SmallVec<[DispatchNodeId; 32]>,
8109 cx: &mut App,
8110 ) {
8111 let Some(event) = event.downcast_ref::<ModifiersChangedEvent>() else {
8112 return;
8113 };
8114 for node_id in dispatch_path.iter().rev() {
8115 let node = self.rendered_frame.dispatch_tree.node(*node_id);
8116 for listener in node.modifiers_changed_listeners.clone() {
8117 listener(event, self, cx);
8118 if !cx.propagate_event {
8119 return;
8120 }
8121 }
8122 }
8123 }
8124
8125 fn active_pending_input(&self) -> Option<&PendingInput> {
8128 self.pending_input
8129 .as_ref()
8130 .filter(|pending_input| pending_input.focus == self.focus)
8131 }
8132
8133 pub fn has_pending_keystrokes(&self) -> bool {
8138 self.pending_input().is_some()
8139 }
8140
8141 #[cfg(test)]
8142 pub(crate) fn pending_input_is_none(&self) -> bool {
8143 self.pending_input.is_none()
8144 }
8145
8146 pub(crate) fn clear_pending_keystrokes(&mut self, cx: &mut App) {
8147 if self.pending_input.take().is_some() {
8148 self.defer_pending_input_changed(cx);
8149 }
8150 }
8151
8152 pub fn pending_input(&self) -> Option<PendingInputStatus<'_>> {
8159 self.active_pending_input()
8160 .map(|pending_input| PendingInputStatus {
8161 keystrokes: pending_input.keystrokes.as_slice(),
8162 timeout: pending_input
8163 .timeout
8164 .as_ref()
8165 .map(PendingInputTimeout::status),
8166 })
8167 }
8168
8169 pub fn set_pending_input_timeout_paused<T: 'static>(
8174 &mut self,
8175 owner: &Entity<T>,
8176 paused: bool,
8177 cx: &mut App,
8178 ) -> bool {
8179 let owner_id = owner.entity_id();
8180 if !paused {
8181 return self.resume_pending_input_timeout(owner_id, cx);
8182 }
8183
8184 let timeout = self
8185 .pending_input
8186 .as_ref()
8187 .filter(|pending_input| pending_input.focus == self.focus)
8188 .and_then(|pending_input| pending_input.timeout.as_ref());
8189 let Some(timeout) = timeout else {
8190 return false;
8191 };
8192 if timeout.is_paused() {
8193 return false;
8194 }
8195
8196 let release_subscription = self.observe_release(owner, cx, move |_, window, cx| {
8197 window.resume_pending_input_timeout(owner_id, cx);
8198 });
8199 let now = cx.background_executor().now();
8200 let changed = self
8201 .pending_input
8202 .as_mut()
8203 .filter(|pending_input| pending_input.focus == self.focus)
8204 .and_then(|pending_input| pending_input.timeout.as_mut())
8205 .is_some_and(|timeout| {
8206 timeout.pause(
8207 PendingInputTimeoutPause {
8208 owner_id,
8209 _release_subscription: release_subscription,
8210 },
8211 now,
8212 )
8213 });
8214
8215 if changed {
8216 self.defer_pending_input_changed(cx);
8217 }
8218 changed
8219 }
8220
8221 fn resume_pending_input_timeout(&mut self, owner_id: EntityId, cx: &mut App) -> bool {
8222 let Some(remaining) = self
8223 .pending_input
8224 .as_ref()
8225 .and_then(|pending_input| pending_input.timeout.as_ref())
8226 .filter(|timeout| timeout.pause_owner_id() == Some(owner_id))
8227 .map(|timeout| timeout.remaining)
8228 else {
8229 return false;
8230 };
8231
8232 let (started_at, task) = self.start_pending_input_timeout(remaining, cx);
8233 let changed = self
8234 .pending_input
8235 .as_mut()
8236 .and_then(|pending_input| pending_input.timeout.as_mut())
8237 .is_some_and(|timeout| timeout.resume(owner_id, started_at, task));
8238
8239 if changed {
8240 self.defer_pending_input_changed(cx);
8241 }
8242 changed
8243 }
8244
8245 pub fn pending_input_keystrokes(&self) -> Option<&[Keystroke]> {
8247 self.pending_input()
8248 .map(|pending_input| pending_input.keystrokes())
8249 }
8250
8251 fn replay_pending_input(&mut self, replays: SmallVec<[Replay; 1]>, cx: &mut App) {
8252 let node_id = self.focus_node_id_in_rendered_frame(self.focus);
8253 let dispatch_path = self.rendered_frame.dispatch_tree.dispatch_path(node_id);
8254
8255 'replay: for replay in replays {
8256 let event = KeyDownEvent {
8257 keystroke: replay.keystroke.clone(),
8258 is_held: false,
8259 prefer_character_input: true,
8260 };
8261
8262 cx.propagate_event = true;
8263 for binding in replay.bindings {
8264 self.dispatch_action_on_node(node_id, binding.action.as_ref(), cx);
8265 if !cx.propagate_event {
8266 self.dispatch_keystroke_observers(
8267 &replay.keystroke,
8268 InputPreference::KeyBindings,
8269 Some(binding.action.as_ref()),
8270 Vec::default(),
8271 cx,
8272 );
8273 continue 'replay;
8274 }
8275 }
8276
8277 self.dispatch_key_down_up_event(&event, &dispatch_path, cx);
8278 if !cx.propagate_event {
8279 continue 'replay;
8280 }
8281 if let Some(input) = replay.keystroke.key_char.as_ref().cloned()
8282 && let Some(mut input_handler) = self.platform_window.take_input_handler()
8283 {
8284 input_handler.dispatch_input(&input, self, cx);
8285 self.platform_window.set_input_handler(input_handler)
8286 }
8287 }
8288 }
8289
8290 fn focus_node_id_in_rendered_frame(&self, focus_id: Option<FocusId>) -> DispatchNodeId {
8291 focus_id
8292 .and_then(|focus_id| {
8293 self.rendered_frame
8294 .dispatch_tree
8295 .focusable_node_id(focus_id)
8296 })
8297 .unwrap_or_else(|| self.rendered_frame.dispatch_tree.root_node_id())
8298 }
8299
8300 fn dispatch_action_on_node(
8301 &mut self,
8302 node_id: DispatchNodeId,
8303 action: &dyn Action,
8304 cx: &mut App,
8305 ) {
8306 self.dispatch_action_on_node_inner(node_id, action, cx);
8307
8308 if !cx.propagate_event
8309 && cx.cursor_hide_mode == CursorHideMode::OnTypingAndAction
8310 && self.last_input_was_keyboard()
8311 {
8312 cx.platform.hide_cursor_until_mouse_moves();
8313 }
8314 }
8315
8316 fn dispatch_action_on_node_inner(
8317 &mut self,
8318 node_id: DispatchNodeId,
8319 action: &dyn Action,
8320 cx: &mut App,
8321 ) {
8322 let dispatch_path = self.rendered_frame.dispatch_tree.dispatch_path(node_id);
8323
8324 cx.propagate_event = true;
8326 if let Some(mut global_listeners) = cx
8327 .global_action_listeners
8328 .remove(&action.as_any().type_id())
8329 {
8330 for listener in &global_listeners {
8331 #[cfg(feature = "profiler")]
8332 self.window_profiler.begin_action_handler(action, cx);
8333 listener(action.as_any(), DispatchPhase::Capture, cx);
8334 #[cfg(feature = "profiler")]
8335 self.window_profiler.end_action_handler();
8336 if !cx.propagate_event {
8337 break;
8338 }
8339 }
8340
8341 global_listeners.extend(
8342 cx.global_action_listeners
8343 .remove(&action.as_any().type_id())
8344 .unwrap_or_default(),
8345 );
8346
8347 cx.global_action_listeners
8348 .insert(action.as_any().type_id(), global_listeners);
8349 }
8350
8351 if !cx.propagate_event {
8352 return;
8353 }
8354
8355 for node_id in &dispatch_path {
8357 let node = self.rendered_frame.dispatch_tree.node(*node_id);
8358 for DispatchActionListener {
8359 action_type,
8360 listener,
8361 } in node.action_listeners.clone()
8362 {
8363 let any_action = action.as_any();
8364 if action_type == any_action.type_id() {
8365 #[cfg(feature = "profiler")]
8366 self.window_profiler.begin_action_handler(action, cx);
8367 listener(any_action, DispatchPhase::Capture, self, cx);
8368 #[cfg(feature = "profiler")]
8369 self.window_profiler.end_action_handler();
8370
8371 if !cx.propagate_event {
8372 return;
8373 }
8374 }
8375 }
8376 }
8377
8378 for node_id in dispatch_path.iter().rev() {
8380 let node = self.rendered_frame.dispatch_tree.node(*node_id);
8381 for DispatchActionListener {
8382 action_type,
8383 listener,
8384 } in node.action_listeners.clone()
8385 {
8386 let any_action = action.as_any();
8387 if action_type == any_action.type_id() {
8388 cx.propagate_event = false; #[cfg(feature = "profiler")]
8390 self.window_profiler.begin_action_handler(action, cx);
8391 listener(any_action, DispatchPhase::Bubble, self, cx);
8392 #[cfg(feature = "profiler")]
8393 self.window_profiler.end_action_handler();
8394
8395 if !cx.propagate_event {
8396 return;
8397 }
8398 }
8399 }
8400 }
8401
8402 if let Some(mut global_listeners) = cx
8404 .global_action_listeners
8405 .remove(&action.as_any().type_id())
8406 {
8407 for listener in global_listeners.iter().rev() {
8408 cx.propagate_event = false; #[cfg(feature = "profiler")]
8411 self.window_profiler.begin_action_handler(action, cx);
8412 listener(action.as_any(), DispatchPhase::Bubble, cx);
8413 #[cfg(feature = "profiler")]
8414 self.window_profiler.end_action_handler();
8415 if !cx.propagate_event {
8416 break;
8417 }
8418 }
8419
8420 global_listeners.extend(
8421 cx.global_action_listeners
8422 .remove(&action.as_any().type_id())
8423 .unwrap_or_default(),
8424 );
8425
8426 cx.global_action_listeners
8427 .insert(action.as_any().type_id(), global_listeners);
8428 }
8429 }
8430
8431 pub fn observe_global<G: Global>(
8436 &mut self,
8437 cx: &mut App,
8438 f: impl Fn(&mut Window, &mut App) + 'static,
8439 ) -> Subscription {
8440 let window_handle = self.handle;
8441 let (subscription, activate) = cx.global_observers.insert(
8442 TypeId::of::<G>(),
8443 Box::new(move |cx| {
8444 window_handle
8445 .update(cx, |_, window, cx| f(window, cx))
8446 .is_ok()
8447 }),
8448 );
8449 cx.defer(move |_| activate());
8450 subscription
8451 }
8452
8453 pub fn activate(&self) {
8458 self.platform_window.activate(None);
8459 }
8460
8461 pub fn activate_with_token(&self, token: &str) -> bool {
8469 !token.is_empty() && self.platform_window.activate(Some(token))
8470 }
8471
8472 pub fn request_attention(&self) {
8474 self.platform_window.request_attention();
8475 }
8476
8477 pub fn minimize_window(&self) {
8479 self.platform_window.minimize();
8480 }
8481
8482 pub fn set_visible(&self, visible: bool) {
8487 self.platform_window.set_visible(visible);
8488 }
8489
8490 pub fn toggle_fullscreen(&self) {
8492 self.platform_window.toggle_fullscreen();
8493 }
8494
8495 pub fn toggle_simple_fullscreen(&self) {
8500 self.platform_window.toggle_simple_fullscreen();
8501 }
8502
8503 pub fn invalidate_character_coordinates(&self) {
8507 self.on_next_frame(|window, cx| {
8508 if let Some(mut input_handler) = window.platform_window.take_input_handler() {
8509 if let Some(bounds) = input_handler.selected_bounds(window, cx) {
8510 window.platform_window.update_ime_position(bounds);
8511 }
8512 window.platform_window.set_input_handler(input_handler);
8513 }
8514 });
8515 }
8516
8517 pub fn prompt<T>(
8523 &mut self,
8524 level: PromptLevel,
8525 message: &str,
8526 detail: Option<&str>,
8527 answers: &[T],
8528 cx: &mut App,
8529 ) -> oneshot::Receiver<usize>
8530 where
8531 T: Clone + Into<PromptButton>,
8532 {
8533 let prompt_builder = cx.prompt_builder.take();
8534 let Some(prompt_builder) = prompt_builder else {
8535 unreachable!("Re-entrant window prompting is not supported by GPUI");
8536 };
8537
8538 let answers = answers
8539 .iter()
8540 .map(|answer| answer.clone().into())
8541 .collect::<Vec<_>>();
8542
8543 let receiver = match &prompt_builder {
8544 PromptBuilder::Default => self
8545 .platform_window
8546 .prompt(level, message, detail, &answers)
8547 .unwrap_or_else(|| {
8548 self.build_custom_prompt(&prompt_builder, level, message, detail, &answers, cx)
8549 }),
8550 PromptBuilder::Custom(_) => {
8551 self.build_custom_prompt(&prompt_builder, level, message, detail, &answers, cx)
8552 }
8553 };
8554
8555 cx.prompt_builder = Some(prompt_builder);
8556
8557 receiver
8558 }
8559
8560 fn build_custom_prompt(
8561 &mut self,
8562 prompt_builder: &PromptBuilder,
8563 level: PromptLevel,
8564 message: &str,
8565 detail: Option<&str>,
8566 answers: &[PromptButton],
8567 cx: &mut App,
8568 ) -> oneshot::Receiver<usize> {
8569 let (sender, receiver) = oneshot::channel();
8570 let handle = PromptHandle::new(sender);
8571 let handle = (prompt_builder)(level, message, detail, answers, handle, self, cx);
8572 self.prompt = Some(handle);
8573 self.refresh();
8574 receiver
8575 }
8576
8577 pub fn has_active_prompt(&self) -> bool {
8582 self.prompt.is_some()
8583 }
8584
8585 pub fn context_stack(&self) -> Vec<KeyContext> {
8589 let node_id = self.focus_node_id_in_rendered_frame(self.focus);
8590 let dispatch_tree = &self.rendered_frame.dispatch_tree;
8591 dispatch_tree
8592 .dispatch_path(node_id)
8593 .iter()
8594 .filter_map(move |&node_id| dispatch_tree.node(node_id).context.clone())
8595 .collect()
8596 }
8597
8598 pub fn available_actions(&self, cx: &App) -> Vec<Box<dyn Action>> {
8602 let node_id = self.focus_node_id_in_rendered_frame(self.focus);
8603 let mut actions = self.rendered_frame.dispatch_tree.available_actions(node_id);
8604 for action_type in cx.global_action_listeners.keys() {
8605 if let Err(ix) = actions.binary_search_by_key(action_type, |a| a.as_any().type_id()) {
8606 let action = cx.actions.build_action_type(action_type).ok();
8607 if let Some(action) = action {
8608 actions.insert(ix, action);
8609 }
8610 }
8611 }
8612 actions
8613 }
8614
8615 pub fn bindings_for_action(&self, action: &dyn Action) -> Vec<KeyBinding> {
8620 self.rendered_frame
8621 .dispatch_tree
8622 .bindings_for_action(action, &self.context_stack())
8623 }
8624
8625 pub fn highest_precedence_binding_for_action(&self, action: &dyn Action) -> Option<KeyBinding> {
8629 self.rendered_frame
8630 .dispatch_tree
8631 .highest_precedence_binding_for_action(action, &self.context_stack())
8632 }
8633
8634 pub fn bindings_for_action_in_context(
8636 &self,
8637 action: &dyn Action,
8638 context: KeyContext,
8639 ) -> Vec<KeyBinding> {
8640 let dispatch_tree = &self.rendered_frame.dispatch_tree;
8641 dispatch_tree.bindings_for_action(action, &[context])
8642 }
8643
8644 pub fn highest_precedence_binding_for_action_in_context(
8647 &self,
8648 action: &dyn Action,
8649 context: KeyContext,
8650 ) -> Option<KeyBinding> {
8651 let dispatch_tree = &self.rendered_frame.dispatch_tree;
8652 dispatch_tree.highest_precedence_binding_for_action(action, &[context])
8653 }
8654
8655 pub fn bindings_for_action_in(
8659 &self,
8660 action: &dyn Action,
8661 focus_handle: &FocusHandle,
8662 ) -> Vec<KeyBinding> {
8663 let dispatch_tree = &self.rendered_frame.dispatch_tree;
8664 let Some(context_stack) = self.context_stack_for_focus_handle(focus_handle) else {
8665 return vec![];
8666 };
8667 dispatch_tree.bindings_for_action(action, &context_stack)
8668 }
8669
8670 pub fn highest_precedence_binding_for_action_in(
8674 &self,
8675 action: &dyn Action,
8676 focus_handle: &FocusHandle,
8677 ) -> Option<KeyBinding> {
8678 let dispatch_tree = &self.rendered_frame.dispatch_tree;
8679 let context_stack = self.context_stack_for_focus_handle(focus_handle)?;
8680 dispatch_tree.highest_precedence_binding_for_action(action, &context_stack)
8681 }
8682
8683 pub fn possible_bindings_for_input(&self, input: &[Keystroke]) -> Vec<KeyBinding> {
8687 self.rendered_frame
8688 .dispatch_tree
8689 .possible_next_bindings_for_input(input, &self.context_stack())
8690 }
8691
8692 fn context_stack_for_focus_handle(
8693 &self,
8694 focus_handle: &FocusHandle,
8695 ) -> Option<Vec<KeyContext>> {
8696 let dispatch_tree = &self.rendered_frame.dispatch_tree;
8697 let node_id = dispatch_tree.focusable_node_id(focus_handle.id)?;
8698 let context_stack: Vec<_> = dispatch_tree
8699 .dispatch_path(node_id)
8700 .into_iter()
8701 .filter_map(|node_id| dispatch_tree.node(node_id).context.clone())
8702 .collect();
8703 Some(context_stack)
8704 }
8705
8706 pub fn listener_for<T: 'static, E>(
8712 &self,
8713 view: &Entity<T>,
8714 f: impl Fn(&mut T, &E, &mut Window, &mut Context<T>) + 'static,
8715 ) -> impl Fn(&E, &mut Window, &mut App) + 'static {
8716 let view = view.downgrade();
8717 move |e: &E, window: &mut Window, cx: &mut App| {
8718 view.update(cx, |view, cx| f(view, e, window, cx)).ok();
8719 }
8720 }
8721
8722 pub fn handler_for<E: 'static, Callback: Fn(&mut E, &mut Window, &mut Context<E>) + 'static>(
8727 &self,
8728 entity: &Entity<E>,
8729 f: Callback,
8730 ) -> impl Fn(&mut Window, &mut App) + 'static {
8731 let entity = entity.downgrade();
8732 move |window: &mut Window, cx: &mut App| {
8733 entity.update(cx, |entity, cx| f(entity, window, cx)).ok();
8734 }
8735 }
8736
8737 pub fn on_window_should_close(
8743 &self,
8744 cx: &App,
8745 f: impl Fn(&mut Window, &mut App) -> bool + 'static,
8746 ) {
8747 let mut cx = self.to_async(cx);
8748 self.platform_window.on_should_close(Box::new(move || {
8749 cx.update(|window, cx| f(window, cx)).unwrap_or(true)
8750 }))
8751 }
8752
8753 pub fn on_action(
8762 &mut self,
8763 action_type: TypeId,
8764 listener: impl Fn(&dyn Any, DispatchPhase, &mut Window, &mut App) + 'static,
8765 ) {
8766 self.invalidator.debug_assert_paint();
8767
8768 self.next_frame
8769 .dispatch_tree
8770 .on_action(action_type, Rc::new(listener));
8771 }
8772
8773 pub fn on_action_when(
8778 &mut self,
8779 condition: bool,
8780 action_type: TypeId,
8781 listener: impl Fn(&dyn Any, DispatchPhase, &mut Window, &mut App) + 'static,
8782 ) {
8783 self.invalidator.debug_assert_paint();
8784
8785 if condition {
8786 self.next_frame
8787 .dispatch_tree
8788 .on_action(action_type, Rc::new(listener));
8789 }
8790 }
8791
8792 pub fn gpu_specs(&self) -> Option<GpuSpecs> {
8796 self.platform_window.gpu_specs()
8797 }
8798
8799 #[cfg(any(
8804 target_os = "linux",
8805 target_os = "freebsd",
8806 target_os = "windows",
8807 target_os = "macos"
8808 ))]
8809 pub fn gpu_context(&self) -> Option<Box<dyn std::any::Any>> {
8810 self.platform_window.gpu_context()
8811 }
8812
8813 #[cfg(any(
8818 target_family = "wasm",
8819 target_os = "linux",
8820 target_os = "freebsd",
8821 target_os = "macos"
8822 ))]
8823 pub fn gpu_context_info(&self) -> Option<Box<dyn std::any::Any>> {
8824 self.platform_window.gpu_context_info()
8825 }
8826
8827 #[cfg(any(
8833 target_os = "linux",
8834 target_os = "freebsd",
8835 target_os = "windows",
8836 target_os = "macos"
8837 ))]
8838 pub fn gpu_device_lost(&self) -> Option<bool> {
8839 self.platform_window.gpu_device_lost()
8840 }
8841
8842 pub fn titlebar_double_click(&self) {
8846 self.platform_window
8847 .titlebar_double_click(self.is_resizable, self.is_minimizable);
8848 }
8849
8850 pub fn window_title(&self) -> String {
8854 self.platform_window.get_title()
8855 }
8856
8857 pub fn tabbed_windows(&self) -> Option<Vec<SystemWindowTab>> {
8860 self.platform_window.tabbed_windows()
8861 }
8862
8863 pub fn tab_bar_visible(&self) -> bool {
8866 self.platform_window.tab_bar_visible()
8867 }
8868
8869 pub fn merge_all_windows(&self) {
8872 self.platform_window.merge_all_windows()
8873 }
8874
8875 pub fn move_tab_to_new_window(&self) {
8878 self.platform_window.move_tab_to_new_window()
8879 }
8880
8881 pub fn toggle_window_tab_overview(&self) {
8884 self.platform_window.toggle_window_tab_overview()
8885 }
8886
8887 pub fn set_tabbing_identifier(&self, tabbing_identifier: Option<String>) {
8890 self.platform_window
8891 .set_tabbing_identifier(tabbing_identifier)
8892 }
8893
8894 pub fn play_system_bell(&self) {
8897 self.platform_window.play_system_bell()
8898 }
8899
8900 pub fn is_a11y_enabled(&self) -> bool {
8907 self.a11y.is_enabled()
8908 }
8909
8910 pub fn is_a11y_active(&self) -> bool {
8921 self.a11y.is_active()
8922 }
8923
8924 pub fn set_a11y_forced(&mut self, forced: bool) {
8930 self.a11y.set_forced(forced);
8931 self.refresh();
8932 }
8933
8934 pub fn debug_a11y_tree_json(&self) -> Option<String> {
8936 self.a11y.debug_tree_json()
8937 }
8938
8939 pub fn a11y_tree(&self) -> Option<&accesskit::TreeUpdate> {
8944 self.a11y.last_tree_update()
8945 }
8946
8947 pub fn a11y_node_bounds(&self, node: accesskit::NodeId) -> Option<Bounds<Pixels>> {
8949 self.a11y.last_node_bounds(node)
8950 }
8951
8952 pub fn a11y_frame_number(&self) -> u64 {
8955 self.a11y.frame_number()
8956 }
8957
8958 pub fn on_a11y_action(
8964 &mut self,
8965 node_id: accesskit::NodeId,
8966 action: accesskit::Action,
8967 listener: impl FnMut(Option<&accesskit::ActionData>, &mut Window, &mut App) + 'static,
8968 ) {
8969 self.a11y
8970 .action_listeners
8971 .entry(node_id)
8972 .or_default()
8973 .push((action, Box::new(listener)));
8974 }
8975
8976 #[cfg(not(target_family = "wasm"))]
8981 pub fn perform_a11y_action(
8982 &mut self,
8983 target: accesskit::NodeId,
8984 action: accesskit::Action,
8985 data: Option<accesskit::ActionData>,
8986 cx: &mut App,
8987 ) {
8988 self.handle_a11y_action(
8989 accesskit::ActionRequest {
8990 action,
8991 target_tree: accesskit::TreeId::ROOT,
8992 target_node: target,
8993 data,
8994 },
8995 cx,
8996 );
8997 }
8998
8999 #[cfg(not(target_family = "wasm"))]
9000 pub(crate) fn handle_a11y_action(&mut self, request: accesskit::ActionRequest, cx: &mut App) {
9001 if let Some(mut listeners) = self.a11y.action_listeners.remove(&request.target_node) {
9004 let extra_data = request.data.as_ref();
9005 let mut matched = false;
9006 for (action, listener) in &mut listeners {
9007 if *action == request.action {
9008 listener(extra_data, self, cx);
9009 matched = true;
9010 }
9011 }
9012 self.a11y
9013 .action_listeners
9014 .insert(request.target_node, listeners);
9015 if matched {
9016 return;
9017 }
9018 }
9019
9020 match request.action {
9022 accesskit::Action::Click => {
9023 if let Some(bounds) = self.a11y.node_bounds.get(&request.target_node).copied() {
9024 let center = bounds.center();
9025 let mouse_down = PlatformInput::MouseDown(crate::MouseDownEvent {
9026 button: MouseButton::Left,
9027 position: center,
9028 modifiers: Modifiers::default(),
9029 click_count: 1,
9030 first_mouse: false,
9031 });
9032 let mouse_up = PlatformInput::MouseUp(MouseUpEvent {
9033 button: MouseButton::Left,
9034 position: center,
9035 modifiers: Modifiers::default(),
9036 click_count: 1,
9037 });
9038 self.dispatch_event(mouse_down, cx);
9039 self.dispatch_event(mouse_up, cx);
9040 }
9041 }
9042 accesskit::Action::Focus => {
9043 if let Some(focus_id) = self.a11y.focus_ids.get(&request.target_node).copied()
9044 && let Some(handle) = FocusHandle::for_id(focus_id, &cx.focus_handles)
9045 {
9046 self.focus(&handle, cx);
9047 }
9048 }
9049 accesskit::Action::Blur => {
9050 self.blur(cx);
9051 }
9052 _ => {
9053 log::debug!(
9054 "Unhandled a11y action: {:?} on {:?}",
9055 request.action,
9056 request.target_node
9057 );
9058 }
9059 }
9060 }
9061
9062 #[cfg(any(feature = "inspector", debug_assertions))]
9067 pub fn toggle_inspector(&mut self, cx: &mut App) {
9068 self.inspector = match self.inspector {
9069 None => Some(cx.new(|_| Inspector::new())),
9070 Some(_) => {
9071 self.rendered_frame.next_inspector_instance_ids = FxHashMap::default();
9072 self.rendered_frame.inspector_hitboxes = FxHashMap::default();
9073 self.next_frame.next_inspector_instance_ids = FxHashMap::default();
9074 self.next_frame.inspector_hitboxes = FxHashMap::default();
9075 None
9076 }
9077 };
9078 self.refresh();
9079 }
9080
9081 pub fn is_inspector_picking(&self, _cx: &App) -> bool {
9083 #[cfg(any(feature = "inspector", debug_assertions))]
9084 {
9085 if let Some(inspector) = &self.inspector {
9086 return inspector.read(_cx).is_picking();
9087 }
9088 }
9089 false
9090 }
9091
9092 #[cfg(any(feature = "inspector", debug_assertions))]
9097 pub fn with_inspector_state<T: 'static, R>(
9098 &mut self,
9099 inspector_id: Option<&crate::InspectorElementId>,
9100 cx: &mut App,
9101 f: impl FnOnce(&mut Option<T>, &mut Self) -> R,
9102 ) -> Option<R> {
9103 let inspector_id = inspector_id?;
9104 let inspector = self.inspector.as_ref()?;
9105 if inspector.read(cx).active_element_id() != Some(inspector_id) {
9106 return None;
9107 }
9108 let inspector = inspector.clone();
9109 Some(inspector.update(cx, |inspector, _cx| {
9110 inspector.with_active_element_state(self, f)
9111 }))
9112 }
9113
9114 #[cfg(any(feature = "inspector", debug_assertions))]
9115 pub(crate) fn inspector_enabled(&self) -> bool {
9116 self.inspector.is_some()
9117 }
9118
9119 #[cfg(any(feature = "inspector", debug_assertions))]
9120 pub(crate) fn build_inspector_element_id(
9121 &mut self,
9122 path: crate::InspectorElementPath,
9123 ) -> crate::InspectorElementId {
9124 self.invalidator.debug_assert_paint_or_prepaint();
9125 let path = Rc::new(path);
9126 let next_instance_id = self
9127 .next_frame
9128 .next_inspector_instance_ids
9129 .entry(path.clone())
9130 .or_insert(0);
9131 let instance_id = *next_instance_id;
9132 *next_instance_id += 1;
9133 crate::InspectorElementId { path, instance_id }
9134 }
9135
9136 #[cfg(any(feature = "inspector", debug_assertions))]
9137 fn prepaint_inspector(&mut self, inspector_width: Pixels, cx: &mut App) -> Option<AnyElement> {
9138 if let Some(inspector) = self.inspector.take() {
9139 let mut inspector_element = AnyView::from(inspector.clone()).into_any_element();
9140 inspector_element.prepaint_as_root(
9141 point(self.viewport_size.width - inspector_width, px(0.0)),
9142 size(inspector_width, self.viewport_size.height).into(),
9143 self,
9144 cx,
9145 );
9146 self.inspector = Some(inspector);
9147 Some(inspector_element)
9148 } else {
9149 None
9150 }
9151 }
9152
9153 #[cfg(any(feature = "inspector", debug_assertions))]
9154 fn paint_inspector(&mut self, mut inspector_element: Option<AnyElement>, cx: &mut App) {
9155 if let Some(mut inspector_element) = inspector_element {
9156 inspector_element.paint(self, cx);
9157 };
9158 }
9159
9160 #[cfg(any(feature = "inspector", debug_assertions))]
9165 pub fn insert_inspector_hitbox(
9166 &mut self,
9167 hitbox_id: HitboxId,
9168 inspector_id: Option<&crate::InspectorElementId>,
9169 cx: &App,
9170 ) {
9171 self.invalidator.debug_assert_paint_or_prepaint();
9172 if !self.is_inspector_picking(cx) {
9173 return;
9174 }
9175 if let Some(inspector_id) = inspector_id {
9176 self.next_frame
9177 .inspector_hitboxes
9178 .insert(hitbox_id, inspector_id.clone());
9179 }
9180 }
9181
9182 #[cfg(any(feature = "inspector", debug_assertions))]
9183 fn paint_inspector_hitbox(&mut self, cx: &App) {
9184 if let Some(inspector) = self.inspector.as_ref() {
9185 let inspector = inspector.read(cx);
9186 if let Some((hitbox_id, _)) = self.hovered_inspector_hitbox(inspector, &self.next_frame)
9187 && let Some(hitbox) = self
9188 .next_frame
9189 .hitboxes
9190 .iter()
9191 .find(|hitbox| hitbox.id == hitbox_id)
9192 {
9193 self.paint_quad(PaintQuad::new(hitbox.bounds).background(crate::rgba(0x61afef4d)));
9194 }
9195 }
9196 }
9197
9198 #[cfg(any(feature = "inspector", debug_assertions))]
9199 fn handle_inspector_mouse_event(&mut self, event: &dyn Any, cx: &mut App) {
9200 let Some(inspector) = self.inspector.clone() else {
9201 return;
9202 };
9203 if event.downcast_ref::<MouseMoveEvent>().is_some() {
9204 inspector.update(cx, |inspector, _cx| {
9205 if let Some((_, inspector_id)) =
9206 self.hovered_inspector_hitbox(inspector, &self.rendered_frame)
9207 {
9208 inspector.hover(inspector_id, self);
9209 }
9210 });
9211 } else if event.downcast_ref::<crate::MouseDownEvent>().is_some() {
9212 inspector.update(cx, |inspector, _cx| {
9213 if let Some((_, inspector_id)) =
9214 self.hovered_inspector_hitbox(inspector, &self.rendered_frame)
9215 {
9216 inspector.select(inspector_id, self);
9217 }
9218 });
9219 } else if let Some(event) = event.downcast_ref::<crate::ScrollWheelEvent>() {
9220 const SCROLL_LINES: f32 = 3.0;
9222 const SCROLL_PIXELS_PER_LAYER: f32 = 36.0;
9223 let delta_y = event
9224 .delta
9225 .pixel_delta(px(SCROLL_PIXELS_PER_LAYER / SCROLL_LINES))
9226 .y;
9227 if let Some(inspector) = self.inspector.clone() {
9228 inspector.update(cx, |inspector, _cx| {
9229 if let Some(depth) = inspector.pick_depth.as_mut() {
9230 *depth += f32::from(delta_y) / SCROLL_PIXELS_PER_LAYER;
9231 let max_depth = self.mouse_hit_test.entries.len() as f32 - 0.5;
9232 if *depth < 0.0 {
9233 *depth = 0.0;
9234 } else if *depth > max_depth {
9235 *depth = max_depth;
9236 }
9237 if let Some((_, inspector_id)) =
9238 self.hovered_inspector_hitbox(inspector, &self.rendered_frame)
9239 {
9240 inspector.set_active_element_id(inspector_id, self);
9241 }
9242 }
9243 });
9244 }
9245 }
9246 }
9247
9248 #[cfg(any(feature = "inspector", debug_assertions))]
9249 fn hovered_inspector_hitbox(
9250 &self,
9251 inspector: &Inspector,
9252 frame: &Frame,
9253 ) -> Option<(HitboxId, crate::InspectorElementId)> {
9254 if let Some(pick_depth) = inspector.pick_depth {
9255 let depth = (pick_depth as i64).try_into().unwrap_or(0);
9256 let max_skipped = self.mouse_hit_test.entries.len().saturating_sub(1);
9257 let skip_count = (depth as usize).min(max_skipped);
9258 for hitbox_id in self.mouse_hit_test.ordered_ids.iter().skip(skip_count) {
9259 if let Some(inspector_id) = frame.inspector_hitboxes.get(hitbox_id) {
9260 return Some((*hitbox_id, inspector_id.clone()));
9261 }
9262 }
9263 }
9264 None
9265 }
9266
9267 #[cfg(any(test, feature = "test-support"))]
9271 pub fn set_modifiers(&mut self, modifiers: Modifiers) {
9272 self.modifiers = modifiers;
9273 }
9274
9275 #[cfg(any(test, feature = "test-support"))]
9280 pub fn simulate_mouse_move(&mut self, position: Point<Pixels>, cx: &mut App) {
9281 let event = PlatformInput::MouseMove(MouseMoveEvent {
9282 position,
9283 modifiers: self.modifiers,
9284 pressed_button: None,
9285 });
9286 let _ = self.dispatch_event(event, cx);
9287 }
9288
9289 #[inline(never)]
9290 fn take_element_state(
9291 &mut self,
9292 global_id: &GlobalElementId,
9293 state_type: TypeId,
9294 ) -> ((GlobalElementId, TypeId), Option<ElementStateBox>) {
9295 let key = (global_id.clone(), state_type);
9296 self.next_frame.accessed_element_states.push(key.clone());
9297 let state = self
9298 .next_frame
9299 .element_states
9300 .remove(&key)
9301 .or_else(|| self.rendered_frame.element_states.remove(&key));
9302 (key, state)
9303 }
9304
9305 #[inline(never)]
9306 fn insert_element_state(
9307 &mut self,
9308 key: (GlobalElementId, TypeId),
9309 state: ElementStateBox,
9310 ) -> Option<ElementStateBox> {
9311 self.next_frame.element_states.insert(key, state)
9312 }
9313
9314 #[inline(never)]
9315 fn observe_keyed_state<S: 'static>(state: &Entity<S>, current_view: EntityId, cx: &mut App) {
9316 cx.observe(state, move |_, cx| {
9317 cx.notify(current_view);
9318 })
9319 .detach();
9320 }
9321}
9322
9323slotmap::new_key_type! {
9325 pub struct WindowId;
9327}
9328
9329impl WindowId {
9330 pub fn as_u64(&self) -> u64 {
9332 self.0.as_ffi()
9333 }
9334}
9335
9336impl From<u64> for WindowId {
9337 fn from(value: u64) -> Self {
9338 WindowId(slotmap::KeyData::from_ffi(value))
9339 }
9340}
9341
9342#[derive(Deref, DerefMut)]
9373pub struct WindowHandle<V> {
9374 #[deref]
9375 #[deref_mut]
9376 pub(crate) any_handle: AnyWindowHandle,
9377 state_type: PhantomData<fn(V) -> V>,
9378}
9379
9380impl<V> Debug for WindowHandle<V> {
9381 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
9382 f.debug_struct("WindowHandle")
9383 .field("any_handle", &self.any_handle.id.as_u64())
9384 .finish()
9385 }
9386}
9387
9388impl<V: 'static + Render> WindowHandle<V> {
9389 pub fn new(id: WindowId) -> Self {
9395 WindowHandle {
9396 any_handle: AnyWindowHandle {
9397 id,
9398 state_type: TypeId::of::<V>(),
9399 root_entity_type_name: std::any::type_name::<V>(),
9400 },
9401 state_type: PhantomData,
9402 }
9403 }
9404
9405 #[cfg(any(test, feature = "test-support"))]
9411 pub fn root<C>(&self, cx: &mut C) -> Result<Entity<V>>
9412 where
9413 C: AppContext,
9414 {
9415 cx.update_window(self.any_handle, |root_view, _, _| {
9416 root_view
9417 .downcast::<V>()
9418 .map_err(|_| anyhow!("the type of the window's root view has changed"))
9419 })?
9420 }
9421
9422 pub fn update<C, R>(
9431 &self,
9432 cx: &mut C,
9433 update: impl FnOnce(&mut V, &mut Window, &mut Context<V>) -> R,
9434 ) -> Result<R>
9435 where
9436 C: AppContext,
9437 {
9438 cx.update_window(self.any_handle, |root_view, window, cx| {
9439 let view = root_view
9440 .downcast::<V>()
9441 .map_err(|_| anyhow!("the type of the window's root view has changed"))?;
9442
9443 Ok(view.update(cx, |view, cx| update(view, window, cx)))
9444 })?
9445 }
9446
9447 pub fn read<'a>(&self, cx: &'a App) -> Result<&'a V> {
9453 let x = cx
9454 .windows
9455 .get(self.id)
9456 .and_then(|window| {
9457 window
9458 .as_deref()
9459 .and_then(|window| window.root.clone())
9460 .map(|root_view| root_view.downcast::<V>())
9461 })
9462 .context("window not found")?
9463 .map_err(|_| anyhow!("the type of the window's root view has changed"))?;
9464
9465 Ok(x.read(cx))
9466 }
9467
9468 pub fn read_with<C, R>(&self, cx: &C, read_with: impl FnOnce(&V, &App) -> R) -> Result<R>
9474 where
9475 C: AppContext,
9476 {
9477 cx.read_window(self, |root_view, cx| read_with(root_view.read(cx), cx))
9478 }
9479
9480 pub fn entity<C>(&self, cx: &C) -> Result<Entity<V>>
9486 where
9487 C: AppContext,
9488 {
9489 cx.read_window(self, |root_view, _cx| root_view)
9490 }
9491
9492 pub fn is_active(&self, cx: &mut App) -> Option<bool> {
9496 cx.update_window(self.any_handle, |_, window, _| window.is_window_active())
9497 .ok()
9498 }
9499}
9500
9501impl<V> Copy for WindowHandle<V> {}
9502
9503impl<V> Clone for WindowHandle<V> {
9504 fn clone(&self) -> Self {
9505 *self
9506 }
9507}
9508
9509impl<V> PartialEq for WindowHandle<V> {
9510 fn eq(&self, other: &Self) -> bool {
9511 self.any_handle == other.any_handle
9512 }
9513}
9514
9515impl<V> Eq for WindowHandle<V> {}
9516
9517impl<V> Hash for WindowHandle<V> {
9518 fn hash<H: Hasher>(&self, state: &mut H) {
9519 self.any_handle.hash(state);
9520 }
9521}
9522
9523impl<V: 'static> From<WindowHandle<V>> for AnyWindowHandle {
9524 fn from(val: WindowHandle<V>) -> Self {
9525 val.any_handle
9526 }
9527}
9528
9529#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
9534pub struct AnyWindowHandle {
9535 pub(crate) id: WindowId,
9536 state_type: TypeId,
9537 root_entity_type_name: &'static str,
9538}
9539
9540impl AnyWindowHandle {
9541 pub fn window_id(&self) -> WindowId {
9543 self.id
9544 }
9545
9546 pub fn root_entity_type_name(&self) -> &'static str {
9551 self.root_entity_type_name
9552 }
9553
9554 pub fn downcast<T: 'static>(&self) -> Option<WindowHandle<T>> {
9560 if TypeId::of::<T>() == self.state_type {
9561 Some(WindowHandle {
9562 any_handle: *self,
9563 state_type: PhantomData,
9564 })
9565 } else {
9566 None
9567 }
9568 }
9569
9570 pub fn update<C, R>(
9574 self,
9575 cx: &mut C,
9576 update: impl FnOnce(AnyView, &mut Window, &mut App) -> R,
9577 ) -> Result<R>
9578 where
9579 C: AppContext,
9580 {
9581 cx.update_window(self, update)
9582 }
9583
9584 pub fn read<T, C, R>(self, cx: &C, read: impl FnOnce(Entity<T>, &App) -> R) -> Result<R>
9588 where
9589 C: AppContext,
9590 T: 'static,
9591 {
9592 let view = self
9593 .downcast::<T>()
9594 .context("the type of the window's root view has changed")?;
9595
9596 cx.read_window(&view, read)
9597 }
9598}
9599
9600impl HasWindowHandle for Window {
9601 fn window_handle(&self) -> Result<raw_window_handle::WindowHandle<'_>, HandleError> {
9602 self.platform_window.window_handle()
9603 }
9604}
9605
9606impl HasDisplayHandle for Window {
9607 fn display_handle(
9608 &self,
9609 ) -> std::result::Result<raw_window_handle::DisplayHandle<'_>, HandleError> {
9610 self.platform_window.display_handle()
9611 }
9612}
9613
9614#[derive(Clone, Debug, Eq, PartialEq, Hash)]
9633pub enum ElementId {
9634 View(EntityId),
9636 Integer(u64),
9638 Name(SharedString),
9640 Uuid(Uuid),
9642 FocusHandle(FocusId),
9644 NamedInteger(SharedString, u64),
9646 Path(Arc<std::path::Path>),
9648 CodeLocation(core::panic::Location<'static>),
9650 NamedChild(Arc<ElementId>, SharedString),
9652 OpaqueId([u8; 20]),
9654}
9655
9656impl ElementId {
9657 pub fn named_usize(name: impl Into<SharedString>, integer: usize) -> ElementId {
9661 Self::NamedInteger(name.into(), integer as u64)
9662 }
9663}
9664
9665impl Display for ElementId {
9666 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
9667 match self {
9668 ElementId::View(entity_id) => write!(f, "view-{}", entity_id)?,
9669 ElementId::Integer(ix) => write!(f, "{}", ix)?,
9670 ElementId::Name(name) => write!(f, "{}", name)?,
9671 ElementId::FocusHandle(_) => write!(f, "FocusHandle")?,
9672 ElementId::NamedInteger(s, i) => write!(f, "{}-{}", s, i)?,
9673 ElementId::Uuid(uuid) => write!(f, "{}", uuid)?,
9674 ElementId::Path(path) => write!(f, "{}", path.display())?,
9675 ElementId::CodeLocation(location) => write!(f, "{}", location)?,
9676 ElementId::NamedChild(id, name) => write!(f, "{}-{}", id, name)?,
9677 ElementId::OpaqueId(opaque_id) => write!(f, "{:x?}", opaque_id)?,
9678 }
9679
9680 Ok(())
9681 }
9682}
9683
9684impl TryInto<SharedString> for ElementId {
9685 type Error = anyhow::Error;
9686
9687 fn try_into(self) -> anyhow::Result<SharedString> {
9688 if let ElementId::Name(name) = self {
9689 Ok(name)
9690 } else {
9691 anyhow::bail!("element id is not string")
9692 }
9693 }
9694}
9695
9696impl From<u64> for ElementId {
9697 fn from(id: u64) -> Self {
9698 ElementId::Integer(id)
9699 }
9700}
9701
9702impl From<usize> for ElementId {
9703 fn from(id: usize) -> Self {
9704 ElementId::Integer(id as u64)
9705 }
9706}
9707
9708impl From<i32> for ElementId {
9709 fn from(id: i32) -> Self {
9710 Self::Integer(id as u64)
9711 }
9712}
9713
9714impl From<SharedString> for ElementId {
9715 fn from(name: SharedString) -> Self {
9716 ElementId::Name(name)
9717 }
9718}
9719
9720impl From<String> for ElementId {
9721 fn from(name: String) -> Self {
9722 ElementId::Name(name.into())
9723 }
9724}
9725
9726impl From<Arc<str>> for ElementId {
9727 fn from(name: Arc<str>) -> Self {
9728 ElementId::Name(name.into())
9729 }
9730}
9731
9732impl From<Arc<std::path::Path>> for ElementId {
9733 fn from(path: Arc<std::path::Path>) -> Self {
9734 ElementId::Path(path)
9735 }
9736}
9737
9738impl From<&'static str> for ElementId {
9739 fn from(name: &'static str) -> Self {
9740 ElementId::Name(SharedString::new_static(name))
9741 }
9742}
9743
9744impl<'a> From<&'a FocusHandle> for ElementId {
9745 fn from(handle: &'a FocusHandle) -> Self {
9746 ElementId::FocusHandle(handle.id)
9747 }
9748}
9749
9750impl From<(&'static str, EntityId)> for ElementId {
9751 fn from((name, id): (&'static str, EntityId)) -> Self {
9752 ElementId::NamedInteger(SharedString::new_static(name), id.as_u64())
9753 }
9754}
9755
9756impl From<(&'static str, usize)> for ElementId {
9757 fn from((name, id): (&'static str, usize)) -> Self {
9758 ElementId::NamedInteger(SharedString::new_static(name), id as u64)
9759 }
9760}
9761
9762impl From<(SharedString, usize)> for ElementId {
9763 fn from((name, id): (SharedString, usize)) -> Self {
9764 ElementId::NamedInteger(name, id as u64)
9765 }
9766}
9767
9768impl From<(&'static str, u64)> for ElementId {
9769 fn from((name, id): (&'static str, u64)) -> Self {
9770 ElementId::NamedInteger(SharedString::new_static(name), id)
9771 }
9772}
9773
9774impl From<Uuid> for ElementId {
9775 fn from(value: Uuid) -> Self {
9776 Self::Uuid(value)
9777 }
9778}
9779
9780impl From<(&'static str, u32)> for ElementId {
9781 fn from((name, id): (&'static str, u32)) -> Self {
9782 ElementId::NamedInteger(SharedString::new_static(name), u64::from(id))
9783 }
9784}
9785
9786impl<T: Into<SharedString>> From<(ElementId, T)> for ElementId {
9787 fn from((id, name): (ElementId, T)) -> Self {
9788 ElementId::NamedChild(Arc::new(id), name.into())
9789 }
9790}
9791
9792impl From<&'static core::panic::Location<'static>> for ElementId {
9793 fn from(location: &'static core::panic::Location<'static>) -> Self {
9794 ElementId::CodeLocation(*location)
9795 }
9796}
9797
9798impl From<[u8; 20]> for ElementId {
9799 fn from(opaque_id: [u8; 20]) -> Self {
9800 ElementId::OpaqueId(opaque_id)
9801 }
9802}
9803
9804#[derive(Clone, derive_setters::Setters)]
9809#[setters(into, generate = false)]
9810pub struct PaintQuad {
9811 pub bounds: Bounds<Pixels>,
9813 #[setters(generate)]
9815 pub corner_radii: Corners<Pixels>,
9816 pub corner_smoothing: f32,
9818 #[setters(generate)]
9820 pub background: Background,
9821 #[setters(generate)]
9823 pub border_widths: Edges<Pixels>,
9824 #[setters(generate)]
9826 pub border_color: Background,
9827 #[setters(generate)]
9829 pub border_style: BorderStyle,
9830 pub border_dashed_length: f32,
9832 pub border_dashed_gap: f32,
9834}
9835
9836impl PaintQuad {
9837 pub fn new(bounds: impl Into<Bounds<Pixels>>) -> Self {
9839 Self {
9840 bounds: bounds.into(),
9841 corner_radii: (0.).into(),
9842 corner_smoothing: 0.0,
9843 background: transparent_black().into(),
9844 border_widths: (0.).into(),
9845 border_color: transparent_black().into(),
9846 border_style: BorderStyle::default(),
9847 border_dashed_length: crate::scene::DEFAULT_BORDER_DASHED_LENGTH,
9848 border_dashed_gap: crate::scene::DEFAULT_BORDER_DASHED_GAP,
9849 }
9850 }
9851
9852 pub fn corner_smoothing(self, amount: f32) -> Self {
9854 PaintQuad {
9855 corner_smoothing: amount.clamp(0.0, 1.0),
9856 ..self
9857 }
9858 }
9859
9860 pub fn border_dashed_length(self, length_per_border_width: f32) -> Self {
9864 PaintQuad {
9865 border_dashed_length: length_per_border_width.max(0.0),
9866 ..self
9867 }
9868 }
9869
9870 pub fn border_dashed_gap(self, gap_per_border_width: f32) -> Self {
9874 PaintQuad {
9875 border_dashed_gap: gap_per_border_width.max(0.0),
9876 ..self
9877 }
9878 }
9879}
9880
9881#[deprecated(note = "replaced with builder syntax for PaintQuad")]
9886pub fn quad(
9887 bounds: Bounds<Pixels>,
9888 corner_radii: impl Into<Corners<Pixels>>,
9889 background: impl Into<Background>,
9890 border_widths: impl Into<Edges<Pixels>>,
9891 border_color: impl Into<Background>,
9892 border_style: BorderStyle,
9893) -> PaintQuad {
9894 PaintQuad::new(bounds)
9895 .corner_radii(corner_radii)
9896 .background(background)
9897 .border_widths(border_widths)
9898 .border_color(border_color)
9899 .border_style(border_style)
9900}
9901
9902#[deprecated(note = "replaced with builder syntax for PaintQuad")]
9904pub fn fill(bounds: impl Into<Bounds<Pixels>>, background: impl Into<Background>) -> PaintQuad {
9905 PaintQuad::new(bounds).background(background)
9906}
9907
9908#[deprecated(note = "replaced with builder syntax for PaintQuad")]
9910pub fn outline(
9911 bounds: impl Into<Bounds<Pixels>>,
9912 border_color: impl Into<Background>,
9913 border_style: BorderStyle,
9914) -> PaintQuad {
9915 PaintQuad::new(bounds)
9916 .border_widths(1f32)
9917 .border_color(border_color)
9918 .border_style(border_style)
9919}
9920
9921#[inline(never)]
9922fn with_element_arena_erased(callback: &mut dyn FnMut(&mut Arena)) -> Result<(), AccessError> {
9923 if let Some(arena_pointer) = CURRENT_ELEMENT_ARENA.try_with(Cell::get)? {
9924 let arena_cell = unsafe { &*arena_pointer };
9927 callback(&mut arena_cell.borrow_mut());
9928 Ok(())
9929 } else {
9930 ELEMENT_ARENA.try_with(|arena| callback(&mut arena.borrow_mut()))
9931 }
9932}
9933
9934#[cfg(test)]
9935mod pointer_tests;
9936
9937#[cfg(test)]
9938mod tests {
9939 use super::*;
9940 use crate::util::FluentBuilder;
9941 use crate::{
9942 AnyWindowHandle, Bounds, ColorExt, ContentMask, Context, DispatchPhase, DragMoveEvent,
9943 Empty, ExternalDragPayload, ExternalPaths, FileDragPaths, FileDropEvent, FocusHandle, Font,
9944 FontMetrics, ImageSource, InlineLayout, InlineLayoutRequest, InputEvent,
9945 InteractiveElement, IntoElement, KeyDownEvent, Keystroke, LineLayout, LongPressEvent,
9946 MouseButton, MouseDownEvent, MouseMoveEvent, ParentElement, Pixels, PlatformInput,
9947 PlatformTextSystem, Point, PreparedRasterStyle, RasterColorEffect, RasterStyleRequest,
9948 RasterizedGlyph, Render, RequestFrameOptions, ScaledPixels, ShaderBool,
9949 StatefulInteractiveElement, Styled, TestApp, TestAppContext, TestTextSystem,
9950 TextLayoutRequest, TouchDragEvent, TouchEvent, TouchId, TouchPhase, Underline,
9951 UnderlineStyle, Window, WindowAppearance, WindowOptions, canvas, div, hsla, img,
9952 linear_color_stop, linear_gradient, point, px, size,
9953 };
9954 use image::{Frame as ImageFrame, ImageBuffer, Rgba};
9955 use proptest::prelude::*;
9956 use smallvec::smallvec;
9957 use std::{path::PathBuf, sync::Mutex as StdMutex};
9958
9959 #[test]
9960 fn hit_test_preserves_inline_regions_occlusion_and_metadata() {
9961 let bounds = Bounds::new(Point::default(), size(px(100.), px(80.)));
9962 let background = Hitbox {
9963 id: HitboxId(1),
9964 bounds,
9965 content_mask: ContentMask {
9966 bounds,
9967 ..Default::default()
9968 },
9969 behavior: HitboxBehavior::Normal,
9970 tags: vec!["background".into()],
9971 fragments: None,
9972 };
9973 let mut inline = Hitbox {
9974 id: HitboxId(2),
9975 bounds,
9976 content_mask: ContentMask {
9977 bounds: Bounds::new(Point::default(), size(px(80.), px(80.))),
9978 ..Default::default()
9979 },
9980 behavior: HitboxBehavior::Normal,
9981 tags: vec!["inline".into()],
9982 fragments: Some(Arc::from([
9983 Bounds::new(point(px(60.), px(0.)), size(px(40.), px(40.))),
9984 Bounds::new(point(px(0.), px(40.)), size(px(40.), px(40.))),
9985 ])),
9986 };
9987
9988 for behavior in [
9989 HitboxBehavior::Normal,
9990 HitboxBehavior::BlockMouseExceptScroll,
9991 HitboxBehavior::BlockMouse,
9992 ] {
9993 inline.behavior = behavior;
9994
9995 for (position, inside_inline, inside_background) in [
9996 (point(px(70.), px(20.)), true, true),
9997 (point(px(20.), px(60.)), true, true),
9998 (point(px(20.), px(20.)), false, true),
9999 (point(px(90.), px(20.)), false, true),
10000 (point(px(120.), px(20.)), false, false),
10001 ] {
10002 let hit_test = HitTest::new([&inline, &background].into_iter(), position);
10003 let mut hovered = Vec::new();
10004 let mut scrollable = Vec::new();
10005
10006 if inside_inline {
10007 hovered.push(inline.id);
10008 scrollable.push(inline.id);
10009 }
10010
10011 if inside_background && (!inside_inline || behavior == HitboxBehavior::Normal) {
10012 hovered.push(background.id);
10013 }
10014
10015 if inside_background && (!inside_inline || behavior != HitboxBehavior::BlockMouse) {
10016 scrollable.push(background.id);
10017 }
10018
10019 assert_eq!(
10020 hit_test.iter_hovered().copied().collect::<Vec<_>>(),
10021 hovered
10022 );
10023 assert_eq!(
10024 hit_test.iter_scrollable().copied().collect::<Vec<_>>(),
10025 scrollable
10026 );
10027
10028 for (depth, hitbox) in [&inline, &background].into_iter().enumerate() {
10029 let entry = hit_test.entry(&hitbox.id).unwrap();
10030 assert_eq!(entry.depth(), depth);
10031 assert_eq!(entry.tags(), &hitbox.tags);
10032 }
10033 }
10034 }
10035 }
10036
10037 proptest! {
10038 #[test]
10039 fn glyph_origin_quantization_preserves_position_with_bounded_error(
10040 x in -10_000.0f32..10_000.0,
10041 y in -10_000.0f32..10_000.0,
10042 ) {
10043 let (integer, variant) =
10044 quantize_glyph_origin(point(ScaledPixels(x), ScaledPixels(y)));
10045 let reconstructed_x =
10046 integer.x.0 + f32::from(variant.x) / f32::from(SUBPIXEL_VARIANTS_X);
10047 let reconstructed_y =
10048 integer.y.0 + f32::from(variant.y) / f32::from(SUBPIXEL_VARIANTS_Y);
10049 let maximum_error_x = 0.5 / f32::from(SUBPIXEL_VARIANTS_X) + 0.001;
10050 let maximum_error_y = 0.5 / f32::from(SUBPIXEL_VARIANTS_Y) + 0.001;
10051
10052 prop_assert!(variant.x < SUBPIXEL_VARIANTS_X);
10053 prop_assert!(variant.y < SUBPIXEL_VARIANTS_Y);
10054 prop_assert_eq!(variant.y, 0, "vertical glyph variants change baseline snapping");
10055 prop_assert!((reconstructed_x - x).abs() <= maximum_error_x);
10056 prop_assert!((reconstructed_y - y).abs() <= maximum_error_y);
10057
10058 let (color_origin, color_variant) =
10059 quantize_color_glyph_origin(point(ScaledPixels(x), ScaledPixels(y)));
10060 prop_assert_eq!(color_variant, Point::default());
10061 prop_assert!((color_origin.x.0 - x).abs() <= 0.501);
10062 prop_assert!((color_origin.y.0 - y).abs() <= 0.501);
10063 }
10064 }
10065
10066 #[derive(Default)]
10067 struct RasterFormatTextSystem {
10068 rasterized: StdMutex<Vec<RenderGlyphParams>>,
10069 fail_next: AtomicBool,
10070 }
10071
10072 impl PlatformTextSystem for RasterFormatTextSystem {
10073 fn add_fonts(&self, _fonts: Vec<Cow<'static, [u8]>>) -> anyhow::Result<()> {
10074 Ok(())
10075 }
10076
10077 fn all_font_names(&self) -> Vec<String> {
10078 Vec::new()
10079 }
10080
10081 fn font_id(&self, descriptor: &Font) -> anyhow::Result<FontId> {
10082 PlatformTextSystem::font_id(&TestTextSystem, descriptor)
10083 }
10084
10085 fn font_metrics(&self, font_id: FontId) -> FontMetrics {
10086 PlatformTextSystem::font_metrics(&TestTextSystem, font_id)
10087 }
10088
10089 fn typographic_bounds(
10090 &self,
10091 font_id: FontId,
10092 glyph_id: GlyphId,
10093 ) -> anyhow::Result<Bounds<f32>> {
10094 PlatformTextSystem::typographic_bounds(&TestTextSystem, font_id, glyph_id)
10095 }
10096
10097 fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> anyhow::Result<Size<f32>> {
10098 PlatformTextSystem::advance(&TestTextSystem, font_id, glyph_id)
10099 }
10100
10101 fn glyph_for_char(&self, font_id: FontId, character: char) -> Option<GlyphId> {
10102 PlatformTextSystem::glyph_for_char(&TestTextSystem, font_id, character)
10103 }
10104
10105 fn rasterize_glyph(&self, params: &RenderGlyphParams) -> anyhow::Result<RasterizedGlyph> {
10106 self.rasterized.lock().unwrap().push(params.clone());
10107 if self.fail_next.swap(false, SeqCst) {
10108 anyhow::bail!("injected glyph rasterization failure");
10109 }
10110
10111 let (format, pixels) = match params.glyph_id.0 {
10112 1 => (RasterizedGlyphFormat::AlphaMask, vec![0, 255]),
10113 2 => (
10114 RasterizedGlyphFormat::BgraSubpixelMask,
10115 vec![1, 2, 3, 0, 4, 5, 6, 0],
10116 ),
10117 3 | 5 => (
10118 RasterizedGlyphFormat::BgraColor,
10119 vec![10, 20, 30, 128, 40, 50, 60, 255],
10120 ),
10121 node_id => anyhow::bail!("unexpected scripted glyph {node_id}"),
10122 };
10123 let size = size(DevicePixels(2), DevicePixels(1));
10124 Ok(RasterizedGlyph {
10125 bounds: Bounds {
10126 origin: point(DevicePixels(-1), DevicePixels(-2)),
10127 size,
10128 },
10129 size,
10130 format,
10131 pixels,
10132 })
10133 }
10134
10135 fn prepare_raster_style(&self, mut request: RasterStyleRequest) -> PreparedRasterStyle {
10136 if request.requested_mode == GlyphRenderMode::Color {
10137 request.foreground_dependency = match request.glyph_id {
10138 GlyphId(3) => crate::ForegroundDependency::AlphaOnly,
10139 _glyph_id => request.foreground_dependency,
10140 };
10141
10142 PreparedRasterStyle::preblend(request)
10143 } else {
10144 PreparedRasterStyle::independent(request.requested_mode)
10145 }
10146 }
10147
10148 fn layout_text(&self, request: TextLayoutRequest<'_>) -> LineLayout {
10149 PlatformTextSystem::layout_text(&TestTextSystem, request)
10150 }
10151
10152 fn layout_inline(&self, request: InlineLayoutRequest<'_>) -> InlineLayout {
10153 PlatformTextSystem::layout_inline(&TestTextSystem, request)
10154 }
10155 }
10156
10157 struct RasterFormatView;
10158
10159 impl Render for RasterFormatView {
10160 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
10161 let color = hsla(0.6, 0.7, 0.4, 0.8);
10162 let alternate_color = hsla(0.1, 0.6, 0.3, 0.8);
10163 div().size_full().opacity(0.5).child(
10164 canvas(
10165 |_, _, _| (),
10166 move |_, _, window, _| {
10167 for (index, glyph_id) in [GlyphId(1), GlyphId(2)].into_iter().enumerate() {
10168 let offset = px(index as f32 * 5.0);
10169 let mut origin = point(px(5.0) + offset, px(10.0) + offset);
10170
10171 if index == 0 {
10172 origin += point(px(0.13), px(0.245));
10173 }
10174
10175 window
10176 .paint_glyph(origin, FontId(7), glyph_id, px(16.0), color)
10177 .unwrap();
10178 }
10179
10180 let mut index = 0;
10181
10182 for glyph_id in [GlyphId(3), GlyphId(5)] {
10183 for foreground in [color, alternate_color] {
10184 let offset = px(index as f32 * 5.0);
10185 let origin = point(px(15.0) + offset, px(20.0) + offset);
10186
10187 window
10188 .paint_emoji_with_color(
10189 origin,
10190 FontId(7),
10191 glyph_id,
10192 px(16.0),
10193 foreground,
10194 )
10195 .unwrap();
10196 index += 1;
10197 }
10198 }
10199 },
10200 )
10201 .size_full(),
10202 )
10203 }
10204 }
10205
10206 #[test]
10207 fn returned_raster_format_drives_atlas_and_scene_behavior() {
10208 let text_system = Arc::new(RasterFormatTextSystem::default());
10209 let mut app = TestApp::with_text_system(text_system.clone());
10210 let mut test_window = app.open_window(|_, _| RasterFormatView);
10211 test_window.draw();
10212
10213 test_window.update(|_, window, _| {
10214 assert_eq!(window.rendered_primitive_counts(), (0, 2, 0, 4));
10215 let scene = &window.rendered_frame.scene;
10216 let expected_color = hsla(0.6, 0.7, 0.4, 0.8).into();
10217
10218 assert_eq!(scene.monochrome_sprites[0].color, expected_color);
10219 assert_eq!(scene.monochrome_sprites[1].color, expected_color);
10220 assert!(
10221 scene
10222 .polychrome_sprites
10223 .iter()
10224 .all(|sprite| sprite.opacity == 1.0)
10225 );
10226 assert_eq!(
10227 scene.monochrome_sprites[0].bounds.origin,
10228 point(ScaledPixels(9.0), ScaledPixels(18.0))
10229 );
10230 assert_eq!(
10231 scene.monochrome_sprites[0].tile.texture_id.kind,
10232 crate::AtlasTextureKind::Monochrome
10233 );
10234 assert_eq!(
10235 scene.monochrome_sprites[1].tile.texture_id.kind,
10236 crate::AtlasTextureKind::Subpixel
10237 );
10238 assert_eq!(
10239 scene.polychrome_sprites[0].tile.texture_id.kind,
10240 crate::AtlasTextureKind::Polychrome
10241 );
10242 let first_tile = scene.polychrome_sprites[0].tile;
10243 let second_tile = scene.polychrome_sprites[1].tile;
10244 let third_tile = scene.polychrome_sprites[2].tile;
10245 let fourth_tile = scene.polychrome_sprites[3].tile;
10246 assert_eq!(first_tile, second_tile);
10247 assert_ne!(third_tile, fourth_tile);
10248 });
10249
10250 let rasterized = text_system.rasterized.lock().unwrap();
10251 let color_styles = rasterized
10252 .iter()
10253 .filter(|params| params.raster_style.mode == GlyphRenderMode::Color)
10254 .map(|params| params.raster_style.color_effect)
10255 .collect::<Vec<_>>();
10256 assert_eq!(
10257 color_styles,
10258 [
10259 RasterColorEffect::Preblend(crate::Rgba8::new(0, 0, 0, 204)),
10260 RasterColorEffect::Preblend(crate::Rgba8::new(31, 88, 173, 204)),
10261 RasterColorEffect::Preblend(crate::Rgba8::new(122, 86, 31, 204)),
10262 ]
10263 );
10264 }
10265
10266 struct FragmentFailureView;
10267
10268 impl Render for FragmentFailureView {
10269 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
10270 div().size_full().child("x😀")
10271 }
10272 }
10273
10274 #[test]
10275 fn a_failed_glyph_does_not_hide_the_rest_of_its_fragment() {
10276 let text_system = Arc::new(RasterFormatTextSystem {
10277 rasterized: StdMutex::default(),
10278 fail_next: AtomicBool::new(true),
10279 });
10280 let mut app = TestApp::with_text_system(text_system.clone());
10281 let mut test_window = app.open_window(|_, _| FragmentFailureView);
10282 test_window.draw();
10283
10284 test_window.update(|_, window, _| {
10285 assert!(!window.rendered_frame.scene.subpixel_sprites.is_empty());
10286 });
10287
10288 let glyph_ids = text_system
10289 .rasterized
10290 .lock()
10291 .unwrap()
10292 .iter()
10293 .map(|params| params.glyph_id)
10294 .collect::<Vec<_>>();
10295 assert!(
10296 glyph_ids.starts_with(&[GlyphId(1), GlyphId(2)]),
10297 "unexpected rasterization sequence: {glyph_ids:?}"
10298 );
10299 }
10300
10301 #[gpui::test]
10302 async fn test_focus_handle_release(cx: &mut TestAppContext) {
10303 let (handle, survivor) = cx.update(|cx| (cx.focus_handle(), cx.focus_handle()));
10304 let handle_id = handle.id;
10305 let weak_handle = handle.downgrade();
10306 let retained = handle.clone();
10307 drop(handle);
10308
10309 cx.update(|_| {});
10310 cx.read(|cx| {
10311 let handles = cx.focus_handles.read();
10312 assert_eq!(handles.handles.len(), 2);
10313 assert!(handles.dropped_handles.is_empty());
10314 });
10315 assert_eq!(weak_handle.upgrade().as_ref(), Some(&retained));
10316
10317 cx.background_spawn(async move { drop(retained) }).await;
10318 assert_eq!(weak_handle.upgrade(), None);
10319 cx.read(|cx| {
10320 assert_eq!(cx.focus_handles.read().dropped_handles, [handle_id]);
10321 });
10322 cx.update(|_| {});
10323 cx.read(|cx| {
10324 let handles = cx.focus_handles.read();
10325 assert_eq!(handles.handles.keys().collect::<Vec<_>>(), [survivor.id]);
10326 assert!(handles.dropped_handles.is_empty());
10327 });
10328
10329 let replacements = cx.update(|cx| [cx.focus_handle(), cx.focus_handle()]);
10330 assert_eq!(weak_handle.upgrade(), None);
10331 cx.update(|cx| {
10332 drop(replacements);
10333 cx.defer(move |_| drop(survivor));
10334 });
10335 cx.read(|cx| {
10336 let handles = cx.focus_handles.read();
10337 assert_eq!(handles.handles.len(), 0);
10338 assert!(handles.dropped_handles.is_empty());
10339 });
10340 }
10341
10342 #[gpui::test]
10343 fn test_dropped_focus_handle_blurs_only_its_windows(cx: &mut TestAppContext) {
10344 let windows = [
10345 cx.add_window(|_, _| EmptyView),
10346 cx.add_window(|_, _| EmptyView),
10347 cx.add_window(|_, _| EmptyView),
10348 cx.add_window(|_, _| EmptyView),
10349 cx.add_window(|_, _| EmptyView),
10350 ];
10351 let (handle, other_handle, survivor) =
10352 cx.update(|cx| (cx.focus_handle(), cx.focus_handle(), cx.focus_handle()));
10353 for window in &windows[..2] {
10354 window
10355 .update(cx, |_, window, cx| window.focus(&handle, cx))
10356 .expect("window exists");
10357 }
10358 windows[2]
10359 .update(cx, |_, window, cx| window.focus(&other_handle, cx))
10360 .expect("window exists");
10361 windows[3]
10362 .update(cx, |_, window, cx| window.focus(&survivor, cx))
10363 .expect("window exists");
10364
10365 cx.update(|cx| {
10366 for _ in 0..1_000 {
10367 drop(cx.focus_handle());
10368 }
10369 drop((handle, other_handle));
10370 });
10371 for window in &windows[..3] {
10372 window
10373 .update(cx, |_, window, _| assert_eq!(window.focus, None))
10374 .expect("window exists");
10375 }
10376 windows[3]
10377 .update(cx, |_, window, cx| {
10378 assert_eq!(window.focused(cx).as_ref(), Some(&survivor));
10379 assert_eq!(window.focus_generation, 1);
10380 })
10381 .expect("window exists");
10382 windows[4]
10383 .update(cx, |_, window, _| {
10384 assert_eq!(window.focus, None);
10385 assert_eq!(window.focus_generation, 0);
10386 })
10387 .expect("window exists");
10388 }
10389
10390 #[cfg(feature = "profiler")]
10391 #[gpui::test]
10392 fn test_frame_requests_and_skips_are_journaled(cx: &mut TestAppContext) {
10393 use crate::FrameRequestSource;
10394 use crate::profiler::journal::{
10395 self, ForegroundJournalEntry, FrameSkipReason, IntervalBoundary,
10396 };
10397 use scheduler::Instant;
10398
10399 cx.app.borrow_mut().mode = crate::app::GpuiMode::Production;
10400 let window = cx.add_window(|_, _| EmptyView);
10401 let platform_window = cx.test_window(window.into());
10402 platform_window.simulate_frame_request(RequestFrameOptions::default());
10403 let (journal, _guard) = journal::install_test_foreground_journal(64, 4);
10404 let mut collector = journal.collector();
10405
10406 for (signal_at, source) in [
10407 (Some(Instant::now()), FrameRequestSource::NativeCallback),
10408 (Some(Instant::now()), FrameRequestSource::LocalSchedule),
10409 (None, FrameRequestSource::NativeCallback),
10410 ] {
10411 platform_window.simulate_frame_request(RequestFrameOptions {
10412 signal_at,
10413 signal_source: source,
10414 require_presentation: true,
10415 ..Default::default()
10416 });
10417 let entries = collector.collect_unseen().entries;
10418 let signal_index = entries
10419 .iter()
10420 .position(|entry| {
10421 matches!(
10422 entry,
10423 ForegroundJournalEntry::PlatformSignal(signal)
10424 if signal.window_id == window.window_id()
10425 && signal.signal_at == signal_at
10426 && signal.source == source
10427 && signal_at.is_none_or(|at| signal.handled_at >= at)
10428 )
10429 })
10430 .expect("frame request metadata");
10431 let skip_index = entries
10432 .iter()
10433 .position(|entry| {
10434 matches!(
10435 entry,
10436 ForegroundJournalEntry::Boundary(IntervalBoundary::FrameSkipped(skipped))
10437 if skipped.window_id == window.window_id()
10438 && skipped.reason == FrameSkipReason::NoRenderNeeded
10439 )
10440 })
10441 .expect("completed no-render boundary");
10442 assert!(signal_index < skip_index);
10443 }
10444 }
10445
10446 #[cfg(feature = "profiler")]
10447 #[gpui::test]
10448 fn test_frame_demand_survives_throttling(cx: &mut TestAppContext) {
10449 use crate::profiler::journal::{
10450 self, ForegroundJournalEntry, FrameSkipReason, IntervalBoundary,
10451 };
10452 use scheduler::Instant;
10453
10454 cx.app.borrow_mut().mode = crate::app::GpuiMode::Production;
10456 let (journal, _guard) = journal::install_test_foreground_journal(256, 4);
10457 let mut collector = journal.collector();
10458 for initially_dirty in [true, false] {
10459 let window = cx
10461 .app
10462 .borrow_mut()
10463 .open_window(
10464 WindowOptions {
10465 inactive_frame_interval: Some(Duration::from_secs(3600)),
10466 ..Default::default()
10467 },
10468 |_, cx| cx.new(|_| EmptyView),
10469 )
10470 .expect("open inactive test window");
10471 let platform_window = cx.test_window(window.into());
10472 platform_window.simulate_active_status_change(false);
10473 platform_window.simulate_frame_request(RequestFrameOptions::default());
10474 let callback_ran = Rc::new(Cell::new(false));
10475 let draws_before = window
10476 .update(cx, |_, window, _| {
10477 let draws_before = window
10478 .frame_duration_snapshot()
10479 .draw_duration_histogram
10480 .len();
10481 if initially_dirty {
10482 window.refresh();
10483 }
10484 window.on_next_frame({
10485 let callback_ran = callback_ran.clone();
10486 move |window, _| {
10487 callback_ran.set(true);
10488 window.refresh();
10489 }
10490 });
10491 draws_before
10492 })
10493 .expect("schedule throttled frame");
10494 let input_at = Instant::now();
10496 journal::record_input(journal::InputTiming {
10497 kind: "test",
10498 start: input_at,
10499 end: input_at,
10500 caused_invalidation: false,
10501 });
10502 collector.collect_unseen();
10503 platform_window.simulate_frame_request(RequestFrameOptions {
10504 signal_at: Some(Instant::now()),
10505 ..Default::default()
10506 });
10507 window
10508 .update(cx, |_, window, _| {
10509 assert_eq!(
10510 window
10511 .frame_duration_snapshot()
10512 .draw_duration_histogram
10513 .len(),
10514 draws_before,
10515 );
10516 })
10517 .expect("inspect deferred frame");
10518 assert!(!callback_ran.get());
10519 assert!(collector.collect_unseen().entries.iter().any(|entry| {
10520 matches!(
10521 entry,
10522 ForegroundJournalEntry::Boundary(IntervalBoundary::FrameSkipped(skipped))
10523 if skipped.window_id == window.window_id()
10524 && skipped.reason == FrameSkipReason::InactiveFrameRateLimit
10525 )
10526 }));
10527 platform_window.simulate_frame_request(RequestFrameOptions {
10528 signal_at: Some(Instant::now()),
10529 require_presentation: true,
10530 ..Default::default()
10531 });
10532 window
10533 .update(cx, |_, window, _| {
10534 assert_eq!(
10535 window
10536 .frame_duration_snapshot()
10537 .draw_duration_histogram
10538 .len(),
10539 draws_before + 1,
10540 );
10541 })
10542 .expect("inspect presented frame");
10543 assert!(callback_ran.get());
10544 }
10545 }
10546
10547 #[gpui::test]
10551 fn test_window_visibility(cx: &mut TestAppContext) {
10552 use crate::WindowVisibility;
10553
10554 let window = cx.add_window(|_, _| EmptyView);
10555 let observed = Rc::new(RefCell::new(Vec::new()));
10556 let _subscription = window
10557 .update(cx, {
10558 let observed = observed.clone();
10559 move |_, window, _| {
10560 assert_eq!(window.visibility(), WindowVisibility::Visible);
10561 assert!(window.is_visible());
10562 window.observe_window_visibility(move |visibility, window, _| {
10563 assert_eq!(window.visibility(), visibility);
10564 observed.borrow_mut().push(visibility);
10565 })
10566 }
10567 })
10568 .unwrap();
10569 let test_window = cx.test_window(window.into());
10570 let frame_wake_count = test_window.frame_wake_count();
10571
10572 test_window.simulate_visibility_change(WindowVisibility::Hidden);
10573 assert_eq!(*observed.borrow(), [WindowVisibility::Hidden]);
10574 window
10575 .update(cx, |_, window, _| assert!(!window.is_visible()))
10576 .unwrap();
10577
10578 test_window.simulate_visibility_change(WindowVisibility::Hidden);
10580 assert_eq!(observed.borrow().len(), 1);
10581
10582 test_window.simulate_visibility_change(WindowVisibility::Visible);
10583 assert_eq!(
10584 *observed.borrow(),
10585 [WindowVisibility::Hidden, WindowVisibility::Visible]
10586 );
10587 window
10588 .update(cx, |_, window, _| assert!(window.is_visible()))
10589 .unwrap();
10590 assert_eq!(test_window.frame_wake_count(), frame_wake_count);
10591 }
10592
10593 #[gpui::test]
10596 fn test_display_tracking(cx: &mut TestAppContext) {
10597 use crate::{DisplayChanges, DisplayEvent, DisplayId, Subscription};
10598
10599 let sixty_hertz = Duration::from_secs(1) / 60;
10600 let one_hundred_twenty_hertz = Duration::from_secs(1) / 120;
10601 let one_hundred_forty_four_hertz = Duration::from_secs(1) / 144;
10602
10603 let display_events = Rc::new(RefCell::new(Vec::new()));
10604 let _display_subscription = cx.update({
10605 let display_events = display_events.clone();
10606 move |cx| cx.observe_displays(move |event, _| display_events.borrow_mut().push(event))
10607 });
10608
10609 let window = cx.add_window(|window, cx| DisplayTracker {
10610 refresh_intervals: Vec::new(),
10611 _subscription: cx.observe_window_display(
10612 window,
10613 |tracker: &mut DisplayTracker, window, cx| {
10614 tracker.refresh_intervals.push(window.refresh_interval(cx));
10615 },
10616 ),
10617 });
10618 let window_notifications = Rc::new(RefCell::new(Vec::new()));
10619 let _window_subscription = window
10620 .update(cx, {
10621 let window_notifications = window_notifications.clone();
10622 move |_, window, cx| {
10623 assert_eq!(window.refresh_interval(cx), Some(sixty_hertz));
10624 window.observe_window_display(move |window, cx| {
10625 window_notifications
10626 .borrow_mut()
10627 .push((window.display_id, window.refresh_interval(cx)));
10628 })
10629 }
10630 })
10631 .unwrap();
10632 let test_window = cx.test_window(window.into());
10633
10634 cx.test_platform()
10639 .connect_display(DisplayId(2), Some(one_hundred_twenty_hertz));
10640 test_window.simulate_move_to_display(DisplayId(2));
10641 assert_eq!(
10642 window_notifications.take(),
10643 [(Some(DisplayId(2)), Some(one_hundred_twenty_hertz))]
10644 );
10645 assert_eq!(display_events.take(), [DisplayEvent::Added(DisplayId(2))]);
10646 cx.simulate_display_refresh_interval_change(DisplayId(2), Some(one_hundred_twenty_hertz));
10647 assert!(window_notifications.borrow().is_empty());
10648
10649 test_window.simulate_move_to_display(DisplayId(2));
10651 assert!(window_notifications.borrow().is_empty());
10652
10653 cx.simulate_display_refresh_interval_change(
10654 DisplayId(2),
10655 Some(one_hundred_forty_four_hertz),
10656 );
10657 assert_eq!(
10658 window_notifications.take(),
10659 [(Some(DisplayId(2)), Some(one_hundred_forty_four_hertz))]
10660 );
10661
10662 cx.simulate_display_refresh_interval_change(DisplayId(1), Some(one_hundred_twenty_hertz));
10664 cx.simulate_display_removed(DisplayId(1));
10665 assert!(window_notifications.borrow().is_empty());
10666
10667 assert_eq!(
10668 display_events.take(),
10669 [
10670 DisplayEvent::Changed {
10671 id: DisplayId(2),
10672 changes: DisplayChanges::REFRESH_INTERVAL,
10673 },
10674 DisplayEvent::Changed {
10675 id: DisplayId(1),
10676 changes: DisplayChanges::REFRESH_INTERVAL,
10677 },
10678 DisplayEvent::Removed(DisplayId(1)),
10679 ]
10680 );
10681 window
10682 .update(cx, |tracker, _, _| {
10683 assert_eq!(
10684 tracker.refresh_intervals,
10685 [
10686 Some(one_hundred_twenty_hertz),
10687 Some(one_hundred_forty_four_hertz)
10688 ]
10689 );
10690 })
10691 .unwrap();
10692
10693 struct DisplayTracker {
10694 refresh_intervals: Vec<Option<Duration>>,
10695 _subscription: Subscription,
10696 }
10697
10698 impl Render for DisplayTracker {
10699 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
10700 Empty
10701 }
10702 }
10703 }
10704
10705 #[gpui::test]
10706 fn test_fully_visible_bounds_preserve_layout_viewport(cx: &mut TestAppContext) {
10707 struct InsetsProbe(std::rc::Rc<std::cell::Cell<usize>>);
10708
10709 impl Render for InsetsProbe {
10710 fn render(&mut self, window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
10711 self.0.set(self.0.get() + 1);
10712 div().child(format!("{:?}", window.insets().ime.bottom))
10713 }
10714 }
10715
10716 let renders = std::rc::Rc::new(std::cell::Cell::new(0));
10717 let window = cx.add_window({
10718 let renders = renders.clone();
10719 move |_, _| InsetsProbe(renders)
10720 });
10721 let mut platform_window = cx.test_window(window.into());
10722 platform_window.simulate_resize(size(px(400.), px(800.)));
10723 window
10724 .update(cx, |_, window, _| {
10725 assert_eq!(
10726 window.visual_viewport_bounds(),
10727 Bounds::new(Point::default(), size(px(400.), px(800.)))
10728 );
10729 assert_eq!(
10730 window.fully_visible_bounds(),
10731 window.visual_viewport_bounds()
10732 );
10733 })
10734 .unwrap();
10735
10736 platform_window.simulate_frame_request(RequestFrameOptions::default());
10737 let wakes = platform_window.frame_wake_count();
10738 let visual_bounds = Bounds::new(point(px(10.), px(40.)), size(px(380.), px(460.)));
10739 platform_window.simulate_visual_viewport_change(visual_bounds);
10740 assert!(platform_window.frame_wake_count() > wakes);
10741 platform_window.simulate_frame_request(RequestFrameOptions::default());
10742 let wakes = platform_window.frame_wake_count();
10743 let insets = crate::WindowInsets {
10744 safe_area: crate::Edges {
10745 top: px(60.),
10746 right: px(20.),
10747 bottom: px(30.),
10748 left: px(-10.),
10749 },
10750 ime: crate::Edges {
10751 bottom: px(350.),
10752 ..Default::default()
10753 },
10754 };
10755 let renders_before_insets = renders.get();
10756 platform_window.simulate_insets_change(insets.clone());
10757 assert!(platform_window.frame_wake_count() > wakes);
10758 platform_window.simulate_frame_request(RequestFrameOptions::default());
10759 assert!(renders.get() > renders_before_insets);
10760 window
10761 .update(cx, |_, window, cx| {
10762 let focus = cx.focus_handle();
10763 window.focus(&focus, cx);
10764 assert_eq!(window.insets(), insets);
10765 assert_eq!(window.viewport_size(), size(px(400.), px(800.)));
10766 assert_eq!(window.visual_viewport_bounds(), visual_bounds);
10767 assert_eq!(
10768 window.fully_visible_bounds(),
10769 Bounds::new(point(px(10.), px(60.)), size(px(370.), px(390.)))
10770 );
10771 window.request_virtual_keyboard();
10772 window.dismiss_virtual_keyboard();
10773 assert!(focus.is_focused(window));
10774 assert_eq!(window.viewport_size(), size(px(400.), px(800.)));
10775 })
10776 .unwrap();
10777 assert_eq!(platform_window.virtual_keyboard_requests(), 1);
10778 assert_eq!(platform_window.virtual_keyboard_dismissals(), 1);
10779
10780 platform_window.simulate_insets_change(crate::WindowInsets {
10781 safe_area: crate::Edges {
10782 top: px(900.),
10783 left: px(500.),
10784 ..Default::default()
10785 },
10786 ..Default::default()
10787 });
10788 window
10789 .update(cx, |_, window, _| {
10790 assert_eq!(
10791 window.fully_visible_bounds().size,
10792 size(Pixels::ZERO, Pixels::ZERO)
10793 );
10794 })
10795 .unwrap();
10796
10797 use crate::PlatformWindow as _;
10799 platform_window.on_insets_changed(Box::new(|_| {}));
10800 platform_window.simulate_insets_change(crate::WindowInsets::default());
10801 window
10802 .update(cx, |_, window, _| {
10803 assert_eq!(window.insets(), crate::WindowInsets::default());
10804 })
10805 .unwrap();
10806 }
10807
10808 struct EmptyView;
10809
10810 impl Render for EmptyView {
10811 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
10812 div()
10813 }
10814 }
10815
10816 fn shadow_gradient() -> Background {
10817 linear_gradient(
10818 90.,
10819 linear_color_stop(hsla(0., 1., 0.5, 0.8), 0.),
10820 linear_color_stop(hsla(2. / 3., 1., 0.5, 0.6), 1.),
10821 )
10822 }
10823
10824 struct ShadowBackgroundView;
10825
10826 impl Render for ShadowBackgroundView {
10827 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
10828 div()
10829 .m(px(40.))
10830 .w(px(80.))
10831 .h(px(60.))
10832 .rounded(px(8.))
10833 .bg(white())
10834 .opacity(0.5)
10835 .shadow(vec![
10836 BoxShadow::new(px(4.), px(2.), shadow_gradient())
10837 .blur_radius(px(6.))
10838 .spread_radius(px(2.)),
10839 BoxShadow::new(px(-3.), px(1.), hsla(0.3, 0.7, 0.4, 0.4)),
10840 BoxShadow::new(px(0.), px(2.), shadow_gradient())
10841 .blur_radius(px(4.))
10842 .spread_radius(px(3.))
10843 .inset(),
10844 ])
10845 }
10846 }
10847
10848 struct CornerSmoothingSceneView {
10849 image: Arc<RenderImage>,
10850 }
10851
10852 fn assert_smoothing(name: &str, minimum_count: usize, values: impl IntoIterator<Item = f32>) {
10853 let values = values.into_iter().collect::<Vec<_>>();
10854 assert!(values.len() >= minimum_count, "missing {name}");
10855 assert!(
10856 values.iter().all(|&value| value == 1.0),
10857 "{name}: {values:?}"
10858 );
10859 }
10860
10861 impl Render for CornerSmoothingSceneView {
10862 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
10863 let mut image = img(ImageSource::Render(self.image.clone()))
10864 .size(px(24.))
10865 .rounded(px(8.));
10866 image.style().corner_smoothing = Some(1.5);
10867
10868 let mut root = div()
10869 .size(px(80.))
10870 .rounded(px(24.))
10871 .bg(crate::blue())
10872 .border_t(px(2.))
10873 .border_r(px(4.))
10874 .border_b(px(6.))
10875 .border_l(px(8.))
10876 .border_color(crate::white())
10877 .shadow(vec![
10878 BoxShadow::new(px(2.), px(3.), crate::black().opacity(0.5))
10879 .blur_radius(px(5.))
10880 .spread_radius(px(1.)),
10881 BoxShadow::new(px(1.), px(1.), crate::white().opacity(0.4))
10882 .blur_radius(px(3.))
10883 .inset(),
10884 ])
10885 .backdrop_blur(px(4.))
10886 .blur(px(2.))
10887 .child(image);
10888 root.style().corner_smoothing = Some(1.5);
10889 root
10890 }
10891 }
10892
10893 #[gpui::test]
10894 fn shadow_backgrounds_survive_painting_with_geometry_and_opacity(cx: &mut TestAppContext) {
10895 let window = cx.add_window(|_, _| ShadowBackgroundView);
10896 let shadows = window
10897 .update(cx, |_, window, _| {
10898 window.rendered_frame.scene.shadows.clone()
10899 })
10900 .unwrap();
10901
10902 assert_eq!(shadows.len(), 3);
10903 let gradient = shadow_gradient();
10904 let drop_gradient = shadows
10905 .iter()
10906 .find(|shadow| shadow.inset == ShaderBool::Disabled && shadow.color == gradient)
10907 .expect("gradient drop shadow should be painted");
10908 let inset_gradient = shadows
10909 .iter()
10910 .find(|shadow| shadow.inset == ShaderBool::Enabled && shadow.color == gradient)
10911 .expect("gradient inset shadow should be painted");
10912 let solid = shadows
10913 .iter()
10914 .find(|shadow| shadow.color.as_solid().is_some())
10915 .expect("solid shadows should remain supported");
10916
10917 assert_eq!(
10918 solid.color.as_solid(),
10919 Some(hsla(0.3, 0.7, 0.4, 0.4)),
10920 "shadow alpha stays intrinsic until the group composite"
10921 );
10922 assert_eq!(drop_gradient.element_bounds, inset_gradient.element_bounds);
10923 assert!(drop_gradient.bounds.size.width > drop_gradient.element_bounds.size.width);
10924 assert!(drop_gradient.bounds.size.height > drop_gradient.element_bounds.size.height);
10925 assert!(inset_gradient.bounds.size.width < inset_gradient.element_bounds.size.width);
10926 assert!(inset_gradient.bounds.size.height < inset_gradient.element_bounds.size.height);
10927 assert!(drop_gradient.order < inset_gradient.order);
10928 }
10929
10930 #[gpui::test]
10931 fn corner_smoothing_reaches_every_styled_scene_primitive(cx: &mut TestAppContext) {
10932 let frame = ImageFrame::new(ImageBuffer::from_pixel(2, 2, Rgba([255, 255, 255, 255])));
10933 let image = Arc::new(RenderImage::new(smallvec![frame]));
10934 let window = cx.add_window(move |_, _| CornerSmoothingSceneView { image });
10935
10936 window
10937 .update(cx, |_, window, _| {
10938 let scene = &window.rendered_frame.scene;
10939 assert_smoothing(
10940 "quads",
10941 2,
10942 scene.quads.iter().map(|quad| quad.corner_smoothing),
10943 );
10944 assert_smoothing(
10945 "shadows",
10946 2,
10947 scene.shadows.iter().map(|shadow| shadow.corner_smoothing),
10948 );
10949 assert_smoothing(
10950 "backdrop filters",
10951 1,
10952 scene
10953 .backdrop_filters
10954 .iter()
10955 .map(|filter| filter.corner_smoothing),
10956 );
10957 assert_smoothing(
10958 "filter boundaries",
10959 2,
10960 scene
10961 .filter_boundaries
10962 .iter()
10963 .filter(|boundary| !boundary.filters.is_empty())
10964 .map(|boundary| boundary.corner_smoothing),
10965 );
10966 assert_smoothing(
10967 "images",
10968 1,
10969 scene
10970 .polychrome_sprites
10971 .iter()
10972 .map(|image| image.corner_smoothing),
10973 );
10974 for inset in [ShaderBool::Disabled, ShaderBool::Enabled] {
10975 assert!(scene.shadows.iter().any(|shadow| shadow.inset == inset));
10976 }
10977
10978 let border = scene
10979 .quads
10980 .iter()
10981 .find(|quad| quad.border_widths.top.0 > 0.0)
10982 .expect("border quad");
10983 let scale = window.scale_factor();
10984 assert_eq!(
10985 [
10986 border.border_widths.top.0,
10987 border.border_widths.right.0,
10988 border.border_widths.bottom.0,
10989 border.border_widths.left.0,
10990 ],
10991 [2.0, 4.0, 6.0, 8.0].map(|width| width * scale)
10992 );
10993 })
10994 .unwrap();
10995 }
10996
10997 struct OpensWindowOnPaint {
10998 opened: Rc<Cell<bool>>,
10999 }
11000
11001 impl Render for OpensWindowOnPaint {
11002 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
11003 let opened = self.opened.clone();
11004 div()
11005 .size_full()
11006 .child(canvas(
11007 |_, _, _| {},
11008 move |_, _, _window, cx| {
11009 if !opened.replace(true) {
11010 cx.open_window(WindowOptions::default(), |_, cx| cx.new(|_| EmptyView))
11011 .unwrap();
11012 }
11013 },
11014 ))
11015 .child(div().child("after"))
11019 }
11020 }
11021
11022 #[test]
11028 fn test_window_opened_during_draw_defers_arena_clear() {
11029 let mut cx = TestAppContext::single();
11030
11031 let opened = Rc::new(Cell::new(false));
11032 let window = cx.add_window({
11034 let opened = opened.clone();
11035 move |_, _| OpensWindowOnPaint { opened }
11036 });
11037
11038 assert!(opened.get());
11039 assert_eq!(cx.windows().len(), 2);
11040
11041 cx.update_window(window.into(), |_, window, cx| window.draw(cx).clear(cx))
11044 .unwrap();
11045 }
11046
11047 #[test]
11048 fn test_scale_factor_change_preserves_bounds_and_survives_resize() {
11049 let mut cx = TestAppContext::single();
11050 let window = cx.add_window(|_, _| EmptyView);
11051 let handle: AnyWindowHandle = window.into();
11052 let window_state = |cx: &mut TestAppContext| {
11053 cx.update_window(handle, |_, window, _| {
11054 (
11055 window.scale_factor(),
11056 window.bounds(),
11057 window.viewport_size(),
11058 )
11059 })
11060 .unwrap()
11061 };
11062
11063 let (scale_factor, mut expected_bounds, _) = window_state(&mut cx);
11064 assert_eq!(scale_factor, 2.0);
11065
11066 for (scale_factor, resized_size) in [
11067 (1.0, size(px(800.), px(600.))),
11068 (1.25, size(px(640.), px(480.))),
11069 (2.0, size(px(1024.), px(768.))),
11070 ] {
11071 cx.simulate_window_scale_factor_change(handle, scale_factor);
11072 assert_eq!(
11073 window_state(&mut cx),
11074 (scale_factor, expected_bounds, expected_bounds.size)
11075 );
11076
11077 cx.simulate_window_resize(handle, resized_size);
11078 expected_bounds.size = resized_size;
11079 assert_eq!(
11080 window_state(&mut cx),
11081 (scale_factor, expected_bounds, resized_size)
11082 );
11083 }
11084 }
11085
11086 #[gpui::test]
11091 fn test_frame_waker_fires_on_frame_demand(cx: &mut TestAppContext) {
11092 let window = cx.add_window(|_, _| EmptyView);
11093 let test_window = cx.test_window(window.into());
11094
11095 assert!(
11099 test_window.frame_wake_count() >= 1,
11100 "opening a window must wake the frame source for the initial frame"
11101 );
11102
11103 test_window.simulate_frame_request(RequestFrameOptions::default());
11105
11106 let baseline = test_window.frame_wake_count();
11109 test_window.simulate_frame_request(RequestFrameOptions::default());
11110 window.update(cx, |_, _, _| {}).unwrap();
11111 assert_eq!(
11112 test_window.frame_wake_count(),
11113 baseline,
11114 "clean frames and non-notifying updates must not wake the frame source"
11115 );
11116
11117 window.update(cx, |_, _, cx| cx.notify()).unwrap();
11119 assert!(
11120 test_window.frame_wake_count() > baseline,
11121 "notifying a view in an idle window must wake the frame source"
11122 );
11123
11124 test_window.simulate_frame_request(RequestFrameOptions::default());
11126 let baseline = test_window.frame_wake_count();
11127 test_window.simulate_frame_request(RequestFrameOptions::default());
11128 assert_eq!(
11129 test_window.frame_wake_count(),
11130 baseline,
11131 "serving demand must return the window to idle"
11132 );
11133
11134 window
11136 .update(cx, |_, window, _| window.on_next_frame(|_, _| {}))
11137 .unwrap();
11138 assert!(
11139 test_window.frame_wake_count() > baseline,
11140 "scheduling a next-frame callback in an idle window must wake the frame source"
11141 );
11142 }
11143
11144 #[gpui::test]
11149 fn test_pending_next_frame_callbacks_are_not_stranded(cx: &mut TestAppContext) {
11150 let window = cx.add_window(|_, _| EmptyView);
11151 let test_window = cx.test_window(window.into());
11152 test_window.simulate_frame_request(RequestFrameOptions::default());
11155
11156 let callback_ran = Rc::new(Cell::new(false));
11157 window
11158 .update(cx, {
11159 let callback_ran = callback_ran.clone();
11160 move |_, window, _| {
11161 window.on_next_frame(move |_, _| callback_ran.set(true));
11162 }
11163 })
11164 .unwrap();
11165
11166 let baseline = test_window.frame_wake_count();
11167 test_window.simulate_frame_request(RequestFrameOptions::default());
11168 assert!(
11175 test_window.frame_wake_count() > baseline || callback_ran.get(),
11176 "a frame request with pending next-frame callbacks must either run them or re-arm the frame source"
11177 );
11178 }
11179
11180 #[gpui::test]
11181 fn test_window_reports_no_raw_handle_instead_of_panicking(cx: &mut TestAppContext) {
11182 use raw_window_handle::{HandleError, HasDisplayHandle as _, HasWindowHandle as _};
11183
11184 let window = cx.add_window(|_, _| EmptyView);
11185 window
11186 .update(cx, |_, window, _| {
11187 assert!(matches!(
11188 window.window_handle(),
11189 Err(HandleError::NotSupported)
11190 ));
11191 assert!(matches!(
11192 window.display_handle(),
11193 Err(HandleError::NotSupported)
11194 ));
11195 })
11196 .unwrap();
11197 }
11198
11199 #[gpui::test]
11200 fn test_window_visibility_can_be_changed(cx: &mut TestAppContext) {
11201 for show in [false, true] {
11202 let window = cx.update(|cx| {
11203 cx.open_window(WindowOptions::new().show(show), |_, cx| {
11204 cx.new(|_| EmptyView)
11205 })
11206 .unwrap()
11207 });
11208 let platform_window = cx.test_window(window.into());
11209 assert_eq!(platform_window.is_visible(), show);
11210
11211 for visible in [false, false, true, true, false, true] {
11212 window
11213 .update(cx, |_, window, _| window.set_visible(visible))
11214 .unwrap();
11215 assert_eq!(platform_window.is_visible(), visible);
11216 }
11217 }
11218 }
11219
11220 #[gpui::test]
11221 fn test_appearance_change_runs_after_app_update(cx: &mut TestAppContext) {
11222 let window = cx.add_window(|_, _| EmptyView);
11223 let observed_appearance = Rc::new(Cell::new(None));
11224 let _subscription = window
11225 .update(cx, {
11226 let observed_appearance = observed_appearance.clone();
11227 move |_, window, _| {
11228 window.observe_window_appearance(move |window, _| {
11229 observed_appearance.set(Some(window.appearance()));
11230 })
11231 }
11232 })
11233 .unwrap();
11234 let test_window = cx.test_window(window.into());
11235
11236 cx.update(|_| {
11237 test_window.simulate_appearance_change(WindowAppearance::Dark);
11238 assert_eq!(observed_appearance.get(), None);
11239 });
11240 cx.run_until_parked();
11241
11242 assert_eq!(observed_appearance.get(), Some(WindowAppearance::Dark));
11243 }
11244
11245 #[gpui::test]
11246 fn queued_frame_callback_wakes_a_parked_render_loop(cx: &mut TestAppContext) {
11247 let window = cx.add_window(|_, _| Empty);
11248 let test_window = cx.test_window(window.into());
11249
11250 assert!(test_window.simulate_scheduled_frame());
11251 assert!(test_window.simulate_scheduled_frame());
11252 assert!(!test_window.frame_scheduled());
11253
11254 cx.update_window(window.into(), |_, window, _| {
11255 window.active.set(true);
11256 window.on_next_frame(|_, _| {});
11257 })
11258 .unwrap();
11259 assert!(
11260 test_window.frame_scheduled(),
11261 "queuing work on a parked window must wake the render loop"
11262 );
11263
11264 assert!(test_window.simulate_scheduled_frame());
11265 assert!(
11266 test_window.frame_scheduled(),
11267 "presenting the frame must await one compositor callback"
11268 );
11269 assert!(test_window.simulate_scheduled_frame());
11270 assert!(!test_window.frame_scheduled());
11271 }
11272
11273 #[gpui::test]
11274 fn pending_presentation_wakes_a_parked_render_loop(cx: &mut TestAppContext) {
11275 let window = cx.add_window(|_, _| Empty);
11276 let test_window = cx.test_window(window.into());
11277
11278 assert!(test_window.simulate_scheduled_frame());
11279 assert!(test_window.simulate_scheduled_frame());
11280 assert!(!test_window.frame_scheduled());
11281
11282 cx.update_window(window.into(), |_, window, cx| window.draw(cx).clear(cx))
11283 .unwrap();
11284
11285 assert!(
11286 test_window.frame_scheduled(),
11287 "a rendered scene awaiting presentation must wake the render loop"
11288 );
11289 }
11290
11291 #[gpui::test]
11292 fn callback_queued_during_a_frame_requests_a_follow_up(cx: &mut TestAppContext) {
11293 let window = cx.add_window(|_, _| Empty);
11294 let test_window = cx.test_window(window.into());
11295
11296 let callback_ran = Rc::new(Cell::new(false));
11297 cx.update_window(window.into(), |_, window, _| {
11298 window.active.set(true);
11301 let callback_ran = callback_ran.clone();
11302 window.on_next_frame(move |window, _| {
11303 window.on_next_frame(move |_, _| callback_ran.set(true));
11304 });
11305 })
11306 .unwrap();
11307
11308 assert!(test_window.simulate_scheduled_frame());
11309 assert!(!callback_ran.get());
11310 assert!(
11311 test_window.frame_scheduled(),
11312 "a callback queued mid-frame must schedule a follow-up before the loop parks"
11313 );
11314
11315 assert!(test_window.simulate_scheduled_frame());
11316 assert!(callback_ran.get());
11317 }
11318
11319 struct RootView {
11320 explicit_size: bool,
11321 child_bounds: Rc<Cell<Bounds<Pixels>>>,
11322 }
11323
11324 impl Render for RootView {
11325 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
11326 let child_bounds = self.child_bounds.clone();
11327 let root = div().flex().flex_col().child(
11328 canvas(
11329 move |bounds, _, _| child_bounds.set(bounds),
11330 |_, _, _, _| {},
11331 )
11332 .size_full(),
11333 );
11334 if self.explicit_size {
11335 root.w(px(300.)).h(px(200.))
11336 } else {
11337 root
11338 }
11339 }
11340 }
11341
11342 #[test]
11343 fn auto_sized_window_root_fills_the_window() {
11344 let mut cx = TestAppContext::single();
11345 let child_bounds = Rc::new(Cell::new(Bounds::default()));
11346 let window = cx.add_window({
11347 let child_bounds = child_bounds.clone();
11348 move |_, _| RootView {
11349 explicit_size: false,
11350 child_bounds,
11351 }
11352 });
11353
11354 let viewport_size = cx
11355 .update_window(window.into(), |_, window, cx| {
11356 window.draw(cx).clear(cx);
11357 window.viewport_size()
11358 })
11359 .unwrap();
11360
11361 assert_eq!(child_bounds.get().size, viewport_size);
11362 }
11363
11364 #[test]
11365 fn explicitly_sized_window_root_keeps_its_size() {
11366 let mut cx = TestAppContext::single();
11367 let child_bounds = Rc::new(Cell::new(Bounds::default()));
11368 let window = cx.add_window({
11369 let child_bounds = child_bounds.clone();
11370 move |_, _| RootView {
11371 explicit_size: true,
11372 child_bounds,
11373 }
11374 });
11375
11376 cx.update_window(window.into(), |_, window, cx| {
11377 window.draw(cx).clear(cx);
11378 })
11379 .unwrap();
11380
11381 assert_eq!(child_bounds.get().size, size(px(300.), px(200.)));
11382 }
11383
11384 struct FileDragView {
11385 path: PathBuf,
11386 observed_drag_moves: Rc<RefCell<Vec<Point<Pixels>>>>,
11387 observed_drops: Rc<RefCell<Vec<PathBuf>>>,
11388 }
11389
11390 struct FileDropExitView(Rc<Cell<usize>>);
11391
11392 impl Render for FileDropExitView {
11393 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
11394 div().size_full().on_file_drop_exit({
11395 let observed_file_drop_exit = self.0.clone();
11396 move |_, _, _| observed_file_drop_exit.set(observed_file_drop_exit.get() + 1)
11397 })
11398 }
11399 }
11400
11401 impl Render for FileDragView {
11402 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
11403 div()
11404 .id("file-drag")
11405 .size_full()
11406 .on_drag(self.path.clone(), |_, _, _, cx| cx.new(|_| Empty))
11407 .external_drag_payload(|path: &PathBuf, _, _| {
11408 Some(ExternalDragPayload::Files(FileDragPaths::new([(
11409 path.clone(),
11410 true,
11411 )])))
11412 })
11413 .on_drag_move({
11414 let observed_drag_moves = self.observed_drag_moves.clone();
11415 move |event: &DragMoveEvent<PathBuf>, _, _| {
11416 observed_drag_moves.borrow_mut().push(event.event.position);
11417 }
11418 })
11419 .on_drop({
11420 let observed_drops = self.observed_drops.clone();
11421 move |path: &PathBuf, _, _| observed_drops.borrow_mut().push(path.clone())
11422 })
11423 }
11424 }
11425
11426 #[gpui::test]
11427 fn file_drag_is_promoted_once_and_restored_in_source_window(cx: &mut TestAppContext) {
11428 struct Drag {
11429 window: AnyWindowHandle,
11430 observed_drag_moves: Rc<RefCell<Vec<Point<Pixels>>>>,
11431 observed_drops: Rc<RefCell<Vec<PathBuf>>>,
11432 }
11433
11434 fn start_drag(cx: &mut TestAppContext, path: PathBuf, platform_result: bool) -> Drag {
11435 let observed_drag_moves = Rc::new(RefCell::new(Vec::new()));
11436 let observed_drops = Rc::new(RefCell::new(Vec::new()));
11437 let window: AnyWindowHandle = cx
11438 .add_window({
11439 let observed_drag_moves = observed_drag_moves.clone();
11440 let observed_drops = observed_drops.clone();
11441 move |_, _| FileDragView {
11442 path,
11443 observed_drag_moves,
11444 observed_drops,
11445 }
11446 })
11447 .into();
11448 cx.test_window(window)
11449 .set_start_external_drag_result(platform_result);
11450
11451 let update_result = cx.update_window(window, |_, window, cx| {
11452 window.draw(cx).clear(cx);
11453 window.dispatch_event(
11454 MouseDownEvent {
11455 position: point(px(10.), px(10.)),
11456 button: MouseButton::Left,
11457 modifiers: Default::default(),
11458 click_count: 1,
11459 first_mouse: false,
11460 }
11461 .to_platform_input(),
11462 cx,
11463 );
11464 window.dispatch_event(
11465 MouseMoveEvent {
11466 position: point(px(20.), px(20.)),
11467 pressed_button: Some(MouseButton::Left),
11468 modifiers: Default::default(),
11469 }
11470 .to_platform_input(),
11471 cx,
11472 );
11473 assert!(cx.active_drag.is_some());
11474 });
11475 assert!(
11476 update_result.is_ok(),
11477 "failed to start drag: {update_result:?}"
11478 );
11479
11480 assert!(cx.test_window(window).external_drag_files().is_empty());
11481 Drag {
11482 window,
11483 observed_drag_moves,
11484 observed_drops,
11485 }
11486 }
11487
11488 let successful_path = PathBuf::from("/tmp/successful-drag");
11489 let successful = start_drag(cx, successful_path.clone(), true);
11490 let outside_position = point(px(-1.), px(20.));
11491 let update_result = cx.update_window(successful.window, |_, window, cx| {
11492 window.dispatch_event(
11493 MouseMoveEvent {
11494 position: outside_position,
11495 pressed_button: Some(MouseButton::Left),
11496 modifiers: Default::default(),
11497 }
11498 .to_platform_input(),
11499 cx,
11500 );
11501 assert!(cx.active_drag.is_none());
11502 });
11503 assert!(
11504 update_result.is_ok(),
11505 "failed to promote drag: {update_result:?}"
11506 );
11507 assert_eq!(
11508 cx.test_window(successful.window).external_drag_files(),
11509 [(successful_path.clone(), true)]
11510 );
11511 assert_eq!(
11514 successful.observed_drag_moves.borrow().last(),
11515 Some(&outside_position)
11516 );
11517
11518 let first_destination_exit_count = Rc::new(Cell::new(0));
11519 let first_destination: AnyWindowHandle = cx
11520 .add_window({
11521 let first_destination_exit_count = first_destination_exit_count.clone();
11522 move |_, _| FileDropExitView(first_destination_exit_count)
11523 })
11524 .into();
11525 let second_destination_exit_count = Rc::new(Cell::new(0));
11526 let second_destination: AnyWindowHandle = cx
11527 .add_window({
11528 let second_destination_exit_count = second_destination_exit_count.clone();
11529 move |_, _| FileDropExitView(second_destination_exit_count)
11530 })
11531 .into();
11532 let reentry_position = point(px(30.), px(30.));
11533 let external_paths = || ExternalPaths([successful_path.clone()].into_iter().collect());
11534 let update_result = cx.update_window(first_destination, |_, window, cx| {
11535 window.draw(cx).clear(cx);
11536 window.dispatch_event(
11537 FileDropEvent::Entered {
11538 position: reentry_position,
11539 paths: external_paths(),
11540 }
11541 .to_platform_input(),
11542 cx,
11543 );
11544 assert!(
11545 cx.active_drag
11546 .as_ref()
11547 .is_some_and(|drag| drag.value.downcast_ref::<ExternalPaths>().is_some())
11548 );
11549 window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx);
11550 assert!(cx.active_drag.is_none());
11551 assert_eq!(first_destination_exit_count.get(), 1);
11552 assert_eq!(second_destination_exit_count.get(), 0);
11553 });
11554 assert!(
11555 update_result.is_ok(),
11556 "failed to handle drag in first destination window: {update_result:?}"
11557 );
11558
11559 let update_result = cx.update_window(second_destination, |_, window, cx| {
11560 window.draw(cx).clear(cx);
11561 window.dispatch_event(
11562 PlatformInput::KeyDown(KeyDownEvent {
11563 keystroke: Keystroke::parse("down").expect("valid keystroke"),
11564 is_held: false,
11565 prefer_character_input: false,
11566 }),
11567 cx,
11568 );
11569 window.dispatch_event(
11570 FileDropEvent::Entered {
11571 position: reentry_position,
11572 paths: external_paths(),
11573 }
11574 .to_platform_input(),
11575 cx,
11576 );
11577 assert!(
11578 cx.active_drag
11579 .as_ref()
11580 .is_some_and(|drag| drag.value.downcast_ref::<ExternalPaths>().is_some())
11581 );
11582 assert_eq!(first_destination_exit_count.get(), 1);
11583 assert_eq!(second_destination_exit_count.get(), 0);
11584
11585 window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx);
11586 assert!(cx.active_drag.is_none());
11587 assert_eq!(first_destination_exit_count.get(), 1);
11588 assert_eq!(second_destination_exit_count.get(), 1);
11589 });
11590 assert!(
11591 update_result.is_ok(),
11592 "failed to handle drag in second destination window: {update_result:?}"
11593 );
11594
11595 let update_result = cx.update_window(successful.window, |_, window, cx| {
11596 window.dispatch_event(
11597 FileDropEvent::Entered {
11598 position: reentry_position,
11599 paths: external_paths(),
11600 }
11601 .to_platform_input(),
11602 cx,
11603 );
11604 assert!(
11605 cx.active_drag
11606 .as_ref()
11607 .is_some_and(|drag| drag.value.downcast_ref::<PathBuf>().is_some())
11608 );
11609 assert_eq!(
11610 successful.observed_drag_moves.borrow().last(),
11611 Some(&reentry_position)
11612 );
11613
11614 window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx);
11615 assert!(cx.active_drag.is_none());
11616
11617 window.dispatch_event(
11618 FileDropEvent::Entered {
11619 position: reentry_position,
11620 paths: external_paths(),
11621 }
11622 .to_platform_input(),
11623 cx,
11624 );
11625 assert!(
11626 cx.active_drag
11627 .as_ref()
11628 .is_some_and(|drag| drag.value.downcast_ref::<PathBuf>().is_some())
11629 );
11630
11631 window.dispatch_event(
11632 FileDropEvent::Submit {
11633 position: reentry_position,
11634 }
11635 .to_platform_input(),
11636 cx,
11637 );
11638 assert_eq!(
11639 successful.observed_drops.borrow().as_slice(),
11640 std::slice::from_ref(&successful_path)
11641 );
11642 assert!(cx.active_drag.is_none());
11643
11644 window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx);
11645 assert!(cx.active_drag.is_none());
11646 window.dispatch_event(FileDropEvent::Ended.to_platform_input(), cx);
11647 assert!(cx.active_drag.is_none());
11648
11649 window.dispatch_event(
11650 FileDropEvent::Entered {
11651 position: reentry_position,
11652 paths: external_paths(),
11653 }
11654 .to_platform_input(),
11655 cx,
11656 );
11657 assert!(
11658 cx.active_drag
11659 .as_ref()
11660 .is_some_and(|drag| drag.value.downcast_ref::<ExternalPaths>().is_some())
11661 );
11662 window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx);
11663 });
11664 assert!(
11665 update_result.is_ok(),
11666 "failed to restore drag in source window: {update_result:?}"
11667 );
11668
11669 let cancelled_path = PathBuf::from("/tmp/cancelled-drag");
11670 let cancelled = start_drag(cx, cancelled_path.clone(), true);
11671 let update_result = cx.update_window(cancelled.window, |_, window, cx| {
11672 window.dispatch_event(
11673 MouseMoveEvent {
11674 position: outside_position,
11675 pressed_button: Some(MouseButton::Left),
11676 modifiers: Default::default(),
11677 }
11678 .to_platform_input(),
11679 cx,
11680 );
11681 assert!(cx.active_drag.is_none());
11682
11683 window.dispatch_event(
11684 FileDropEvent::Entered {
11685 position: reentry_position,
11686 paths: ExternalPaths([cancelled_path].into_iter().collect()),
11687 }
11688 .to_platform_input(),
11689 cx,
11690 );
11691 assert!(
11692 cx.active_drag
11693 .as_ref()
11694 .is_some_and(|drag| drag.value.downcast_ref::<PathBuf>().is_some())
11695 );
11696 assert!(cx.stop_active_drag(window));
11697 assert!(cx.active_drag.is_none());
11698 });
11699 assert!(
11700 update_result.is_ok(),
11701 "failed to cancel restored drag: {update_result:?}"
11702 );
11703 assert!(!cx.update(|cx| cx.end_platform_drag(cancelled.window.window_id())));
11704
11705 let removed_path = PathBuf::from("/tmp/removed-window-drag");
11706 let removed = start_drag(cx, removed_path, true);
11707 let removed_window_id = removed.window.window_id();
11708 let update_result = cx.update_window(removed.window, |_, window, cx| {
11709 window.dispatch_event(
11710 MouseMoveEvent {
11711 position: outside_position,
11712 pressed_button: Some(MouseButton::Left),
11713 modifiers: Default::default(),
11714 }
11715 .to_platform_input(),
11716 cx,
11717 );
11718 assert!(cx.active_drag.is_none());
11719 window.remove_window();
11720 });
11721 assert!(
11722 update_result.is_ok(),
11723 "failed to remove drag source window: {update_result:?}"
11724 );
11725 assert!(!cx.update(|cx| cx.end_platform_drag(removed_window_id)));
11726
11727 let failed_path = PathBuf::from("/tmp/failed-drag");
11728 let failed = start_drag(cx, failed_path.clone(), false);
11729 let update_result = cx.update_window(failed.window, |_, window, cx| {
11730 for x_position in [-1., -2.] {
11731 window.dispatch_event(
11732 MouseMoveEvent {
11733 position: point(px(x_position), px(20.)),
11734 pressed_button: Some(MouseButton::Left),
11735 modifiers: Default::default(),
11736 }
11737 .to_platform_input(),
11738 cx,
11739 );
11740 }
11741 assert!(cx.active_drag.is_some());
11742 });
11743 assert!(
11744 update_result.is_ok(),
11745 "failed to retain drag after platform failure: {update_result:?}"
11746 );
11747 assert_eq!(
11748 cx.test_window(failed.window).external_drag_files(),
11749 [(failed_path, true)]
11750 );
11751 }
11752
11753 crate::actions!(shortcut_lookup_test, [LookupShortcut]);
11754
11755 struct ShortcutTargets {
11756 a: FocusHandle,
11757 b: FocusHandle,
11758 }
11759
11760 impl Render for ShortcutTargets {
11761 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
11762 div()
11763 .key_context("ShortcutRoot")
11764 .child(div().key_context("ShortcutA").track_focus(&self.a))
11765 .child(div().key_context("ShortcutB").track_focus(&self.b))
11766 }
11767 }
11768
11769 #[gpui::test]
11770 fn shortcut_lookup_follows_focus_between_rendered_nodes(cx: &mut TestAppContext) {
11771 cx.update(|cx| {
11772 cx.bind_keys([
11773 KeyBinding::new("ctrl-r", LookupShortcut, Some("ShortcutRoot")),
11774 KeyBinding::new("ctrl-a", LookupShortcut, Some("ShortcutA")),
11775 KeyBinding::new("ctrl-b", LookupShortcut, Some("ShortcutB")),
11776 ])
11777 });
11778 let window = cx.add_window(|window, cx| {
11779 let a = cx.focus_handle();
11780 let b = cx.focus_handle();
11781 window.focus(&a, cx);
11782 ShortcutTargets { a, b }
11783 });
11784 window
11785 .update(cx, |this, window, cx| {
11786 let keys = |window: &Window| {
11787 window
11788 .bindings_for_action(&LookupShortcut)
11789 .iter()
11790 .map(|binding| binding.keystrokes()[0].key().to_owned())
11791 .collect::<Vec<_>>()
11792 };
11793 let highest = |window: &Window| {
11794 window
11795 .highest_precedence_binding_for_action(&LookupShortcut)
11796 .map(|binding| binding.keystrokes()[0].key().to_owned())
11797 };
11798 assert_eq!(keys(window), ["r", "a"]);
11799 assert_eq!(highest(window).as_deref(), Some("a"));
11800
11801 window.focus(&this.b, cx);
11802 assert_eq!(keys(window), ["r", "b"]);
11803 assert_eq!(highest(window).as_deref(), Some("b"));
11804
11805 window.blur(cx);
11806 assert!(keys(window).is_empty());
11807 assert_eq!(highest(window), None);
11808 })
11809 .unwrap();
11810 }
11811
11812 struct FocusForwarder {
11813 a: FocusHandle,
11814 b: FocusHandle,
11815 }
11816
11817 impl Render for FocusForwarder {
11818 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
11819 div()
11820 .size_full()
11821 .child(div().w(px(50.)).h(px(50.)).track_focus(&self.a))
11822 .child(div().w(px(50.)).h(px(50.)).track_focus(&self.b))
11823 }
11824 }
11825
11826 #[gpui::test]
11830 fn test_focus_moved_by_focus_listener_is_dispatched(cx: &mut TestAppContext) {
11831 let b_focus_count = Rc::new(Cell::new(0));
11832 let window = cx.add_window({
11833 let b_focus_count = b_focus_count.clone();
11834 move |window, cx| {
11835 let a = cx.focus_handle();
11836 let b = cx.focus_handle();
11837 cx.on_focus(&a, window, |this: &mut FocusForwarder, window, cx| {
11838 let b = this.b.clone();
11839 window.focus(&b, cx);
11840 })
11841 .detach();
11842 cx.on_focus(&b, window, move |_, _, _| {
11843 b_focus_count.set(b_focus_count.get() + 1);
11844 })
11845 .detach();
11846 FocusForwarder { a, b }
11847 }
11848 });
11849
11850 window.update(cx, |_, window, _| window.activate()).unwrap();
11851 cx.executor().run_until_parked();
11852
11853 window
11854 .update(cx, |this, window, cx| {
11855 let a = this.a.clone();
11856 window.focus(&a, cx);
11857 })
11858 .unwrap();
11859 cx.executor().run_until_parked();
11860
11861 window
11862 .update(cx, |this, window, _| {
11863 assert!(this.b.is_focused(window));
11864 })
11865 .unwrap();
11866 assert_eq!(b_focus_count.get(), 1);
11867 }
11868
11869 #[gpui::test]
11870 fn claimed_touch_drag_receives_movement_and_release(cx: &mut TestAppContext) {
11871 let events = Rc::new(RefCell::new(Vec::new()));
11872 let window = cx.add_window({
11873 let events = events.clone();
11874 move |_, _| TouchDragListener { events }
11875 });
11876 let touch = TouchId(1);
11877
11878 dispatch_touch(window, cx, touch, TouchPhase::Started, 10.);
11879 dispatch_touch(window, cx, touch, TouchPhase::Moved, 30.);
11880 dispatch_touch(window, cx, touch, TouchPhase::Ended, 40.);
11881
11882 assert_eq!(
11883 events.borrow().as_slice(),
11884 [
11885 (TouchPhase::Started, px(10.)),
11886 (TouchPhase::Moved, px(30.)),
11887 (TouchPhase::Ended, px(40.)),
11888 ]
11889 );
11890 }
11891
11892 struct TouchDragListener {
11893 events: Rc<RefCell<Vec<(TouchPhase, Pixels)>>>,
11894 }
11895
11896 impl Render for TouchDragListener {
11897 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
11898 let events = self.events.clone();
11899 canvas(
11900 |_, _, _| {},
11901 move |_, _, window, _| {
11902 window.on_mouse_event(move |event: &TouchDragEvent, phase, window, _cx| {
11903 if phase != DispatchPhase::Bubble {
11904 return;
11905 }
11906 events.borrow_mut().push((event.phase, event.position.x));
11907 if event.phase == TouchPhase::Started {
11908 window.prevent_default();
11909 }
11910 });
11911 },
11912 )
11913 }
11914 }
11915
11916 #[gpui::test]
11917 fn long_press_is_claimed_only_when_started_prevents_default(cx: &mut TestAppContext) {
11918 for response in [
11919 LongPressResponse::PreventDefault,
11920 LongPressResponse::StopPropagation,
11921 LongPressResponse::None,
11922 ] {
11923 let phases = Rc::new(RefCell::new(Vec::new()));
11924 let window = cx.add_window({
11925 let phases = phases.clone();
11926 move |_, _| LongPressListener { phases, response }
11927 });
11928 dispatch_touch(window, cx, TouchId(1), TouchPhase::Started, 0.);
11929 cx.executor().advance_clock(Duration::from_millis(501));
11930 cx.executor().run_until_parked();
11931 window
11932 .update(cx, |_, window, _| {
11933 assert_eq!(
11934 window.long_press_capture.is_some(),
11935 response == LongPressResponse::PreventDefault
11936 );
11937 })
11938 .unwrap();
11939 dispatch_touch(window, cx, TouchId(1), TouchPhase::Moved, 2.);
11940 dispatch_touch(window, cx, TouchId(1), TouchPhase::Ended, 2.);
11941 window
11942 .update(cx, |_, window, _| {
11943 assert!(window.long_press_capture.is_none());
11944 })
11945 .unwrap();
11946
11947 let phases = phases.borrow();
11948 if response == LongPressResponse::PreventDefault {
11949 assert_eq!(
11950 phases.as_slice(),
11951 [TouchPhase::Started, TouchPhase::Moved, TouchPhase::Ended]
11952 );
11953 } else {
11954 assert_eq!(phases.as_slice(), [TouchPhase::Started]);
11955 }
11956 }
11957 }
11958
11959 #[gpui::test]
11960 fn stale_default_prevention_does_not_claim_long_press(cx: &mut TestAppContext) {
11961 let phases = Rc::new(RefCell::new(Vec::new()));
11962 let window = cx.add_window({
11963 let phases = phases.clone();
11964 move |_, _| LongPressListener {
11965 phases,
11966 response: LongPressResponse::None,
11967 }
11968 });
11969 window
11970 .update(cx, |_, window, _| {
11971 window.prevent_default();
11972 })
11973 .unwrap();
11974
11975 dispatch_touch(window, cx, TouchId(1), TouchPhase::Started, 0.);
11976 cx.executor().advance_clock(Duration::from_millis(501));
11977 cx.executor().run_until_parked();
11978 dispatch_touch(window, cx, TouchId(1), TouchPhase::Moved, 2.);
11979
11980 assert_eq!(phases.borrow().as_slice(), [TouchPhase::Started]);
11981 }
11982
11983 #[gpui::test]
11984 fn resolved_touch_cancels_scheduled_long_press(cx: &mut TestAppContext) {
11985 for (phase, position) in [
11986 (TouchPhase::Ended, 0.),
11987 (TouchPhase::Cancelled, 0.),
11988 (TouchPhase::Moved, 20.),
11989 ] {
11990 let phases = Rc::new(RefCell::new(Vec::new()));
11991 let window = cx.add_window({
11992 let phases = phases.clone();
11993 move |_, _| LongPressListener {
11994 phases,
11995 response: LongPressResponse::PreventDefault,
11996 }
11997 });
11998 dispatch_touch(window, cx, TouchId(1), TouchPhase::Started, 0.);
11999 dispatch_touch(window, cx, TouchId(1), phase, position);
12000 cx.executor().advance_clock(Duration::from_millis(501));
12001 cx.executor().run_until_parked();
12002
12003 assert!(phases.borrow().is_empty(), "{phase:?} allowed long press");
12004 }
12005 }
12006
12007 #[gpui::test]
12008 fn stale_long_press_timer_cannot_affect_replacement_touch(cx: &mut TestAppContext) {
12009 let phases = Rc::new(RefCell::new(Vec::new()));
12010 let window = cx.add_window({
12011 let phases = phases.clone();
12012 move |_, _| LongPressListener {
12013 phases,
12014 response: LongPressResponse::PreventDefault,
12015 }
12016 });
12017 let first_touch = TouchId(1);
12018 dispatch_touch(window, cx, first_touch, TouchPhase::Started, 0.);
12019 cx.executor().advance_clock(Duration::from_millis(250));
12020 dispatch_touch(window, cx, first_touch, TouchPhase::Cancelled, 0.);
12021 dispatch_touch(window, cx, TouchId(2), TouchPhase::Started, 10.);
12022
12023 cx.executor().advance_clock(Duration::from_millis(251));
12024 cx.executor().run_until_parked();
12025 assert!(phases.borrow().is_empty());
12026
12027 cx.executor().advance_clock(Duration::from_millis(250));
12028 cx.executor().run_until_parked();
12029 assert_eq!(phases.borrow().as_slice(), [TouchPhase::Started]);
12030 }
12031
12032 #[gpui::test]
12033 fn test_underline_exclusions_preserve_device_geometry(cx: &mut TestAppContext) {
12034 test_underline_paint_at_scales(cx, |window| {
12035 let (numerator, denominator) = match window.scale_factor() {
12036 1.0 => (1, 1),
12037 1.25 => (5, 4),
12038 1.5 => (3, 2),
12039 2.0 => (2, 1),
12040 3.0 => (3, 1),
12041 scale => panic!("unexpected scale {scale}"),
12042 };
12043 let snap = |quarters: i32| {
12044 let scaled = quarters * numerator;
12045 let divisor = 4 * denominator;
12046 let rounded =
12047 scaled.abs() / divisor + i32::from(2 * (scaled.abs() % divisor) > divisor);
12048 ScaledPixels((scaled.signum() * rounded) as f32)
12049 };
12050 let stroke = |quarters| {
12051 if quarters == 0 {
12052 ScaledPixels(0.)
12053 } else {
12054 snap(quarters).max(ScaledPixels(1.))
12055 }
12056 };
12057
12058 for origin in [-101, -3, -2, -1, 0, 1, 2, 3, 101] {
12059 for width in [0, 1, 3, 84] {
12060 for thickness in [0, 1, 4, 7] {
12061 for wavy in [false, true] {
12062 let style = UnderlineStyle {
12063 thickness: px(thickness as f32 / 4.),
12064 color: Some(hsla(0.25, 0.5, 0.75, 0.5)),
12065 wavy,
12066 };
12067 let bounds = Bounds::new(
12068 point(snap(origin), snap(-origin)),
12069 size(
12070 stroke(width),
12071 stroke(thickness) * if wavy { 3. } else { 1. },
12072 ),
12073 );
12074 let origin = point(px(origin as f32 / 4.), px(-origin as f32 / 4.));
12075 let width = px(width as f32 / 4.);
12076 assert_eq!(window.underline_bounds(origin, width, &style), bounds);
12077 let original = paint_test_underlines(window, |window| {
12078 window.paint_underline(origin, width, &style);
12079 })
12080 .to_vec();
12081 assert_eq!(original.len(), usize::from(!bounds.is_empty()));
12082 if let Some(underline) = original.first() {
12083 assert_eq!(underline.bounds, bounds);
12084 assert_eq!(underline.thickness, stroke(thickness));
12085 assert_eq!(underline.wavy, wavy.into());
12086 assert_eq!(underline.color, hsla(0.25, 0.5, 0.75, 0.5).into());
12087 }
12088
12089 let underlines = paint_test_underlines(window, |window| {
12090 window.paint_underline_with_exclusions(origin, width, &style, &[]);
12091 });
12092 assert_eq!(underlines.len(), original.len());
12093 for (actual, original) in underlines.iter().zip(&original) {
12094 assert_same_underline_geometry(actual, original);
12095 assert_eq!(actual.content_mask, original.content_mask);
12096 assert_eq!(actual.clip_bounds, original.clip_bounds);
12097 }
12098
12099 let left = bounds.left().0 as i32;
12100 let right = bounds.right().0 as i32;
12101 let exclusions = [
12102 (15, 25),
12103 (3, 7),
12104 (5, 10),
12105 (10, 12),
12106 (0, 0),
12107 (4, 2),
12108 (-50, -1),
12109 (70, 100),
12110 ]
12111 .map(|(start, end)| {
12112 ScaledPixels((left + start) as f32)
12113 ..ScaledPixels((left + end) as f32)
12114 });
12115 let underlines = paint_test_underlines(window, |window| {
12116 window.paint_underline_with_exclusions(
12117 origin,
12118 width,
12119 &style,
12120 &exclusions,
12121 );
12122 });
12123 let expected = (left..right)
12124 .filter(|column| {
12125 !bounds.is_empty()
12126 && !exclusions.iter().any(|span| {
12127 span.contains(&ScaledPixels(*column as f32))
12128 })
12129 })
12130 .collect::<Vec<_>>();
12131 assert_eq!(underline_device_columns(underlines), expected);
12132 let mut expected_clip_spans: Vec<(ScaledPixels, ScaledPixels)> =
12133 Vec::new();
12134 for column in &expected {
12135 let start = ScaledPixels(*column as f32);
12136 let end = ScaledPixels((*column + 1) as f32);
12137 if let Some((_, previous_end)) = expected_clip_spans.last_mut() {
12138 if *previous_end == start {
12139 *previous_end = end;
12140 continue;
12141 }
12142 }
12143 expected_clip_spans.push((start, end));
12144 }
12145 let actual_clip_spans = underlines
12146 .iter()
12147 .filter_map(|underline| {
12148 let visible_bounds = underline
12149 .bounds
12150 .intersect(&underline.content_mask.bounds)
12151 .intersect(&underline.clip_bounds);
12152 (!visible_bounds.is_empty())
12153 .then_some((visible_bounds.left(), visible_bounds.right()))
12154 })
12155 .collect::<Vec<_>>();
12156 assert_eq!(actual_clip_spans, expected_clip_spans);
12157 for underline in underlines {
12158 assert_same_underline_geometry(underline, &original[0]);
12159 assert_eq!(underline.content_mask, original[0].content_mask);
12160 let visible_bounds = underline
12161 .bounds
12162 .intersect(&underline.content_mask.bounds)
12163 .intersect(&underline.clip_bounds);
12164 let full_visible_bounds =
12165 bounds.intersect(&original[0].content_mask.bounds);
12166 assert_eq!(visible_bounds.top(), full_visible_bounds.top());
12167 assert_eq!(visible_bounds.bottom(), full_visible_bounds.bottom());
12168 }
12169 }
12170 }
12171 }
12172 }
12173 });
12174 }
12175
12176 #[gpui::test]
12177 fn test_underline_exclusions_preserve_helvetica_warning_edge(cx: &mut TestAppContext) {
12178 test_underline_paint_at_scales(cx, |window| {
12179 if window.scale_factor() != 2.0 {
12180 return;
12181 }
12182 let style = UnderlineStyle {
12183 thickness: px(1.),
12184 color: None,
12185 wavy: true,
12186 };
12187 let origin = point(px(2.888_183_8), px(15.357_917));
12188 let width = px(23.848_145) - origin.x;
12189 let original = paint_test_underlines(window, |window| {
12190 window.paint_underline(origin, width, &style);
12191 })[0];
12192 assert_eq!(original.bounds.left(), ScaledPixels(6.));
12193 assert_eq!(original.bounds.right(), ScaledPixels(48.));
12194 let point_bounds = window.underline_bounds(origin, px(10.829_102), &style);
12195 assert_eq!(point_bounds.left(), ScaledPixels(6.));
12196 assert_eq!(point_bounds.right(), ScaledPixels(28.));
12197
12198 let underlines = paint_test_underlines(window, |window| {
12199 window.paint_underline_with_exclusions(
12200 origin,
12201 width,
12202 &style,
12203 &[point_bounds.left()..point_bounds.right()],
12204 );
12205 });
12206 assert_eq!(underlines.len(), 1);
12207 assert_same_underline_geometry(&underlines[0], &original);
12208 assert_eq!(underlines[0].content_mask, original.content_mask);
12209 assert_eq!(underlines[0].clip_bounds.left(), point_bounds.right());
12210 assert_eq!(underlines[0].clip_bounds.right(), original.bounds.right());
12211 assert_eq!(
12212 underline_device_columns(underlines),
12213 (28..48).collect::<Vec<_>>()
12214 );
12215 });
12216 }
12217
12218 #[gpui::test]
12219 fn test_underline_exclusions_preserve_content_mask_and_opacity(cx: &mut TestAppContext) {
12220 test_underline_paint_at_scales(cx, |window| {
12221 let scale = window.scale_factor();
12222 let original_mask = window.content_mask();
12223 let mask = ContentMask {
12224 bounds: Bounds::from_corners(point(px(4.), px(10.)), point(px(16.), px(12.))),
12225 fade_out: Edges {
12226 left: px(4.),
12227 top: px(0.5),
12228 right: px(2.),
12229 bottom: px(0.25),
12230 },
12231 };
12232 let style = UnderlineStyle {
12233 thickness: px(2.),
12234 color: Some(hsla(0.25, 0.5, 0.75, 0.5)),
12235 wavy: true,
12236 };
12237 let origin = point(px(-1.25), px(9.25));
12238 let width = px(24.);
12239 window.next_frame.scene.clear();
12240 window.with_content_mask(Some(mask), |window| {
12241 window.with_element_opacity(Some(0.5), |window| {
12242 window.with_element_opacity(Some(0.5), |window| {
12243 window.paint_underline(origin, width, &style);
12244 let original = window.next_frame.scene.underlines[0];
12245 assert_eq!(original.color, hsla(0.25, 0.5, 0.75, 0.5).into());
12246 window.paint_underline_with_exclusions(
12247 origin,
12248 width,
12249 &style,
12250 &[ScaledPixels(6. * scale)..ScaledPixels(8. * scale)],
12251 );
12252 let underlines = &window.next_frame.scene.underlines[1..];
12253 assert_eq!(underlines.len(), 2);
12254 let original_bounds = original.content_mask.bounds;
12255 let expected_mask = original.content_mask;
12256 let visible_bounds = original.bounds.intersect(&original_bounds);
12257 let expected_clip_spans = [(4. * scale, 6. * scale), (8. * scale, 16. * scale)].map(
12258 |(left, right)| {
12259 Bounds::from_corners(
12260 point(ScaledPixels(left), visible_bounds.top()),
12261 point(ScaledPixels(right), visible_bounds.bottom()),
12262 )
12263 },
12264 );
12265 assert!(expected_mask.fade_out.left > ScaledPixels(0.));
12266 assert!(expected_mask.fade_out.right > ScaledPixels(0.));
12267 assert!(expected_mask.fade_out.top > ScaledPixels(0.));
12268 assert!(expected_mask.fade_out.bottom > ScaledPixels(0.));
12269 let left_fade_end = expected_mask.bounds.left() + expected_mask.fade_out.left;
12270 assert!(expected_clip_spans[0].right() > expected_mask.bounds.left());
12271 assert!(expected_clip_spans[0].right() < left_fade_end);
12272 assert_eq!(
12273 underlines.iter().map(|underline| underline.clip_bounds).collect::<Vec<_>>(),
12274 expected_clip_spans
12275 );
12276 assert_eq!(
12277 underlines.iter().map(|underline| underline.content_mask).collect::<Vec<_>>(),
12278 vec![expected_mask; 2],
12279 "a clip boundary inside the 4px left fade must retain the original fade ramp"
12280 );
12281 for underline in underlines {
12282 assert_same_underline_geometry(underline, &original);
12283 assert_eq!(underline.bounds, original.bounds);
12284 }
12285 });
12286 });
12287 });
12288 let boundaries = &window.next_frame.scene.filter_boundaries;
12289 assert_eq!(boundaries.len(), 4);
12290 assert_eq!(
12291 boundaries
12292 .iter()
12293 .map(|b| (b.is_start, b.opacity))
12294 .collect::<Vec<_>>(),
12295 vec![(true, 0.5), (true, 0.5), (false, 0.5), (false, 0.5)],
12296 );
12297 assert_eq!(window.content_mask(), original_mask);
12298 });
12299 }
12300
12301 #[gpui::test]
12302 fn test_underline_exclusions_adversarial_permutations(cx: &mut TestAppContext) {
12303 test_underline_paint_at_scales(cx, |window| {
12304 let style = UnderlineStyle {
12305 thickness: px(1.),
12306 color: None,
12307 wavy: true,
12308 };
12309 let origin = point(px(-4.), px(-1.));
12310 let width = px(8.);
12311 let left = (-4. * window.scale_factor()) as i32;
12312 let right = (4. * window.scale_factor()) as i32;
12313 let endpoints = [-16, -8, -1, 0, 1, 8, 16];
12314 for first_start in endpoints {
12315 for first_end in endpoints {
12316 for second_start in endpoints {
12317 for second_end in endpoints {
12318 let exclusions = [first_start..first_end, second_start..second_end];
12319 let expected = (left..right)
12320 .filter(|column| {
12321 !exclusions.iter().any(|span| span.contains(column))
12322 })
12323 .collect::<Vec<_>>();
12324 let exclusions = exclusions.map(|span| {
12325 ScaledPixels(span.start as f32)..ScaledPixels(span.end as f32)
12326 });
12327 let underlines = paint_test_underlines(window, |window| {
12328 window.paint_underline_with_exclusions(
12329 origin,
12330 width,
12331 &style,
12332 &exclusions,
12333 );
12334 });
12335 assert_eq!(
12336 underline_device_columns(underlines),
12337 expected,
12338 "{exclusions:?}"
12339 );
12340 }
12341 }
12342 }
12343 }
12344 });
12345 }
12346
12347 #[derive(Clone, Copy, PartialEq)]
12348 enum LongPressResponse {
12349 PreventDefault,
12350 StopPropagation,
12351 None,
12352 }
12353
12354 struct LongPressListener {
12355 phases: Rc<RefCell<Vec<TouchPhase>>>,
12356 response: LongPressResponse,
12357 }
12358
12359 impl Render for LongPressListener {
12360 fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
12361 let entity = cx.entity();
12362 let phases = self.phases.clone();
12363 let response = self.response;
12364 canvas(
12365 |_, _, _| {},
12366 move |_, _, window, _| {
12367 window.on_mouse_event(move |event: &LongPressEvent, phase, window, cx| {
12368 if phase != DispatchPhase::Bubble {
12369 return;
12370 }
12371 phases.borrow_mut().push(event.phase);
12372 match response {
12373 LongPressResponse::PreventDefault => {
12374 window.capture_long_press(&entity);
12375 window.prevent_default();
12376 }
12377 LongPressResponse::StopPropagation => cx.stop_propagation(),
12378 LongPressResponse::None => {}
12379 }
12380 });
12381 },
12382 )
12383 }
12384 }
12385
12386 struct UnderlineTestView(Rc<dyn Fn(&mut Window)>);
12387
12388 impl Render for UnderlineTestView {
12389 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
12390 let paint = self.0.clone();
12391 canvas(
12392 |_, _, _| {},
12393 move |_, _, window, _| {
12394 window.content_mask_stack.push(ContentMask {
12395 bounds: Bounds::from_corners(
12396 point(px(-1000.), px(-1000.)),
12397 point(px(1000.), px(1000.)),
12398 ),
12399 ..Default::default()
12400 });
12401 paint(window);
12402 window.content_mask_stack.pop();
12403 },
12404 )
12405 .size_full()
12406 }
12407 }
12408
12409 fn test_underline_paint_at_scales(
12410 cx: &mut TestAppContext,
12411 paint: impl Fn(&mut Window) + 'static,
12412 ) {
12413 let window = cx.add_window(move |_, _| UnderlineTestView(Rc::new(paint)));
12414 for scale in [1., 1.25, 1.5, 2., 3.] {
12415 cx.simulate_window_scale_factor_change(window.into(), scale);
12416 cx.update_window(window.into(), |_, window, cx| window.draw(cx).clear(cx))
12417 .unwrap();
12418 }
12419 }
12420
12421 fn paint_test_underlines(window: &mut Window, paint: impl FnOnce(&mut Window)) -> &[Underline] {
12422 window.next_frame.scene.clear();
12423 paint(window);
12424 &window.next_frame.scene.underlines
12425 }
12426
12427 fn assert_same_underline_geometry(actual: &Underline, expected: &Underline) {
12428 assert_eq!(actual.bounds, expected.bounds);
12429 assert_eq!(actual.thickness, expected.thickness);
12430 assert_eq!(actual.wavy, expected.wavy);
12431 assert_eq!(actual.color, expected.color);
12432 assert_eq!(actual.padding, expected.padding);
12433 }
12434
12435 fn underline_device_columns(underlines: &[Underline]) -> Vec<i32> {
12436 underlines
12437 .iter()
12438 .flat_map(|underline| {
12439 let bounds = underline
12440 .bounds
12441 .intersect(&underline.content_mask.bounds)
12442 .intersect(&underline.clip_bounds);
12443 bounds.left().0 as i32..bounds.right().0 as i32
12444 })
12445 .collect()
12446 }
12447
12448 fn dispatch_touch<T: 'static>(
12449 window: crate::WindowHandle<T>,
12450 cx: &mut TestAppContext,
12451 id: TouchId,
12452 phase: TouchPhase,
12453 x: f32,
12454 ) {
12455 window
12456 .update(cx, |_, window, cx| {
12457 window.dispatch_event(
12458 TouchEvent {
12459 id,
12460 phase,
12461 position: point(px(x), px(0.)),
12462 predicted_position: None,
12463 force: None,
12464 }
12465 .to_platform_input(),
12466 cx,
12467 );
12468 })
12469 .unwrap();
12470 }
12471
12472 struct CachedOpacityChild {
12473 renders: Rc<Cell<usize>>,
12474 paints: Rc<Cell<usize>>,
12475 }
12476
12477 impl Render for CachedOpacityChild {
12478 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
12479 self.renders.set(self.renders.get() + 1);
12480 let paints = self.paints.clone();
12481
12482 div().size(px(20.0)).bg(crate::red()).child(
12483 canvas(
12484 |_, _, _| (),
12485 move |bounds, _, window, _| {
12486 paints.set(paints.get() + 1);
12487 window.paint_quad(PaintQuad::new(bounds).background(crate::blue()));
12488 },
12489 )
12490 .size_full()
12491 .opacity(0.5),
12492 )
12493 }
12494 }
12495
12496 struct CachedOpacityParent {
12497 opacity: Option<f32>,
12498 child: Entity<CachedOpacityChild>,
12499 cached: bool,
12500 }
12501
12502 impl Render for CachedOpacityParent {
12503 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
12504 let mut child_style = div().size(px(20.0));
12505 let child = if self.cached {
12506 self.child
12507 .clone()
12508 .cached(child_style.style().clone())
12509 .into_any_element()
12510 } else {
12511 self.child.clone().into_any_element()
12512 };
12513
12514 div()
12515 .size(px(40.0))
12516 .when_some(self.opacity, |parent, opacity| parent.opacity(opacity))
12517 .child(child)
12518 }
12519 }
12520
12521 #[test]
12522 fn parent_opacity_changes_replay_cached_canvas_groups() {
12523 let renders = Rc::new(Cell::new(0));
12524 let paints = Rc::new(Cell::new(0));
12525 let mut app = TestApp::new();
12526 let mut test_window = app.open_window(|_, cx| CachedOpacityParent {
12527 opacity: None,
12528 cached: true,
12529 child: cx.new(|_| CachedOpacityChild {
12530 renders: renders.clone(),
12531 paints: paints.clone(),
12532 }),
12533 });
12534 test_window.draw();
12535 let initial_renders = renders.get();
12536 let initial_paints = paints.get();
12537 assert!(initial_renders > 0);
12538 assert!(initial_paints > 0);
12539
12540 let scene_snapshot = |scene: &Scene| {
12541 (
12542 scene.render_commands().to_vec(),
12543 scene
12544 .quads
12545 .iter()
12546 .map(|quad| (quad.order, quad.bounds, quad.content_mask, quad.background))
12547 .collect::<Vec<_>>(),
12548 scene
12549 .filter_boundaries
12550 .iter()
12551 .map(|boundary| {
12552 (
12553 boundary.bounds,
12554 boundary.content_mask,
12555 boundary.opacity,
12556 boundary.is_start,
12557 )
12558 })
12559 .collect::<Vec<_>>(),
12560 )
12561 };
12562
12563 for opacity in [Some(0.5), Some(0.25), Some(1.0), Some(0.0), None] {
12564 let mut fresh_app = TestApp::new();
12565 let mut fresh_window = fresh_app.open_window(|_, cx| CachedOpacityParent {
12566 opacity,
12567 cached: false,
12568 child: cx.new(|_| CachedOpacityChild {
12569 renders: Rc::new(Cell::new(0)),
12570 paints: Rc::new(Cell::new(0)),
12571 }),
12572 });
12573 fresh_window.draw();
12574 let fresh_scene =
12575 fresh_window.update(|_, window, _| scene_snapshot(&window.rendered_frame.scene));
12576
12577 test_window.update(|view, _, cx| {
12578 view.opacity = opacity;
12579 cx.notify();
12580 });
12581 test_window.draw();
12582 test_window.update(|_, window, _| {
12583 let scene = &window.rendered_frame.scene;
12584 assert_eq!(scene_snapshot(scene), fresh_scene, "opacity {opacity:?}");
12585 });
12586 assert_eq!(renders.get(), initial_renders);
12587 assert_eq!(paints.get(), initial_paints);
12588 }
12589 }
12590}
12591
12592#[cfg(all(test, any(feature = "inspector", debug_assertions)))]
12593mod inspector_tests {
12594 use super::*;
12595 use crate::{
12596 DivInspectorState, InspectorElementId, InteractiveElement as _, MouseDownEvent,
12597 ParentElement as _, StyleRefinement, Styled as _, TestAppContext, div,
12598 };
12599
12600 #[gpui::test]
12601 fn inspector_only_tracks_its_open_window(cx: &mut TestAppContext) {
12602 let windows = [
12603 cx.add_window(|_, cx| InspectorTestRoot {
12604 child: cx.new(|_| InspectorTestView::default()),
12605 }),
12606 cx.add_window(|_, cx| InspectorTestRoot {
12607 child: cx.new(|_| InspectorTestView::default()),
12608 }),
12609 ];
12610 for window in windows {
12611 assert_closed_inspector(window.into(), cx);
12612 }
12613 for _ in 0..2 {
12614 cx.update_window(windows[0].into(), |root, window, cx| {
12615 let root = root.downcast::<InspectorTestRoot>().expect("test root");
12616 let child_widths = root.read(cx).child.read(cx).child_widths.clone();
12617 window.toggle_inspector(cx);
12618 window.draw(cx).clear(cx);
12619 assert_eq!(child_widths.borrow().as_slice(), &[px(10.); 3]);
12620 let path = window
12621 .rendered_frame
12622 .next_inspector_instance_ids
12623 .iter()
12624 .find_map(|(path, count)| (*count == 3).then(|| path.clone()))
12625 .expect("anonymous siblings share an inspector path");
12626 let selected_id = InspectorElementId {
12627 path,
12628 instance_id: 1,
12629 };
12630 let position = window
12631 .rendered_frame
12632 .hitboxes
12633 .iter()
12634 .find(|hitbox| {
12635 window.rendered_frame.inspector_hitboxes.get(&hitbox.id)
12636 == Some(&selected_id)
12637 })
12638 .expect("middle sibling is pickable")
12639 .bounds
12640 .center();
12641 window.simulate_mouse_move(position, cx);
12642 window.dispatch_event(
12643 PlatformInput::MouseDown(MouseDownEvent {
12644 position,
12645 button: MouseButton::Left,
12646 modifiers: Modifiers::default(),
12647 click_count: 1,
12648 first_mouse: false,
12649 }),
12650 cx,
12651 );
12652 window.dispatch_event(
12653 PlatformInput::MouseUp(MouseUpEvent {
12654 position,
12655 button: MouseButton::Left,
12656 modifiers: Modifiers::default(),
12657 click_count: 1,
12658 }),
12659 cx,
12660 );
12661 assert!(!window.is_inspector_picking(cx));
12662 assert_eq!(
12663 window
12664 .inspector
12665 .as_ref()
12666 .expect("open inspector")
12667 .read(cx)
12668 .active_element_id(),
12669 Some(&selected_id)
12670 );
12671 window.draw(cx).clear(cx);
12672 window.with_inspector_state::<DivInspectorState, _>(
12673 Some(&selected_id),
12674 cx,
12675 |state, _| {
12676 let state = state.as_mut().expect("selected div has style state");
12677 assert_eq!(
12678 state.base_style.size.width,
12679 Some(crate::Length::from(px(10.)))
12680 );
12681 state.base_style.size.width = Some(crate::Length::from(px(25.)));
12682 },
12683 );
12684 window.refresh();
12685 window.draw(cx).clear(cx);
12686 assert_eq!(
12687 child_widths.borrow().as_slice(),
12688 &[px(10.), px(25.), px(10.)]
12689 );
12690 })
12691 .expect("pick and edit a cached child");
12692 assert_closed_inspector(windows[1].into(), cx);
12693 windows[0]
12694 .update(cx, |_, window, cx| window.toggle_inspector(cx))
12695 .expect("close inspector");
12696 assert_closed_inspector(windows[0].into(), cx);
12697 }
12698 }
12699
12700 struct InspectorTestRoot {
12701 child: Entity<InspectorTestView>,
12702 }
12703
12704 impl Render for InspectorTestRoot {
12705 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
12706 self.child
12707 .clone()
12708 .cached(StyleRefinement::default().size(px(100.)))
12709 }
12710 }
12711
12712 #[derive(Default)]
12713 struct InspectorTestView {
12714 child_widths: Rc<RefCell<Vec<Pixels>>>,
12715 }
12716
12717 impl Render for InspectorTestView {
12718 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
12719 let child_widths = self.child_widths.clone();
12720 div()
12721 .on_children_prepainted(move |bounds, _, _| {
12722 *child_widths.borrow_mut() =
12723 bounds.iter().map(|bounds| bounds.size.width).collect();
12724 })
12725 .with_dynamic_prepaint_order(|_, _| SmallVec::from_iter([2, 0, 1]))
12726 .id("inspector-root")
12727 .flex()
12728 .children((0..3).map(|_| div().size(px(10.)).flex_shrink_0()))
12729 }
12730 }
12731
12732 fn assert_closed_inspector(window: AnyWindowHandle, cx: &mut TestAppContext) {
12733 cx.update_window(window, |root, window, cx| {
12734 window.draw(cx).clear(cx);
12735 window.draw(cx).clear(cx);
12736 let root = root.downcast::<InspectorTestRoot>().expect("test root");
12737 assert_eq!(
12738 root.read(cx)
12739 .child
12740 .read(cx)
12741 .child_widths
12742 .borrow()
12743 .as_slice(),
12744 &[px(10.); 3]
12745 );
12746 for frame in [&window.rendered_frame, &window.next_frame] {
12747 assert_eq!(frame.next_inspector_instance_ids.capacity(), 0);
12748 assert_eq!(frame.inspector_hitboxes.capacity(), 0);
12749 }
12750 })
12751 .expect("closed inspector has no bookkeeping and no style overrides");
12752 }
12753}