#[cfg(any(feature = "inspector", debug_assertions))]use crate::Inspector;use crate::{ Action, AnyDrag, AnyElement, AnyImageCache, AnyTooltip, AnyView, App, AppContext, Arena, Asset, AsyncWindowContext, AtlasTile, AvailableSpace, Background, BorderStyle, Bounds, BoxShadow, Capslock, Context, Corners, CursorHideMode, CursorStyle, Decorations, DevicePixels, DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, FontId, Global, GlobalElementId, GlyphId, GpuSpecs, Hsla, InputHandler, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeyEvent, Keystroke, KeystrokeEvent, LayoutId, LineLayoutIndex, Modifiers, ModifiersChangedEvent, MonochromeSprite, MouseButton, MouseEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, Priority, PromptButton, PromptLevel, Quad, Render, RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, Replay, ResizeEdge, SMOOTH_SVG_SCALE_FACTOR, SUBPIXEL_VARIANTS_X, SUBPIXEL_VARIANTS_Y, ScaledPixels, Scene, Shadow, SharedString, Size, StrikethroughStyle, Style, SubpixelSprite, SubscriberSet, Subscription, SystemWindowTab, SystemWindowTabController, TabStopMap, TaffyLayoutEngine, Task, TextRenderingMode, TextStyle, TextStyleRefinement, ThermalState, TransformationMatrix, Underline, UnderlineStyle, WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowControls, WindowDecorations, WindowOptions, WindowParams, WindowTextSystem, point, prelude::*, profiler, px, rems, size, transparent_black,}; use anyhow::{Context as _, Result, anyhow};use collections::{FxHashMap, FxHashSet};#[cfg(target_os = "macos")]use core_video::pixel_buffer::CVPixelBuffer;use derive_more::{Deref, DerefMut};use futures::FutureExt;use futures::channel::oneshot;use gpui_util::post_inc;use gpui_util::{ResultExt, measure};#[cfg(feature = "input-latency-histogram")]use hdrhistogram::Histogram;use itertools::FoldWhile::{Continue, Done};use itertools::Itertools;use parking_lot::RwLock;use raw_window_handle::{HandleError, HasDisplayHandle, HasWindowHandle};use refineable::Refineable;use scheduler::Instant;use slotmap::SlotMap;use smallvec::SmallVec;use std::{ any::{Any, TypeId}, borrow::Cow, cell::{Cell, RefCell}, cmp, fmt::{Debug, Display}, hash::{Hash, Hasher}, marker::PhantomData, mem, ops::{DerefMut, Range}, rc::Rc, sync::{ Arc, Weak, atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst}, }, time::Duration,};use uuid::Uuid; pub(crate) mod a11y;mod prompts; pub use a11y::A11ySubtreeBuilder; use self::a11y::A11y;#[cfg(not(target_family = "wasm"))]use self::a11y::ROOT_NODE_ID;use crate::util::{ atomic_incr_if_not_zero, ceil_to_device_pixel, floor_to_device_pixel, round_half_toward_zero, round_half_toward_zero_f64, round_stroke_to_device_pixel, round_to_device_pixel,};pub use prompts::*; /// Default window size used when no explicit size is provided.pub const DEFAULT_WINDOW_SIZE: Size<Pixels> = size(px(1536.), px(1095.)); /// A 6:5 aspect ratio minimum window size to be used for functional,/// additional-to-main-Zed windows, like the settings and rules library windows.pub const DEFAULT_ADDITIONAL_WINDOW_SIZE: Size<Pixels> = Size { width: Pixels(900.), height: Pixels(750.),}; /// Represents the two different phases when dispatching events.#[derive(Default, Copy, Clone, Debug, Eq, PartialEq)]pub enum DispatchPhase { /// After the capture phase comes the bubble phase, in which mouse event listeners are /// invoked front to back and keyboard event listeners are invoked from the focused element /// to the root of the element tree. This is the phase you'll most commonly want to use when /// registering event listeners. #[default] Bubble, /// During the initial capture phase, mouse event listeners are invoked back to front, and keyboard /// listeners are invoked from the root of the tree downward toward the focused element. This phase /// is used for special purposes such as clearing the "pressed" state for click events. If /// you stop event propagation during this phase, you need to know what you're doing. Handlers /// outside of the immediate region may rely on detecting non-local events during this phase. Capture,} impl DispatchPhase { /// Returns true if this represents the "bubble" phase. #[inline] pub fn bubble(self) -> bool { self == DispatchPhase::Bubble } /// Returns true if this represents the "capture" phase. #[inline] pub fn capture(self) -> bool { self == DispatchPhase::Capture }} struct WindowInvalidatorInner { pub dirty: bool, pub draw_phase: DrawPhase, pub dirty_views: FxHashSet<EntityId>, pub update_count: usize, pub frame_dirty: FrameDirtyAccumulator,} /// Per-frame invalidation bookkeeping, drained at draw time and emitted to the/// frame profiler. Tracks when the current frame first became dirty and how/// many invalidations were coalesced into it. Only populated while/// `profiler::frame_trace_enabled()` is set.#[derive(Default)]struct FrameDirtyAccumulator { dirty_at: Option<Instant>, invalidations: u64,} #[derive(Clone)]pub(crate) struct WindowInvalidator { inner: Rc<RefCell<WindowInvalidatorInner>>,} impl WindowInvalidator { pub fn new() -> Self { WindowInvalidator { inner: Rc::new(RefCell::new(WindowInvalidatorInner { dirty: true, draw_phase: DrawPhase::None, dirty_views: FxHashSet::default(), update_count: 0, frame_dirty: FrameDirtyAccumulator::default(), })), } } pub fn invalidate_view(&self, entity: EntityId, cx: &mut App) -> bool { let mut inner = self.inner.borrow_mut(); inner.update_count += 1; inner.dirty_views.insert(entity); if inner.draw_phase == DrawPhase::None { Self::record_frame_dirty(&mut inner); inner.dirty = true; cx.push_effect(Effect::Notify { emitter: entity }); true } else { false } } pub fn is_dirty(&self) -> bool { self.inner.borrow().dirty } pub fn set_dirty(&self, dirty: bool) { let mut inner = self.inner.borrow_mut(); inner.dirty = dirty; if dirty { inner.update_count += 1; Self::record_frame_dirty(&mut inner); } } pub fn set_phase(&self, phase: DrawPhase) { self.inner.borrow_mut().draw_phase = phase } pub fn update_count(&self) -> usize { self.inner.borrow().update_count } fn record_frame_dirty(inner: &mut WindowInvalidatorInner) { if profiler::frame_trace_enabled() { inner.frame_dirty.dirty_at.get_or_insert_with(Instant::now); inner.frame_dirty.invalidations += 1; } } fn take_frame_dirty(&self) -> FrameDirtyAccumulator { mem::take(&mut self.inner.borrow_mut().frame_dirty) } pub fn take_views(&self) -> FxHashSet<EntityId> { mem::take(&mut self.inner.borrow_mut().dirty_views) } pub fn replace_views(&self, views: FxHashSet<EntityId>) { self.inner.borrow_mut().dirty_views = views; } pub fn not_drawing(&self) -> bool { self.inner.borrow().draw_phase == DrawPhase::None } #[track_caller] pub fn debug_assert_paint(&self) { debug_assert!( matches!(self.inner.borrow().draw_phase, DrawPhase::Paint), "this method can only be called during paint" ); } #[track_caller] pub fn debug_assert_prepaint(&self) { debug_assert!( matches!(self.inner.borrow().draw_phase, DrawPhase::Prepaint), "this method can only be called during request_layout, or prepaint" ); } #[track_caller] pub fn debug_assert_paint_or_prepaint(&self) { debug_assert!( matches!( self.inner.borrow().draw_phase, DrawPhase::Paint | DrawPhase::Prepaint ), "this method can only be called during request_layout, prepaint, or paint" ); }} type AnyObserver = Box<dyn FnMut(&mut Window, &mut App) -> bool + 'static>; pub(crate) type AnyWindowFocusListener = Box<dyn FnMut(&WindowFocusEvent, &mut Window, &mut App) -> bool + 'static>; pub(crate) struct WindowFocusEvent { pub(crate) previous_focus_path: SmallVec<[FocusId; 8]>, pub(crate) current_focus_path: SmallVec<[FocusId; 8]>,} impl WindowFocusEvent { pub fn is_focus_in(&self, focus_id: FocusId) -> bool { !self.previous_focus_path.contains(&focus_id) && self.current_focus_path.contains(&focus_id) } pub fn is_focus_out(&self, focus_id: FocusId) -> bool { self.previous_focus_path.contains(&focus_id) && !self.current_focus_path.contains(&focus_id) }} /// This is provided when subscribing for `Context::on_focus_out` events.pub struct FocusOutEvent { /// A weak focus handle representing what was blurred. pub blurred: WeakFocusHandle,} slotmap::new_key_type! { /// A globally unique identifier for a focusable element. pub struct FocusId;} thread_local! { /// Fallback arena used when no app-specific arena is active. /// In production, each window draw sets CURRENT_ELEMENT_ARENA to the app's arena. pub(crate) static ELEMENT_ARENA: RefCell<Arena> = RefCell::new(Arena::new(1024 * 1024)); /// Points to the current App's element arena during draw operations. /// This allows multiple test Apps to have isolated arenas, preventing /// cross-session corruption when the scheduler interleaves their tasks. static CURRENT_ELEMENT_ARENA: Cell<Option<*const RefCell<Arena>>> = const { Cell::new(None) };} /// Whether a window draw is currently in progress on this thread.////// This holds exactly while an `ElementArenaScope` is active: nested scopes/// restore the previous (still set) arena pointer, so `CURRENT_ELEMENT_ARENA`/// is `Some` from the outermost draw's start to its end.////// The `on_request_frame` callback uses this to defer draw requests that/// arrive re-entrantly while a draw is already on the stack (e.g. via nested/// message pumping in the Windows window procedure), instead of running a/// nested draw or panicking on the already-borrowed App.fn draw_in_progress() -> bool { CURRENT_ELEMENT_ARENA.with(|current| current.get().is_some())} /// Allocates an element in the current arena. Uses the app-specific arena if one/// is active (during draw), otherwise falls back to the thread-local ELEMENT_ARENA.pub(crate) fn with_element_arena<R>(f: impl FnOnce(&mut Arena) -> R) -> R { CURRENT_ELEMENT_ARENA.with(|current| { if let Some(arena_ptr) = current.get() { // SAFETY: The pointer is valid for the duration of the draw operation // that set it, and we're being called during that same draw. let arena_cell = unsafe { &*arena_ptr }; f(&mut arena_cell.borrow_mut()) } else { ELEMENT_ARENA.with_borrow_mut(f) } })} /// Scope guard that sets CURRENT_ELEMENT_ARENA for the duration of a draw/// operation and tracks the arena's scope depth, so that a nested draw's/// `ArenaClearNeeded::clear` is deferred rather than freeing memory the outer/// draw still references (see `Arena::clear`).////// Call [`ElementArenaScope::exit`] with the same arena that was entered to/// obtain the [`ArenaClearNeeded`] token the draw now owes; requiring `exit`/// makes it impossible to request a clear before the scope has ended. The/// scope's teardown — restoring the thread-local and balancing `begin_scope`/// with `end_scope` — happens in `Drop`, so the arena's scope depth stays/// balanced on every path, including when a panic unwinds a draw before `exit`/// is reached. (If teardown lived only in `exit`, such a panic would leave the/// scope depth permanently elevated and defer every future clear, leaking/// memory unboundedly.)pub(crate) struct ElementArenaScope { /// The entered arena: compared against the argument in `exit`, and /// dereferenced in `Drop` to end its scope (see the SAFETY note there). entered: *const RefCell<Arena>, previous: Option<*const RefCell<Arena>>, exited: bool,} impl ElementArenaScope { /// Enter a scope where element allocations use the given arena. pub(crate) fn enter(arena: &RefCell<Arena>) -> Self { arena.borrow_mut().begin_scope(); let previous = CURRENT_ELEMENT_ARENA.with(|current| { let prev = current.get(); current.set(Some(arena as *const RefCell<Arena>)); prev }); Self { entered: arena as *const RefCell<Arena>, previous, exited: false, } } /// End the scope: restores the previously-current arena and ends the /// arena's clear-deferral scope. Returns the token for the arena clear the /// draw now owes; producing it here makes it impossible to request a clear /// before the scope has ended (which would be silently deferred forever). /// /// Panics if passed a different arena than was entered: ending the scope /// of the wrong arena would unbalance two arenas' scope depths, allowing /// one of them to clear while a draw still references its memory. pub(crate) fn exit(mut self, arena: &RefCell<Arena>) -> ArenaClearNeeded { assert!( std::ptr::eq(self.entered, arena), "ElementArenaScope::exit called with a different arena than was entered" ); self.exited = true; // Teardown (restoring the thread-local and ending the arena's // clear-deferral scope) runs in `Drop`, which fires both here — `self` // is dropped as `exit` returns, before the token reaches the caller — // and when a panic unwinds the draw before `exit` is reached. ArenaClearNeeded::new(arena) }} impl Drop for ElementArenaScope { fn drop(&mut self) { // Teardown lives here (rather than in `exit`) so it runs exactly once on // every path: `exit` consumes and drops the guard on the normal path, // and unwinding drops it on the panic path. Balancing `begin_scope` here // keeps the arena's scope depth correct even when a draw panics; if this // only happened in `exit`, a panic between `enter` and `exit` would leave // the depth elevated and defer every future clear. CURRENT_ELEMENT_ARENA.with(|current| { current.set(self.previous); }); // SAFETY: `entered` came from a `&RefCell<Arena>` in `enter`, and the // arena (owned by the `App` being drawn) outlives this guard on both the // normal and unwinding paths, since the guard is a local of the draw. unsafe { &*self.entered }.borrow_mut().end_scope(); if !self.exited && !std::thread::panicking() { debug_assert!(false, "ElementArenaScope dropped without calling exit()"); log::error!( "ElementArenaScope dropped without calling exit(); \ the arena clear for this draw was never requested" ); } }} /// Returned when the element arena has been used and so must be cleared before the next draw.#[must_use]pub struct ArenaClearNeeded { /// Identity of the arena that was drawn into. Only ever compared against /// another pointer in `clear`; never dereferenced. arena: *const RefCell<Arena>,} impl ArenaClearNeeded { /// Create a new ArenaClearNeeded token for the App whose arena was drawn /// into. Private: the only way to obtain one is [`ElementArenaScope::exit`]. fn new(arena: &RefCell<Arena>) -> Self { Self { arena: arena as *const RefCell<Arena>, } } /// Clear the element arena of the App the draw ran against. If an enclosing /// draw is still in progress (this draw was nested inside it), the clear is /// deferred to the enclosing draw's own `ArenaClearNeeded` so that its live /// allocations aren't freed. /// /// Panics if passed a different App than the draw ran against, since /// clearing another App's arena could free memory its draws still /// reference. pub fn clear(self, cx: &mut App) { assert!( std::ptr::eq(self.arena, &cx.element_arena), "ArenaClearNeeded::clear called with a different App than the draw ran against" ); cx.element_arena.borrow_mut().clear(); }} pub(crate) type FocusMap = RwLock<SlotMap<FocusId, FocusRef>>;pub(crate) struct FocusRef { pub(crate) ref_count: AtomicUsize, pub(crate) tab_index: isize, pub(crate) tab_stop: bool,} impl FocusId { /// Obtains whether the element associated with this handle is currently focused. pub fn is_focused(&self, window: &Window) -> bool { window.focus == Some(*self) } /// Obtains whether the element associated with this handle contains the focused /// element or is itself focused. pub fn contains_focused(&self, window: &Window, cx: &App) -> bool { window .focused(cx) .is_some_and(|focused| self.contains(focused.id, window)) } /// Obtains whether the element associated with this handle is contained within the /// focused element or is itself focused. pub fn within_focused(&self, window: &Window, cx: &App) -> bool { let focused = window.focused(cx); focused.is_some_and(|focused| focused.id.contains(*self, window)) } /// Obtains whether this handle contains the given handle in the most recently rendered frame. pub(crate) fn contains(&self, other: Self, window: &Window) -> bool { window .rendered_frame .dispatch_tree .focus_contains(*self, other) }} /// A handle which can be used to track and manipulate the focused element in a window.pub struct FocusHandle { pub(crate) id: FocusId, handles: Arc<FocusMap>, /// The index of this element in the tab order. pub tab_index: isize, /// Whether this element can be focused by tab navigation. pub tab_stop: bool,} impl std::fmt::Debug for FocusHandle { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_fmt(format_args!("FocusHandle({:?})", self.id)) }} impl FocusHandle { pub(crate) fn new(handles: &Arc<FocusMap>) -> Self { let id = handles.write().insert(FocusRef { ref_count: AtomicUsize::new(1), tab_index: 0, tab_stop: false, }); Self { id, tab_index: 0, tab_stop: false, handles: handles.clone(), } } pub(crate) fn for_id(id: FocusId, handles: &Arc<FocusMap>) -> Option<Self> { let lock = handles.read(); let focus = lock.get(id)?;Lines 1–500 of 7080. Load more
#[cfg(any(feature = "inspector", debug_assertions))] use crate::Inspector; use crate::{ Action, AnyDrag, AnyElement, AnyImageCache, AnyTooltip, AnyView, App, AppContext, Arena, Asset, AsyncWindowContext, AtlasTile, AvailableSpace, Background, BorderStyle, Bounds, BoxShadow, Capslock, Context, Corners, CursorHideMode, CursorStyle, Decorations, DevicePixels, DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, FontId, Global, GlobalElementId, GlyphId, GpuSpecs, Hsla, InputHandler, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeyEvent, Keystroke, KeystrokeEvent, LayoutId, LineLayoutIndex, Modifiers, ModifiersChangedEvent, MonochromeSprite, MouseButton, MouseEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, Priority, PromptButton, PromptLevel, Quad, Render, RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, Replay, ResizeEdge, SMOOTH_SVG_SCALE_FACTOR, SUBPIXEL_VARIANTS_X, SUBPIXEL_VARIANTS_Y, ScaledPixels, Scene, Shadow, SharedString, Size, StrikethroughStyle, Style, SubpixelSprite, SubscriberSet, Subscription, SystemWindowTab, SystemWindowTabController, TabStopMap, TaffyLayoutEngine, Task, TextRenderingMode, TextStyle, TextStyleRefinement, ThermalState, TransformationMatrix, Underline, UnderlineStyle, WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowControls, WindowDecorations, WindowOptions, WindowParams, WindowTextSystem, point, prelude::*, profiler, px, rems, size, transparent_black, }; use anyhow::{Context as _, Result, anyhow}; use collections::{FxHashMap, FxHashSet}; #[cfg(target_os = "macos")] use core_video::pixel_buffer::CVPixelBuffer; use derive_more::{Deref, DerefMut}; use futures::FutureExt; use futures::channel::oneshot; use gpui_util::post_inc; use gpui_util::{ResultExt, measure}; #[cfg(feature = "input-latency-histogram")] use hdrhistogram::Histogram; use itertools::FoldWhile::{Continue, Done}; use itertools::Itertools; use parking_lot::RwLock; use raw_window_handle::{HandleError, HasDisplayHandle, HasWindowHandle}; use refineable::Refineable; use scheduler::Instant; use slotmap::SlotMap; use smallvec::SmallVec; use std::{ any::{Any, TypeId}, borrow::Cow, cell::{Cell, RefCell}, cmp, fmt::{Debug, Display}, hash::{Hash, Hasher}, marker::PhantomData, mem, ops::{DerefMut, Range}, rc::Rc, sync::{ Arc, Weak, atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst}, }, time::Duration, }; use uuid::Uuid; pub(crate) mod a11y; mod prompts; pub use a11y::A11ySubtreeBuilder; use self::a11y::A11y; #[cfg(not(target_family = "wasm"))] use self::a11y::ROOT_NODE_ID; use crate::util::{ atomic_incr_if_not_zero, ceil_to_device_pixel, floor_to_device_pixel, round_half_toward_zero, round_half_toward_zero_f64, round_stroke_to_device_pixel, round_to_device_pixel, }; pub use prompts::*; /// Default window size used when no explicit size is provided. pub const DEFAULT_WINDOW_SIZE: Size<Pixels> = size(px(1536.), px(1095.)); /// A 6:5 aspect ratio minimum window size to be used for functional, /// additional-to-main-Zed windows, like the settings and rules library windows. pub const DEFAULT_ADDITIONAL_WINDOW_SIZE: Size<Pixels> = Size { width: Pixels(900.), height: Pixels(750.), }; /// Represents the two different phases when dispatching events. #[derive(Default, Copy, Clone, Debug, Eq, PartialEq)] pub enum DispatchPhase { /// After the capture phase comes the bubble phase, in which mouse event listeners are /// invoked front to back and keyboard event listeners are invoked from the focused element /// to the root of the element tree. This is the phase you'll most commonly want to use when /// registering event listeners. #[default] Bubble, /// During the initial capture phase, mouse event listeners are invoked back to front, and keyboard /// listeners are invoked from the root of the tree downward toward the focused element. This phase /// is used for special purposes such as clearing the "pressed" state for click events. If /// you stop event propagation during this phase, you need to know what you're doing. Handlers /// outside of the immediate region may rely on detecting non-local events during this phase. Capture, } impl DispatchPhase { /// Returns true if this represents the "bubble" phase. #[inline] pub fn bubble(self) -> bool { self == DispatchPhase::Bubble } /// Returns true if this represents the "capture" phase. #[inline] pub fn capture(self) -> bool { self == DispatchPhase::Capture } } struct WindowInvalidatorInner { pub dirty: bool, pub draw_phase: DrawPhase, pub dirty_views: FxHashSet<EntityId>, pub update_count: usize, pub frame_dirty: FrameDirtyAccumulator, } /// Per-frame invalidation bookkeeping, drained at draw time and emitted to the /// frame profiler. Tracks when the current frame first became dirty and how /// many invalidations were coalesced into it. Only populated while /// `profiler::frame_trace_enabled()` is set. #[derive(Default)] struct FrameDirtyAccumulator { dirty_at: Option<Instant>, invalidations: u64, } #[derive(Clone)] pub(crate) struct WindowInvalidator { inner: Rc<RefCell<WindowInvalidatorInner>>, } impl WindowInvalidator { pub fn new() -> Self { WindowInvalidator { inner: Rc::new(RefCell::new(WindowInvalidatorInner { dirty: true, draw_phase: DrawPhase::None, dirty_views: FxHashSet::default(), update_count: 0, frame_dirty: FrameDirtyAccumulator::default(), })), } } pub fn invalidate_view(&self, entity: EntityId, cx: &mut App) -> bool { let mut inner = self.inner.borrow_mut(); inner.update_count += 1; inner.dirty_views.insert(entity); if inner.draw_phase == DrawPhase::None { Self::record_frame_dirty(&mut inner); inner.dirty = true; cx.push_effect(Effect::Notify { emitter: entity }); true } else { false } } pub fn is_dirty(&self) -> bool { self.inner.borrow().dirty } pub fn set_dirty(&self, dirty: bool) { let mut inner = self.inner.borrow_mut(); inner.dirty = dirty; if dirty { inner.update_count += 1; Self::record_frame_dirty(&mut inner); } } pub fn set_phase(&self, phase: DrawPhase) { self.inner.borrow_mut().draw_phase = phase } pub fn update_count(&self) -> usize { self.inner.borrow().update_count } fn record_frame_dirty(inner: &mut WindowInvalidatorInner) { if profiler::frame_trace_enabled() { inner.frame_dirty.dirty_at.get_or_insert_with(Instant::now); inner.frame_dirty.invalidations += 1; } } fn take_frame_dirty(&self) -> FrameDirtyAccumulator { mem::take(&mut self.inner.borrow_mut().frame_dirty) } pub fn take_views(&self) -> FxHashSet<EntityId> { mem::take(&mut self.inner.borrow_mut().dirty_views) } pub fn replace_views(&self, views: FxHashSet<EntityId>) { self.inner.borrow_mut().dirty_views = views; } pub fn not_drawing(&self) -> bool { self.inner.borrow().draw_phase == DrawPhase::None } #[track_caller] pub fn debug_assert_paint(&self) { debug_assert!( matches!(self.inner.borrow().draw_phase, DrawPhase::Paint), "this method can only be called during paint" ); } #[track_caller] pub fn debug_assert_prepaint(&self) { debug_assert!( matches!(self.inner.borrow().draw_phase, DrawPhase::Prepaint), "this method can only be called during request_layout, or prepaint" ); } #[track_caller] pub fn debug_assert_paint_or_prepaint(&self) { debug_assert!( matches!( self.inner.borrow().draw_phase, DrawPhase::Paint | DrawPhase::Prepaint ), "this method can only be called during request_layout, prepaint, or paint" ); } } type AnyObserver = Box<dyn FnMut(&mut Window, &mut App) -> bool + 'static>; pub(crate) type AnyWindowFocusListener = Box<dyn FnMut(&WindowFocusEvent, &mut Window, &mut App) -> bool + 'static>; pub(crate) struct WindowFocusEvent { pub(crate) previous_focus_path: SmallVec<[FocusId; 8]>, pub(crate) current_focus_path: SmallVec<[FocusId; 8]>, } impl WindowFocusEvent { pub fn is_focus_in(&self, focus_id: FocusId) -> bool { !self.previous_focus_path.contains(&focus_id) && self.current_focus_path.contains(&focus_id) } pub fn is_focus_out(&self, focus_id: FocusId) -> bool { self.previous_focus_path.contains(&focus_id) && !self.current_focus_path.contains(&focus_id) } } /// This is provided when subscribing for `Context::on_focus_out` events. pub struct FocusOutEvent { /// A weak focus handle representing what was blurred. pub blurred: WeakFocusHandle, } slotmap::new_key_type! { /// A globally unique identifier for a focusable element. pub struct FocusId; } thread_local! { /// Fallback arena used when no app-specific arena is active. /// In production, each window draw sets CURRENT_ELEMENT_ARENA to the app's arena. pub(crate) static ELEMENT_ARENA: RefCell<Arena> = RefCell::new(Arena::new(1024 * 1024)); /// Points to the current App's element arena during draw operations. /// This allows multiple test Apps to have isolated arenas, preventing /// cross-session corruption when the scheduler interleaves their tasks. static CURRENT_ELEMENT_ARENA: Cell<Option<*const RefCell<Arena>>> = const { Cell::new(None) }; } /// Whether a window draw is currently in progress on this thread. /// /// This holds exactly while an `ElementArenaScope` is active: nested scopes /// restore the previous (still set) arena pointer, so `CURRENT_ELEMENT_ARENA` /// is `Some` from the outermost draw's start to its end. /// /// The `on_request_frame` callback uses this to defer draw requests that /// arrive re-entrantly while a draw is already on the stack (e.g. via nested /// message pumping in the Windows window procedure), instead of running a /// nested draw or panicking on the already-borrowed App. fn draw_in_progress() -> bool { CURRENT_ELEMENT_ARENA.with(|current| current.get().is_some()) } /// Allocates an element in the current arena. Uses the app-specific arena if one /// is active (during draw), otherwise falls back to the thread-local ELEMENT_ARENA. pub(crate) fn with_element_arena<R>(f: impl FnOnce(&mut Arena) -> R) -> R { CURRENT_ELEMENT_ARENA.with(|current| { if let Some(arena_ptr) = current.get() { // SAFETY: The pointer is valid for the duration of the draw operation // that set it, and we're being called during that same draw. let arena_cell = unsafe { &*arena_ptr }; f(&mut arena_cell.borrow_mut()) } else { ELEMENT_ARENA.with_borrow_mut(f) } }) } /// Scope guard that sets CURRENT_ELEMENT_ARENA for the duration of a draw /// operation and tracks the arena's scope depth, so that a nested draw's /// `ArenaClearNeeded::clear` is deferred rather than freeing memory the outer /// draw still references (see `Arena::clear`). /// /// Call [`ElementArenaScope::exit`] with the same arena that was entered to /// obtain the [`ArenaClearNeeded`] token the draw now owes; requiring `exit` /// makes it impossible to request a clear before the scope has ended. The /// scope's teardown — restoring the thread-local and balancing `begin_scope` /// with `end_scope` — happens in `Drop`, so the arena's scope depth stays /// balanced on every path, including when a panic unwinds a draw before `exit` /// is reached. (If teardown lived only in `exit`, such a panic would leave the /// scope depth permanently elevated and defer every future clear, leaking /// memory unboundedly.) pub(crate) struct ElementArenaScope { /// The entered arena: compared against the argument in `exit`, and /// dereferenced in `Drop` to end its scope (see the SAFETY note there). entered: *const RefCell<Arena>, previous: Option<*const RefCell<Arena>>, exited: bool, } impl ElementArenaScope { /// Enter a scope where element allocations use the given arena. pub(crate) fn enter(arena: &RefCell<Arena>) -> Self { arena.borrow_mut().begin_scope(); let previous = CURRENT_ELEMENT_ARENA.with(|current| { let prev = current.get(); current.set(Some(arena as *const RefCell<Arena>)); prev }); Self { entered: arena as *const RefCell<Arena>, previous, exited: false, } } /// End the scope: restores the previously-current arena and ends the /// arena's clear-deferral scope. Returns the token for the arena clear the /// draw now owes; producing it here makes it impossible to request a clear /// before the scope has ended (which would be silently deferred forever). /// /// Panics if passed a different arena than was entered: ending the scope /// of the wrong arena would unbalance two arenas' scope depths, allowing /// one of them to clear while a draw still references its memory. pub(crate) fn exit(mut self, arena: &RefCell<Arena>) -> ArenaClearNeeded { assert!( std::ptr::eq(self.entered, arena), "ElementArenaScope::exit called with a different arena than was entered" ); self.exited = true; // Teardown (restoring the thread-local and ending the arena's // clear-deferral scope) runs in `Drop`, which fires both here — `self` // is dropped as `exit` returns, before the token reaches the caller — // and when a panic unwinds the draw before `exit` is reached. ArenaClearNeeded::new(arena) } } impl Drop for ElementArenaScope { fn drop(&mut self) { // Teardown lives here (rather than in `exit`) so it runs exactly once on // every path: `exit` consumes and drops the guard on the normal path, // and unwinding drops it on the panic path. Balancing `begin_scope` here // keeps the arena's scope depth correct even when a draw panics; if this // only happened in `exit`, a panic between `enter` and `exit` would leave // the depth elevated and defer every future clear. CURRENT_ELEMENT_ARENA.with(|current| { current.set(self.previous); }); // SAFETY: `entered` came from a `&RefCell<Arena>` in `enter`, and the // arena (owned by the `App` being drawn) outlives this guard on both the // normal and unwinding paths, since the guard is a local of the draw. unsafe { &*self.entered }.borrow_mut().end_scope(); if !self.exited && !std::thread::panicking() { debug_assert!(false, "ElementArenaScope dropped without calling exit()"); log::error!( "ElementArenaScope dropped without calling exit(); \ the arena clear for this draw was never requested" ); } } } /// Returned when the element arena has been used and so must be cleared before the next draw. #[must_use] pub struct ArenaClearNeeded { /// Identity of the arena that was drawn into. Only ever compared against /// another pointer in `clear`; never dereferenced. arena: *const RefCell<Arena>, } impl ArenaClearNeeded { /// Create a new ArenaClearNeeded token for the App whose arena was drawn /// into. Private: the only way to obtain one is [`ElementArenaScope::exit`]. fn new(arena: &RefCell<Arena>) -> Self { Self { arena: arena as *const RefCell<Arena>, } } /// Clear the element arena of the App the draw ran against. If an enclosing /// draw is still in progress (this draw was nested inside it), the clear is /// deferred to the enclosing draw's own `ArenaClearNeeded` so that its live /// allocations aren't freed. /// /// Panics if passed a different App than the draw ran against, since /// clearing another App's arena could free memory its draws still /// reference. pub fn clear(self, cx: &mut App) { assert!( std::ptr::eq(self.arena, &cx.element_arena), "ArenaClearNeeded::clear called with a different App than the draw ran against" ); cx.element_arena.borrow_mut().clear(); } } pub(crate) type FocusMap = RwLock<SlotMap<FocusId, FocusRef>>; pub(crate) struct FocusRef { pub(crate) ref_count: AtomicUsize, pub(crate) tab_index: isize, pub(crate) tab_stop: bool, } impl FocusId { /// Obtains whether the element associated with this handle is currently focused. pub fn is_focused(&self, window: &Window) -> bool { window.focus == Some(*self) } /// Obtains whether the element associated with this handle contains the focused /// element or is itself focused. pub fn contains_focused(&self, window: &Window, cx: &App) -> bool { window .focused(cx) .is_some_and(|focused| self.contains(focused.id, window)) } /// Obtains whether the element associated with this handle is contained within the /// focused element or is itself focused. pub fn within_focused(&self, window: &Window, cx: &App) -> bool { let focused = window.focused(cx); focused.is_some_and(|focused| focused.id.contains(*self, window)) } /// Obtains whether this handle contains the given handle in the most recently rendered frame. pub(crate) fn contains(&self, other: Self, window: &Window) -> bool { window .rendered_frame .dispatch_tree .focus_contains(*self, other) } } /// A handle which can be used to track and manipulate the focused element in a window. pub struct FocusHandle { pub(crate) id: FocusId, handles: Arc<FocusMap>, /// The index of this element in the tab order. pub tab_index: isize, /// Whether this element can be focused by tab navigation. pub tab_stop: bool, } impl std::fmt::Debug for FocusHandle { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_fmt(format_args!("FocusHandle({:?})", self.id)) } } impl FocusHandle { pub(crate) fn new(handles: &Arc<FocusMap>) -> Self { let id = handles.write().insert(FocusRef { ref_count: AtomicUsize::new(1), tab_index: 0, tab_stop: false, }); Self { id, tab_index: 0, tab_stop: false, handles: handles.clone(), } } pub(crate) fn for_id(id: FocusId, handles: &Arc<FocusMap>) -> Option<Self> { let lock = handles.read(); let focus = lock.get(id)?;