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:
parent
8b55494351
commit
39fb1d567d
21 changed files with 825 additions and 497 deletions
|
@ -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()
|
||||
}
|
||||
|
||||
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) {
|
||||
|
|
|
@ -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 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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
cx.with_element_state::<AnyViewState, _>(global_id.unwrap(), |element_state, cx| {
|
||||
let content_mask = cx.content_mask();
|
||||
let text_style = cx.text_style();
|
||||
|
||||
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));
|
||||
}
|
||||
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();
|
||||
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)
|
||||
(
|
||||
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,13 +1875,114 @@ 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>,
|
||||
global_id: &GlobalElementId,
|
||||
f: impl FnOnce(Option<S>, &mut Self) -> (R, 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"
|
||||
);
|
||||
|
||||
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)]
|
||||
{
|
||||
anyhow::anyhow!(
|
||||
"invalid element state type for id, requested {:?}, actual: {:?}",
|
||||
std::any::type_name::<S>(),
|
||||
type_name
|
||||
)
|
||||
}
|
||||
|
||||
#[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
|
||||
|
@ -1922,90 +1995,22 @@ 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 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)]
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
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;
|
||||
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(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();
|
||||
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
|
||||
});
|
||||
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 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 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),
|
||||
);
|
||||
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,
|
||||
},
|
||||
)
|
||||
})
|
||||
(
|
||||
(
|
||||
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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue