Incorporate ElementId as part of the Element::id trait method and expose GlobalId (#11101)

We're planning to associate "selection sources" with global element ids
to allow arbitrary UI text to be selected in GPUI. Previously, global
ids were not exposed outside the framework and we entangled management
of the element id stack with element state access. This was more
acceptable when element state was the only place we used global element
ids, but now that we're planning to use them more places, it makes sense
to deal with element identity as a first-class part of the element
system. We now ensure that the stack of element ids which forms the
current global element id is correctly managed in every phase of element
layout and paint and make the global id available to each element
method. In a subsequent PR, we'll use the global element id as part of
implementing arbitrary selection for UI text.

Release Notes:

- N/A

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
This commit is contained in:
Nathan Sobo
2024-04-28 13:59:21 -06:00
committed by GitHub
co-authored by Antonio Scandurra
parent 8b55494351
commit 39fb1d567d
21 changed files with 825 additions and 497 deletions
+9 -2
View File
@@ -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,
+11 -5
View File
@@ -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,
+11 -1
View File
@@ -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,
+14 -2
View File
@@ -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,
+85 -38
View File
@@ -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);
}
}
+38 -25
View File
@@ -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()
}
cx.request_layout(&style, [])
});
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, [])
});
(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) {
+9 -2
View File
@@ -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,
+17 -7
View File
@@ -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
+60 -15
View File
@@ -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)
},
);
}
+37 -22
View File
@@ -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 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)
})
});
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);
}