Detailed changes
@@ -25,10 +25,11 @@ use gpui::{
anchored, deferred, div, fill, outline, point, px, quad, relative, size, svg,
transparent_black, Action, AnchorCorner, AnyElement, AvailableSpace, Bounds, ClipboardItem,
ContentMask, Corners, CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Entity,
- Hitbox, Hsla, InteractiveElement, IntoElement, ModifiersChangedEvent, MouseButton,
- MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad, ParentElement, Pixels, ScrollDelta,
- ScrollWheelEvent, ShapedLine, SharedString, Size, Stateful, StatefulInteractiveElement, Style,
- Styled, TextRun, TextStyle, TextStyleRefinement, View, ViewContext, WeakView, WindowContext,
+ GlobalElementId, Hitbox, Hsla, InteractiveElement, IntoElement, ModifiersChangedEvent,
+ MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad, ParentElement, Pixels,
+ ScrollDelta, ScrollWheelEvent, ShapedLine, SharedString, Size, Stateful,
+ StatefulInteractiveElement, Style, Styled, TextRun, TextStyle, TextStyleRefinement, View,
+ ViewContext, WeakView, WindowContext,
};
use itertools::Itertools;
use language::language_settings::ShowWhitespaceSetting;
@@ -2270,7 +2271,7 @@ impl EditorElement {
}
cx.paint_layer(layout.gutter_hitbox.bounds, |cx| {
- cx.with_element_id(Some("gutter_fold_indicators"), |cx| {
+ cx.with_element_namespace("gutter_fold_indicators", |cx| {
for fold_indicator in layout.fold_indicators.iter_mut().flatten() {
fold_indicator.paint(cx);
}
@@ -2419,7 +2420,7 @@ impl EditorElement {
};
cx.set_cursor_style(cursor_style, &layout.text_hitbox);
- cx.with_element_id(Some("folds"), |cx| self.paint_folds(layout, cx));
+ cx.with_element_namespace("folds", |cx| self.paint_folds(layout, cx));
let invisible_display_ranges = self.paint_highlights(layout, cx);
self.paint_lines(&invisible_display_ranges, layout, cx);
self.paint_redactions(layout, cx);
@@ -3446,7 +3447,15 @@ impl Element for EditorElement {
type RequestLayoutState = ();
type PrepaintState = EditorLayout;
- fn request_layout(&mut self, cx: &mut WindowContext) -> (gpui::LayoutId, ()) {
+ fn id(&self) -> Option<ElementId> {
+ None
+ }
+
+ fn request_layout(
+ &mut self,
+ _: Option<&GlobalElementId>,
+ cx: &mut WindowContext,
+ ) -> (gpui::LayoutId, ()) {
self.editor.update(cx, |editor, cx| {
editor.set_style(self.style.clone(), cx);
@@ -3490,6 +3499,7 @@ impl Element for EditorElement {
fn prepaint(
&mut self,
+ _: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
@@ -3666,19 +3676,22 @@ impl Element for EditorElement {
.width;
let mut scroll_width =
longest_line_width.max(max_visible_line_width) + overscroll.width;
- let mut blocks = self.build_blocks(
- start_row..end_row,
- &snapshot,
- &hitbox,
- &text_hitbox,
- &mut scroll_width,
- &gutter_dimensions,
- em_width,
- gutter_dimensions.width + gutter_dimensions.margin,
- line_height,
- &line_layouts,
- cx,
- );
+
+ let mut blocks = cx.with_element_namespace("blocks", |cx| {
+ self.build_blocks(
+ start_row..end_row,
+ &snapshot,
+ &hitbox,
+ &text_hitbox,
+ &mut scroll_width,
+ &gutter_dimensions,
+ em_width,
+ gutter_dimensions.width + gutter_dimensions.margin,
+ line_height,
+ &line_layouts,
+ cx,
+ )
+ });
let scroll_pixel_position = point(
scroll_position.x * em_width,
@@ -3740,7 +3753,7 @@ impl Element for EditorElement {
}
});
- cx.with_element_id(Some("blocks"), |cx| {
+ cx.with_element_namespace("blocks", |cx| {
self.layout_blocks(
&mut blocks,
&hitbox,
@@ -3776,7 +3789,7 @@ impl Element for EditorElement {
cx,
);
- let folds = cx.with_element_id(Some("folds"), |cx| {
+ let folds = cx.with_element_namespace("folds", |cx| {
self.layout_folds(
&snapshot,
content_origin,
@@ -3837,7 +3850,7 @@ impl Element for EditorElement {
let mouse_context_menu = self.layout_mouse_context_menu(cx);
let fold_indicators = if gutter_settings.folds {
- cx.with_element_id(Some("gutter_fold_indicators"), |cx| {
+ cx.with_element_namespace("gutter_fold_indicators", |cx| {
self.layout_gutter_fold_indicators(
fold_statuses,
line_height,
@@ -3930,6 +3943,7 @@ impl Element for EditorElement {
fn paint(
&mut self,
+ _: Option<&GlobalElementId>,
bounds: Bounds<gpui::Pixels>,
_: &mut Self::RequestLayoutState,
layout: &mut Self::PrepaintState,
@@ -3962,7 +3976,7 @@ impl Element for EditorElement {
self.paint_text(layout, cx);
if !layout.blocks.is_empty() {
- cx.with_element_id(Some("blocks"), |cx| {
+ cx.with_element_namespace("blocks", |cx| {
self.paint_blocks(layout, cx);
});
}
@@ -20,7 +20,7 @@ pub trait AssetSource: 'static + Send + Sync {
impl AssetSource for () {
fn load(&self, path: &str) -> Result<Cow<'static, [u8]>> {
Err(anyhow!(
- "get called on empty asset provider with \"{}\"",
+ "load called on empty asset provider with \"{}\"",
path
))
}
@@ -52,14 +52,26 @@ pub trait Element: 'static + IntoElement {
/// provided to [`Element::paint`].
type PrepaintState: 'static;
+ /// If this element has a unique identifier, return it here. This is used to track elements across frames, and
+ /// will cause a GlobalElementId to be passed to the request_layout, prepaint, and paint methods.
+ ///
+ /// The global id can in turn be used to access state that's connected to an element with the same id across
+ /// frames. This id must be unique among children of the first containing element with an id.
+ fn id(&self) -> Option<ElementId>;
+
/// Before an element can be painted, we need to know where it's going to be and how big it is.
/// Use this method to request a layout from Taffy and initialize the element's state.
- fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState);
+ fn request_layout(
+ &mut self,
+ id: Option<&GlobalElementId>,
+ cx: &mut WindowContext,
+ ) -> (LayoutId, Self::RequestLayoutState);
/// After laying out an element, we need to commit its bounds to the current frame for hitbox
/// purposes. The state argument is the same state that was returned from [`Element::request_layout()`].
fn prepaint(
&mut self,
+ id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
@@ -69,6 +81,7 @@ pub trait Element: 'static + IntoElement {
/// The state argument is the same state that was returned from [`Element::request_layout()`].
fn paint(
&mut self,
+ id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
prepaint: &mut Self::PrepaintState,
@@ -164,18 +177,33 @@ impl<C: RenderOnce> Element for Component<C> {
type RequestLayoutState = AnyElement;
type PrepaintState = ();
- fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
+ fn id(&self) -> Option<ElementId> {
+ None
+ }
+
+ fn request_layout(
+ &mut self,
+ _id: Option<&GlobalElementId>,
+ cx: &mut WindowContext,
+ ) -> (LayoutId, Self::RequestLayoutState) {
let mut element = self.0.take().unwrap().render(cx).into_any_element();
let layout_id = element.request_layout(cx);
(layout_id, element)
}
- fn prepaint(&mut self, _: Bounds<Pixels>, element: &mut AnyElement, cx: &mut WindowContext) {
+ fn prepaint(
+ &mut self,
+ _id: Option<&GlobalElementId>,
+ _: Bounds<Pixels>,
+ element: &mut AnyElement,
+ cx: &mut WindowContext,
+ ) {
element.prepaint(cx);
}
fn paint(
&mut self,
+ _id: Option<&GlobalElementId>,
_: Bounds<Pixels>,
element: &mut Self::RequestLayoutState,
_: &mut Self::PrepaintState,
@@ -194,8 +222,8 @@ impl<C: RenderOnce> IntoElement for Component<C> {
}
/// A globally unique identifier for an element, used to track state across frames.
-#[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)]
-pub(crate) struct GlobalElementId(SmallVec<[ElementId; 32]>);
+#[derive(Deref, DerefMut, Default, Debug, Eq, PartialEq, Hash)]
+pub struct GlobalElementId(pub(crate) SmallVec<[ElementId; 32]>);
trait ElementObject {
fn inner_element(&mut self) -> &mut dyn Any;
@@ -224,17 +252,20 @@ pub struct Drawable<E: Element> {
enum ElementDrawPhase<RequestLayoutState, PrepaintState> {
#[default]
Start,
- RequestLayoutState {
+ RequestLayout {
layout_id: LayoutId,
+ global_id: Option<GlobalElementId>,
request_layout: RequestLayoutState,
},
LayoutComputed {
layout_id: LayoutId,
+ global_id: Option<GlobalElementId>,
available_space: Size<AvailableSpace>,
request_layout: RequestLayoutState,
},
- PrepaintState {
+ Prepaint {
node_id: DispatchNodeId,
+ global_id: Option<GlobalElementId>,
bounds: Bounds<Pixels>,
request_layout: RequestLayoutState,
prepaint: PrepaintState,
@@ -254,9 +285,21 @@ impl<E: Element> Drawable<E> {
fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
match mem::take(&mut self.phase) {
ElementDrawPhase::Start => {
- let (layout_id, request_layout) = self.element.request_layout(cx);
- self.phase = ElementDrawPhase::RequestLayoutState {
+ let global_id = self.element.id().map(|element_id| {
+ cx.window.element_id_stack.push(element_id);
+ GlobalElementId(cx.window.element_id_stack.clone())
+ });
+
+ let (layout_id, request_layout) =
+ self.element.request_layout(global_id.as_ref(), cx);
+
+ if global_id.is_some() {
+ cx.window.element_id_stack.pop();
+ }
+
+ self.phase = ElementDrawPhase::RequestLayout {
layout_id,
+ global_id,
request_layout,
};
layout_id
@@ -267,25 +310,40 @@ impl<E: Element> Drawable<E> {
pub(crate) fn prepaint(&mut self, cx: &mut WindowContext) {
match mem::take(&mut self.phase) {
- ElementDrawPhase::RequestLayoutState {
+ ElementDrawPhase::RequestLayout {
layout_id,
+ global_id,
mut request_layout,
}
| ElementDrawPhase::LayoutComputed {
layout_id,
+ global_id,
mut request_layout,
..
} => {
+ if let Some(element_id) = self.element.id() {
+ cx.window.element_id_stack.push(element_id);
+ debug_assert_eq!(global_id.as_ref().unwrap().0, cx.window.element_id_stack);
+ }
+
let bounds = cx.layout_bounds(layout_id);
let node_id = cx.window.next_frame.dispatch_tree.push_node();
- let prepaint = self.element.prepaint(bounds, &mut request_layout, cx);
- self.phase = ElementDrawPhase::PrepaintState {
+ let prepaint =
+ self.element
+ .prepaint(global_id.as_ref(), bounds, &mut request_layout, cx);
+ cx.window.next_frame.dispatch_tree.pop_node();
+
+ if global_id.is_some() {
+ cx.window.element_id_stack.pop();
+ }
+
+ self.phase = ElementDrawPhase::Prepaint {
node_id,
+ global_id,
bounds,
request_layout,
prepaint,
};
- cx.window.next_frame.dispatch_tree.pop_node();
}
_ => panic!("must call request_layout before prepaint"),
}
@@ -296,16 +354,32 @@ impl<E: Element> Drawable<E> {
cx: &mut WindowContext,
) -> (E::RequestLayoutState, E::PrepaintState) {
match mem::take(&mut self.phase) {
- ElementDrawPhase::PrepaintState {
+ ElementDrawPhase::Prepaint {
node_id,
+ global_id,
bounds,
mut request_layout,
mut prepaint,
..
} => {
+ if let Some(element_id) = self.element.id() {
+ cx.window.element_id_stack.push(element_id);
+ debug_assert_eq!(global_id.as_ref().unwrap().0, cx.window.element_id_stack);
+ }
+
cx.window.next_frame.dispatch_tree.set_active_node(node_id);
- self.element
- .paint(bounds, &mut request_layout, &mut prepaint, cx);
+ self.element.paint(
+ global_id.as_ref(),
+ bounds,
+ &mut request_layout,
+ &mut prepaint,
+ cx,
+ );
+
+ if global_id.is_some() {
+ cx.window.element_id_stack.pop();
+ }
+
self.phase = ElementDrawPhase::Painted;
(request_layout, prepaint)
}
@@ -323,13 +397,15 @@ impl<E: Element> Drawable<E> {
}
let layout_id = match mem::take(&mut self.phase) {
- ElementDrawPhase::RequestLayoutState {
+ ElementDrawPhase::RequestLayout {
layout_id,
+ global_id,
request_layout,
} => {
cx.compute_layout(layout_id, available_space);
self.phase = ElementDrawPhase::LayoutComputed {
layout_id,
+ global_id,
available_space,
request_layout,
};
@@ -337,6 +413,7 @@ impl<E: Element> Drawable<E> {
}
ElementDrawPhase::LayoutComputed {
layout_id,
+ global_id,
available_space: prev_available_space,
request_layout,
} => {
@@ -345,6 +422,7 @@ impl<E: Element> Drawable<E> {
}
self.phase = ElementDrawPhase::LayoutComputed {
layout_id,
+ global_id,
available_space,
request_layout,
};
@@ -454,13 +532,22 @@ impl Element for AnyElement {
type RequestLayoutState = ();
type PrepaintState = ();
- fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
+ fn id(&self) -> Option<ElementId> {
+ None
+ }
+
+ fn request_layout(
+ &mut self,
+ _: Option<&GlobalElementId>,
+ cx: &mut WindowContext,
+ ) -> (LayoutId, Self::RequestLayoutState) {
let layout_id = self.request_layout(cx);
(layout_id, ())
}
fn prepaint(
&mut self,
+ _: Option<&GlobalElementId>,
_: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
@@ -470,6 +557,7 @@ impl Element for AnyElement {
fn paint(
&mut self,
+ _: Option<&GlobalElementId>,
_: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
_: &mut Self::PrepaintState,
@@ -506,12 +594,21 @@ impl Element for Empty {
type RequestLayoutState = ();
type PrepaintState = ();
- fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
+ fn id(&self) -> Option<ElementId> {
+ None
+ }
+
+ fn request_layout(
+ &mut self,
+ _id: Option<&GlobalElementId>,
+ cx: &mut WindowContext,
+ ) -> (LayoutId, Self::RequestLayoutState) {
(cx.request_layout(&Style::default(), None), ())
}
fn prepaint(
&mut self,
+ _id: Option<&GlobalElementId>,
_bounds: Bounds<Pixels>,
_state: &mut Self::RequestLayoutState,
_cx: &mut WindowContext,
@@ -520,6 +617,7 @@ impl Element for Empty {
fn paint(
&mut self,
+ _id: Option<&GlobalElementId>,
_bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
_prepaint: &mut Self::PrepaintState,
@@ -2,8 +2,8 @@ use smallvec::SmallVec;
use taffy::style::{Display, Position};
use crate::{
- point, AnyElement, Bounds, Element, IntoElement, LayoutId, ParentElement, Pixels, Point, Size,
- Style, WindowContext,
+ point, AnyElement, Bounds, Element, GlobalElementId, IntoElement, LayoutId, ParentElement,
+ Pixels, Point, Size, Style, WindowContext,
};
/// The state that the anchored element element uses to track its children.
@@ -72,8 +72,13 @@ impl Element for Anchored {
type RequestLayoutState = AnchoredState;
type PrepaintState = ();
+ fn id(&self) -> Option<crate::ElementId> {
+ None
+ }
+
fn request_layout(
&mut self,
+ _id: Option<&GlobalElementId>,
cx: &mut WindowContext,
) -> (crate::LayoutId, Self::RequestLayoutState) {
let child_layout_ids = self
@@ -95,6 +100,7 @@ impl Element for Anchored {
fn prepaint(
&mut self,
+ _id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
@@ -177,6 +183,7 @@ impl Element for Anchored {
fn paint(
&mut self,
+ _id: Option<&GlobalElementId>,
_bounds: crate::Bounds<crate::Pixels>,
_request_layout: &mut Self::RequestLayoutState,
_prepaint: &mut Self::PrepaintState,
@@ -1,6 +1,6 @@
use std::time::{Duration, Instant};
-use crate::{AnyElement, Element, ElementId, IntoElement};
+use crate::{AnyElement, Element, ElementId, GlobalElementId, IntoElement};
pub use easing::*;
@@ -86,15 +86,19 @@ struct AnimationState {
impl<E: IntoElement + 'static> Element for AnimationElement<E> {
type RequestLayoutState = AnyElement;
-
type PrepaintState = ();
+ fn id(&self) -> Option<ElementId> {
+ Some(self.id.clone())
+ }
+
fn request_layout(
&mut self,
+ global_id: Option<&GlobalElementId>,
cx: &mut crate::WindowContext,
) -> (crate::LayoutId, Self::RequestLayoutState) {
- cx.with_element_state(Some(self.id.clone()), |state, cx| {
- let state = state.unwrap().unwrap_or_else(|| AnimationState {
+ cx.with_element_state(global_id.unwrap(), |state, cx| {
+ let state = state.unwrap_or_else(|| AnimationState {
start: Instant::now(),
});
let mut delta =
@@ -130,12 +134,13 @@ impl<E: IntoElement + 'static> Element for AnimationElement<E> {
})
}
- ((element.request_layout(cx), element), Some(state))
+ ((element.request_layout(cx), element), state)
})
}
fn prepaint(
&mut self,
+ _id: Option<&GlobalElementId>,
_bounds: crate::Bounds<crate::Pixels>,
element: &mut Self::RequestLayoutState,
cx: &mut crate::WindowContext,
@@ -145,6 +150,7 @@ impl<E: IntoElement + 'static> Element for AnimationElement<E> {
fn paint(
&mut self,
+ _id: Option<&GlobalElementId>,
_bounds: crate::Bounds<crate::Pixels>,
element: &mut Self::RequestLayoutState,
_: &mut Self::PrepaintState,
@@ -1,6 +1,9 @@
use refineable::Refineable as _;
-use crate::{Bounds, Element, IntoElement, Pixels, Style, StyleRefinement, Styled, WindowContext};
+use crate::{
+ Bounds, Element, ElementId, GlobalElementId, IntoElement, Pixels, Style, StyleRefinement,
+ Styled, WindowContext,
+};
/// Construct a canvas element with the given paint callback.
/// Useful for adding short term custom drawing to a view.
@@ -35,8 +38,13 @@ impl<T: 'static> Element for Canvas<T> {
type RequestLayoutState = Style;
type PrepaintState = Option<T>;
+ fn id(&self) -> Option<ElementId> {
+ None
+ }
+
fn request_layout(
&mut self,
+ _id: Option<&GlobalElementId>,
cx: &mut WindowContext,
) -> (crate::LayoutId, Self::RequestLayoutState) {
let mut style = Style::default();
@@ -47,6 +55,7 @@ impl<T: 'static> Element for Canvas<T> {
fn prepaint(
&mut self,
+ _id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
_request_layout: &mut Style,
cx: &mut WindowContext,
@@ -56,6 +65,7 @@ impl<T: 'static> Element for Canvas<T> {
fn paint(
&mut self,
+ _id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
style: &mut Style,
prepaint: &mut Self::PrepaintState,
@@ -1,4 +1,6 @@
-use crate::{AnyElement, Bounds, Element, IntoElement, LayoutId, Pixels, WindowContext};
+use crate::{
+ AnyElement, Bounds, Element, GlobalElementId, IntoElement, LayoutId, Pixels, WindowContext,
+};
/// Builds a `Deferred` element, which delays the layout and paint of its child.
pub fn deferred(child: impl IntoElement) -> Deferred {
@@ -29,13 +31,22 @@ impl Element for Deferred {
type RequestLayoutState = ();
type PrepaintState = ();
- fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, ()) {
+ fn id(&self) -> Option<crate::ElementId> {
+ None
+ }
+
+ fn request_layout(
+ &mut self,
+ _id: Option<&GlobalElementId>,
+ cx: &mut WindowContext,
+ ) -> (LayoutId, ()) {
let layout_id = self.child.as_mut().unwrap().request_layout(cx);
(layout_id, ())
}
fn prepaint(
&mut self,
+ _id: Option<&GlobalElementId>,
_bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
@@ -47,6 +58,7 @@ impl Element for Deferred {
fn paint(
&mut self,
+ _id: Option<&GlobalElementId>,
_bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
_prepaint: &mut Self::PrepaintState,
@@ -17,11 +17,11 @@
use crate::{
point, px, size, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, Bounds,
- ClickEvent, DispatchPhase, Element, ElementId, FocusHandle, Global, Hitbox, HitboxId,
- IntoElement, IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, ModifiersChangedEvent,
- MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point,
- Render, ScrollWheelEvent, SharedString, Size, Style, StyleRefinement, Styled, Task, TooltipId,
- View, Visibility, WindowContext,
+ ClickEvent, DispatchPhase, Element, ElementId, FocusHandle, Global, GlobalElementId, Hitbox,
+ HitboxId, IntoElement, IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId,
+ ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
+ ParentElement, Pixels, Point, Render, ScrollWheelEvent, SharedString, Size, Style,
+ StyleRefinement, Styled, Task, TooltipId, View, Visibility, WindowContext,
};
use collections::HashMap;
use refineable::Refineable;
@@ -1123,23 +1123,34 @@ impl Element for Div {
type RequestLayoutState = DivFrameState;
type PrepaintState = Option<Hitbox>;
- fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
+ fn id(&self) -> Option<ElementId> {
+ self.interactivity.element_id.clone()
+ }
+
+ fn request_layout(
+ &mut self,
+ global_id: Option<&GlobalElementId>,
+ cx: &mut WindowContext,
+ ) -> (LayoutId, Self::RequestLayoutState) {
let mut child_layout_ids = SmallVec::new();
- let layout_id = self.interactivity.request_layout(cx, |style, cx| {
- cx.with_text_style(style.text_style().cloned(), |cx| {
- child_layout_ids = self
- .children
- .iter_mut()
- .map(|child| child.request_layout(cx))
- .collect::<SmallVec<_>>();
- cx.request_layout(&style, child_layout_ids.iter().copied())
- })
- });
+ let layout_id = self
+ .interactivity
+ .request_layout(global_id, cx, |style, cx| {
+ cx.with_text_style(style.text_style().cloned(), |cx| {
+ child_layout_ids = self
+ .children
+ .iter_mut()
+ .map(|child| child.request_layout(cx))
+ .collect::<SmallVec<_>>();
+ cx.request_layout(&style, child_layout_ids.iter().copied())
+ })
+ });
(layout_id, DivFrameState { child_layout_ids })
}
fn prepaint(
&mut self,
+ global_id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
@@ -1178,6 +1189,7 @@ impl Element for Div {
};
self.interactivity.prepaint(
+ global_id,
bounds,
content_size,
cx,
@@ -1194,13 +1206,14 @@ impl Element for Div {
fn paint(
&mut self,
+ global_id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
hitbox: &mut Option<Hitbox>,
cx: &mut WindowContext,
) {
self.interactivity
- .paint(bounds, hitbox.as_ref(), cx, |_style, cx| {
+ .paint(global_id, bounds, hitbox.as_ref(), cx, |_style, cx| {
for child in &mut self.children {
child.paint(cx);
}
@@ -1220,7 +1233,7 @@ impl IntoElement for Div {
/// interactivity in the `Div` element.
#[derive(Default)]
pub struct Interactivity {
- /// The element ID of the element
+ /// The element ID of the element. In id is required to support a stateful subset of the interactivity such as on_click.
pub element_id: Option<ElementId>,
/// Whether the element was clicked. This will only be present after layout.
pub active: Option<bool>,
@@ -1276,11 +1289,12 @@ impl Interactivity {
/// Layout this element according to this interactivity state's configured styles
pub fn request_layout(
&mut self,
+ global_id: Option<&GlobalElementId>,
cx: &mut WindowContext,
f: impl FnOnce(Style, &mut WindowContext) -> LayoutId,
) -> LayoutId {
- cx.with_element_state::<InteractiveElementState, _>(
- self.element_id.clone(),
+ cx.with_optional_element_state::<InteractiveElementState, _>(
+ global_id,
|element_state, cx| {
let mut element_state =
element_state.map(|element_state| element_state.unwrap_or_default());
@@ -1339,14 +1353,15 @@ impl Interactivity {
/// Commit the bounds of this element according to this interactivity state's configured styles.
pub fn prepaint<R>(
&mut self,
+ global_id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
content_size: Size<Pixels>,
cx: &mut WindowContext,
f: impl FnOnce(&Style, Point<Pixels>, Option<Hitbox>, &mut WindowContext) -> R,
) -> R {
self.content_size = content_size;
- cx.with_element_state::<InteractiveElementState, _>(
- self.element_id.clone(),
+ cx.with_optional_element_state::<InteractiveElementState, _>(
+ global_id,
|element_state, cx| {
let mut element_state =
element_state.map(|element_state| element_state.unwrap_or_default());
@@ -1454,14 +1469,15 @@ impl Interactivity {
/// with the current scroll offset
pub fn paint(
&mut self,
+ global_id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
hitbox: Option<&Hitbox>,
cx: &mut WindowContext,
f: impl FnOnce(&Style, &mut WindowContext),
) {
self.hovered = hitbox.map(|hitbox| hitbox.is_hovered(cx));
- cx.with_element_state::<InteractiveElementState, _>(
- self.element_id.clone(),
+ cx.with_optional_element_state::<InteractiveElementState, _>(
+ global_id,
|element_state, cx| {
let mut element_state =
element_state.map(|element_state| element_state.unwrap_or_default());
@@ -1487,7 +1503,7 @@ impl Interactivity {
cx.with_content_mask(style.overflow_mask(bounds, cx.rem_size()), |cx| {
if let Some(hitbox) = hitbox {
#[cfg(debug_assertions)]
- self.paint_debug_info(hitbox, &style, cx);
+ self.paint_debug_info(global_id, hitbox, &style, cx);
if !cx.has_active_drag() {
if let Some(mouse_cursor) = style.mouse_cursor {
@@ -1521,13 +1537,19 @@ impl Interactivity {
}
#[cfg(debug_assertions)]
- fn paint_debug_info(&mut self, hitbox: &Hitbox, style: &Style, cx: &mut WindowContext) {
- if self.element_id.is_some()
+ fn paint_debug_info(
+ &mut self,
+ global_id: Option<&GlobalElementId>,
+ hitbox: &Hitbox,
+ style: &Style,
+ cx: &mut WindowContext,
+ ) {
+ if global_id.is_some()
&& (style.debug || style.debug_below || cx.has_global::<crate::DebugBelow>())
&& hitbox.is_hovered(cx)
{
const FONT_SIZE: crate::Pixels = crate::Pixels(10.);
- let element_id = format!("{:?}", self.element_id.as_ref().unwrap());
+ let element_id = format!("{:?}", global_id.unwrap());
let str_len = element_id.len();
let render_debug_text = |cx: &mut WindowContext| {
@@ -2064,8 +2086,13 @@ impl Interactivity {
}
/// Compute the visual style for this element, based on the current bounds and the element's state.
- pub fn compute_style(&self, hitbox: Option<&Hitbox>, cx: &mut WindowContext) -> Style {
- cx.with_element_state(self.element_id.clone(), |element_state, cx| {
+ pub fn compute_style(
+ &self,
+ global_id: Option<&GlobalElementId>,
+ hitbox: Option<&Hitbox>,
+ cx: &mut WindowContext,
+ ) -> Style {
+ cx.with_optional_element_state(global_id, |element_state, cx| {
let mut element_state =
element_state.map(|element_state| element_state.unwrap_or_default());
let style = self.compute_style_internal(hitbox, element_state.as_mut(), cx);
@@ -2264,27 +2291,37 @@ where
type RequestLayoutState = E::RequestLayoutState;
type PrepaintState = E::PrepaintState;
- fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
- self.element.request_layout(cx)
+ fn id(&self) -> Option<ElementId> {
+ self.element.id()
+ }
+
+ fn request_layout(
+ &mut self,
+ id: Option<&GlobalElementId>,
+ cx: &mut WindowContext,
+ ) -> (LayoutId, Self::RequestLayoutState) {
+ self.element.request_layout(id, cx)
}
fn prepaint(
&mut self,
+ id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
state: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
) -> E::PrepaintState {
- self.element.prepaint(bounds, state, cx)
+ self.element.prepaint(id, bounds, state, cx)
}
fn paint(
&mut self,
+ id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
prepaint: &mut Self::PrepaintState,
cx: &mut WindowContext,
) {
- self.element.paint(bounds, request_layout, prepaint, cx)
+ self.element.paint(id, bounds, request_layout, prepaint, cx)
}
}
@@ -2347,27 +2384,37 @@ where
type RequestLayoutState = E::RequestLayoutState;
type PrepaintState = E::PrepaintState;
- fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
- self.element.request_layout(cx)
+ fn id(&self) -> Option<ElementId> {
+ self.element.id()
+ }
+
+ fn request_layout(
+ &mut self,
+ id: Option<&GlobalElementId>,
+ cx: &mut WindowContext,
+ ) -> (LayoutId, Self::RequestLayoutState) {
+ self.element.request_layout(id, cx)
}
fn prepaint(
&mut self,
+ id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
state: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
) -> E::PrepaintState {
- self.element.prepaint(bounds, state, cx)
+ self.element.prepaint(id, bounds, state, cx)
}
fn paint(
&mut self,
+ id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
prepaint: &mut Self::PrepaintState,
cx: &mut WindowContext,
) {
- self.element.paint(bounds, request_layout, prepaint, cx);
+ self.element.paint(id, bounds, request_layout, prepaint, cx);
}
}
@@ -3,9 +3,10 @@ use std::path::PathBuf;
use std::sync::Arc;
use crate::{
- point, px, size, AbsoluteLength, Asset, Bounds, DefiniteLength, DevicePixels, Element, Hitbox,
- ImageData, InteractiveElement, Interactivity, IntoElement, LayoutId, Length, Pixels, SharedUri,
- Size, StyleRefinement, Styled, SvgSize, UriOrPath, WindowContext,
+ point, px, size, AbsoluteLength, Asset, Bounds, DefiniteLength, DevicePixels, Element,
+ ElementId, GlobalElementId, Hitbox, ImageData, InteractiveElement, Interactivity, IntoElement,
+ LayoutId, Length, Pixels, SharedUri, Size, StyleRefinement, Styled, SvgSize, UriOrPath,
+ WindowContext,
};
use futures::{AsyncReadExt, Future};
use image::{ImageBuffer, ImageError};
@@ -232,42 +233,54 @@ impl Element for Img {
type RequestLayoutState = ();
type PrepaintState = Option<Hitbox>;
- fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
- let layout_id = self.interactivity.request_layout(cx, |mut style, cx| {
- if let Some(data) = self.source.data(cx) {
- let image_size = data.size();
- match (style.size.width, style.size.height) {
- (Length::Auto, Length::Auto) => {
- style.size = Size {
- width: Length::Definite(DefiniteLength::Absolute(
- AbsoluteLength::Pixels(px(image_size.width.0 as f32)),
- )),
- height: Length::Definite(DefiniteLength::Absolute(
- AbsoluteLength::Pixels(px(image_size.height.0 as f32)),
- )),
+ fn id(&self) -> Option<ElementId> {
+ self.interactivity.element_id.clone()
+ }
+
+ fn request_layout(
+ &mut self,
+ global_id: Option<&GlobalElementId>,
+ cx: &mut WindowContext,
+ ) -> (LayoutId, Self::RequestLayoutState) {
+ let layout_id = self
+ .interactivity
+ .request_layout(global_id, cx, |mut style, cx| {
+ if let Some(data) = self.source.data(cx) {
+ let image_size = data.size();
+ match (style.size.width, style.size.height) {
+ (Length::Auto, Length::Auto) => {
+ style.size = Size {
+ width: Length::Definite(DefiniteLength::Absolute(
+ AbsoluteLength::Pixels(px(image_size.width.0 as f32)),
+ )),
+ height: Length::Definite(DefiniteLength::Absolute(
+ AbsoluteLength::Pixels(px(image_size.height.0 as f32)),
+ )),
+ }
}
+ _ => {}
}
- _ => {}
}
- }
- cx.request_layout(&style, [])
- });
+ cx.request_layout(&style, [])
+ });
(layout_id, ())
}
fn prepaint(
&mut self,
+ global_id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
) -> Option<Hitbox> {
self.interactivity
- .prepaint(bounds, bounds.size, cx, |_, _, hitbox, _| hitbox)
+ .prepaint(global_id, bounds, bounds.size, cx, |_, _, hitbox, _| hitbox)
}
fn paint(
&mut self,
+ global_id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
hitbox: &mut Self::PrepaintState,
@@ -275,7 +288,7 @@ impl Element for Img {
) {
let source = self.source.clone();
self.interactivity
- .paint(bounds, hitbox.as_ref(), cx, |style, cx| {
+ .paint(global_id, bounds, hitbox.as_ref(), cx, |style, cx| {
let corner_radii = style.corner_radii.to_pixels(bounds.size, cx.rem_size());
if let Some(data) = source.data(cx) {
@@ -8,8 +8,8 @@
use crate::{
point, px, size, AnyElement, AvailableSpace, Bounds, ContentMask, DispatchPhase, Edges,
- Element, FocusHandle, Hitbox, IntoElement, Pixels, Point, ScrollWheelEvent, Size, Style,
- StyleRefinement, Styled, WindowContext,
+ Element, FocusHandle, GlobalElementId, Hitbox, IntoElement, Pixels, Point, ScrollWheelEvent,
+ Size, Style, StyleRefinement, Styled, WindowContext,
};
use collections::VecDeque;
use refineable::Refineable as _;
@@ -704,8 +704,13 @@ impl Element for List {
type RequestLayoutState = ();
type PrepaintState = ListPrepaintState;
+ fn id(&self) -> Option<crate::ElementId> {
+ None
+ }
+
fn request_layout(
&mut self,
+ _id: Option<&GlobalElementId>,
cx: &mut crate::WindowContext,
) -> (crate::LayoutId, Self::RequestLayoutState) {
let layout_id = match self.sizing_behavior {
@@ -770,6 +775,7 @@ impl Element for List {
fn prepaint(
&mut self,
+ _id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
@@ -812,6 +818,7 @@ impl Element for List {
fn paint(
&mut self,
+ _id: Option<&GlobalElementId>,
bounds: Bounds<crate::Pixels>,
_: &mut Self::RequestLayoutState,
prepaint: &mut Self::PrepaintState,
@@ -1,7 +1,7 @@
use crate::{
- geometry::Negate as _, point, px, radians, size, Bounds, Element, Hitbox, InteractiveElement,
- Interactivity, IntoElement, LayoutId, Pixels, Point, Radians, SharedString, Size,
- StyleRefinement, Styled, TransformationMatrix, WindowContext,
+ geometry::Negate as _, point, px, radians, size, Bounds, Element, GlobalElementId, Hitbox,
+ InteractiveElement, Interactivity, IntoElement, LayoutId, Pixels, Point, Radians, SharedString,
+ Size, StyleRefinement, Styled, TransformationMatrix, WindowContext,
};
use util::ResultExt;
@@ -40,25 +40,35 @@ impl Element for Svg {
type RequestLayoutState = ();
type PrepaintState = Option<Hitbox>;
- fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
+ fn id(&self) -> Option<crate::ElementId> {
+ self.interactivity.element_id.clone()
+ }
+
+ fn request_layout(
+ &mut self,
+ global_id: Option<&GlobalElementId>,
+ cx: &mut WindowContext,
+ ) -> (LayoutId, Self::RequestLayoutState) {
let layout_id = self
.interactivity
- .request_layout(cx, |style, cx| cx.request_layout(&style, None));
+ .request_layout(global_id, cx, |style, cx| cx.request_layout(&style, None));
(layout_id, ())
}
fn prepaint(
&mut self,
+ global_id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
) -> Option<Hitbox> {
self.interactivity
- .prepaint(bounds, bounds.size, cx, |_, _, hitbox, _| hitbox)
+ .prepaint(global_id, bounds, bounds.size, cx, |_, _, hitbox, _| hitbox)
}
fn paint(
&mut self,
+ global_id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
hitbox: &mut Option<Hitbox>,
@@ -67,7 +77,7 @@ impl Element for Svg {
Self: Sized,
{
self.interactivity
- .paint(bounds, hitbox.as_ref(), cx, |style, cx| {
+ .paint(global_id, bounds, hitbox.as_ref(), cx, |style, cx| {
if let Some((path, color)) = self.path.as_ref().zip(style.text.color) {
let transformation = self
.transformation
@@ -1,7 +1,8 @@
use crate::{
- ActiveTooltip, AnyTooltip, AnyView, Bounds, DispatchPhase, Element, ElementId, HighlightStyle,
- Hitbox, IntoElement, LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Point,
- SharedString, Size, TextRun, TextStyle, WhiteSpace, WindowContext, WrappedLine, TOOLTIP_DELAY,
+ ActiveTooltip, AnyTooltip, AnyView, Bounds, DispatchPhase, Element, ElementId, GlobalElementId,
+ HighlightStyle, Hitbox, IntoElement, LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
+ Pixels, Point, SharedString, Size, TextRun, TextStyle, WhiteSpace, WindowContext, WrappedLine,
+ TOOLTIP_DELAY,
};
use anyhow::anyhow;
use parking_lot::{Mutex, MutexGuard};
@@ -19,7 +20,15 @@ impl Element for &'static str {
type RequestLayoutState = TextState;
type PrepaintState = ();
- fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
+ fn id(&self) -> Option<ElementId> {
+ None
+ }
+
+ fn request_layout(
+ &mut self,
+ _id: Option<&GlobalElementId>,
+ cx: &mut WindowContext,
+ ) -> (LayoutId, Self::RequestLayoutState) {
let mut state = TextState::default();
let layout_id = state.layout(SharedString::from(*self), None, cx);
(layout_id, state)
@@ -27,6 +36,7 @@ impl Element for &'static str {
fn prepaint(
&mut self,
+ _id: Option<&GlobalElementId>,
_bounds: Bounds<Pixels>,
_text_state: &mut Self::RequestLayoutState,
_cx: &mut WindowContext,
@@ -35,6 +45,7 @@ impl Element for &'static str {
fn paint(
&mut self,
+ _id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
text_state: &mut TextState,
_: &mut (),
@@ -64,7 +75,17 @@ impl Element for SharedString {
type RequestLayoutState = TextState;
type PrepaintState = ();
- fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
+ fn id(&self) -> Option<ElementId> {
+ None
+ }
+
+ fn request_layout(
+ &mut self,
+
+ _id: Option<&GlobalElementId>,
+
+ cx: &mut WindowContext,
+ ) -> (LayoutId, Self::RequestLayoutState) {
let mut state = TextState::default();
let layout_id = state.layout(self.clone(), None, cx);
(layout_id, state)
@@ -72,6 +93,7 @@ impl Element for SharedString {
fn prepaint(
&mut self,
+ _id: Option<&GlobalElementId>,
_bounds: Bounds<Pixels>,
_text_state: &mut Self::RequestLayoutState,
_cx: &mut WindowContext,
@@ -80,6 +102,7 @@ impl Element for SharedString {
fn paint(
&mut self,
+ _id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
text_state: &mut Self::RequestLayoutState,
_: &mut Self::PrepaintState,
@@ -150,7 +173,17 @@ impl Element for StyledText {
type RequestLayoutState = TextState;
type PrepaintState = ();
- fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
+ fn id(&self) -> Option<ElementId> {
+ None
+ }
+
+ fn request_layout(
+ &mut self,
+
+ _id: Option<&GlobalElementId>,
+
+ cx: &mut WindowContext,
+ ) -> (LayoutId, Self::RequestLayoutState) {
let mut state = TextState::default();
let layout_id = state.layout(self.text.clone(), self.runs.take(), cx);
(layout_id, state)
@@ -158,6 +191,7 @@ impl Element for StyledText {
fn prepaint(
&mut self,
+ _id: Option<&GlobalElementId>,
_bounds: Bounds<Pixels>,
_state: &mut Self::RequestLayoutState,
_cx: &mut WindowContext,
@@ -166,6 +200,7 @@ impl Element for StyledText {
fn paint(
&mut self,
+ _id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
text_state: &mut Self::RequestLayoutState,
_: &mut Self::PrepaintState,
@@ -404,18 +439,27 @@ impl Element for InteractiveText {
type RequestLayoutState = TextState;
type PrepaintState = Hitbox;
- fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
- self.text.request_layout(cx)
+ fn id(&self) -> Option<ElementId> {
+ Some(self.element_id.clone())
+ }
+
+ fn request_layout(
+ &mut self,
+ _id: Option<&GlobalElementId>,
+ cx: &mut WindowContext,
+ ) -> (LayoutId, Self::RequestLayoutState) {
+ self.text.request_layout(None, cx)
}
fn prepaint(
&mut self,
+ global_id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
state: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
) -> Hitbox {
- cx.with_element_state::<InteractiveTextState, _>(
- Some(self.element_id.clone()),
+ cx.with_optional_element_state::<InteractiveTextState, _>(
+ global_id,
|interactive_state, cx| {
let interactive_state = interactive_state
.map(|interactive_state| interactive_state.unwrap_or_default());
@@ -429,7 +473,7 @@ impl Element for InteractiveText {
}
}
- self.text.prepaint(bounds, state, cx);
+ self.text.prepaint(None, bounds, state, cx);
let hitbox = cx.insert_hitbox(bounds, false);
(hitbox, interactive_state)
},
@@ -438,15 +482,16 @@ impl Element for InteractiveText {
fn paint(
&mut self,
+ global_id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
text_state: &mut Self::RequestLayoutState,
hitbox: &mut Hitbox,
cx: &mut WindowContext,
) {
cx.with_element_state::<InteractiveTextState, _>(
- Some(self.element_id.clone()),
+ global_id.unwrap(),
|interactive_state, cx| {
- let mut interactive_state = interactive_state.unwrap().unwrap_or_default();
+ let mut interactive_state = interactive_state.unwrap_or_default();
if let Some(click_listener) = self.click_listener.take() {
let mouse_position = cx.mouse_position();
if let Some(ix) = text_state.index_for_position(bounds, mouse_position) {
@@ -576,9 +621,9 @@ impl Element for InteractiveText {
});
}
- self.text.paint(bounds, text_state, &mut (), cx);
+ self.text.paint(None, bounds, text_state, &mut (), cx);
- ((), Some(interactive_state))
+ ((), interactive_state)
},
);
}
@@ -5,9 +5,9 @@
//! elements with uniform height.
use crate::{
- point, px, size, AnyElement, AvailableSpace, Bounds, ContentMask, Element, ElementId, Hitbox,
- InteractiveElement, Interactivity, IntoElement, LayoutId, Pixels, Render, ScrollHandle, Size,
- StyleRefinement, Styled, View, ViewContext, WindowContext,
+ point, px, size, AnyElement, AvailableSpace, Bounds, ContentMask, Element, ElementId,
+ GlobalElementId, Hitbox, InteractiveElement, Interactivity, IntoElement, LayoutId, Pixels,
+ Render, ScrollHandle, Size, StyleRefinement, Styled, View, ViewContext, WindowContext,
};
use smallvec::SmallVec;
use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
@@ -107,26 +107,38 @@ impl Element for UniformList {
type RequestLayoutState = UniformListFrameState;
type PrepaintState = Option<Hitbox>;
- fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
+ fn id(&self) -> Option<ElementId> {
+ self.interactivity.element_id.clone()
+ }
+
+ fn request_layout(
+ &mut self,
+ global_id: Option<&GlobalElementId>,
+ cx: &mut WindowContext,
+ ) -> (LayoutId, Self::RequestLayoutState) {
let max_items = self.item_count;
let item_size = self.measure_item(None, cx);
- let layout_id = self.interactivity.request_layout(cx, |style, cx| {
- cx.request_measured_layout(style, move |known_dimensions, available_space, _cx| {
- let desired_height = item_size.height * max_items;
- let width = known_dimensions
- .width
- .unwrap_or(match available_space.width {
- AvailableSpace::Definite(x) => x,
- AvailableSpace::MinContent | AvailableSpace::MaxContent => item_size.width,
- });
-
- let height = match available_space.height {
- AvailableSpace::Definite(height) => desired_height.min(height),
- AvailableSpace::MinContent | AvailableSpace::MaxContent => desired_height,
- };
- size(width, height)
- })
- });
+ let layout_id = self
+ .interactivity
+ .request_layout(global_id, cx, |style, cx| {
+ cx.request_measured_layout(style, move |known_dimensions, available_space, _cx| {
+ let desired_height = item_size.height * max_items;
+ let width = known_dimensions
+ .width
+ .unwrap_or(match available_space.width {
+ AvailableSpace::Definite(x) => x,
+ AvailableSpace::MinContent | AvailableSpace::MaxContent => {
+ item_size.width
+ }
+ });
+
+ let height = match available_space.height {
+ AvailableSpace::Definite(height) => desired_height.min(height),
+ AvailableSpace::MinContent | AvailableSpace::MaxContent => desired_height,
+ };
+ size(width, height)
+ })
+ });
(
layout_id,
@@ -139,11 +151,12 @@ impl Element for UniformList {
fn prepaint(
&mut self,
+ global_id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
frame_state: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
) -> Option<Hitbox> {
- let style = self.interactivity.compute_style(None, cx);
+ let style = self.interactivity.compute_style(global_id, None, cx);
let border = style.border_widths.to_pixels(cx.rem_size());
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
@@ -167,6 +180,7 @@ impl Element for UniformList {
.and_then(|handle| handle.deferred_scroll_to_item.take());
self.interactivity.prepaint(
+ global_id,
bounds,
content_size,
cx,
@@ -236,13 +250,14 @@ impl Element for UniformList {
fn paint(
&mut self,
+ global_id: Option<&GlobalElementId>,
bounds: Bounds<crate::Pixels>,
request_layout: &mut Self::RequestLayoutState,
hitbox: &mut Option<Hitbox>,
cx: &mut WindowContext,
) {
self.interactivity
- .paint(bounds, hitbox.as_ref(), cx, |_, cx| {
+ .paint(global_id, bounds, hitbox.as_ref(), cx, |_, cx| {
for item in &mut request_layout.items {
item.paint(cx);
}
@@ -1,8 +1,8 @@
use crate::{
seal::Sealed, AnyElement, AnyModel, AnyWeakModel, AppContext, Bounds, ContentMask, Element,
- ElementId, Entity, EntityId, Flatten, FocusHandle, FocusableView, IntoElement, LayoutId, Model,
- PaintIndex, Pixels, PrepaintStateIndex, Render, Style, StyleRefinement, TextStyle, ViewContext,
- VisualContext, WeakModel, WindowContext,
+ ElementId, Entity, EntityId, Flatten, FocusHandle, FocusableView, GlobalElementId, IntoElement,
+ LayoutId, Model, PaintIndex, Pixels, PrepaintStateIndex, Render, Style, StyleRefinement,
+ TextStyle, ViewContext, VisualContext, WeakModel, WindowContext,
};
use anyhow::{Context, Result};
use refineable::Refineable;
@@ -93,36 +93,40 @@ impl<V: Render> Element for View<V> {
type RequestLayoutState = AnyElement;
type PrepaintState = ();
- fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
- cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
- let mut element = self.update(cx, |view, cx| view.render(cx).into_any_element());
- let layout_id = element.request_layout(cx);
- (layout_id, element)
- })
+ fn id(&self) -> Option<ElementId> {
+ Some(ElementId::View(self.entity_id()))
+ }
+
+ fn request_layout(
+ &mut self,
+ _id: Option<&GlobalElementId>,
+ cx: &mut WindowContext,
+ ) -> (LayoutId, Self::RequestLayoutState) {
+ let mut element = self.update(cx, |view, cx| view.render(cx).into_any_element());
+ let layout_id = element.request_layout(cx);
+ (layout_id, element)
}
fn prepaint(
&mut self,
+ _id: Option<&GlobalElementId>,
_: Bounds<Pixels>,
element: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
) {
cx.set_view_id(self.entity_id());
- cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
- element.prepaint(cx)
- })
+ element.prepaint(cx);
}
fn paint(
&mut self,
+ _id: Option<&GlobalElementId>,
_: Bounds<Pixels>,
element: &mut Self::RequestLayoutState,
_: &mut Self::PrepaintState,
cx: &mut WindowContext,
) {
- cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
- element.paint(cx)
- })
+ element.paint(cx);
}
}
@@ -279,112 +283,108 @@ impl Element for AnyView {
type RequestLayoutState = Option<AnyElement>;
type PrepaintState = Option<AnyElement>;
- fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
+ fn id(&self) -> Option<ElementId> {
+ Some(ElementId::View(self.entity_id()))
+ }
+
+ fn request_layout(
+ &mut self,
+ _id: Option<&GlobalElementId>,
+ cx: &mut WindowContext,
+ ) -> (LayoutId, Self::RequestLayoutState) {
if let Some(style) = self.cached_style.as_ref() {
let mut root_style = Style::default();
root_style.refine(style);
let layout_id = cx.request_layout(&root_style, None);
(layout_id, None)
} else {
- cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
- let mut element = (self.render)(self, cx);
- let layout_id = element.request_layout(cx);
- (layout_id, Some(element))
- })
+ let mut element = (self.render)(self, cx);
+ let layout_id = element.request_layout(cx);
+ (layout_id, Some(element))
}
}
fn prepaint(
&mut self,
+ global_id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
element: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
) -> Option<AnyElement> {
cx.set_view_id(self.entity_id());
if self.cached_style.is_some() {
- cx.with_element_state::<AnyViewState, _>(
- Some(ElementId::View(self.entity_id())),
- |element_state, cx| {
- let mut element_state = element_state.unwrap();
-
- let content_mask = cx.content_mask();
- let text_style = cx.text_style();
-
- if let Some(mut element_state) = element_state {
- if element_state.cache_key.bounds == bounds
- && element_state.cache_key.content_mask == content_mask
- && element_state.cache_key.text_style == text_style
- && !cx.window.dirty_views.contains(&self.entity_id())
- && !cx.window.refreshing
- {
- let prepaint_start = cx.prepaint_index();
- cx.reuse_prepaint(element_state.prepaint_range.clone());
- let prepaint_end = cx.prepaint_index();
- element_state.prepaint_range = prepaint_start..prepaint_end;
- return (None, Some(element_state));
- }
+ cx.with_element_state::<AnyViewState, _>(global_id.unwrap(), |element_state, cx| {
+ let content_mask = cx.content_mask();
+ let text_style = cx.text_style();
+
+ if let Some(mut element_state) = element_state {
+ if element_state.cache_key.bounds == bounds
+ && element_state.cache_key.content_mask == content_mask
+ && element_state.cache_key.text_style == text_style
+ && !cx.window.dirty_views.contains(&self.entity_id())
+ && !cx.window.refreshing
+ {
+ let prepaint_start = cx.prepaint_index();
+ cx.reuse_prepaint(element_state.prepaint_range.clone());
+ let prepaint_end = cx.prepaint_index();
+ element_state.prepaint_range = prepaint_start..prepaint_end;
+ return (None, element_state);
}
+ }
- let prepaint_start = cx.prepaint_index();
- let mut element = (self.render)(self, cx);
- element.layout_as_root(bounds.size.into(), cx);
- element.prepaint_at(bounds.origin, cx);
- let prepaint_end = cx.prepaint_index();
-
- (
- Some(element),
- Some(AnyViewState {
- prepaint_range: prepaint_start..prepaint_end,
- paint_range: PaintIndex::default()..PaintIndex::default(),
- cache_key: ViewCacheKey {
- bounds,
- content_mask,
- text_style,
- },
- }),
- )
- },
- )
- } else {
- cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
- let mut element = element.take().unwrap();
- element.prepaint(cx);
- Some(element)
+ let prepaint_start = cx.prepaint_index();
+ let mut element = (self.render)(self, cx);
+ element.layout_as_root(bounds.size.into(), cx);
+ element.prepaint_at(bounds.origin, cx);
+ let prepaint_end = cx.prepaint_index();
+
+ (
+ Some(element),
+ AnyViewState {
+ prepaint_range: prepaint_start..prepaint_end,
+ paint_range: PaintIndex::default()..PaintIndex::default(),
+ cache_key: ViewCacheKey {
+ bounds,
+ content_mask,
+ text_style,
+ },
+ },
+ )
})
+ } else {
+ let mut element = element.take().unwrap();
+ element.prepaint(cx);
+ Some(element)
}
}
fn paint(
&mut self,
+ global_id: Option<&GlobalElementId>,
_bounds: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
element: &mut Self::PrepaintState,
cx: &mut WindowContext,
) {
if self.cached_style.is_some() {
- cx.with_element_state::<AnyViewState, _>(
- Some(ElementId::View(self.entity_id())),
- |element_state, cx| {
- let mut element_state = element_state.unwrap().unwrap();
+ cx.with_element_state::<AnyViewState, _>(global_id.unwrap(), |element_state, cx| {
+ let mut element_state = element_state.unwrap();
- let paint_start = cx.paint_index();
+ let paint_start = cx.paint_index();
- if let Some(element) = element {
- element.paint(cx);
- } else {
- cx.reuse_paint(element_state.paint_range.clone());
- }
+ if let Some(element) = element {
+ element.paint(cx);
+ } else {
+ cx.reuse_paint(element_state.paint_range.clone());
+ }
- let paint_end = cx.paint_index();
- element_state.paint_range = paint_start..paint_end;
+ let paint_end = cx.paint_index();
+ element_state.paint_range = paint_start..paint_end;
- ((), Some(element_state))
- },
- )
- } else {
- cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
- element.as_mut().unwrap().paint(cx);
+ ((), element_state)
})
+ } else {
+ element.as_mut().unwrap().paint(cx);
}
}
}
@@ -353,7 +353,7 @@ pub(crate) struct TooltipRequest {
pub(crate) struct DeferredDraw {
priority: usize,
parent_node: DispatchNodeId,
- element_id_stack: GlobalElementId,
+ element_id_stack: SmallVec<[ElementId; 32]>,
text_style_stack: Vec<TextStyleRefinement>,
element: Option<AnyElement>,
absolute_offset: Point<Pixels>,
@@ -454,9 +454,10 @@ impl Frame {
pub(crate) fn finish(&mut self, prev_frame: &mut Self) {
for element_state_key in &self.accessed_element_states {
- if let Some(element_state) = prev_frame.element_states.remove(element_state_key) {
- self.element_states
- .insert(element_state_key.clone(), element_state);
+ if let Some((element_state_key, element_state)) =
+ prev_frame.element_states.remove_entry(element_state_key)
+ {
+ self.element_states.insert(element_state_key, element_state);
}
}
@@ -477,7 +478,7 @@ pub struct Window {
pub(crate) viewport_size: Size<Pixels>,
layout_engine: Option<TaffyLayoutEngine>,
pub(crate) root_view: Option<AnyView>,
- pub(crate) element_id_stack: GlobalElementId,
+ pub(crate) element_id_stack: SmallVec<[ElementId; 32]>,
pub(crate) text_style_stack: Vec<TextStyleRefinement>,
pub(crate) element_offset_stack: Vec<Point<Pixels>>,
pub(crate) content_mask_stack: Vec<ContentMask<Pixels>>,
@@ -745,7 +746,7 @@ impl Window {
viewport_size: content_size,
layout_engine: Some(TaffyLayoutEngine::new()),
root_view: None,
- element_id_stack: GlobalElementId::default(),
+ element_id_stack: SmallVec::default(),
text_style_stack: Vec::new(),
element_offset_stack: Vec::new(),
content_mask_stack: Vec::new(),
@@ -1499,7 +1500,7 @@ impl<'a> WindowContext<'a> {
window.rendered_frame.accessed_element_states[range.start.accessed_element_states_index
..range.end.accessed_element_states_index]
.iter()
- .cloned(),
+ .map(|(id, type_id)| (GlobalElementId(id.0.clone()), *type_id)),
);
window
.text_system
@@ -1562,7 +1563,7 @@ impl<'a> WindowContext<'a> {
window.rendered_frame.accessed_element_states[range.start.accessed_element_states_index
..range.end.accessed_element_states_index]
.iter()
- .cloned(),
+ .map(|(id, type_id)| (GlobalElementId(id.0.clone()), *type_id)),
);
window
.text_system
@@ -1630,35 +1631,6 @@ impl<'a> WindowContext<'a> {
id
}
- /// 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. This method should
- /// only be called as part of element drawing.
- pub fn with_element_id<R>(
- &mut self,
- id: Option<impl Into<ElementId>>,
- f: impl FnOnce(&mut Self) -> R,
- ) -> R {
- debug_assert!(
- matches!(
- self.window.draw_phase,
- DrawPhase::Prepaint | DrawPhase::Paint
- ),
- "this method can only be called during request_layout, prepaint, or paint"
- );
- 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. This method should only be called during element drawing.
pub fn with_content_mask<R>(
@@ -1903,14 +1875,27 @@ impl<'a> WindowContext<'a> {
})
}
+ /// Provide elements in the called function with a new namespace in which their identiers must be unique.
+ /// This can be used within a custom element to distinguish multiple sets of child elements.
+ pub fn with_element_namespace<R>(
+ &mut self,
+ element_id: impl Into<ElementId>,
+ f: impl FnOnce(&mut Self) -> R,
+ ) -> R {
+ self.window.element_id_stack.push(element_id.into());
+ let result = f(self);
+ self.window.element_id_stack.pop();
+ result
+ }
+
/// 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. This method should only be called as part of element drawing.
pub fn with_element_state<S, R>(
&mut self,
- element_id: Option<ElementId>,
- f: impl FnOnce(Option<Option<S>>, &mut Self) -> (R, Option<S>),
+ global_id: &GlobalElementId,
+ f: impl FnOnce(Option<S>, &mut Self) -> (R, S),
) -> R
where
S: 'static,
@@ -1922,90 +1907,110 @@ impl<'a> WindowContext<'a> {
),
"this method can only be called during request_layout, prepaint, or paint"
);
- let id_is_none = element_id.is_none();
- self.with_element_id(element_id, |cx| {
- if id_is_none {
- let (result, state) = f(None, cx);
- debug_assert!(state.is_none(), "you must not return an element state when passing None for the element id");
- result
- } else {
- let global_id = cx.window().element_id_stack.clone();
- let key = (global_id, TypeId::of::<S>());
- cx.window.next_frame.accessed_element_states.push(key.clone());
-
- if let Some(any) = cx
- .window_mut()
- .next_frame
- .element_states
- .remove(&key)
- .or_else(|| {
- cx.window_mut()
- .rendered_frame
- .element_states
- .remove(&key)
- })
+
+ let key = (GlobalElementId(global_id.0.clone()), TypeId::of::<S>());
+ self.window
+ .next_frame
+ .accessed_element_states
+ .push((GlobalElementId(key.0.clone()), TypeId::of::<S>()));
+
+ if let Some(any) = self
+ .window
+ .next_frame
+ .element_states
+ .remove(&key)
+ .or_else(|| self.window.rendered_frame.element_states.remove(&key))
+ {
+ let ElementStateBox {
+ inner,
+ #[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)]
{
- let ElementStateBox {
- inner,
- #[cfg(debug_assertions)]
+ anyhow::anyhow!(
+ "invalid element state type for id, requested {:?}, actual: {:?}",
+ std::any::type_name::<S>(),
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("reentrant call to with_element_state for the same state type and element id");
- let (result, state) = f(Some(Some(state)), cx);
- state_box.replace(state.expect("you must return "));
- cx.window_mut()
- .next_frame
- .element_states
- .insert(key, ElementStateBox {
- inner: state_box,
- #[cfg(debug_assertions)]
- type_name
- });
- result
- } else {
- let (result, state) = f(Some(None), cx);
- cx.window_mut()
- .next_frame
- .element_states
- .insert(key,
- ElementStateBox {
- inner: Box::new(Some(state.expect("you must return Some<State> when you pass some element id"))),
- #[cfg(debug_assertions)]
- type_name: std::any::type_name::<S>()
- }
-
- );
- result
+ )
}
- }
+
+ #[cfg(not(debug_assertions))]
+ {
+ anyhow::anyhow!(
+ "invalid element state type for id, requested {:?}",
+ std::any::type_name::<S>(),
+ )
+ }
+ })
+ .unwrap();
+
+ let state = state_box.take().expect(
+ "reentrant call to with_element_state for the same state type and element id",
+ );
+ let (result, state) = f(Some(state), self);
+ state_box.replace(state);
+ self.window.next_frame.element_states.insert(
+ key,
+ ElementStateBox {
+ inner: state_box,
+ #[cfg(debug_assertions)]
+ type_name,
+ },
+ );
+ result
+ } else {
+ let (result, state) = f(None, self);
+ self.window.next_frame.element_states.insert(
+ key,
+ ElementStateBox {
+ inner: Box::new(Some(state)),
+ #[cfg(debug_assertions)]
+ type_name: std::any::type_name::<S>(),
+ },
+ );
+ result
+ }
+ }
+
+ /// A variant of `with_element_state` that allows the element's id to be optional. This is a convenience
+ /// method for elements where the element id may or may not be assigned. Prefer using `with_element_state`
+ /// when the element is guaranteed to have an id.
+ pub fn with_optional_element_state<S, R>(
+ &mut self,
+ global_id: Option<&GlobalElementId>,
+ f: impl FnOnce(Option<Option<S>>, &mut Self) -> (R, Option<S>),
+ ) -> R
+ where
+ S: 'static,
+ {
+ debug_assert!(
+ matches!(
+ self.window.draw_phase,
+ DrawPhase::Prepaint | DrawPhase::Paint
+ ),
+ "this method can only be called during request_layout, prepaint, or paint"
+ );
+
+ if let Some(global_id) = global_id {
+ self.with_element_state(global_id, |state, cx| {
+ let (result, state) = f(Some(state), cx);
+ let state =
+ state.expect("you must return some state when you pass some element id");
+ (result, state)
})
+ } else {
+ let (result, state) = f(None, self);
+ debug_assert!(
+ state.is_none(),
+ "you must not return an element state when passing None for the global id"
+ );
+ result
+ }
}
/// Defers the drawing of the given element, scheduling it to be painted on top of the currently-drawn tree
@@ -1,11 +1,11 @@
use editor::{CursorLayout, HighlightedRange, HighlightedRangeLine};
use gpui::{
- div, fill, point, px, relative, AnyElement, Bounds, DispatchPhase, Element, FocusHandle, Font,
- FontStyle, FontWeight, HighlightStyle, Hitbox, Hsla, InputHandler, InteractiveElement,
- Interactivity, IntoElement, LayoutId, Model, ModelContext, ModifiersChangedEvent, MouseButton,
- MouseMoveEvent, Pixels, Point, ShapedLine, StatefulInteractiveElement, StrikethroughStyle,
- Styled, TextRun, TextStyle, UnderlineStyle, WeakView, WhiteSpace, WindowContext,
- WindowTextSystem,
+ div, fill, point, px, relative, AnyElement, Bounds, DispatchPhase, Element, ElementId,
+ FocusHandle, Font, FontStyle, FontWeight, GlobalElementId, HighlightStyle, Hitbox, Hsla,
+ InputHandler, InteractiveElement, Interactivity, IntoElement, LayoutId, Model, ModelContext,
+ ModifiersChangedEvent, MouseButton, MouseMoveEvent, Pixels, Point, ShapedLine,
+ StatefulInteractiveElement, StrikethroughStyle, Styled, TextRun, TextStyle, UnderlineStyle,
+ WeakView, WhiteSpace, WindowContext, WindowTextSystem,
};
use itertools::Itertools;
use language::CursorShape;
@@ -544,26 +544,37 @@ impl Element for TerminalElement {
type RequestLayoutState = ();
type PrepaintState = LayoutState;
- fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
- self.interactivity.occlude_mouse();
- let layout_id = self.interactivity.request_layout(cx, |mut style, cx| {
- style.size.width = relative(1.).into();
- style.size.height = relative(1.).into();
- let layout_id = cx.request_layout(&style, None);
+ fn id(&self) -> Option<ElementId> {
+ self.interactivity.element_id.clone()
+ }
- layout_id
- });
+ fn request_layout(
+ &mut self,
+ global_id: Option<&GlobalElementId>,
+ cx: &mut WindowContext,
+ ) -> (LayoutId, Self::RequestLayoutState) {
+ self.interactivity.occlude_mouse();
+ let layout_id = self
+ .interactivity
+ .request_layout(global_id, cx, |mut style, cx| {
+ style.size.width = relative(1.).into();
+ style.size.height = relative(1.).into();
+ let layout_id = cx.request_layout(&style, None);
+
+ layout_id
+ });
(layout_id, ())
}
fn prepaint(
&mut self,
+ global_id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
) -> Self::PrepaintState {
self.interactivity
- .prepaint(bounds, bounds.size, cx, |_, _, hitbox, cx| {
+ .prepaint(global_id, bounds, bounds.size, cx, |_, _, hitbox, cx| {
let hitbox = hitbox.unwrap();
let settings = ThemeSettings::get_global(cx).clone();
@@ -775,6 +786,7 @@ impl Element for TerminalElement {
fn paint(
&mut self,
+ global_id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
layout: &mut Self::PrepaintState,
@@ -802,7 +814,7 @@ impl Element for TerminalElement {
let cursor = layout.cursor.take();
let hyperlink_tooltip = layout.hyperlink_tooltip.take();
self.interactivity
- .paint(bounds, Some(&layout.hitbox), cx, |_, cx| {
+ .paint(global_id, bounds, Some(&layout.hitbox), cx, |_, cx| {
cx.handle_input(&self.focus, terminal_input_handler);
cx.on_key_event({
@@ -2,9 +2,9 @@ use std::{cell::RefCell, rc::Rc};
use gpui::{
anchored, deferred, div, point, prelude::FluentBuilder, px, AnchorCorner, AnyElement, Bounds,
- DismissEvent, DispatchPhase, Element, ElementId, HitboxId, InteractiveElement, IntoElement,
- LayoutId, ManagedView, MouseDownEvent, ParentElement, Pixels, Point, View, VisualContext,
- WindowContext,
+ DismissEvent, DispatchPhase, Element, ElementId, GlobalElementId, HitboxId, InteractiveElement,
+ IntoElement, LayoutId, ManagedView, MouseDownEvent, ParentElement, Pixels, Point, View,
+ VisualContext, WindowContext,
};
use crate::prelude::*;
@@ -109,21 +109,6 @@ impl<M: ManagedView> PopoverMenu<M> {
}
})
}
-
- fn with_element_state<R>(
- &mut self,
- cx: &mut WindowContext,
- f: impl FnOnce(&mut Self, &mut PopoverMenuElementState<M>, &mut WindowContext) -> R,
- ) -> R {
- cx.with_element_state::<PopoverMenuElementState<M>, _>(
- Some(self.id.clone()),
- |element_state, cx| {
- let mut element_state = element_state.unwrap().unwrap_or_default();
- let result = f(self, &mut element_state, cx);
- (result, Some(element_state))
- },
- )
- }
}
/// Creates a [`PopoverMenu`]
@@ -171,101 +156,118 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
type RequestLayoutState = PopoverMenuFrameState;
type PrepaintState = Option<HitboxId>;
+ fn id(&self) -> Option<ElementId> {
+ Some(self.id.clone())
+ }
+
fn request_layout(
&mut self,
+ global_id: Option<&GlobalElementId>,
cx: &mut WindowContext,
) -> (gpui::LayoutId, Self::RequestLayoutState) {
- self.with_element_state(cx, |this, element_state, cx| {
- let mut menu_layout_id = None;
-
- let menu_element = element_state.menu.borrow_mut().as_mut().map(|menu| {
- let mut anchored = anchored().snap_to_window().anchor(this.anchor);
- if let Some(child_bounds) = element_state.child_bounds {
- anchored = anchored.position(
- this.resolved_attach().corner(child_bounds) + this.resolved_offset(cx),
- );
- }
- let mut element = deferred(anchored.child(div().occlude().child(menu.clone())))
- .with_priority(1)
- .into_any();
-
- menu_layout_id = Some(element.request_layout(cx));
- element
- });
-
- let mut child_element = this.child_builder.take().map(|child_builder| {
- (child_builder)(element_state.menu.clone(), this.menu_builder.clone())
- });
-
- let child_layout_id = child_element
- .as_mut()
- .map(|child_element| child_element.request_layout(cx));
-
- let layout_id = cx.request_layout(
- &gpui::Style::default(),
- menu_layout_id.into_iter().chain(child_layout_id),
- );
-
- (
- layout_id,
- PopoverMenuFrameState {
- child_element,
- child_layout_id,
- menu_element,
- },
- )
- })
+ cx.with_element_state(
+ global_id.unwrap(),
+ |element_state: Option<PopoverMenuElementState<M>>, cx| {
+ let element_state = element_state.unwrap_or_default();
+ let mut menu_layout_id = None;
+
+ let menu_element = element_state.menu.borrow_mut().as_mut().map(|menu| {
+ let mut anchored = anchored().snap_to_window().anchor(self.anchor);
+ if let Some(child_bounds) = element_state.child_bounds {
+ anchored = anchored.position(
+ self.resolved_attach().corner(child_bounds) + self.resolved_offset(cx),
+ );
+ }
+ let mut element = deferred(anchored.child(div().occlude().child(menu.clone())))
+ .with_priority(1)
+ .into_any();
+
+ menu_layout_id = Some(element.request_layout(cx));
+ element
+ });
+
+ let mut child_element = self.child_builder.take().map(|child_builder| {
+ (child_builder)(element_state.menu.clone(), self.menu_builder.clone())
+ });
+
+ let child_layout_id = child_element
+ .as_mut()
+ .map(|child_element| child_element.request_layout(cx));
+
+ let layout_id = cx.request_layout(
+ &gpui::Style::default(),
+ menu_layout_id.into_iter().chain(child_layout_id),
+ );
+
+ (
+ (
+ layout_id,
+ PopoverMenuFrameState {
+ child_element,
+ child_layout_id,
+ menu_element,
+ },
+ ),
+ element_state,
+ )
+ },
+ )
}
fn prepaint(
&mut self,
+ global_id: Option<&GlobalElementId>,
_bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
) -> Option<HitboxId> {
- self.with_element_state(cx, |_this, element_state, cx| {
- if let Some(child) = request_layout.child_element.as_mut() {
- child.prepaint(cx);
- }
+ if let Some(child) = request_layout.child_element.as_mut() {
+ child.prepaint(cx);
+ }
- if let Some(menu) = request_layout.menu_element.as_mut() {
- menu.prepaint(cx);
- }
+ if let Some(menu) = request_layout.menu_element.as_mut() {
+ menu.prepaint(cx);
+ }
- request_layout.child_layout_id.map(|layout_id| {
- let bounds = cx.layout_bounds(layout_id);
+ let hitbox_id = request_layout.child_layout_id.map(|layout_id| {
+ let bounds = cx.layout_bounds(layout_id);
+ cx.with_element_state(global_id.unwrap(), |element_state, _cx| {
+ let mut element_state: PopoverMenuElementState<M> = element_state.unwrap();
element_state.child_bounds = Some(bounds);
- cx.insert_hitbox(bounds, false).id
- })
- })
+ ((), element_state)
+ });
+
+ cx.insert_hitbox(bounds, false).id
+ });
+
+ hitbox_id
}
fn paint(
&mut self,
+ _id: Option<&GlobalElementId>,
_: Bounds<gpui::Pixels>,
request_layout: &mut Self::RequestLayoutState,
child_hitbox: &mut Option<HitboxId>,
cx: &mut WindowContext,
) {
- self.with_element_state(cx, |_this, _element_state, cx| {
- if let Some(mut child) = request_layout.child_element.take() {
- child.paint(cx);
- }
+ if let Some(mut child) = request_layout.child_element.take() {
+ child.paint(cx);
+ }
- if let Some(mut menu) = request_layout.menu_element.take() {
- menu.paint(cx);
+ if let Some(mut menu) = request_layout.menu_element.take() {
+ menu.paint(cx);
- if let Some(child_hitbox) = *child_hitbox {
- // Mouse-downing outside the menu dismisses it, so we don't
- // want a click on the toggle to re-open it.
- cx.on_mouse_event(move |_: &MouseDownEvent, phase, cx| {
- if phase == DispatchPhase::Bubble && child_hitbox.is_hovered(cx) {
- cx.stop_propagation()
- }
- })
- }
+ if let Some(child_hitbox) = *child_hitbox {
+ // Mouse-downing outside the menu dismisses it, so we don't
+ // want a click on the toggle to re-open it.
+ cx.on_mouse_event(move |_: &MouseDownEvent, phase, cx| {
+ if phase == DispatchPhase::Bubble && child_hitbox.is_hovered(cx) {
+ cx.stop_propagation()
+ }
+ })
}
- })
+ }
}
}
@@ -2,8 +2,9 @@ use std::{cell::RefCell, rc::Rc};
use gpui::{
anchored, deferred, div, AnchorCorner, AnyElement, Bounds, DismissEvent, DispatchPhase,
- Element, ElementId, Hitbox, InteractiveElement, IntoElement, LayoutId, ManagedView,
- MouseButton, MouseDownEvent, ParentElement, Pixels, Point, View, VisualContext, WindowContext,
+ Element, ElementId, GlobalElementId, Hitbox, InteractiveElement, IntoElement, LayoutId,
+ ManagedView, MouseButton, MouseDownEvent, ParentElement, Pixels, Point, View, VisualContext,
+ WindowContext,
};
pub struct RightClickMenu<M: ManagedView> {
@@ -40,11 +41,12 @@ impl<M: ManagedView> RightClickMenu<M> {
fn with_element_state<R>(
&mut self,
+ global_id: &GlobalElementId,
cx: &mut WindowContext,
f: impl FnOnce(&mut Self, &mut MenuHandleElementState<M>, &mut WindowContext) -> R,
) -> R {
- cx.with_element_state::<MenuHandleElementState<M>, _>(
- Some(self.id.clone()),
+ cx.with_optional_element_state::<MenuHandleElementState<M>, _>(
+ Some(global_id),
|element_state, cx| {
let mut element_state = element_state.unwrap().unwrap_or_default();
let result = f(self, &mut element_state, cx);
@@ -103,11 +105,16 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
type RequestLayoutState = RequestLayoutState;
type PrepaintState = PrepaintState;
+ fn id(&self) -> Option<ElementId> {
+ Some(self.id.clone())
+ }
+
fn request_layout(
&mut self,
+ id: Option<&GlobalElementId>,
cx: &mut WindowContext,
) -> (gpui::LayoutId, Self::RequestLayoutState) {
- self.with_element_state(cx, |this, element_state, cx| {
+ self.with_element_state(id.unwrap(), cx, |this, element_state, cx| {
let mut menu_layout_id = None;
let menu_element = element_state.menu.borrow_mut().as_mut().map(|menu| {
@@ -152,38 +159,38 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
fn prepaint(
&mut self,
+ _id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
) -> PrepaintState {
- cx.with_element_id(Some(self.id.clone()), |cx| {
- let hitbox = cx.insert_hitbox(bounds, false);
+ let hitbox = cx.insert_hitbox(bounds, false);
- if let Some(child) = request_layout.child_element.as_mut() {
- child.prepaint(cx);
- }
+ if let Some(child) = request_layout.child_element.as_mut() {
+ child.prepaint(cx);
+ }
- if let Some(menu) = request_layout.menu_element.as_mut() {
- menu.prepaint(cx);
- }
+ if let Some(menu) = request_layout.menu_element.as_mut() {
+ menu.prepaint(cx);
+ }
- PrepaintState {
- hitbox,
- child_bounds: request_layout
- .child_layout_id
- .map(|layout_id| cx.layout_bounds(layout_id)),
- }
- })
+ PrepaintState {
+ hitbox,
+ child_bounds: request_layout
+ .child_layout_id
+ .map(|layout_id| cx.layout_bounds(layout_id)),
+ }
}
fn paint(
&mut self,
+ id: Option<&GlobalElementId>,
_bounds: Bounds<gpui::Pixels>,
request_layout: &mut Self::RequestLayoutState,
prepaint_state: &mut Self::PrepaintState,
cx: &mut WindowContext,
) {
- self.with_element_state(cx, |this, element_state, cx| {
+ self.with_element_state(id.unwrap(), cx, |this, element_state, cx| {
if let Some(mut child) = request_layout.child_element.take() {
child.paint(cx);
}
@@ -597,9 +597,9 @@ mod element {
use std::{cell::RefCell, iter, rc::Rc, sync::Arc};
use gpui::{
- px, relative, Along, AnyElement, Axis, Bounds, Element, IntoElement, MouseDownEvent,
- MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Size, Style, WeakView,
- WindowContext,
+ px, relative, Along, AnyElement, Axis, Bounds, Element, GlobalElementId, IntoElement,
+ MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Size, Style,
+ WeakView, WindowContext,
};
use gpui::{CursorStyle, Hitbox};
use parking_lot::Mutex;
@@ -795,8 +795,13 @@ mod element {
type RequestLayoutState = ();
type PrepaintState = PaneAxisLayout;
+ fn id(&self) -> Option<ElementId> {
+ Some(self.basis.into())
+ }
+
fn request_layout(
&mut self,
+ _global_id: Option<&GlobalElementId>,
cx: &mut ui::prelude::WindowContext,
) -> (gpui::LayoutId, Self::RequestLayoutState) {
let mut style = Style::default();
@@ -810,17 +815,16 @@ mod element {
fn prepaint(
&mut self,
+ global_id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
_state: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
) -> PaneAxisLayout {
let dragged_handle = cx.with_element_state::<Rc<RefCell<Option<usize>>>, _>(
- Some(self.basis.into()),
+ global_id.unwrap(),
|state, _cx| {
- let state = state
- .unwrap()
- .unwrap_or_else(|| Rc::new(RefCell::new(None)));
- (state.clone(), Some(state))
+ let state = state.unwrap_or_else(|| Rc::new(RefCell::new(None)));
+ (state.clone(), state)
},
);
let flexes = self.flexes.lock().clone();
@@ -897,6 +901,7 @@ mod element {
fn paint(
&mut self,
+ _id: Option<&GlobalElementId>,
bounds: gpui::Bounds<ui::prelude::Pixels>,
_: &mut Self::RequestLayoutState,
layout: &mut Self::PrepaintState,
@@ -158,8 +158,10 @@ impl Toolbar {
{
let location = item.set_active_pane_item(self.active_item.as_deref(), cx);
cx.subscribe(&item, |this, item, event, cx| {
- if let Some((_, current_location)) =
- this.items.iter_mut().find(|(i, _)| i.id() == item.id())
+ if let Some((_, current_location)) = this
+ .items
+ .iter_mut()
+ .find(|(i, _)| i.id() == item.entity_id())
{
match event {
ToolbarItemEvent::ChangeLocation(new_location) => {
@@ -28,9 +28,10 @@ use futures::{
use gpui::{
actions, canvas, impl_actions, point, relative, size, Action, AnyElement, AnyView, AnyWeakView,
AppContext, AsyncAppContext, AsyncWindowContext, Bounds, DevicePixels, DragMoveEvent,
- Entity as _, EntityId, EventEmitter, FocusHandle, FocusableView, Global, KeyContext, Keystroke,
- LayoutId, ManagedView, Model, ModelContext, PathPromptOptions, Point, PromptLevel, Render,
- Size, Subscription, Task, View, WeakView, WindowHandle, WindowOptions,
+ ElementId, Entity as _, EntityId, EventEmitter, FocusHandle, FocusableView, Global,
+ GlobalElementId, KeyContext, Keystroke, LayoutId, ManagedView, Model, ModelContext,
+ PathPromptOptions, Point, PromptLevel, Render, Size, Subscription, Task, View, WeakView,
+ WindowHandle, WindowOptions,
};
use item::{
FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, PreviewTabsSettings,
@@ -5060,7 +5061,15 @@ impl Element for DisconnectedOverlay {
type RequestLayoutState = AnyElement;
type PrepaintState = ();
- fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
+ fn id(&self) -> Option<ElementId> {
+ None
+ }
+
+ fn request_layout(
+ &mut self,
+ _id: Option<&GlobalElementId>,
+ cx: &mut WindowContext,
+ ) -> (LayoutId, Self::RequestLayoutState) {
let mut background = cx.theme().colors().elevated_surface_background;
background.fade_out(0.2);
let mut overlay = div()
@@ -5083,6 +5092,7 @@ impl Element for DisconnectedOverlay {
fn prepaint(
&mut self,
+ _id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
overlay: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
@@ -5093,6 +5103,7 @@ impl Element for DisconnectedOverlay {
fn paint(
&mut self,
+ _id: Option<&GlobalElementId>,
_: Bounds<Pixels>,
overlay: &mut Self::RequestLayoutState,
_: &mut Self::PrepaintState,