Do an initial pass on refactoring out ElementContext from WindowContext

This commit is contained in:
Mikayla 2024-01-21 18:40:20 -08:00
parent a8990baaac
commit 2f9958621b
No known key found for this signature in database
7 changed files with 368 additions and 310 deletions

View file

@ -35,12 +35,56 @@
//! your own custom layout algorithm or rendering a code editor. //! your own custom layout algorithm or rendering a code editor.
use crate::{ use crate::{
util::FluentBuilder, ArenaBox, AvailableSpace, BorrowWindow, Bounds, ElementId, LayoutId, util::FluentBuilder, AppContext, ArenaBox, AvailableSpace, BorrowWindow, Bounds, ContentMask,
Pixels, Point, Size, ViewContext, WindowContext, ELEMENT_ARENA, ElementId, ElementStateBox, EntityId, IsZero, LayoutId, Pixels, Point, Size, ViewContext,
Window, WindowContext, ELEMENT_ARENA,
}; };
use derive_more::{Deref, DerefMut}; use derive_more::{Deref, DerefMut};
pub(crate) use smallvec::SmallVec; 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<AppContext> for ElementContext<'a> {
fn borrow(&self) -> &AppContext {
self.cx.borrow()
}
}
impl<'a> BorrowMut<AppContext> for ElementContext<'a> {
fn borrow_mut(&mut self) -> &mut AppContext {
self.cx.borrow_mut()
}
}
impl<'a> Borrow<Window> for ElementContext<'a> {
fn borrow(&self) -> &Window {
self.cx.borrow()
}
}
impl<'a> BorrowMut<Window> 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. /// 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. /// 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( fn request_layout(
&mut self, &mut self,
state: Option<Self::State>, state: Option<Self::State>,
cx: &mut WindowContext, cx: &mut ElementContext,
) -> (LayoutId, Self::State); ) -> (LayoutId, Self::State);
/// Once layout has been completed, this method will be called to paint the element to the screen. /// 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()`]. /// The state argument is the same state that was returned from [`Element::request_layout()`].
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext); fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext);
/// Convert this element into a dynamically-typed [`AnyElement`]. /// Convert this element into a dynamically-typed [`AnyElement`].
fn into_any(self) -> AnyElement { fn into_any(self) -> AnyElement {
@ -95,8 +139,8 @@ pub trait IntoElement: Sized {
self, self,
origin: Point<Pixels>, origin: Point<Pixels>,
available_space: Size<T>, available_space: Size<T>,
cx: &mut WindowContext, cx: &mut ElementContext,
f: impl FnOnce(&mut <Self::Element as Element>::State, &mut WindowContext) -> R, f: impl FnOnce(&mut <Self::Element as Element>::State, &mut ElementContext) -> R,
) -> R ) -> R
where where
T: Clone + Default + Debug + Into<AvailableSpace>, T: Clone + Default + Debug + Into<AvailableSpace>,
@ -193,14 +237,19 @@ impl<C: RenderOnce> Element for Component<C> {
fn request_layout( fn request_layout(
&mut self, &mut self,
_: Option<Self::State>, _: Option<Self::State>,
cx: &mut WindowContext, cx: &mut ElementContext,
) -> (LayoutId, Self::State) { ) -> (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); let layout_id = element.request_layout(cx);
(layout_id, element) (layout_id, element)
} }
fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut WindowContext) { fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut ElementContext) {
element.paint(cx) element.paint(cx)
} }
} }
@ -224,21 +273,21 @@ pub(crate) struct GlobalElementId(SmallVec<[ElementId; 32]>);
trait ElementObject { trait ElementObject {
fn element_id(&self) -> Option<ElementId>; fn element_id(&self) -> Option<ElementId>;
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( fn measure(
&mut self, &mut self,
available_space: Size<AvailableSpace>, available_space: Size<AvailableSpace>,
cx: &mut WindowContext, cx: &mut ElementContext,
) -> Size<Pixels>; ) -> Size<Pixels>;
fn draw( fn draw(
&mut self, &mut self,
origin: Point<Pixels>, origin: Point<Pixels>,
available_space: Size<AvailableSpace>, available_space: Size<AvailableSpace>,
cx: &mut WindowContext, cx: &mut ElementContext,
); );
} }
@ -276,7 +325,7 @@ impl<E: Element> DrawableElement<E> {
self.element.as_ref()?.element_id() 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, frame_state) = if let Some(id) = self.element.as_ref().unwrap().element_id()
{ {
let layout_id = cx.with_element_state(id, |element_state, cx| { let layout_id = cx.with_element_state(id, |element_state, cx| {
@ -298,7 +347,7 @@ impl<E: Element> DrawableElement<E> {
layout_id layout_id
} }
fn paint(mut self, cx: &mut WindowContext) -> Option<E::State> { fn paint(mut self, cx: &mut ElementContext) -> Option<E::State> {
match self.phase { match self.phase {
ElementDrawPhase::LayoutRequested { ElementDrawPhase::LayoutRequested {
layout_id, layout_id,
@ -343,7 +392,7 @@ impl<E: Element> DrawableElement<E> {
fn measure( fn measure(
&mut self, &mut self,
available_space: Size<AvailableSpace>, available_space: Size<AvailableSpace>,
cx: &mut WindowContext, cx: &mut ElementContext,
) -> Size<Pixels> { ) -> Size<Pixels> {
if matches!(&self.phase, ElementDrawPhase::Start) { if matches!(&self.phase, ElementDrawPhase::Start) {
self.request_layout(cx); self.request_layout(cx);
@ -384,7 +433,7 @@ impl<E: Element> DrawableElement<E> {
mut self, mut self,
origin: Point<Pixels>, origin: Point<Pixels>,
available_space: Size<AvailableSpace>, available_space: Size<AvailableSpace>,
cx: &mut WindowContext, cx: &mut ElementContext,
) -> Option<E::State> { ) -> Option<E::State> {
self.measure(available_space, cx); self.measure(available_space, cx);
cx.with_absolute_element_offset(origin, |cx| self.paint(cx)) cx.with_absolute_element_offset(origin, |cx| self.paint(cx))
@ -400,18 +449,18 @@ where
self.as_ref().unwrap().element_id() 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) 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); DrawableElement::paint(self.take().unwrap(), cx);
} }
fn measure( fn measure(
&mut self, &mut self,
available_space: Size<AvailableSpace>, available_space: Size<AvailableSpace>,
cx: &mut WindowContext, cx: &mut ElementContext,
) -> Size<Pixels> { ) -> Size<Pixels> {
DrawableElement::measure(self.as_mut().unwrap(), available_space, cx) DrawableElement::measure(self.as_mut().unwrap(), available_space, cx)
} }
@ -420,7 +469,7 @@ where
&mut self, &mut self,
origin: Point<Pixels>, origin: Point<Pixels>,
available_space: Size<AvailableSpace>, available_space: Size<AvailableSpace>,
cx: &mut WindowContext, cx: &mut ElementContext,
) { ) {
DrawableElement::draw(self.take().unwrap(), origin, available_space, cx); 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`. /// Request the layout ID of the element stored in this `AnyElement`.
/// Used for laying out child elements in a parent element. /// 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) self.0.request_layout(cx)
} }
/// Paints the element stored in this `AnyElement`. /// 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) self.0.paint(cx)
} }
@ -456,7 +505,7 @@ impl AnyElement {
pub fn measure( pub fn measure(
&mut self, &mut self,
available_space: Size<AvailableSpace>, available_space: Size<AvailableSpace>,
cx: &mut WindowContext, cx: &mut ElementContext,
) -> Size<Pixels> { ) -> Size<Pixels> {
self.0.measure(available_space, cx) self.0.measure(available_space, cx)
} }
@ -466,7 +515,7 @@ impl AnyElement {
&mut self, &mut self,
origin: Point<Pixels>, origin: Point<Pixels>,
available_space: Size<AvailableSpace>, available_space: Size<AvailableSpace>,
cx: &mut WindowContext, cx: &mut ElementContext,
) { ) {
self.0.draw(origin, available_space, cx) self.0.draw(origin, available_space, cx)
} }
@ -483,13 +532,13 @@ impl Element for AnyElement {
fn request_layout( fn request_layout(
&mut self, &mut self,
_: Option<Self::State>, _: Option<Self::State>,
cx: &mut WindowContext, cx: &mut ElementContext,
) -> (LayoutId, Self::State) { ) -> (LayoutId, Self::State) {
let layout_id = self.request_layout(cx); let layout_id = self.request_layout(cx);
(layout_id, ()) (layout_id, ())
} }
fn paint(&mut self, _: Bounds<Pixels>, _: &mut Self::State, cx: &mut WindowContext) { fn paint(&mut self, _: Bounds<Pixels>, _: &mut Self::State, cx: &mut ElementContext) {
self.paint(cx) self.paint(cx)
} }
} }
@ -531,7 +580,7 @@ impl Element for () {
fn request_layout( fn request_layout(
&mut self, &mut self,
_state: Option<Self::State>, _state: Option<Self::State>,
cx: &mut WindowContext, cx: &mut ElementContext,
) -> (LayoutId, Self::State) { ) -> (LayoutId, Self::State) {
(cx.request_layout(&crate::Style::default(), None), ()) (cx.request_layout(&crate::Style::default(), None), ())
} }
@ -540,7 +589,254 @@ impl Element for () {
&mut self, &mut self,
_bounds: Bounds<Pixels>, _bounds: Bounds<Pixels>,
_state: &mut Self::State, _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<R>(
&mut self,
id: Option<impl Into<ElementId>>,
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<R>(
&mut self,
mask: Option<ContentMask<Pixels>>,
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<R>(&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<R>(&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<R>(
&mut self,
offset: Point<Pixels>,
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<R>(
&mut self,
offset: Point<Pixels>,
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<Pixels> {
self.window()
.next_frame
.element_offset_stack
.last()
.copied()
.unwrap_or_default()
}
/// Obtain the current content mask.
fn content_mask(&self) -> ContentMask<Pixels> {
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<S, R>(
&mut self,
id: ElementId,
f: impl FnOnce(Option<S>, &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::<Option<S>>()
.map_err(|_| {
#[cfg(debug_assertions)]
{
anyhow::anyhow!(
"invalid element state type for id, requested_type {:?}, actual type: {:?}",
std::any::type_name::<S>(),
type_name
)
}
#[cfg(not(debug_assertions))]
{
anyhow::anyhow!(
"invalid element state type for id, requested_type {:?}",
std::any::type_name::<S>(),
)
}
})
.unwrap();
// Actual: Option<AnyElement> <- 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::<S>()
}
);
result
}
})
}
}

View file

@ -24,11 +24,10 @@
use crate::{ use crate::{
point, px, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, BorrowAppContext, point, px, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, BorrowAppContext,
BorrowWindow, Bounds, ClickEvent, DispatchPhase, Element, ElementId, FocusHandle, IntoElement, Bounds, ClickEvent, DispatchPhase, Element, ElementId, FocusHandle, IntoElement, IsZero,
IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent, MouseMoveEvent,
MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Render, ScrollWheelEvent, MouseUpEvent, ParentElement, Pixels, Point, Render, ScrollWheelEvent, SharedString, Size,
SharedString, Size, StackingOrder, Style, StyleRefinement, Styled, Task, View, Visibility, StackingOrder, Style, StyleRefinement, Styled, Task, View, Visibility, WindowContext,
WindowContext,
}; };
use collections::HashMap; use collections::HashMap;
@ -41,6 +40,7 @@ use std::{
fmt::Debug, fmt::Debug,
marker::PhantomData, marker::PhantomData,
mem, mem,
ops::DerefMut,
rc::Rc, rc::Rc,
time::Duration, time::Duration,
}; };
@ -1540,12 +1540,17 @@ impl Interactivity {
let mut can_drop = true; let mut can_drop = true;
if let Some(predicate) = &can_drop_predicate { if let Some(predicate) = &can_drop_predicate {
can_drop = can_drop = predicate(
predicate(drag.value.as_ref(), cx); drag.value.as_ref(),
cx.deref_mut(),
);
} }
if can_drop { if can_drop {
listener(drag.value.as_ref(), cx); listener(
drag.value.as_ref(),
cx.deref_mut(),
);
cx.refresh(); cx.refresh();
cx.stop_propagation(); cx.stop_propagation();
} }
@ -1676,7 +1681,7 @@ impl Interactivity {
*was_hovered = is_hovered; *was_hovered = is_hovered;
drop(was_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(); let mouse_position = cx.mouse_position();
if !cx.has_active_drag() { if !cx.has_active_drag() {
if let Some(group_hover) = self.group_hover_style.as_ref() { 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) if group_bounds.contains(&mouse_position)
&& cx.was_top_layer(&mouse_position, cx.stacking_order()) && cx.was_top_layer(&mouse_position, cx.stacking_order())
{ {
@ -1944,13 +1951,13 @@ impl Interactivity {
if let Some(drag) = cx.active_drag.take() { if let Some(drag) = cx.active_drag.take() {
let mut can_drop = true; let mut can_drop = true;
if let Some(can_drop_predicate) = &self.can_drop_predicate { 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 { if can_drop {
for (state_type, group_drag_style) in &self.group_drag_over_styles { for (state_type, group_drag_style) in &self.group_drag_over_styles {
if let Some(group_bounds) = 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() if *state_type == drag.value.as_ref().type_id()
&& group_bounds.contains(&mouse_position) && group_bounds.contains(&mouse_position)

View file

@ -1,7 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use crate::{ use crate::{
point, size, BorrowWindow, Bounds, DevicePixels, Element, ImageData, InteractiveElement, point, size, Bounds, DevicePixels, Element, ImageData, InteractiveElement,
InteractiveElementState, Interactivity, IntoElement, LayoutId, Pixels, SharedUrl, Size, InteractiveElementState, Interactivity, IntoElement, LayoutId, Pixels, SharedUrl, Size,
StyleRefinement, Styled, WindowContext, StyleRefinement, Styled, WindowContext,
}; };

View file

@ -7,9 +7,9 @@
//! If all of your elements are the same height, see [`UniformList`] for a simpler API //! If all of your elements are the same height, see [`UniformList`] for a simpler API
use crate::{ use crate::{
point, px, AnyElement, AvailableSpace, BorrowAppContext, BorrowWindow, Bounds, ContentMask, point, px, AnyElement, AvailableSpace, BorrowAppContext, Bounds, ContentMask, DispatchPhase,
DispatchPhase, Element, IntoElement, Pixels, Point, ScrollWheelEvent, Size, Style, Element, IntoElement, Pixels, Point, ScrollWheelEvent, Size, Style, StyleRefinement, Styled,
StyleRefinement, Styled, WindowContext, WindowContext,
}; };
use collections::VecDeque; use collections::VecDeque;
use refineable::Refineable as _; use refineable::Refineable as _;

View file

@ -5,9 +5,9 @@
//! elements with uniform height. //! elements with uniform height.
use crate::{ use crate::{
point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, ContentMask, Element, point, px, size, AnyElement, AvailableSpace, Bounds, ContentMask, Element, ElementId,
ElementId, InteractiveElement, InteractiveElementState, Interactivity, IntoElement, LayoutId, InteractiveElement, InteractiveElementState, Interactivity, IntoElement, LayoutId, Pixels,
Pixels, Render, Size, StyleRefinement, Styled, View, ViewContext, WindowContext, Render, Size, StyleRefinement, Styled, View, ViewContext, WindowContext,
}; };
use smallvec::SmallVec; use smallvec::SmallVec;
use std::{cell::RefCell, cmp, ops::Range, rc::Rc}; use std::{cell::RefCell, cmp, ops::Range, rc::Rc};

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
black, fill, point, px, size, BorrowWindow, Bounds, Hsla, LineLayout, Pixels, Point, Result, black, fill, point, px, size, Bounds, Hsla, LineLayout, Pixels, Point, Result, SharedString,
SharedString, UnderlineStyle, WindowContext, WrapBoundary, WrappedLineLayout, UnderlineStyle, WindowContext, WrapBoundary, WrappedLineLayout,
}; };
use derive_more::{Deref, DerefMut}; use derive_more::{Deref, DerefMut};
use smallvec::SmallVec; use smallvec::SmallVec;

View file

@ -39,7 +39,7 @@ use std::{
Arc, Arc,
}, },
}; };
use util::{post_inc, ResultExt}; use util::ResultExt;
const ACTIVE_DRAG_Z_INDEX: u8 = 1; const ACTIVE_DRAG_Z_INDEX: u8 = 1;
@ -50,7 +50,7 @@ pub struct StackingOrder {
#[deref] #[deref]
#[deref_mut] #[deref_mut]
context_stack: SmallVec<[u8; 64]>, context_stack: SmallVec<[u8; 64]>,
id: u32, pub(crate) id: u32,
} }
impl std::fmt::Debug for StackingOrder { impl std::fmt::Debug for StackingOrder {
@ -258,8 +258,8 @@ pub struct Window {
pub(crate) platform_window: Box<dyn PlatformWindow>, pub(crate) platform_window: Box<dyn PlatformWindow>,
display_id: DisplayId, display_id: DisplayId,
sprite_atlas: Arc<dyn PlatformAtlas>, sprite_atlas: Arc<dyn PlatformAtlas>,
rem_size: Pixels, pub(crate) rem_size: Pixels,
viewport_size: Size<Pixels>, pub(crate) viewport_size: Size<Pixels>,
layout_engine: Option<TaffyLayoutEngine>, layout_engine: Option<TaffyLayoutEngine>,
pub(crate) root_view: Option<AnyView>, pub(crate) root_view: Option<AnyView>,
pub(crate) element_id_stack: GlobalElementId, pub(crate) element_id_stack: GlobalElementId,
@ -287,13 +287,6 @@ pub struct Window {
pub(crate) focus_invalidated: bool, pub(crate) focus_invalidated: bool,
} }
pub(crate) struct ElementStateBox {
inner: Box<dyn Any>,
parent_view_id: EntityId,
#[cfg(debug_assertions)]
type_name: &'static str,
}
struct RequestedInputHandler { struct RequestedInputHandler {
view_id: EntityId, view_id: EntityId,
handler: Option<PlatformInputHandler>, handler: Option<PlatformInputHandler>,
@ -304,6 +297,13 @@ struct TooltipRequest {
tooltip: AnyTooltip, tooltip: AnyTooltip,
} }
pub(crate) struct ElementStateBox {
pub(crate) inner: Box<dyn Any>,
pub(crate) parent_view_id: EntityId,
#[cfg(debug_assertions)]
pub(crate) type_name: &'static str,
}
pub(crate) struct Frame { pub(crate) struct Frame {
focus: Option<FocusId>, focus: Option<FocusId>,
window_active: bool, window_active: bool,
@ -314,9 +314,9 @@ pub(crate) struct Frame {
pub(crate) depth_map: Vec<(StackingOrder, EntityId, Bounds<Pixels>)>, pub(crate) depth_map: Vec<(StackingOrder, EntityId, Bounds<Pixels>)>,
pub(crate) z_index_stack: StackingOrder, pub(crate) z_index_stack: StackingOrder,
pub(crate) next_stacking_order_id: u32, pub(crate) next_stacking_order_id: u32,
next_root_z_index: u8, pub(crate) next_root_z_index: u8,
content_mask_stack: Vec<ContentMask<Pixels>>, pub(crate) content_mask_stack: Vec<ContentMask<Pixels>>,
element_offset_stack: Vec<Point<Pixels>>, pub(crate) element_offset_stack: Vec<Point<Pixels>>,
requested_input_handler: Option<RequestedInputHandler>, requested_input_handler: Option<RequestedInputHandler>,
tooltip_request: Option<TooltipRequest>, tooltip_request: Option<TooltipRequest>,
cursor_styles: FxHashMap<EntityId, CursorStyle>, cursor_styles: FxHashMap<EntityId, CursorStyle>,
@ -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<S, R>(
&mut self,
id: ElementId,
f: impl FnOnce(Option<S>, &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::<Option<S>>()
.map_err(|_| {
#[cfg(debug_assertions)]
{
anyhow!(
"invalid element state type for id, requested_type {:?}, actual type: {:?}",
std::any::type_name::<S>(),
type_name
)
}
#[cfg(not(debug_assertions))]
{
anyhow!(
"invalid element state type for id, requested_type {:?}",
std::any::type_name::<S>(),
)
}
})
.unwrap();
// Actual: Option<AnyElement> <- 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::<S>()
}
);
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 /// 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 /// 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 /// 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<Window> + BorrowMut<AppContext> {
fn window_mut(&mut self) -> &mut Window { fn window_mut(&mut self) -> &mut Window {
self.borrow_mut() 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<R>(
&mut self,
id: Option<impl Into<ElementId>>,
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<R>(
&mut self,
mask: Option<ContentMask<Pixels>>,
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<R>(&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<R>(&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<R>(
&mut self,
offset: Point<Pixels>,
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<R>(
&mut self,
offset: Point<Pixels>,
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<Pixels> {
self.window()
.next_frame
.element_offset_stack
.last()
.copied()
.unwrap_or_default()
}
/// Obtain the current content mask.
fn content_mask(&self) -> ContentMask<Pixels> {
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<Window> for WindowContext<'_> { impl Borrow<Window> for WindowContext<'_> {