From 2f9958621ba71598ff1bb5bc6f1887508ce8f7d9 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Sun, 21 Jan 2024 18:40:20 -0800 Subject: [PATCH] Do an initial pass on refactoring out ElementContext from WindowContext --- crates/gpui/src/element.rs | 356 +++++++++++++++++++++-- crates/gpui/src/elements/div.rs | 31 +- crates/gpui/src/elements/img.rs | 2 +- crates/gpui/src/elements/list.rs | 6 +- crates/gpui/src/elements/uniform_list.rs | 6 +- crates/gpui/src/text_system/line.rs | 4 +- crates/gpui/src/window.rs | 273 +---------------- 7 files changed, 368 insertions(+), 310 deletions(-) diff --git a/crates/gpui/src/element.rs b/crates/gpui/src/element.rs index 01bb8ba6bffb961167c2ebff7a31ebf78fa306de..5cfc73cd21fbd342adaeb51856de9b546c588414 100644 --- a/crates/gpui/src/element.rs +++ b/crates/gpui/src/element.rs @@ -35,12 +35,56 @@ //! your own custom layout algorithm or rendering a code editor. use crate::{ - util::FluentBuilder, ArenaBox, AvailableSpace, BorrowWindow, Bounds, ElementId, LayoutId, - Pixels, Point, Size, ViewContext, WindowContext, ELEMENT_ARENA, + util::FluentBuilder, AppContext, ArenaBox, AvailableSpace, BorrowWindow, Bounds, ContentMask, + ElementId, ElementStateBox, EntityId, IsZero, LayoutId, Pixels, Point, Size, ViewContext, + Window, WindowContext, ELEMENT_ARENA, }; use derive_more::{Deref, DerefMut}; pub(crate) use smallvec::SmallVec; -use std::{any::Any, fmt::Debug}; +use std::{ + any::Any, + borrow::{Borrow, BorrowMut}, + fmt::Debug, + mem, + ops::DerefMut, +}; +use util::post_inc; + +/// This context is used for assisting in the implementation of the element trait +#[derive(Deref, DerefMut)] +pub struct ElementContext<'a> { + pub(crate) cx: WindowContext<'a>, +} + +impl<'a> WindowContext<'a> { + pub(crate) fn into_element_cx(self) -> ElementContext<'a> { + ElementContext { cx: self } + } +} + +impl<'a> Borrow for ElementContext<'a> { + fn borrow(&self) -> &AppContext { + self.cx.borrow() + } +} + +impl<'a> BorrowMut for ElementContext<'a> { + fn borrow_mut(&mut self) -> &mut AppContext { + self.cx.borrow_mut() + } +} + +impl<'a> Borrow for ElementContext<'a> { + fn borrow(&self) -> &Window { + self.cx.borrow() + } +} + +impl<'a> BorrowMut for ElementContext<'a> { + fn borrow_mut(&mut self) -> &mut Window { + self.cx.borrow_mut() + } +} /// Implemented by types that participate in laying out and painting the contents of a window. /// Elements form a tree and are laid out according to web-based layout rules, as implemented by Taffy. @@ -56,12 +100,12 @@ pub trait Element: 'static + IntoElement { fn request_layout( &mut self, state: Option, - cx: &mut WindowContext, + cx: &mut ElementContext, ) -> (LayoutId, Self::State); /// Once layout has been completed, this method will be called to paint the element to the screen. /// The state argument is the same state that was returned from [`Element::request_layout()`]. - fn paint(&mut self, bounds: Bounds, state: &mut Self::State, cx: &mut WindowContext); + fn paint(&mut self, bounds: Bounds, state: &mut Self::State, cx: &mut ElementContext); /// Convert this element into a dynamically-typed [`AnyElement`]. fn into_any(self) -> AnyElement { @@ -95,8 +139,8 @@ pub trait IntoElement: Sized { self, origin: Point, available_space: Size, - cx: &mut WindowContext, - f: impl FnOnce(&mut ::State, &mut WindowContext) -> R, + cx: &mut ElementContext, + f: impl FnOnce(&mut ::State, &mut ElementContext) -> R, ) -> R where T: Clone + Default + Debug + Into, @@ -193,14 +237,19 @@ impl Element for Component { fn request_layout( &mut self, _: Option, - cx: &mut WindowContext, + cx: &mut ElementContext, ) -> (LayoutId, Self::State) { - let mut element = self.0.take().unwrap().render(cx).into_any_element(); + let mut element = self + .0 + .take() + .unwrap() + .render(cx.deref_mut()) + .into_any_element(); let layout_id = element.request_layout(cx); (layout_id, element) } - fn paint(&mut self, _: Bounds, element: &mut Self::State, cx: &mut WindowContext) { + fn paint(&mut self, _: Bounds, element: &mut Self::State, cx: &mut ElementContext) { element.paint(cx) } } @@ -224,21 +273,21 @@ pub(crate) struct GlobalElementId(SmallVec<[ElementId; 32]>); trait ElementObject { fn element_id(&self) -> Option; - fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId; + fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId; - fn paint(&mut self, cx: &mut WindowContext); + fn paint(&mut self, cx: &mut ElementContext); fn measure( &mut self, available_space: Size, - cx: &mut WindowContext, + cx: &mut ElementContext, ) -> Size; fn draw( &mut self, origin: Point, available_space: Size, - cx: &mut WindowContext, + cx: &mut ElementContext, ); } @@ -276,7 +325,7 @@ impl DrawableElement { self.element.as_ref()?.element_id() } - fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId { + fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId { let (layout_id, frame_state) = if let Some(id) = self.element.as_ref().unwrap().element_id() { let layout_id = cx.with_element_state(id, |element_state, cx| { @@ -298,7 +347,7 @@ impl DrawableElement { layout_id } - fn paint(mut self, cx: &mut WindowContext) -> Option { + fn paint(mut self, cx: &mut ElementContext) -> Option { match self.phase { ElementDrawPhase::LayoutRequested { layout_id, @@ -343,7 +392,7 @@ impl DrawableElement { fn measure( &mut self, available_space: Size, - cx: &mut WindowContext, + cx: &mut ElementContext, ) -> Size { if matches!(&self.phase, ElementDrawPhase::Start) { self.request_layout(cx); @@ -384,7 +433,7 @@ impl DrawableElement { mut self, origin: Point, available_space: Size, - cx: &mut WindowContext, + cx: &mut ElementContext, ) -> Option { self.measure(available_space, cx); cx.with_absolute_element_offset(origin, |cx| self.paint(cx)) @@ -400,18 +449,18 @@ where self.as_ref().unwrap().element_id() } - fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId { + fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId { DrawableElement::request_layout(self.as_mut().unwrap(), cx) } - fn paint(&mut self, cx: &mut WindowContext) { + fn paint(&mut self, cx: &mut ElementContext) { DrawableElement::paint(self.take().unwrap(), cx); } fn measure( &mut self, available_space: Size, - cx: &mut WindowContext, + cx: &mut ElementContext, ) -> Size { DrawableElement::measure(self.as_mut().unwrap(), available_space, cx) } @@ -420,7 +469,7 @@ where &mut self, origin: Point, available_space: Size, - cx: &mut WindowContext, + cx: &mut ElementContext, ) { DrawableElement::draw(self.take().unwrap(), origin, available_space, cx); } @@ -443,12 +492,12 @@ impl AnyElement { /// Request the layout ID of the element stored in this `AnyElement`. /// Used for laying out child elements in a parent element. - pub fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId { + pub fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId { self.0.request_layout(cx) } /// Paints the element stored in this `AnyElement`. - pub fn paint(&mut self, cx: &mut WindowContext) { + pub fn paint(&mut self, cx: &mut ElementContext) { self.0.paint(cx) } @@ -456,7 +505,7 @@ impl AnyElement { pub fn measure( &mut self, available_space: Size, - cx: &mut WindowContext, + cx: &mut ElementContext, ) -> Size { self.0.measure(available_space, cx) } @@ -466,7 +515,7 @@ impl AnyElement { &mut self, origin: Point, available_space: Size, - cx: &mut WindowContext, + cx: &mut ElementContext, ) { self.0.draw(origin, available_space, cx) } @@ -483,13 +532,13 @@ impl Element for AnyElement { fn request_layout( &mut self, _: Option, - cx: &mut WindowContext, + cx: &mut ElementContext, ) -> (LayoutId, Self::State) { let layout_id = self.request_layout(cx); (layout_id, ()) } - fn paint(&mut self, _: Bounds, _: &mut Self::State, cx: &mut WindowContext) { + fn paint(&mut self, _: Bounds, _: &mut Self::State, cx: &mut ElementContext) { self.paint(cx) } } @@ -531,7 +580,7 @@ impl Element for () { fn request_layout( &mut self, _state: Option, - cx: &mut WindowContext, + cx: &mut ElementContext, ) -> (LayoutId, Self::State) { (cx.request_layout(&crate::Style::default(), None), ()) } @@ -540,7 +589,254 @@ impl Element for () { &mut self, _bounds: Bounds, _state: &mut Self::State, - _cx: &mut WindowContext, + _cx: &mut ElementContext, ) { } } + +impl<'a> ElementContext<'a> { + /// Pushes the given element id onto the global stack and invokes the given closure + /// with a `GlobalElementId`, which disambiguates the given id in the context of its ancestor + /// ids. Because elements are discarded and recreated on each frame, the `GlobalElementId` is + /// used to associate state with identified elements across separate frames. + fn with_element_id( + &mut self, + id: Option>, + f: impl FnOnce(&mut Self) -> R, + ) -> R { + if let Some(id) = id.map(Into::into) { + let window = self.window_mut(); + window.element_id_stack.push(id); + let result = f(self); + let window: &mut Window = self.borrow_mut(); + window.element_id_stack.pop(); + result + } else { + f(self) + } + } + + /// Invoke the given function with the given content mask after intersecting it + /// with the current mask. + fn with_content_mask( + &mut self, + mask: Option>, + f: impl FnOnce(&mut Self) -> R, + ) -> R { + if let Some(mask) = mask { + let mask = mask.intersect(&self.content_mask()); + self.window_mut().next_frame.content_mask_stack.push(mask); + let result = f(self); + self.window_mut().next_frame.content_mask_stack.pop(); + result + } else { + f(self) + } + } + + /// Invoke the given function with the content mask reset to that + /// of the window. + fn break_content_mask(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { + let mask = ContentMask { + bounds: Bounds { + origin: Point::default(), + size: self.window().viewport_size, + }, + }; + let new_stacking_order_id = + post_inc(&mut self.window_mut().next_frame.next_stacking_order_id); + let new_root_z_index = post_inc(&mut self.window_mut().next_frame.next_root_z_index); + let old_stacking_order = mem::take(&mut self.window_mut().next_frame.z_index_stack); + self.window_mut().next_frame.z_index_stack.id = new_stacking_order_id; + self.window_mut() + .next_frame + .z_index_stack + .push(new_root_z_index); + self.window_mut().next_frame.content_mask_stack.push(mask); + let result = f(self); + self.window_mut().next_frame.content_mask_stack.pop(); + self.window_mut().next_frame.z_index_stack = old_stacking_order; + result + } + + /// Called during painting to invoke the given closure in a new stacking context. The given + /// z-index is interpreted relative to the previous call to `stack`. + fn with_z_index(&mut self, z_index: u8, f: impl FnOnce(&mut Self) -> R) -> R { + let new_stacking_order_id = + post_inc(&mut self.window_mut().next_frame.next_stacking_order_id); + let old_stacking_order_id = mem::replace( + &mut self.window_mut().next_frame.z_index_stack.id, + new_stacking_order_id, + ); + self.window_mut().next_frame.z_index_stack.id = new_stacking_order_id; + self.window_mut().next_frame.z_index_stack.push(z_index); + let result = f(self); + self.window_mut().next_frame.z_index_stack.id = old_stacking_order_id; + self.window_mut().next_frame.z_index_stack.pop(); + result + } + + /// Updates the global element offset relative to the current offset. This is used to implement + /// scrolling. + fn with_element_offset( + &mut self, + offset: Point, + f: impl FnOnce(&mut Self) -> R, + ) -> R { + if offset.is_zero() { + return f(self); + }; + + let abs_offset = self.element_offset() + offset; + self.with_absolute_element_offset(abs_offset, f) + } + + /// Updates the global element offset based on the given offset. This is used to implement + /// drag handles and other manual painting of elements. + fn with_absolute_element_offset( + &mut self, + offset: Point, + f: impl FnOnce(&mut Self) -> R, + ) -> R { + self.window_mut() + .next_frame + .element_offset_stack + .push(offset); + let result = f(self); + self.window_mut().next_frame.element_offset_stack.pop(); + result + } + + /// Obtain the current element offset. + fn element_offset(&self) -> Point { + self.window() + .next_frame + .element_offset_stack + .last() + .copied() + .unwrap_or_default() + } + + /// Obtain the current content mask. + fn content_mask(&self) -> ContentMask { + self.window() + .next_frame + .content_mask_stack + .last() + .cloned() + .unwrap_or_else(|| ContentMask { + bounds: Bounds { + origin: Point::default(), + size: self.window().viewport_size, + }, + }) + } + + /// The size of an em for the base font of the application. Adjusting this value allows the + /// UI to scale, just like zooming a web page. + fn rem_size(&self) -> Pixels { + self.window().rem_size + } + + fn parent_view_id(&self) -> EntityId { + *self + .window + .next_frame + .view_stack + .last() + .expect("a view should always be on the stack while drawing") + } + + /// Updates or initializes state for an element with the given id that lives across multiple + /// frames. If an element with this ID existed in the rendered frame, its state will be passed + /// to the given closure. The state returned by the closure will be stored so it can be referenced + /// when drawing the next frame. + pub(crate) fn with_element_state( + &mut self, + id: ElementId, + f: impl FnOnce(Option, &mut Self) -> (R, S), + ) -> R + where + S: 'static, + { + self.with_element_id(Some(id), |cx| { + let global_id = cx.window().element_id_stack.clone(); + + if let Some(any) = cx + .window_mut() + .next_frame + .element_states + .remove(&global_id) + .or_else(|| { + cx.window_mut() + .rendered_frame + .element_states + .remove(&global_id) + }) + { + let ElementStateBox { + inner, + parent_view_id, + #[cfg(debug_assertions)] + type_name + } = any; + // Using the extra inner option to avoid needing to reallocate a new box. + let mut state_box = inner + .downcast::>() + .map_err(|_| { + #[cfg(debug_assertions)] + { + anyhow::anyhow!( + "invalid element state type for id, requested_type {:?}, actual type: {:?}", + std::any::type_name::(), + type_name + ) + } + + #[cfg(not(debug_assertions))] + { + anyhow::anyhow!( + "invalid element state type for id, requested_type {:?}", + std::any::type_name::(), + ) + } + }) + .unwrap(); + + // Actual: Option <- View + // Requested: () <- AnyElement + let state = state_box + .take() + .expect("element state is already on the stack"); + let (result, state) = f(Some(state), cx); + state_box.replace(state); + cx.window_mut() + .next_frame + .element_states + .insert(global_id, ElementStateBox { + inner: state_box, + parent_view_id, + #[cfg(debug_assertions)] + type_name + }); + result + } else { + let (result, state) = f(None, cx); + let parent_view_id = cx.parent_view_id(); + cx.window_mut() + .next_frame + .element_states + .insert(global_id, + ElementStateBox { + inner: Box::new(Some(state)), + parent_view_id, + #[cfg(debug_assertions)] + type_name: std::any::type_name::() + } + + ); + result + } + }) + } +} diff --git a/crates/gpui/src/elements/div.rs b/crates/gpui/src/elements/div.rs index 624630404c9393dce5887fb9a551245c1c90091f..096215d658657af323079af52109d3287f08417f 100644 --- a/crates/gpui/src/elements/div.rs +++ b/crates/gpui/src/elements/div.rs @@ -24,11 +24,10 @@ use crate::{ point, px, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, BorrowAppContext, - BorrowWindow, Bounds, ClickEvent, DispatchPhase, Element, ElementId, FocusHandle, IntoElement, - IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent, - MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Render, ScrollWheelEvent, - SharedString, Size, StackingOrder, Style, StyleRefinement, Styled, Task, View, Visibility, - WindowContext, + Bounds, ClickEvent, DispatchPhase, Element, ElementId, FocusHandle, IntoElement, IsZero, + KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent, MouseMoveEvent, + MouseUpEvent, ParentElement, Pixels, Point, Render, ScrollWheelEvent, SharedString, Size, + StackingOrder, Style, StyleRefinement, Styled, Task, View, Visibility, WindowContext, }; use collections::HashMap; @@ -41,6 +40,7 @@ use std::{ fmt::Debug, marker::PhantomData, mem, + ops::DerefMut, rc::Rc, time::Duration, }; @@ -1540,12 +1540,17 @@ impl Interactivity { let mut can_drop = true; if let Some(predicate) = &can_drop_predicate { - can_drop = - predicate(drag.value.as_ref(), cx); + can_drop = predicate( + drag.value.as_ref(), + cx.deref_mut(), + ); } if can_drop { - listener(drag.value.as_ref(), cx); + listener( + drag.value.as_ref(), + cx.deref_mut(), + ); cx.refresh(); cx.stop_propagation(); } @@ -1676,7 +1681,7 @@ impl Interactivity { *was_hovered = is_hovered; drop(was_hovered); - hover_listener(&is_hovered, cx); + hover_listener(&is_hovered, cx.deref_mut()); } }); } @@ -1921,7 +1926,9 @@ impl Interactivity { let mouse_position = cx.mouse_position(); if !cx.has_active_drag() { if let Some(group_hover) = self.group_hover_style.as_ref() { - if let Some(group_bounds) = GroupBounds::get(&group_hover.group, cx) { + if let Some(group_bounds) = + GroupBounds::get(&group_hover.group, cx.deref_mut()) + { if group_bounds.contains(&mouse_position) && cx.was_top_layer(&mouse_position, cx.stacking_order()) { @@ -1944,13 +1951,13 @@ impl Interactivity { if let Some(drag) = cx.active_drag.take() { let mut can_drop = true; if let Some(can_drop_predicate) = &self.can_drop_predicate { - can_drop = can_drop_predicate(drag.value.as_ref(), cx); + can_drop = can_drop_predicate(drag.value.as_ref(), cx.deref_mut()); } if can_drop { for (state_type, group_drag_style) in &self.group_drag_over_styles { if let Some(group_bounds) = - GroupBounds::get(&group_drag_style.group, cx) + GroupBounds::get(&group_drag_style.group, cx.deref_mut()) { if *state_type == drag.value.as_ref().type_id() && group_bounds.contains(&mouse_position) diff --git a/crates/gpui/src/elements/img.rs b/crates/gpui/src/elements/img.rs index e9a2e0676d9158c50454595f32f097f5b34043c8..505879b901ce8263f6c70ac5be48a2c8d28c8ad9 100644 --- a/crates/gpui/src/elements/img.rs +++ b/crates/gpui/src/elements/img.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use crate::{ - point, size, BorrowWindow, Bounds, DevicePixels, Element, ImageData, InteractiveElement, + point, size, Bounds, DevicePixels, Element, ImageData, InteractiveElement, InteractiveElementState, Interactivity, IntoElement, LayoutId, Pixels, SharedUrl, Size, StyleRefinement, Styled, WindowContext, }; diff --git a/crates/gpui/src/elements/list.rs b/crates/gpui/src/elements/list.rs index a3ed95ef33f1499baa205a785b6a0ec689f5d4e9..ec69f9a973fcbb1fae8e1b7c6d41274341e5b53b 100644 --- a/crates/gpui/src/elements/list.rs +++ b/crates/gpui/src/elements/list.rs @@ -7,9 +7,9 @@ //! If all of your elements are the same height, see [`UniformList`] for a simpler API use crate::{ - point, px, AnyElement, AvailableSpace, BorrowAppContext, BorrowWindow, Bounds, ContentMask, - DispatchPhase, Element, IntoElement, Pixels, Point, ScrollWheelEvent, Size, Style, - StyleRefinement, Styled, WindowContext, + point, px, AnyElement, AvailableSpace, BorrowAppContext, Bounds, ContentMask, DispatchPhase, + Element, IntoElement, Pixels, Point, ScrollWheelEvent, Size, Style, StyleRefinement, Styled, + WindowContext, }; use collections::VecDeque; use refineable::Refineable as _; diff --git a/crates/gpui/src/elements/uniform_list.rs b/crates/gpui/src/elements/uniform_list.rs index e92d920666d1d164ef47e50f4f5cea181cae1484..a5c586304eec7a9d271e5dcf1866616b27eeb4f3 100644 --- a/crates/gpui/src/elements/uniform_list.rs +++ b/crates/gpui/src/elements/uniform_list.rs @@ -5,9 +5,9 @@ //! elements with uniform height. use crate::{ - point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, ContentMask, Element, - ElementId, InteractiveElement, InteractiveElementState, Interactivity, IntoElement, LayoutId, - Pixels, Render, Size, StyleRefinement, Styled, View, ViewContext, WindowContext, + point, px, size, AnyElement, AvailableSpace, Bounds, ContentMask, Element, ElementId, + InteractiveElement, InteractiveElementState, Interactivity, IntoElement, LayoutId, Pixels, + Render, Size, StyleRefinement, Styled, View, ViewContext, WindowContext, }; use smallvec::SmallVec; use std::{cell::RefCell, cmp, ops::Range, rc::Rc}; diff --git a/crates/gpui/src/text_system/line.rs b/crates/gpui/src/text_system/line.rs index a89fa30dfafdd51255339e070f146f097f3fd5ef..94121503ae8b100fb7f8565b38b3af174a4c6886 100644 --- a/crates/gpui/src/text_system/line.rs +++ b/crates/gpui/src/text_system/line.rs @@ -1,6 +1,6 @@ use crate::{ - black, fill, point, px, size, BorrowWindow, Bounds, Hsla, LineLayout, Pixels, Point, Result, - SharedString, UnderlineStyle, WindowContext, WrapBoundary, WrappedLineLayout, + black, fill, point, px, size, Bounds, Hsla, LineLayout, Pixels, Point, Result, SharedString, + UnderlineStyle, WindowContext, WrapBoundary, WrappedLineLayout, }; use derive_more::{Deref, DerefMut}; use smallvec::SmallVec; diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 9697d162aee3dc0e97f2dc5320653dcbc0fddb0f..39771c6fc992c1a3ec194c4add2708cc91814667 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -39,7 +39,7 @@ use std::{ Arc, }, }; -use util::{post_inc, ResultExt}; +use util::ResultExt; const ACTIVE_DRAG_Z_INDEX: u8 = 1; @@ -50,7 +50,7 @@ pub struct StackingOrder { #[deref] #[deref_mut] context_stack: SmallVec<[u8; 64]>, - id: u32, + pub(crate) id: u32, } impl std::fmt::Debug for StackingOrder { @@ -258,8 +258,8 @@ pub struct Window { pub(crate) platform_window: Box, display_id: DisplayId, sprite_atlas: Arc, - rem_size: Pixels, - viewport_size: Size, + pub(crate) rem_size: Pixels, + pub(crate) viewport_size: Size, layout_engine: Option, pub(crate) root_view: Option, pub(crate) element_id_stack: GlobalElementId, @@ -287,13 +287,6 @@ pub struct Window { pub(crate) focus_invalidated: bool, } -pub(crate) struct ElementStateBox { - inner: Box, - parent_view_id: EntityId, - #[cfg(debug_assertions)] - type_name: &'static str, -} - struct RequestedInputHandler { view_id: EntityId, handler: Option, @@ -304,6 +297,13 @@ struct TooltipRequest { tooltip: AnyTooltip, } +pub(crate) struct ElementStateBox { + pub(crate) inner: Box, + pub(crate) parent_view_id: EntityId, + #[cfg(debug_assertions)] + pub(crate) type_name: &'static str, +} + pub(crate) struct Frame { focus: Option, window_active: bool, @@ -314,9 +314,9 @@ pub(crate) struct Frame { pub(crate) depth_map: Vec<(StackingOrder, EntityId, Bounds)>, pub(crate) z_index_stack: StackingOrder, pub(crate) next_stacking_order_id: u32, - next_root_z_index: u8, - content_mask_stack: Vec>, - element_offset_stack: Vec>, + pub(crate) next_root_z_index: u8, + pub(crate) content_mask_stack: Vec>, + pub(crate) element_offset_stack: Vec>, requested_input_handler: Option, tooltip_request: Option, cursor_styles: FxHashMap, @@ -2078,108 +2078,6 @@ impl<'a> WindowContext<'a> { }) } - /// Updates or initializes state for an element with the given id that lives across multiple - /// frames. If an element with this ID existed in the rendered frame, its state will be passed - /// to the given closure. The state returned by the closure will be stored so it can be referenced - /// when drawing the next frame. - pub(crate) fn with_element_state( - &mut self, - id: ElementId, - f: impl FnOnce(Option, &mut Self) -> (R, S), - ) -> R - where - S: 'static, - { - self.with_element_id(Some(id), |cx| { - let global_id = cx.window().element_id_stack.clone(); - - if let Some(any) = cx - .window_mut() - .next_frame - .element_states - .remove(&global_id) - .or_else(|| { - cx.window_mut() - .rendered_frame - .element_states - .remove(&global_id) - }) - { - let ElementStateBox { - inner, - parent_view_id, - #[cfg(debug_assertions)] - type_name - } = any; - // Using the extra inner option to avoid needing to reallocate a new box. - let mut state_box = inner - .downcast::>() - .map_err(|_| { - #[cfg(debug_assertions)] - { - anyhow!( - "invalid element state type for id, requested_type {:?}, actual type: {:?}", - std::any::type_name::(), - type_name - ) - } - - #[cfg(not(debug_assertions))] - { - anyhow!( - "invalid element state type for id, requested_type {:?}", - std::any::type_name::(), - ) - } - }) - .unwrap(); - - // Actual: Option <- View - // Requested: () <- AnyElement - let state = state_box - .take() - .expect("element state is already on the stack"); - let (result, state) = f(Some(state), cx); - state_box.replace(state); - cx.window_mut() - .next_frame - .element_states - .insert(global_id, ElementStateBox { - inner: state_box, - parent_view_id, - #[cfg(debug_assertions)] - type_name - }); - result - } else { - let (result, state) = f(None, cx); - let parent_view_id = cx.parent_view_id(); - cx.window_mut() - .next_frame - .element_states - .insert(global_id, - ElementStateBox { - inner: Box::new(Some(state)), - parent_view_id, - #[cfg(debug_assertions)] - type_name: std::any::type_name::() - } - - ); - result - } - }) - } - - fn parent_view_id(&self) -> EntityId { - *self - .window - .next_frame - .view_stack - .last() - .expect("a view should always be on the stack while drawing") - } - /// Sets an input handler, such as [`ElementInputHandler`][element_input_handler], which interfaces with the /// platform to receive textual input with proper integration with concerns such /// as IME interactions. This handler will be active for the upcoming frame until the following frame is @@ -2406,149 +2304,6 @@ pub trait BorrowWindow: BorrowMut + BorrowMut { fn window_mut(&mut self) -> &mut Window { self.borrow_mut() } - - /// Pushes the given element id onto the global stack and invokes the given closure - /// with a `GlobalElementId`, which disambiguates the given id in the context of its ancestor - /// ids. Because elements are discarded and recreated on each frame, the `GlobalElementId` is - /// used to associate state with identified elements across separate frames. - fn with_element_id( - &mut self, - id: Option>, - f: impl FnOnce(&mut Self) -> R, - ) -> R { - if let Some(id) = id.map(Into::into) { - let window = self.window_mut(); - window.element_id_stack.push(id); - let result = f(self); - let window: &mut Window = self.borrow_mut(); - window.element_id_stack.pop(); - result - } else { - f(self) - } - } - - /// Invoke the given function with the given content mask after intersecting it - /// with the current mask. - fn with_content_mask( - &mut self, - mask: Option>, - f: impl FnOnce(&mut Self) -> R, - ) -> R { - if let Some(mask) = mask { - let mask = mask.intersect(&self.content_mask()); - self.window_mut().next_frame.content_mask_stack.push(mask); - let result = f(self); - self.window_mut().next_frame.content_mask_stack.pop(); - result - } else { - f(self) - } - } - - /// Invoke the given function with the content mask reset to that - /// of the window. - fn break_content_mask(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { - let mask = ContentMask { - bounds: Bounds { - origin: Point::default(), - size: self.window().viewport_size, - }, - }; - let new_stacking_order_id = - post_inc(&mut self.window_mut().next_frame.next_stacking_order_id); - let new_root_z_index = post_inc(&mut self.window_mut().next_frame.next_root_z_index); - let old_stacking_order = mem::take(&mut self.window_mut().next_frame.z_index_stack); - self.window_mut().next_frame.z_index_stack.id = new_stacking_order_id; - self.window_mut() - .next_frame - .z_index_stack - .push(new_root_z_index); - self.window_mut().next_frame.content_mask_stack.push(mask); - let result = f(self); - self.window_mut().next_frame.content_mask_stack.pop(); - self.window_mut().next_frame.z_index_stack = old_stacking_order; - result - } - - /// Called during painting to invoke the given closure in a new stacking context. The given - /// z-index is interpreted relative to the previous call to `stack`. - fn with_z_index(&mut self, z_index: u8, f: impl FnOnce(&mut Self) -> R) -> R { - let new_stacking_order_id = - post_inc(&mut self.window_mut().next_frame.next_stacking_order_id); - let old_stacking_order_id = mem::replace( - &mut self.window_mut().next_frame.z_index_stack.id, - new_stacking_order_id, - ); - self.window_mut().next_frame.z_index_stack.id = new_stacking_order_id; - self.window_mut().next_frame.z_index_stack.push(z_index); - let result = f(self); - self.window_mut().next_frame.z_index_stack.id = old_stacking_order_id; - self.window_mut().next_frame.z_index_stack.pop(); - result - } - - /// Updates the global element offset relative to the current offset. This is used to implement - /// scrolling. - fn with_element_offset( - &mut self, - offset: Point, - f: impl FnOnce(&mut Self) -> R, - ) -> R { - if offset.is_zero() { - return f(self); - }; - - let abs_offset = self.element_offset() + offset; - self.with_absolute_element_offset(abs_offset, f) - } - - /// Updates the global element offset based on the given offset. This is used to implement - /// drag handles and other manual painting of elements. - fn with_absolute_element_offset( - &mut self, - offset: Point, - f: impl FnOnce(&mut Self) -> R, - ) -> R { - self.window_mut() - .next_frame - .element_offset_stack - .push(offset); - let result = f(self); - self.window_mut().next_frame.element_offset_stack.pop(); - result - } - - /// Obtain the current element offset. - fn element_offset(&self) -> Point { - self.window() - .next_frame - .element_offset_stack - .last() - .copied() - .unwrap_or_default() - } - - /// Obtain the current content mask. - fn content_mask(&self) -> ContentMask { - self.window() - .next_frame - .content_mask_stack - .last() - .cloned() - .unwrap_or_else(|| ContentMask { - bounds: Bounds { - origin: Point::default(), - size: self.window().viewport_size, - }, - }) - } - - /// The size of an em for the base font of the application. Adjusting this value allows the - /// UI to scale, just like zooming a web page. - fn rem_size(&self) -> Pixels { - self.window().rem_size - } } impl Borrow for WindowContext<'_> {