Compare commits

...
Sign in to create a new pull request.

3 commits

Author SHA1 Message Date
Antonio Scandurra
676f6f490e WIP 2023-12-18 19:58:27 +01:00
Antonio Scandurra
be3c0ff419 WIP 2023-12-18 18:43:22 +01:00
Antonio Scandurra
2330256741 WIP 2023-12-18 14:55:59 +01:00
17 changed files with 732 additions and 810 deletions

View file

@ -889,7 +889,7 @@ impl EditorElement {
} }
let fold_corner_radius = 0.15 * layout.position_map.line_height; let fold_corner_radius = 0.15 * layout.position_map.line_height;
cx.with_element_id(Some("folds"), |cx| { cx.with_element_id(Some("folds"), |_, cx| {
let snapshot = &layout.position_map.snapshot; let snapshot = &layout.position_map.snapshot;
for fold in snapshot.folds_in_range(layout.visible_anchor_range.clone()) { for fold in snapshot.folds_in_range(layout.visible_anchor_range.clone()) {
let fold_range = fold.range.clone(); let fold_range = fold.range.clone();
@ -938,7 +938,7 @@ impl EditorElement {
fold_bounds.size, fold_bounds.size,
cx, cx,
|fold_element_state, cx| { |fold_element_state, cx| {
if fold_element_state.is_active() { if fold_element_state.is_active {
cx.theme().colors().ghost_element_active cx.theme().colors().ghost_element_active
} else if fold_bounds.contains(&cx.mouse_position()) { } else if fold_bounds.contains(&cx.mouse_position()) {
cx.theme().colors().ghost_element_hover cx.theme().colors().ghost_element_hover
@ -1996,7 +1996,7 @@ impl EditorElement {
.width; .width;
let scroll_width = longest_line_width.max(max_visible_line_width) + overscroll.width; let scroll_width = longest_line_width.max(max_visible_line_width) + overscroll.width;
let (scroll_width, blocks) = cx.with_element_id(Some("editor_blocks"), |cx| { let (scroll_width, blocks) = cx.with_element_id(Some("editor_blocks"), |_, cx| {
self.layout_blocks( self.layout_blocks(
start_row..end_row, start_row..end_row,
&snapshot, &snapshot,
@ -2081,7 +2081,7 @@ impl EditorElement {
cx, cx,
); );
let mut fold_indicators = cx.with_element_id(Some("gutter_fold_indicators"), |cx| { let mut fold_indicators = cx.with_element_id(Some("gutter_fold_indicators"), |_, cx| {
editor.render_fold_indicators( editor.render_fold_indicators(
fold_statuses, fold_statuses,
&style, &style,
@ -2734,13 +2734,9 @@ enum Invisible {
} }
impl Element for EditorElement { impl Element for EditorElement {
type State = (); type FrameState = ();
fn layout( fn layout(&mut self, cx: &mut gpui::WindowContext) -> (gpui::LayoutId, Self::FrameState) {
&mut self,
element_state: Option<Self::State>,
cx: &mut gpui::WindowContext,
) -> (gpui::LayoutId, Self::State) {
self.editor.update(cx, |editor, cx| { self.editor.update(cx, |editor, cx| {
editor.set_style(self.style.clone(), cx); editor.set_style(self.style.clone(), cx);
@ -2788,7 +2784,7 @@ impl Element for EditorElement {
fn paint( fn paint(
&mut self, &mut self,
bounds: Bounds<gpui::Pixels>, bounds: Bounds<gpui::Pixels>,
element_state: &mut Self::State, _element_state: &mut Self::FrameState,
cx: &mut gpui::WindowContext, cx: &mut gpui::WindowContext,
) { ) {
let editor = self.editor.clone(); let editor = self.editor.clone();
@ -2823,7 +2819,7 @@ impl Element for EditorElement {
self.paint_mouse_listeners(bounds, gutter_bounds, text_bounds, &layout, cx); self.paint_mouse_listeners(bounds, gutter_bounds, text_bounds, &layout, cx);
if !layout.blocks.is_empty() { if !layout.blocks.is_empty() {
cx.with_element_id(Some("editor_blocks"), |cx| { cx.with_element_id(Some("editor_blocks"), |_, cx| {
self.paint_blocks(bounds, &mut layout, cx); self.paint_blocks(bounds, &mut layout, cx);
}); });
} }

View file

@ -4,7 +4,7 @@ use crate::{
}; };
use derive_more::{Deref, DerefMut}; use derive_more::{Deref, DerefMut};
pub(crate) use smallvec::SmallVec; pub(crate) use smallvec::SmallVec;
use std::{any::Any, fmt::Debug}; use std::{any::Any, fmt::Debug, mem};
pub trait Render: 'static + Sized { pub trait Render: 'static + Sized {
type Element: Element + 'static; type Element: Element + 'static;
@ -28,30 +28,20 @@ pub trait IntoElement: Sized {
origin: Point<Pixels>, origin: Point<Pixels>,
available_space: Size<T>, available_space: Size<T>,
cx: &mut WindowContext, cx: &mut WindowContext,
f: impl FnOnce(&mut <Self::Element as Element>::State, &mut WindowContext) -> R, f: impl FnOnce(&mut <Self::Element as Element>::FrameState, &mut WindowContext) -> R,
) -> R ) -> R
where where
T: Clone + Default + Debug + Into<AvailableSpace>, T: Clone + Default + Debug + Into<AvailableSpace>,
{ {
let element = self.into_element(); let element = self.into_element();
let element_id = element.element_id();
let element = DrawableElement { let element = DrawableElement {
element: Some(element), element: Some(element),
phase: ElementDrawPhase::Start, phase: ElementDrawPhase::Start,
}; };
let frame_state = let mut frame_state =
DrawableElement::draw(element, origin, available_space.map(Into::into), cx); DrawableElement::draw(element, origin, available_space.map(Into::into), cx);
f(&mut frame_state, cx)
if let Some(mut frame_state) = frame_state {
f(&mut frame_state, cx)
} else {
cx.with_element_state(element_id.unwrap(), |element_state, cx| {
let mut element_state = element_state.unwrap();
let result = f(&mut element_state, cx);
(result, element_state)
})
}
} }
fn map<U>(self, f: impl FnOnce(Self) -> U) -> U fn map<U>(self, f: impl FnOnce(Self) -> U) -> U
@ -84,15 +74,16 @@ pub trait IntoElement: Sized {
} }
pub trait Element: 'static + IntoElement { pub trait Element: 'static + IntoElement {
type State: 'static; type FrameState: 'static;
fn layout( fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState);
fn paint(
&mut self, &mut self,
state: Option<Self::State>, bounds: Bounds<Pixels>,
state: &mut Self::FrameState,
cx: &mut WindowContext, cx: &mut WindowContext,
) -> (LayoutId, Self::State); );
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext);
fn into_any(self) -> AnyElement { fn into_any(self) -> AnyElement {
AnyElement::new(self) AnyElement::new(self)
@ -111,7 +102,7 @@ pub struct Component<C> {
pub struct CompositeElementState<C: RenderOnce> { pub struct CompositeElementState<C: RenderOnce> {
rendered_element: Option<<C::Rendered as IntoElement>::Element>, rendered_element: Option<<C::Rendered as IntoElement>::Element>,
rendered_element_state: Option<<<C::Rendered as IntoElement>::Element as Element>::State>, rendered_element_state: Option<<<C::Rendered as IntoElement>::Element as Element>::FrameState>,
} }
impl<C> Component<C> { impl<C> Component<C> {
@ -123,48 +114,30 @@ impl<C> Component<C> {
} }
impl<C: RenderOnce> Element for Component<C> { impl<C: RenderOnce> Element for Component<C> {
type State = CompositeElementState<C>; type FrameState = CompositeElementState<C>;
fn layout( fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
&mut self,
state: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
let mut element = self.component.take().unwrap().render(cx).into_element(); let mut element = self.component.take().unwrap().render(cx).into_element();
if let Some(element_id) = element.element_id() { let (layout_id, state) = element.layout(cx);
let layout_id = let state = CompositeElementState {
cx.with_element_state(element_id, |state, cx| element.layout(state, cx)); rendered_element: Some(element),
let state = CompositeElementState { rendered_element_state: Some(state),
rendered_element: Some(element), };
rendered_element_state: None, (layout_id, state)
};
(layout_id, state)
} else {
let (layout_id, state) =
element.layout(state.and_then(|s| s.rendered_element_state), cx);
let state = CompositeElementState {
rendered_element: Some(element),
rendered_element_state: Some(state),
};
(layout_id, state)
}
} }
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) { fn paint(
&mut self,
bounds: Bounds<Pixels>,
state: &mut Self::FrameState,
cx: &mut WindowContext,
) {
let mut element = state.rendered_element.take().unwrap(); let mut element = state.rendered_element.take().unwrap();
if let Some(element_id) = element.element_id() { element.paint(
cx.with_element_state(element_id, |element_state, cx| { bounds,
let mut element_state = element_state.unwrap(); &mut state.rendered_element_state.as_mut().unwrap(),
element.paint(bounds, &mut element_state, cx); cx,
((), element_state) );
});
} else {
element.paint(
bounds,
&mut state.rendered_element_state.as_mut().unwrap(),
cx,
);
}
} }
} }
@ -227,7 +200,7 @@ trait ElementObject {
pub struct DrawableElement<E: Element> { pub struct DrawableElement<E: Element> {
element: Option<E>, element: Option<E>,
phase: ElementDrawPhase<E::State>, phase: ElementDrawPhase<E::FrameState>,
} }
#[derive(Default)] #[derive(Default)]
@ -236,12 +209,12 @@ enum ElementDrawPhase<S> {
Start, Start,
LayoutRequested { LayoutRequested {
layout_id: LayoutId, layout_id: LayoutId,
frame_state: Option<S>, frame_state: S,
}, },
LayoutComputed { LayoutComputed {
layout_id: LayoutId, layout_id: LayoutId,
available_space: Size<AvailableSpace>, available_space: Size<AvailableSpace>,
frame_state: Option<S>, frame_state: S,
}, },
} }
@ -259,17 +232,7 @@ impl<E: Element> DrawableElement<E> {
} }
fn layout(&mut self, cx: &mut WindowContext) -> LayoutId { fn layout(&mut self, cx: &mut WindowContext) -> LayoutId {
let (layout_id, frame_state) = if let Some(id) = self.element.as_ref().unwrap().element_id() let (layout_id, frame_state) = self.element.as_mut().unwrap().layout(cx);
{
let layout_id = cx.with_element_state(id, |element_state, cx| {
self.element.as_mut().unwrap().layout(element_state, cx)
});
(layout_id, None)
} else {
let (layout_id, frame_state) = self.element.as_mut().unwrap().layout(None, cx);
(layout_id, Some(frame_state))
};
self.phase = ElementDrawPhase::LayoutRequested { self.phase = ElementDrawPhase::LayoutRequested {
layout_id, layout_id,
frame_state, frame_state,
@ -277,42 +240,23 @@ impl<E: Element> DrawableElement<E> {
layout_id layout_id
} }
fn paint(mut self, cx: &mut WindowContext) -> Option<E::State> { fn paint(mut self, cx: &mut WindowContext) -> E::FrameState {
match self.phase { match self.phase {
ElementDrawPhase::LayoutRequested { ElementDrawPhase::LayoutRequested {
layout_id, layout_id,
frame_state, mut frame_state,
} }
| ElementDrawPhase::LayoutComputed { | ElementDrawPhase::LayoutComputed {
layout_id, layout_id,
frame_state, mut frame_state,
.. ..
} => { } => {
let bounds = cx.layout_bounds(layout_id); let bounds = cx.layout_bounds(layout_id);
self.element
if let Some(mut frame_state) = frame_state { .take()
self.element .unwrap()
.take() .paint(bounds, &mut frame_state, cx);
.unwrap() frame_state
.paint(bounds, &mut frame_state, cx);
Some(frame_state)
} else {
let element_id = self
.element
.as_ref()
.unwrap()
.element_id()
.expect("if we don't have frame state, we should have element state");
cx.with_element_state(element_id, |element_state, cx| {
let mut element_state = element_state.unwrap();
self.element
.take()
.unwrap()
.paint(bounds, &mut element_state, cx);
((), element_state)
});
None
}
} }
_ => panic!("must call layout before paint"), _ => panic!("must call layout before paint"),
@ -328,30 +272,34 @@ impl<E: Element> DrawableElement<E> {
self.layout(cx); self.layout(cx);
} }
let layout_id = match &mut self.phase { let layout_id = match mem::take(&mut self.phase) {
ElementDrawPhase::LayoutRequested { ElementDrawPhase::LayoutRequested {
layout_id, layout_id,
frame_state, frame_state,
} => { } => {
cx.compute_layout(*layout_id, available_space); cx.compute_layout(layout_id, available_space);
let layout_id = *layout_id;
self.phase = ElementDrawPhase::LayoutComputed { self.phase = ElementDrawPhase::LayoutComputed {
layout_id, layout_id,
available_space, available_space,
frame_state: frame_state.take(), frame_state,
}; };
layout_id layout_id
} }
ElementDrawPhase::LayoutComputed { ElementDrawPhase::LayoutComputed {
layout_id, layout_id,
available_space: prev_available_space, available_space: prev_available_space,
.. frame_state,
} => { } => {
if available_space != *prev_available_space { if available_space != prev_available_space {
cx.compute_layout(*layout_id, available_space); cx.compute_layout(layout_id, available_space);
*prev_available_space = available_space;
} }
*layout_id self.phase = ElementDrawPhase::LayoutComputed {
layout_id,
available_space,
frame_state,
};
layout_id
} }
_ => panic!("cannot measure after painting"), _ => panic!("cannot measure after painting"),
}; };
@ -364,7 +312,7 @@ impl<E: Element> DrawableElement<E> {
origin: Point<Pixels>, origin: Point<Pixels>,
available_space: Size<AvailableSpace>, available_space: Size<AvailableSpace>,
cx: &mut WindowContext, cx: &mut WindowContext,
) -> Option<E::State> { ) -> E::FrameState {
self.measure(available_space, cx); self.measure(available_space, cx);
cx.with_absolute_element_offset(origin, |cx| self.paint(cx)) cx.with_absolute_element_offset(origin, |cx| self.paint(cx))
} }
@ -373,7 +321,7 @@ impl<E: Element> DrawableElement<E> {
impl<E> ElementObject for Option<DrawableElement<E>> impl<E> ElementObject for Option<DrawableElement<E>>
where where
E: Element, E: Element,
E::State: 'static, E::FrameState: 'static,
{ {
fn element_id(&self) -> Option<ElementId> { fn element_id(&self) -> Option<ElementId> {
self.as_ref().unwrap().element_id() self.as_ref().unwrap().element_id()
@ -411,7 +359,7 @@ impl AnyElement {
pub fn new<E>(element: E) -> Self pub fn new<E>(element: E) -> Self
where where
E: 'static + Element, E: 'static + Element,
E::State: Any, E::FrameState: Any,
{ {
let element = frame_alloc(|| Some(DrawableElement::new(element))) let element = frame_alloc(|| Some(DrawableElement::new(element)))
.map(|element| element as &mut dyn ElementObject); .map(|element| element as &mut dyn ElementObject);
@ -451,18 +399,14 @@ impl AnyElement {
} }
impl Element for AnyElement { impl Element for AnyElement {
type State = (); type FrameState = ();
fn layout( fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
&mut self,
_: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
let layout_id = self.layout(cx); let layout_id = self.layout(cx);
(layout_id, ()) (layout_id, ())
} }
fn paint(&mut self, _: Bounds<Pixels>, _: &mut Self::State, cx: &mut WindowContext) { fn paint(&mut self, _: Bounds<Pixels>, _: &mut Self::FrameState, cx: &mut WindowContext) {
self.paint(cx) self.paint(cx)
} }
} }
@ -499,20 +443,16 @@ impl IntoElement for () {
} }
impl Element for () { impl Element for () {
type State = (); type FrameState = ();
fn layout( fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
&mut self,
_state: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
(cx.request_layout(&crate::Style::default(), None), ()) (cx.request_layout(&crate::Style::default(), None), ())
} }
fn paint( fn paint(
&mut self, &mut self,
_bounds: Bounds<Pixels>, _bounds: Bounds<Pixels>,
_state: &mut Self::State, _state: &mut Self::FrameState,
_cx: &mut WindowContext, _cx: &mut WindowContext,
) { ) {
} }

View file

@ -27,13 +27,9 @@ impl IntoElement for Canvas {
} }
impl Element for Canvas { impl Element for Canvas {
type State = Style; type FrameState = Style;
fn layout( fn layout(&mut self, cx: &mut WindowContext) -> (crate::LayoutId, Self::FrameState) {
&mut self,
_: Option<Self::State>,
cx: &mut WindowContext,
) -> (crate::LayoutId, Self::State) {
let mut style = Style::default(); let mut style = Style::default();
style.refine(&self.style); style.refine(&self.style);
let layout_id = cx.request_layout(&style, []); let layout_id = cx.request_layout(&style, []);

View file

@ -1,9 +1,10 @@
use crate::{ use crate::{
point, px, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, BorrowAppContext, point, px, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, BorrowAppContext,
BorrowWindow, Bounds, ClickEvent, DispatchPhase, Element, ElementId, FocusHandle, IntoElement, BorrowWindow, Bounds, ClickEvent, DispatchPhase, Element, ElementId, FocusHandle,
KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent, MouseMoveEvent, GlobalElementId, IntoElement, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton,
MouseUpEvent, ParentElement, Pixels, Point, Render, ScrollWheelEvent, SharedString, Size, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Render,
StackingOrder, Style, StyleRefinement, Styled, Task, View, Visibility, WindowContext, ScrollWheelEvent, SharedString, Size, StackingOrder, Style, StyleRefinement, Styled, Task,
View, Visibility, WindowContext,
}; };
use collections::HashMap; use collections::HashMap;
@ -704,33 +705,29 @@ impl ParentElement for Div {
} }
impl Element for Div { impl Element for Div {
type State = DivState; type FrameState = DivState;
fn layout( fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
&mut self,
element_state: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
let mut child_layout_ids = SmallVec::new(); let mut child_layout_ids = SmallVec::new();
let (layout_id, interactive_state) = self.interactivity.layout( let mut is_active = false;
element_state.map(|s| s.interactive_state), let layout_id = self.interactivity.layout(cx, |style, element_state, cx| {
cx, cx.with_text_style(style.text_style().cloned(), |cx| {
|style, cx| { child_layout_ids = self
cx.with_text_style(style.text_style().cloned(), |cx| { .children
child_layout_ids = self .iter_mut()
.children .map(|child| child.layout(cx))
.iter_mut() .collect::<SmallVec<_>>();
.map(|child| child.layout(cx)) is_active = element_state.as_ref().map_or(false, |(_, element_state)| {
.collect::<SmallVec<_>>(); element_state.pending_mouse_down.is_some()
cx.request_layout(&style, child_layout_ids.iter().copied()) });
}) cx.request_layout(&style, child_layout_ids.iter().copied())
}, })
); });
( (
layout_id, layout_id,
DivState { DivState {
interactive_state,
child_layout_ids, child_layout_ids,
is_active,
}, },
) )
} }
@ -738,7 +735,7 @@ impl Element for Div {
fn paint( fn paint(
&mut self, &mut self,
bounds: Bounds<Pixels>, bounds: Bounds<Pixels>,
element_state: &mut Self::State, element_state: &mut Self::FrameState,
cx: &mut WindowContext, cx: &mut WindowContext,
) { ) {
let mut child_min = point(Pixels::MAX, Pixels::MAX); let mut child_min = point(Pixels::MAX, Pixels::MAX);
@ -774,12 +771,8 @@ impl Element for Div {
(child_max - child_min).into() (child_max - child_min).into()
}; };
self.interactivity.paint( self.interactivity
bounds, .paint(bounds, content_size, cx, |style, _, scroll_offset, cx| {
content_size,
&mut element_state.interactive_state,
cx,
|style, scroll_offset, cx| {
let z_index = style.z_index.unwrap_or(0); let z_index = style.z_index.unwrap_or(0);
cx.with_z_index(z_index, |cx| { cx.with_z_index(z_index, |cx| {
@ -795,8 +788,7 @@ impl Element for Div {
}) })
}); });
}) })
}, });
);
} }
} }
@ -814,16 +806,7 @@ impl IntoElement for Div {
pub struct DivState { pub struct DivState {
child_layout_ids: SmallVec<[LayoutId; 2]>, child_layout_ids: SmallVec<[LayoutId; 2]>,
interactive_state: InteractiveElementState, pub is_active: bool,
}
impl DivState {
pub fn is_active(&self) -> bool {
self.interactive_state
.pending_mouse_down
.as_ref()
.map_or(false, |pending| pending.borrow().is_some())
}
} }
pub struct Interactivity { pub struct Interactivity {
@ -879,43 +862,70 @@ impl InteractiveBounds {
impl Interactivity { impl Interactivity {
pub fn layout( pub fn layout(
&mut self, &mut self,
element_state: Option<InteractiveElementState>,
cx: &mut WindowContext, cx: &mut WindowContext,
f: impl FnOnce(Style, &mut WindowContext) -> LayoutId, f: impl FnOnce(
) -> (LayoutId, InteractiveElementState) { Style,
let mut element_state = element_state.unwrap_or_default(); Option<(&GlobalElementId, &mut InteractiveElementState)>,
&mut WindowContext,
) -> LayoutId,
) -> LayoutId {
let style = self.compute_style(None, cx);
self.with_element_state(cx, |this, mut element_state, cx| {
if let Some((_, element_state)) = element_state.as_mut() {
// Ensure a focus handle exists if we're focusable.
// If there's an explicit focus handle we're tracking, use that. Otherwise
// create a new handle and store it in the element state, which lives for as
// as frames contain an element with this id.
if this.focusable {
this.tracked_focus_handle.get_or_insert_with(|| {
element_state
.focus_handle
.get_or_insert_with(|| cx.focus_handle())
.clone()
});
}
// Ensure we store a focus handle in our element state if we're focusable. if let Some(scroll_handle) = this.scroll_handle.as_ref() {
// If there's an explicit focus handle we're tracking, use that. Otherwise element_state.scroll_offset = Some(scroll_handle.0.borrow().offset.clone());
// create a new handle and store it in the element state, which lives for as }
// as frames contain an element with this id. }
if self.focusable {
element_state.focus_handle.get_or_insert_with(|| {
self.tracked_focus_handle
.clone()
.unwrap_or_else(|| cx.focus_handle())
});
}
if let Some(scroll_handle) = self.scroll_handle.as_ref() { f(style, element_state, cx)
element_state.scroll_offset = Some(scroll_handle.0.borrow().offset.clone()); })
}
let style = self.compute_style(None, &mut element_state, cx);
let layout_id = f(style, cx);
(layout_id, element_state)
} }
pub fn paint( pub fn paint(
&mut self, &mut self,
bounds: Bounds<Pixels>, bounds: Bounds<Pixels>,
content_size: Size<Pixels>, content_size: Size<Pixels>,
element_state: &mut InteractiveElementState,
cx: &mut WindowContext, cx: &mut WindowContext,
f: impl FnOnce(Style, Point<Pixels>, &mut WindowContext), f: impl FnOnce(
Style,
Option<(&GlobalElementId, &mut InteractiveElementState)>,
Point<Pixels>,
&mut WindowContext,
),
) { ) {
let style = self.compute_style(Some(bounds), element_state, cx); let style = self.compute_style(Some(bounds), cx);
self.with_element_state(cx, |this, element_state, cx| {
this.paint_internal(style, bounds, content_size, element_state, cx, f)
})
}
fn paint_internal(
&mut self,
style: Style,
bounds: Bounds<Pixels>,
content_size: Size<Pixels>,
mut element_state: Option<(&GlobalElementId, &mut InteractiveElementState)>,
cx: &mut WindowContext,
f: impl FnOnce(
Style,
Option<(&GlobalElementId, &mut InteractiveElementState)>,
Point<Pixels>,
&mut WindowContext,
),
) {
if style.visibility == Visibility::Hidden { if style.visibility == Visibility::Hidden {
return; return;
} }
@ -1052,10 +1062,10 @@ impl Interactivity {
} }
} }
// If this element can be focused, register a mouse down listener // If self element can be focused, register a mouse down listener
// that will automatically transfer focus when hitting the element. // that will automatically transfer focus when hitting the element.
// This behavior can be suppressed by using `cx.prevent_default()`. // This behavior can be suppressed by using `cx.prevent_default()`.
if let Some(focus_handle) = element_state.focus_handle.clone() { if let Some(focus_handle) = self.tracked_focus_handle.clone() {
cx.on_mouse_event({ cx.on_mouse_event({
let interactive_bounds = interactive_bounds.clone(); let interactive_bounds = interactive_bounds.clone();
move |event: &MouseDownEvent, phase, cx| { move |event: &MouseDownEvent, phase, cx| {
@ -1164,263 +1174,301 @@ impl Interactivity {
} }
} }
let click_listeners = mem::take(&mut self.click_listeners); if let Some((global_element_id, element_state)) = element_state.as_mut() {
let mut drag_listener = mem::take(&mut self.drag_listener); let click_listeners = mem::take(&mut self.click_listeners);
let mut drag_listener = mem::take(&mut self.drag_listener);
if !click_listeners.is_empty() || drag_listener.is_some() { if !click_listeners.is_empty() || drag_listener.is_some() {
let pending_mouse_down = element_state let global_element_id = global_element_id.clone();
.pending_mouse_down if let Some(mouse_down) = element_state.pending_mouse_down.clone() {
.get_or_insert_with(Default::default) if drag_listener.is_some() {
.clone(); let interactive_bounds = interactive_bounds.clone();
let mouse_down = pending_mouse_down.borrow().clone(); let global_element_id = global_element_id.clone();
if let Some(mouse_down) = mouse_down { cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
if drag_listener.is_some() { if cx.active_drag.is_some() {
let active_state = element_state if phase == DispatchPhase::Capture {
.clicked_state cx.notify();
.get_or_insert_with(Default::default) }
.clone(); } else if phase == DispatchPhase::Bubble
let interactive_bounds = interactive_bounds.clone(); && interactive_bounds.visibly_contains(&event.position, cx)
&& (event.position - mouse_down.position).magnitude()
> DRAG_THRESHOLD
{
let (drag_value, drag_listener) = drag_listener
.take()
.expect("The notify below should invalidate self callback");
cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| { cx.with_element_state::<InteractiveElementState, _>(
if cx.active_drag.is_some() { &global_element_id,
if phase == DispatchPhase::Capture { |element_state, _cx| {
if let Some(element_state) = element_state {
element_state.clicked_state =
ElementClickedState::default();
}
},
);
let cursor_offset = event.position - bounds.origin;
let drag = (drag_listener)(drag_value.as_ref(), cx);
cx.active_drag = Some(AnyDrag {
view: drag,
value: drag_value,
cursor_offset,
});
cx.notify(); cx.notify();
cx.stop_propagation();
} }
} else if phase == DispatchPhase::Bubble });
&& interactive_bounds.visibly_contains(&event.position, cx) }
&& (event.position - mouse_down.position).magnitude() > DRAG_THRESHOLD
{
let (drag_value, drag_listener) = drag_listener
.take()
.expect("The notify below should invalidate this callback");
*active_state.borrow_mut() = ElementClickedState::default(); let interactive_bounds = interactive_bounds.clone();
let cursor_offset = event.position - bounds.origin; cx.on_mouse_event(move |event: &MouseUpEvent, phase, cx| {
let drag = (drag_listener)(drag_value.as_ref(), cx); if phase == DispatchPhase::Bubble
cx.active_drag = Some(AnyDrag { && interactive_bounds.visibly_contains(&event.position, cx)
view: drag, {
value: drag_value, let mouse_click = ClickEvent {
cursor_offset, down: mouse_down.clone(),
}); up: event.clone(),
};
for listener in &click_listeners {
listener(&mouse_click, cx);
}
}
cx.with_element_state::<InteractiveElementState, _>(
&global_element_id,
|element_state, _cx| {
if let Some(element_state) = element_state {
element_state.pending_mouse_down = None;
}
},
);
cx.notify();
});
} else {
let interactive_bounds = interactive_bounds.clone();
cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
if phase == DispatchPhase::Bubble
&& event.button == MouseButton::Left
&& interactive_bounds.visibly_contains(&event.position, cx)
{
cx.with_element_state::<InteractiveElementState, _>(
&global_element_id,
|element_state, _cx| {
if let Some(element_state) = element_state {
element_state.pending_mouse_down = Some(event.clone());
}
},
);
cx.notify();
}
});
}
}
if let Some(hover_listener) = self.hover_listener.take() {
let interactive_bounds = interactive_bounds.clone();
let global_element_id = global_element_id.clone();
cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
if phase != DispatchPhase::Bubble {
return;
}
cx.with_element_state::<InteractiveElementState, _>(
&global_element_id,
|element_state, cx| {
if let Some(element_state) = element_state {
let is_hovered = interactive_bounds
.visibly_contains(&event.position, cx)
&& element_state.pending_mouse_down.is_none();
if is_hovered != element_state.hover_state {
element_state.hover_state = is_hovered;
hover_listener(&is_hovered, cx);
}
}
},
);
});
}
if let Some(tooltip_builder) = self.tooltip_builder.take() {
let interactive_bounds = interactive_bounds.clone();
cx.on_mouse_event({
let global_element_id = global_element_id.clone();
move |event: &MouseMoveEvent, phase, cx| {
cx.with_element_state::<InteractiveElementState, _>(
&global_element_id,
|element_state, cx| {
if let Some(element_state) = element_state {
let is_hovered = interactive_bounds
.visibly_contains(&event.position, cx)
&& element_state.pending_mouse_down.is_none();
if !is_hovered {
element_state.active_tooltip = None;
return;
}
if phase != DispatchPhase::Bubble {
return;
}
if element_state.active_tooltip.is_none() {
let task = cx.spawn({
let global_element_id = global_element_id.clone();
let tooltip_builder = tooltip_builder.clone();
move |mut cx| async move {
cx.background_executor().timer(TOOLTIP_DELAY).await;
cx.update(|_, cx| {
cx.with_element_state::<InteractiveElementState, _>(
&global_element_id,
|element_state, cx| {
if let Some(element_state) = element_state {
element_state.active_tooltip = Some(
ActiveTooltip {
tooltip: Some(AnyTooltip {
view: tooltip_builder(cx),
cursor_offset: cx.mouse_position(),
}),
_task: None,
},
);
cx.notify();
}
});
})
.ok();
}
});
element_state.active_tooltip = Some(ActiveTooltip {
tooltip: None,
_task: Some(task),
});
}
}
},
);
}
});
let global_element_id = global_element_id.clone();
cx.on_mouse_event(move |_: &MouseDownEvent, _, cx| {
cx.with_element_state::<InteractiveElementState, _>(
&global_element_id,
|element_state, _cx| {
if let Some(element_state) = element_state {
element_state.active_tooltip = None;
}
},
);
});
if let Some(active_tooltip) = element_state.active_tooltip.as_ref() {
if active_tooltip.tooltip.is_some() {
cx.active_tooltip = active_tooltip.tooltip.clone()
}
}
}
if element_state.clicked_state.is_clicked() {
let global_element_id = global_element_id.clone();
cx.on_mouse_event(move |_: &MouseUpEvent, phase, cx| {
if phase == DispatchPhase::Capture {
cx.with_element_state::<InteractiveElementState, _>(
&global_element_id,
|element_state, cx| {
if let Some(element_state) = element_state {
element_state.clicked_state = ElementClickedState::default();
cx.notify();
}
},
);
}
});
} else {
let active_group_bounds = self
.group_active_style
.as_ref()
.and_then(|group_active| GroupBounds::get(&group_active.group, cx));
let interactive_bounds = interactive_bounds.clone();
let global_element_id = global_element_id.clone();
cx.on_mouse_event(move |down: &MouseDownEvent, phase, cx| {
if phase == DispatchPhase::Bubble && !cx.default_prevented() {
let group = active_group_bounds
.map_or(false, |bounds| bounds.contains(&down.position));
let element = interactive_bounds.visibly_contains(&down.position, cx);
if group || element {
cx.with_element_state::<InteractiveElementState, _>(
&global_element_id,
|element_state, cx| {
if let Some(element_state) = element_state {
element_state.clicked_state =
ElementClickedState { group, element };
cx.notify();
}
},
);
}
}
});
}
let overflow = style.overflow;
if overflow.x == Overflow::Scroll || overflow.y == Overflow::Scroll {
if let Some(scroll_handle) = &self.scroll_handle {
scroll_handle.0.borrow_mut().overflow = overflow;
}
let scroll_offset = element_state
.scroll_offset
.get_or_insert_with(Rc::default)
.clone();
let line_height = cx.line_height();
let scroll_max = (content_size - bounds.size).max(&Size::default());
let interactive_bounds = interactive_bounds.clone();
cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
if phase == DispatchPhase::Bubble
&& interactive_bounds.visibly_contains(&event.position, cx)
{
let mut scroll_offset = scroll_offset.borrow_mut();
let old_scroll_offset = *scroll_offset;
let delta = event.delta.pixel_delta(line_height);
if overflow.x == Overflow::Scroll {
scroll_offset.x =
(scroll_offset.x + delta.x).clamp(-scroll_max.width, px(0.));
}
if overflow.y == Overflow::Scroll {
scroll_offset.y =
(scroll_offset.y + delta.y).clamp(-scroll_max.height, px(0.));
}
if *scroll_offset != old_scroll_offset {
cx.notify(); cx.notify();
cx.stop_propagation(); cx.stop_propagation();
} }
});
}
let interactive_bounds = interactive_bounds.clone();
cx.on_mouse_event(move |event: &MouseUpEvent, phase, cx| {
if phase == DispatchPhase::Bubble
&& interactive_bounds.visibly_contains(&event.position, cx)
{
let mouse_click = ClickEvent {
down: mouse_down.clone(),
up: event.clone(),
};
for listener in &click_listeners {
listener(&mouse_click, cx);
}
}
*pending_mouse_down.borrow_mut() = None;
cx.notify();
});
} else {
let interactive_bounds = interactive_bounds.clone();
cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
if phase == DispatchPhase::Bubble
&& event.button == MouseButton::Left
&& interactive_bounds.visibly_contains(&event.position, cx)
{
*pending_mouse_down.borrow_mut() = Some(event.clone());
cx.notify();
} }
}); });
} }
} }
if let Some(hover_listener) = self.hover_listener.take() {
let was_hovered = element_state
.hover_state
.get_or_insert_with(Default::default)
.clone();
let has_mouse_down = element_state
.pending_mouse_down
.get_or_insert_with(Default::default)
.clone();
let interactive_bounds = interactive_bounds.clone();
cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
if phase != DispatchPhase::Bubble {
return;
}
let is_hovered = interactive_bounds.visibly_contains(&event.position, cx)
&& has_mouse_down.borrow().is_none();
let mut was_hovered = was_hovered.borrow_mut();
if is_hovered != was_hovered.clone() {
*was_hovered = is_hovered;
drop(was_hovered);
hover_listener(&is_hovered, cx);
}
});
}
if let Some(tooltip_builder) = self.tooltip_builder.take() {
let active_tooltip = element_state
.active_tooltip
.get_or_insert_with(Default::default)
.clone();
let pending_mouse_down = element_state
.pending_mouse_down
.get_or_insert_with(Default::default)
.clone();
let interactive_bounds = interactive_bounds.clone();
cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
let is_hovered = interactive_bounds.visibly_contains(&event.position, cx)
&& pending_mouse_down.borrow().is_none();
if !is_hovered {
active_tooltip.borrow_mut().take();
return;
}
if phase != DispatchPhase::Bubble {
return;
}
if active_tooltip.borrow().is_none() {
let task = cx.spawn({
let active_tooltip = active_tooltip.clone();
let tooltip_builder = tooltip_builder.clone();
move |mut cx| async move {
cx.background_executor().timer(TOOLTIP_DELAY).await;
cx.update(|_, cx| {
active_tooltip.borrow_mut().replace(ActiveTooltip {
tooltip: Some(AnyTooltip {
view: tooltip_builder(cx),
cursor_offset: cx.mouse_position(),
}),
_task: None,
});
cx.notify();
})
.ok();
}
});
active_tooltip.borrow_mut().replace(ActiveTooltip {
tooltip: None,
_task: Some(task),
});
}
});
let active_tooltip = element_state
.active_tooltip
.get_or_insert_with(Default::default)
.clone();
cx.on_mouse_event(move |_: &MouseDownEvent, _, _| {
active_tooltip.borrow_mut().take();
});
if let Some(active_tooltip) = element_state
.active_tooltip
.get_or_insert_with(Default::default)
.borrow()
.as_ref()
{
if active_tooltip.tooltip.is_some() {
cx.active_tooltip = active_tooltip.tooltip.clone()
}
}
}
let active_state = element_state
.clicked_state
.get_or_insert_with(Default::default)
.clone();
if active_state.borrow().is_clicked() {
cx.on_mouse_event(move |_: &MouseUpEvent, phase, cx| {
if phase == DispatchPhase::Capture {
*active_state.borrow_mut() = ElementClickedState::default();
cx.notify();
}
});
} else {
let active_group_bounds = self
.group_active_style
.as_ref()
.and_then(|group_active| GroupBounds::get(&group_active.group, cx));
let interactive_bounds = interactive_bounds.clone();
cx.on_mouse_event(move |down: &MouseDownEvent, phase, cx| {
if phase == DispatchPhase::Bubble && !cx.default_prevented() {
let group =
active_group_bounds.map_or(false, |bounds| bounds.contains(&down.position));
let element = interactive_bounds.visibly_contains(&down.position, cx);
if group || element {
*active_state.borrow_mut() = ElementClickedState { group, element };
cx.notify();
}
}
});
}
let overflow = style.overflow;
if overflow.x == Overflow::Scroll || overflow.y == Overflow::Scroll {
if let Some(scroll_handle) = &self.scroll_handle {
scroll_handle.0.borrow_mut().overflow = overflow;
}
let scroll_offset = element_state
.scroll_offset
.get_or_insert_with(Rc::default)
.clone();
let line_height = cx.line_height();
let scroll_max = (content_size - bounds.size).max(&Size::default());
let interactive_bounds = interactive_bounds.clone();
cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
if phase == DispatchPhase::Bubble
&& interactive_bounds.visibly_contains(&event.position, cx)
{
let mut scroll_offset = scroll_offset.borrow_mut();
let old_scroll_offset = *scroll_offset;
let delta = event.delta.pixel_delta(line_height);
if overflow.x == Overflow::Scroll {
scroll_offset.x =
(scroll_offset.x + delta.x).clamp(-scroll_max.width, px(0.));
}
if overflow.y == Overflow::Scroll {
scroll_offset.y =
(scroll_offset.y + delta.y).clamp(-scroll_max.height, px(0.));
}
if *scroll_offset != old_scroll_offset {
cx.notify();
cx.stop_propagation();
}
}
});
}
if let Some(group) = self.group.clone() { if let Some(group) = self.group.clone() {
GroupBounds::push(group, bounds, cx); GroupBounds::push(group, bounds, cx);
} }
let scroll_offset = element_state let scroll_offset = element_state
.scroll_offset
.as_ref() .as_ref()
.map(|scroll_offset| *scroll_offset.borrow()); .and_then(|(_, element_state)| Some(*element_state.scroll_offset.as_ref()?.borrow()));
let key_down_listeners = mem::take(&mut self.key_down_listeners); let key_down_listeners = mem::take(&mut self.key_down_listeners);
let key_up_listeners = mem::take(&mut self.key_up_listeners); let key_up_listeners = mem::take(&mut self.key_up_listeners);
let action_listeners = mem::take(&mut self.action_listeners); let action_listeners = mem::take(&mut self.action_listeners);
cx.with_key_dispatch( cx.with_key_dispatch(
self.key_context.clone(), self.key_context.clone(),
element_state.focus_handle.clone(), self.tracked_focus_handle.clone(),
|_, cx| { |_, cx| {
for listener in key_down_listeners { for listener in key_down_listeners {
cx.on_key_event(move |event: &KeyDownEvent, phase, cx| { cx.on_key_event(move |event: &KeyDownEvent, phase, cx| {
@ -1438,7 +1486,7 @@ impl Interactivity {
cx.on_action(action_type, listener) cx.on_action(action_type, listener)
} }
f(style, scroll_offset.unwrap_or_default(), cx) f(style, element_state, scroll_offset.unwrap_or_default(), cx)
}, },
); );
@ -1447,10 +1495,31 @@ impl Interactivity {
} }
} }
fn with_element_state<R>(
&mut self,
cx: &mut WindowContext,
f: impl FnOnce(
&mut Self,
Option<(&GlobalElementId, &mut InteractiveElementState)>,
&mut WindowContext,
) -> R,
) -> R {
cx.with_element_id(self.element_id.clone(), |global_element_id, cx| {
if let Some(global_element_id) = global_element_id {
cx.with_element_state(&global_element_id, |element_state, cx| {
let mut element_state = element_state
.get_or_insert_with(|| Box::<InteractiveElementState>::default());
f(self, Some((&global_element_id, &mut element_state)), cx)
})
} else {
f(self, None, cx)
}
})
}
pub fn compute_style( pub fn compute_style(
&self, &mut self,
bounds: Option<Bounds<Pixels>>, bounds: Option<Bounds<Pixels>>,
element_state: &mut InteractiveElementState,
cx: &mut WindowContext, cx: &mut WindowContext,
) -> Style { ) -> Style {
let mut style = Style::default(); let mut style = Style::default();
@ -1521,21 +1590,21 @@ impl Interactivity {
} }
} }
let clicked_state = element_state self.with_element_state(cx, |this, element_state, _cx| {
.clicked_state if let Some((_, element_state)) = element_state {
.get_or_insert_with(Default::default) if element_state.clicked_state.group {
.borrow(); if let Some(group) = this.group_active_style.as_ref() {
if clicked_state.group { style.refine(&group.style)
if let Some(group) = self.group_active_style.as_ref() { }
style.refine(&group.style) }
}
}
if let Some(active_style) = self.active_style.as_ref() { if let Some(active_style) = this.active_style.as_ref() {
if clicked_state.element { if element_state.clicked_state.element {
style.refine(active_style) style.refine(active_style)
}
}
} }
} })
}); });
style style
@ -1583,11 +1652,11 @@ impl Default for Interactivity {
#[derive(Default)] #[derive(Default)]
pub struct InteractiveElementState { pub struct InteractiveElementState {
pub focus_handle: Option<FocusHandle>, pub focus_handle: Option<FocusHandle>,
pub clicked_state: Option<Rc<RefCell<ElementClickedState>>>, pub clicked_state: ElementClickedState,
pub hover_state: Option<Rc<RefCell<bool>>>, pub hover_state: bool,
pub pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>, pub pending_mouse_down: Option<MouseDownEvent>,
pub scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>, pub scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
pub active_tooltip: Option<Rc<RefCell<Option<ActiveTooltip>>>>, pub active_tooltip: Option<ActiveTooltip>,
} }
pub struct ActiveTooltip { pub struct ActiveTooltip {
@ -1663,17 +1732,18 @@ impl<E> Element for Focusable<E>
where where
E: Element, E: Element,
{ {
type State = E::State; type FrameState = E::FrameState;
fn layout( fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
&mut self, self.element.layout(cx)
state: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
self.element.layout(state, cx)
} }
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) { fn paint(
&mut self,
bounds: Bounds<Pixels>,
state: &mut Self::FrameState,
cx: &mut WindowContext,
) {
self.element.paint(bounds, state, cx) self.element.paint(bounds, state, cx)
} }
} }
@ -1737,17 +1807,18 @@ impl<E> Element for Stateful<E>
where where
E: Element, E: Element,
{ {
type State = E::State; type FrameState = E::FrameState;
fn layout( fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
&mut self, self.element.layout(cx)
state: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
self.element.layout(state, cx)
} }
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) { fn paint(
&mut self,
bounds: Bounds<Pixels>,
state: &mut Self::FrameState,
cx: &mut WindowContext,
) {
self.element.paint(bounds, state, cx) self.element.paint(bounds, state, cx)
} }
} }

View file

@ -1,9 +1,8 @@
use std::sync::Arc; use std::sync::Arc;
use crate::{ use crate::{
point, size, Bounds, DevicePixels, Element, ImageData, InteractiveElement, point, size, Bounds, DevicePixels, Element, ImageData, InteractiveElement, Interactivity,
InteractiveElementState, Interactivity, IntoElement, LayoutId, Pixels, SharedString, Size, IntoElement, LayoutId, Pixels, SharedString, Size, StyleRefinement, Styled, WindowContext,
StyleRefinement, Styled, WindowContext,
}; };
use futures::FutureExt; use futures::FutureExt;
use media::core_video::CVImageBuffer; use media::core_video::CVImageBuffer;
@ -69,30 +68,19 @@ impl Img {
} }
impl Element for Img { impl Element for Img {
type State = InteractiveElementState; type FrameState = ();
fn layout( fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
&mut self, let layout_id = self
element_state: Option<Self::State>, .interactivity
cx: &mut WindowContext, .layout(cx, |style, _, cx| cx.request_layout(&style, []));
) -> (LayoutId, Self::State) { (layout_id, ())
self.interactivity
.layout(element_state, cx, |style, cx| cx.request_layout(&style, []))
} }
fn paint( fn paint(&mut self, bounds: Bounds<Pixels>, _: &mut Self::FrameState, cx: &mut WindowContext) {
&mut self,
bounds: Bounds<Pixels>,
element_state: &mut Self::State,
cx: &mut WindowContext,
) {
let source = self.source.clone(); let source = self.source.clone();
self.interactivity.paint( self.interactivity
bounds, .paint(bounds, bounds.size, cx, |style, _, _, cx| {
bounds.size,
element_state,
cx,
|style, _scroll_offset, cx| {
let corner_radii = style.corner_radii.to_pixels(bounds.size, cx.rem_size()); let corner_radii = style.corner_radii.to_pixels(bounds.size, cx.rem_size());
cx.with_z_index(1, |cx| { cx.with_z_index(1, |cx| {
match source { match source {
@ -130,8 +118,7 @@ impl Element for Img {
} }
}; };
}); });
}, })
)
} }
} }

View file

@ -300,13 +300,9 @@ pub struct ListOffset {
} }
impl Element for List { impl Element for List {
type State = (); type FrameState = ();
fn layout( fn layout(&mut self, cx: &mut crate::WindowContext) -> (crate::LayoutId, Self::FrameState) {
&mut self,
_state: Option<Self::State>,
cx: &mut crate::WindowContext,
) -> (crate::LayoutId, Self::State) {
let mut style = Style::default(); let mut style = Style::default();
style.refine(&self.style); style.refine(&self.style);
let layout_id = cx.with_text_style(style.text_style().cloned(), |cx| { let layout_id = cx.with_text_style(style.text_style().cloned(), |cx| {
@ -318,7 +314,7 @@ impl Element for List {
fn paint( fn paint(
&mut self, &mut self,
bounds: crate::Bounds<crate::Pixels>, bounds: crate::Bounds<crate::Pixels>,
_state: &mut Self::State, _state: &mut Self::FrameState,
cx: &mut crate::WindowContext, cx: &mut crate::WindowContext,
) { ) {
let state = &mut *self.state.0.borrow_mut(); let state = &mut *self.state.0.borrow_mut();

View file

@ -58,13 +58,9 @@ impl ParentElement for Overlay {
} }
impl Element for Overlay { impl Element for Overlay {
type State = OverlayState; type FrameState = OverlayState;
fn layout( fn layout(&mut self, cx: &mut WindowContext) -> (crate::LayoutId, Self::FrameState) {
&mut self,
_: Option<Self::State>,
cx: &mut WindowContext,
) -> (crate::LayoutId, Self::State) {
let child_layout_ids = self let child_layout_ids = self
.children .children
.iter_mut() .iter_mut()
@ -83,7 +79,7 @@ impl Element for Overlay {
fn paint( fn paint(
&mut self, &mut self,
bounds: crate::Bounds<crate::Pixels>, bounds: crate::Bounds<crate::Pixels>,
element_state: &mut Self::State, element_state: &mut Self::FrameState,
cx: &mut WindowContext, cx: &mut WindowContext,
) { ) {
if element_state.child_layout_ids.is_empty() { if element_state.child_layout_ids.is_empty() {

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
Bounds, Element, ElementId, InteractiveElement, InteractiveElementState, Interactivity, Bounds, Element, ElementId, InteractiveElement, Interactivity, IntoElement, LayoutId, Pixels,
IntoElement, LayoutId, Pixels, SharedString, StyleRefinement, Styled, WindowContext, SharedString, StyleRefinement, Styled, WindowContext,
}; };
use util::ResultExt; use util::ResultExt;
@ -24,28 +24,21 @@ impl Svg {
} }
impl Element for Svg { impl Element for Svg {
type State = InteractiveElementState; type FrameState = ();
fn layout( fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
&mut self, let layout_id = self
element_state: Option<Self::State>, .interactivity
cx: &mut WindowContext, .layout(cx, |style, _, cx| cx.request_layout(&style, None));
) -> (LayoutId, Self::State) { (layout_id, ())
self.interactivity.layout(element_state, cx, |style, cx| {
cx.request_layout(&style, None)
})
} }
fn paint( fn paint(&mut self, bounds: Bounds<Pixels>, _: &mut Self::FrameState, cx: &mut WindowContext)
&mut self, where
bounds: Bounds<Pixels>,
element_state: &mut Self::State,
cx: &mut WindowContext,
) where
Self: Sized, Self: Sized,
{ {
self.interactivity self.interactivity
.paint(bounds, bounds.size, element_state, cx, |style, _, cx| { .paint(bounds, bounds.size, cx, |style, _, _, cx| {
if let Some((path, color)) = self.path.as_ref().zip(style.text.color) { if let Some((path, color)) = self.path.as_ref().zip(style.text.color) {
cx.paint_svg(bounds, path.clone(), color).log_err(); cx.paint_svg(bounds, path.clone(), color).log_err();
} }

View file

@ -1,22 +1,18 @@
use crate::{ use crate::{
Bounds, DispatchPhase, Element, ElementId, HighlightStyle, IntoElement, LayoutId, BorrowWindow, Bounds, DispatchPhase, Element, ElementId, HighlightStyle, IntoElement, LayoutId,
MouseDownEvent, MouseUpEvent, Pixels, Point, SharedString, Size, TextRun, TextStyle, MouseDownEvent, MouseUpEvent, Pixels, Point, SharedString, Size, TextRun, TextStyle,
WhiteSpace, WindowContext, WrappedLine, WhiteSpace, WindowContext, WrappedLine,
}; };
use anyhow::anyhow; use anyhow::anyhow;
use parking_lot::{Mutex, MutexGuard}; use parking_lot::{Mutex, MutexGuard};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::{cell::Cell, mem, ops::Range, rc::Rc, sync::Arc}; use std::{mem, ops::Range, sync::Arc};
use util::ResultExt; use util::ResultExt;
impl Element for &'static str { impl Element for &'static str {
type State = TextState; type FrameState = TextState;
fn layout( fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
&mut self,
_: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
let mut state = TextState::default(); let mut state = TextState::default();
let layout_id = state.layout(SharedString::from(*self), None, cx); let layout_id = state.layout(SharedString::from(*self), None, cx);
(layout_id, state) (layout_id, state)
@ -40,13 +36,9 @@ impl IntoElement for &'static str {
} }
impl Element for SharedString { impl Element for SharedString {
type State = TextState; type FrameState = TextState;
fn layout( fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
&mut self,
_: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
let mut state = TextState::default(); let mut state = TextState::default();
let layout_id = state.layout(self.clone(), None, cx); let layout_id = state.layout(self.clone(), None, cx);
(layout_id, state) (layout_id, state)
@ -116,19 +108,20 @@ impl StyledText {
} }
impl Element for StyledText { impl Element for StyledText {
type State = TextState; type FrameState = TextState;
fn layout( fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
&mut self,
_: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
let mut state = TextState::default(); let mut state = TextState::default();
let layout_id = state.layout(self.text.clone(), self.runs.take(), cx); let layout_id = state.layout(self.text.clone(), self.runs.take(), cx);
(layout_id, state) (layout_id, state)
} }
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) { fn paint(
&mut self,
bounds: Bounds<Pixels>,
state: &mut Self::FrameState,
cx: &mut WindowContext,
) {
state.paint(bounds, &self.text, cx) state.paint(bounds, &self.text, cx)
} }
} }
@ -295,9 +288,9 @@ struct InteractiveTextClickEvent {
mouse_up_index: usize, mouse_up_index: usize,
} }
#[derive(Default)]
pub struct InteractiveTextState { pub struct InteractiveTextState {
text_state: TextState, mouse_down_index: Option<usize>,
mouse_down_index: Rc<Cell<Option<usize>>>,
} }
impl InteractiveText { impl InteractiveText {
@ -329,86 +322,93 @@ impl InteractiveText {
} }
impl Element for InteractiveText { impl Element for InteractiveText {
type State = InteractiveTextState; type FrameState = TextState;
fn layout( fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
&mut self, self.text.layout(cx)
state: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
if let Some(InteractiveTextState {
mouse_down_index, ..
}) = state
{
let (layout_id, text_state) = self.text.layout(None, cx);
let element_state = InteractiveTextState {
text_state,
mouse_down_index,
};
(layout_id, element_state)
} else {
let (layout_id, text_state) = self.text.layout(None, cx);
let element_state = InteractiveTextState {
text_state,
mouse_down_index: Rc::default(),
};
(layout_id, element_state)
}
} }
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) { fn paint(
if let Some(click_listener) = self.click_listener.take() { &mut self,
if let Some(ix) = state bounds: Bounds<Pixels>,
.text_state text_state: &mut Self::FrameState,
.index_for_position(bounds, cx.mouse_position()) cx: &mut WindowContext,
{ ) {
if self cx.with_element_id(Some(self.element_id.clone()), |global_element_id, cx| {
.clickable_ranges let global_element_id = global_element_id.unwrap();
.iter() cx.with_element_state::<InteractiveTextState, _>(
.any(|range| range.contains(&ix)) &global_element_id,
{ |element_state, cx| {
cx.set_cursor_style(crate::CursorStyle::PointingHand) let element_state = element_state.get_or_insert_with(Default::default);
} if let Some(click_listener) = self.click_listener.take() {
} if let Some(ix) = text_state.index_for_position(bounds, cx.mouse_position())
let text_state = state.text_state.clone();
let mouse_down = state.mouse_down_index.clone();
if let Some(mouse_down_index) = mouse_down.get() {
let clickable_ranges = mem::take(&mut self.clickable_ranges);
cx.on_mouse_event(move |event: &MouseUpEvent, phase, cx| {
if phase == DispatchPhase::Bubble {
if let Some(mouse_up_index) =
text_state.index_for_position(bounds, event.position)
{ {
click_listener( if self
&clickable_ranges, .clickable_ranges
InteractiveTextClickEvent { .iter()
mouse_down_index, .any(|range| range.contains(&ix))
mouse_up_index, {
}, cx.set_cursor_style(crate::CursorStyle::PointingHand)
cx, }
)
} }
mouse_down.take(); let text_state = text_state.clone();
cx.notify(); if let Some(mouse_down_index) = element_state.mouse_down_index {
} let global_element_id = global_element_id.clone();
}); let clickable_ranges = mem::take(&mut self.clickable_ranges);
} else { cx.on_mouse_event(move |event: &MouseUpEvent, phase, cx| {
cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| { if phase == DispatchPhase::Bubble {
if phase == DispatchPhase::Bubble { if let Some(mouse_up_index) =
if let Some(mouse_down_index) = text_state.index_for_position(bounds, event.position)
text_state.index_for_position(bounds, event.position) {
{ click_listener(
mouse_down.set(Some(mouse_down_index)); &clickable_ranges,
cx.notify(); InteractiveTextClickEvent {
mouse_down_index,
mouse_up_index,
},
cx,
)
}
cx.with_element_state::<InteractiveTextState, _>(
&global_element_id,
|element_state, cx| {
if let Some(element_state) = element_state {
element_state.mouse_down_index = None;
cx.notify();
}
},
);
}
});
} else {
let global_element_id = global_element_id.clone();
cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
if phase == DispatchPhase::Bubble {
if let Some(mouse_down_index) =
text_state.index_for_position(bounds, event.position)
{
cx.with_element_state::<InteractiveTextState, _>(
&global_element_id,
|element_state, cx| {
if let Some(element_state) = element_state {
element_state.mouse_down_index =
Some(mouse_down_index);
cx.notify();
}
},
);
}
}
});
} }
} }
}); },
} )
} });
self.text.paint(bounds, &mut state.text_state, cx) self.text.paint(bounds, text_state, cx)
} }
} }

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, ContentMask, Element, point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, ContentMask, Element,
ElementId, InteractiveElement, InteractiveElementState, Interactivity, IntoElement, LayoutId, ElementId, InteractiveElement, Interactivity, IntoElement, LayoutId, Pixels, Point, Render,
Pixels, Point, Render, Size, StyleRefinement, Styled, View, ViewContext, WindowContext, Size, StyleRefinement, Styled, View, ViewContext, WindowContext,
}; };
use smallvec::SmallVec; use smallvec::SmallVec;
use std::{cell::RefCell, cmp, ops::Range, rc::Rc}; use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
@ -99,70 +99,40 @@ impl Styled for UniformList {
} }
} }
#[derive(Default)]
pub struct UniformListState {
interactive: InteractiveElementState,
item_size: Size<Pixels>,
}
impl Element for UniformList { impl Element for UniformList {
type State = UniformListState; type FrameState = Size<Pixels>;
fn layout( fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
&mut self,
state: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
let max_items = self.item_count; let max_items = self.item_count;
let item_size = state let item_size = self.measure_item(None, cx);
.as_ref() let layout_id = self.interactivity.layout(cx, |style, _, cx| {
.map(|s| s.item_size) cx.request_measured_layout(style, move |known_dimensions, available_space, _cx| {
.unwrap_or_else(|| self.measure_item(None, cx)); let desired_height = item_size.height * max_items;
let width = known_dimensions
.width
.unwrap_or(match available_space.width {
AvailableSpace::Definite(x) => x,
AvailableSpace::MinContent | AvailableSpace::MaxContent => item_size.width,
});
let height = match available_space.height {
AvailableSpace::Definite(height) => desired_height.min(height),
AvailableSpace::MinContent | AvailableSpace::MaxContent => desired_height,
};
size(width, height)
})
});
let (layout_id, interactive) = (layout_id, item_size)
self.interactivity
.layout(state.map(|s| s.interactive), 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 element_state = UniformListState {
interactive,
item_size,
};
(layout_id, element_state)
} }
fn paint( fn paint(
&mut self, &mut self,
bounds: Bounds<crate::Pixels>, bounds: Bounds<crate::Pixels>,
element_state: &mut Self::State, item_size: &mut Self::FrameState,
cx: &mut WindowContext, cx: &mut WindowContext,
) { ) {
let style = let item_size = *item_size;
self.interactivity let style = self.interactivity.compute_style(Some(bounds), cx);
.compute_style(Some(bounds), &mut element_state.interactive, cx);
let border = style.border_widths.to_pixels(cx.rem_size()); let border = style.border_widths.to_pixels(cx.rem_size());
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size()); let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
@ -172,26 +142,24 @@ impl Element for UniformList {
- point(border.right + padding.right, border.bottom + padding.bottom), - point(border.right + padding.right, border.bottom + padding.bottom),
); );
let item_size = element_state.item_size;
let content_size = Size { let content_size = Size {
width: padded_bounds.size.width, width: padded_bounds.size.width,
height: item_size.height * self.item_count + padding.top + padding.bottom, height: item_size.height * self.item_count + padding.top + padding.bottom,
}; };
let shared_scroll_offset = element_state
.interactive
.scroll_offset
.get_or_insert_with(Rc::default)
.clone();
let item_height = self.measure_item(Some(padded_bounds.size.width), cx).height; let item_height = self.measure_item(Some(padded_bounds.size.width), cx).height;
self.interactivity.paint( self.interactivity.paint(
bounds, bounds,
content_size, content_size,
&mut element_state.interactive,
cx, cx,
|style, mut scroll_offset, cx| { |style, element_state, mut scroll_offset, cx| {
let (_, element_state) = element_state.unwrap();
let shared_scroll_offset = element_state
.scroll_offset
.get_or_insert_with(Rc::default)
.clone();
let border = style.border_widths.to_pixels(cx.rem_size()); let border = style.border_widths.to_pixels(cx.rem_size());
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size()); let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());

View file

@ -1465,7 +1465,9 @@ extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
unsafe { unsafe {
let window_state = get_window_state(this); let window_state = get_window_state(this);
let mut draw = window_state.lock().draw.take().unwrap(); let mut draw = window_state.lock().draw.take().unwrap();
let t0 = std::time::Instant::now();
let scene = draw().log_err(); let scene = draw().log_err();
eprintln!("window draw: {:?}", t0.elapsed());
let mut window_state = window_state.lock(); let mut window_state = window_state.lock();
window_state.draw = Some(draw); window_state.draw = Some(draw);
if let Some(scene) = scene { if let Some(scene) = scene {

View file

@ -79,19 +79,15 @@ impl<V: 'static> View<V> {
} }
impl<V: Render> Element for View<V> { impl<V: Render> Element for View<V> {
type State = Option<AnyElement>; type FrameState = Option<AnyElement>;
fn layout( fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
&mut self,
_state: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
let mut element = self.update(cx, |view, cx| view.render(cx).into_any()); let mut element = self.update(cx, |view, cx| view.render(cx).into_any());
let layout_id = element.layout(cx); let layout_id = element.layout(cx);
(layout_id, Some(element)) (layout_id, Some(element))
} }
fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut WindowContext) { fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::FrameState, cx: &mut WindowContext) {
element.take().unwrap().paint(cx); element.take().unwrap().paint(cx);
} }
} }
@ -227,18 +223,14 @@ impl<V: Render> From<View<V>> for AnyView {
} }
impl Element for AnyView { impl Element for AnyView {
type State = Option<AnyElement>; type FrameState = Option<AnyElement>;
fn layout( fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
&mut self,
_state: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
let (layout_id, state) = (self.layout)(self, cx); let (layout_id, state) = (self.layout)(self, cx);
(layout_id, Some(state)) (layout_id, Some(state))
} }
fn paint(&mut self, _: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) { fn paint(&mut self, _: Bounds<Pixels>, state: &mut Self::FrameState, cx: &mut WindowContext) {
debug_assert!( debug_assert!(
state.is_some(), state.is_some(),
"state is None. Did you include an AnyView twice in the tree?" "state is None. Did you include an AnyView twice in the tree?"

View file

@ -1977,17 +1977,18 @@ pub trait BorrowWindow: BorrowMut<Window> + BorrowMut<AppContext> {
fn with_element_id<R>( fn with_element_id<R>(
&mut self, &mut self,
id: Option<impl Into<ElementId>>, id: Option<impl Into<ElementId>>,
f: impl FnOnce(&mut Self) -> R, f: impl FnOnce(Option<GlobalElementId>, &mut Self) -> R,
) -> R { ) -> R {
if let Some(id) = id.map(Into::into) { if let Some(id) = id.map(Into::into) {
let window = self.window_mut(); let window = self.window_mut();
window.element_id_stack.push(id.into()); window.element_id_stack.push(id.into());
let result = f(self); let global_element_id = window.element_id_stack.clone();
let result = f(Some(global_element_id), self);
let window: &mut Window = self.borrow_mut(); let window: &mut Window = self.borrow_mut();
window.element_id_stack.pop(); window.element_id_stack.pop();
result result
} else { } else {
f(self) f(None, self)
} }
} }
@ -2071,36 +2072,40 @@ pub trait BorrowWindow: BorrowMut<Window> + BorrowMut<AppContext> {
/// when drawing the next frame. /// when drawing the next frame.
fn with_element_state<S, R>( fn with_element_state<S, R>(
&mut self, &mut self,
id: ElementId, global_element_id: &GlobalElementId,
f: impl FnOnce(Option<S>, &mut Self) -> (R, S), f: impl FnOnce(&mut Option<Box<S>>, &mut Self) -> R,
) -> R ) -> R
where where
S: 'static, S: 'static,
{ {
self.with_element_id(Some(id), |cx| { let state = if self.window_mut().drawing {
let global_id = cx.window().element_id_stack.clone(); self.window_mut()
if let Some(any) = cx
.window_mut()
.next_frame .next_frame
.element_states .element_states
.remove(&global_id) .remove(global_element_id)
.or_else(|| { .or_else(|| {
cx.window_mut() self.window_mut()
.rendered_frame .rendered_frame
.element_states .element_states
.remove(&global_id) .remove(global_element_id)
}) })
{ } else {
self.window_mut()
.rendered_frame
.element_states
.remove(global_element_id)
};
let mut state = state
.map(|any| {
let ElementStateBox { let ElementStateBox {
inner, inner,
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
type_name type_name,
} = any; } = any;
// Using the extra inner option to avoid needing to reallocate a new box. inner
let mut state_box = inner .downcast::<S>()
.downcast::<Option<S>>()
.map_err(|_| { .map_err(|_| {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
{ {
@ -2119,42 +2124,32 @@ pub trait BorrowWindow: BorrowMut<Window> + BorrowMut<AppContext> {
) )
} }
}) })
.unwrap(); .unwrap()
});
// Actual: Option<AnyElement> <- View let result = f(&mut state, self);
// Requested: () <- AnyElemet
let state = state_box if let Some(state) = state {
.take() let state = ElementStateBox {
.expect("element state is already on the stack"); inner: state,
let (result, state) = f(Some(state), cx);
state_box.replace(state); #[cfg(debug_assertions)]
cx.window_mut() type_name: std::any::type_name::<S>(),
};
if self.window().drawing {
self.window_mut()
.next_frame .next_frame
.element_states .element_states
.insert(global_id, ElementStateBox { .insert(global_element_id.clone(), state);
inner: state_box,
#[cfg(debug_assertions)]
type_name
});
result
} else { } else {
let (result, state) = f(None, cx); self.window_mut()
cx.window_mut() .rendered_frame
.next_frame
.element_states .element_states
.insert(global_id, .insert(global_element_id.clone(), state);
ElementStateBox {
inner: Box::new(Some(state)),
#[cfg(debug_assertions)]
type_name: std::any::type_name::<S>()
}
);
result
} }
}) }
result
} }
/// Obtain the current content mask. /// Obtain the current content mask.

View file

@ -2,10 +2,10 @@ use editor::{Cursor, HighlightedRange, HighlightedRangeLine};
use gpui::{ use gpui::{
black, div, fill, point, px, red, relative, AnyElement, AsyncWindowContext, AvailableSpace, black, div, fill, point, px, red, relative, AnyElement, AsyncWindowContext, AvailableSpace,
Bounds, DispatchPhase, Element, ElementId, ExternalPaths, FocusHandle, Font, FontStyle, Bounds, DispatchPhase, Element, ElementId, ExternalPaths, FocusHandle, Font, FontStyle,
FontWeight, HighlightStyle, Hsla, InteractiveElement, InteractiveElementState, Interactivity, FontWeight, HighlightStyle, Hsla, InteractiveElement, Interactivity, IntoElement, LayoutId,
IntoElement, LayoutId, Model, ModelContext, ModifiersChangedEvent, MouseButton, Pixels, Model, ModelContext, ModifiersChangedEvent, MouseButton, Pixels, PlatformInputHandler, Point,
PlatformInputHandler, Point, Rgba, ShapedLine, Size, StatefulInteractiveElement, Styled, Rgba, ShapedLine, Size, StatefulInteractiveElement, Styled, TextRun, TextStyle, TextSystem,
TextRun, TextStyle, TextSystem, UnderlineStyle, WhiteSpace, WindowContext, UnderlineStyle, WhiteSpace, WindowContext,
}; };
use itertools::Itertools; use itertools::Itertools;
use language::CursorShape; use language::CursorShape;
@ -759,30 +759,22 @@ impl TerminalElement {
} }
impl Element for TerminalElement { impl Element for TerminalElement {
type State = InteractiveElementState; type FrameState = ();
fn layout( fn layout(&mut self, cx: &mut WindowContext<'_>) -> (LayoutId, Self::FrameState) {
&mut self, let layout_id = self.interactivity.layout(cx, |mut style, _, cx| {
element_state: Option<Self::State>, style.size.width = relative(1.).into();
cx: &mut WindowContext<'_>, style.size.height = relative(1.).into();
) -> (LayoutId, Self::State) { cx.request_layout(&style, None)
let (layout_id, interactive_state) = });
self.interactivity
.layout(element_state, 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, ())
});
(layout_id, interactive_state)
} }
fn paint( fn paint(
&mut self, &mut self,
bounds: Bounds<Pixels>, bounds: Bounds<Pixels>,
state: &mut Self::State, state: &mut Self::FrameState,
cx: &mut WindowContext<'_>, cx: &mut WindowContext<'_>,
) { ) {
let mut layout = self.compute_layout(bounds, cx); let mut layout = self.compute_layout(bounds, cx);
@ -812,7 +804,7 @@ impl Element for TerminalElement {
// }) // })
let mut interactivity = mem::take(&mut self.interactivity); let mut interactivity = mem::take(&mut self.interactivity);
interactivity.paint(bounds, bounds.size, state, cx, |_, _, cx| { interactivity.paint(bounds, bounds.size, cx, |_, _, _, cx| {
cx.handle_input(&self.focus, terminal_input_handler); cx.handle_input(&self.focus, terminal_input_handler);
self.register_key_listeners(cx); self.register_key_listeners(cx);

View file

@ -126,20 +126,19 @@ pub struct PopoverMenuState<M> {
} }
impl<M: ManagedView> Element for PopoverMenu<M> { impl<M: ManagedView> Element for PopoverMenu<M> {
type State = PopoverMenuState<M>; type FrameState = PopoverMenuState<M>;
fn layout( fn layout(&mut self, cx: &mut WindowContext) -> (gpui::LayoutId, Self::FrameState) {
&mut self,
element_state: Option<Self::State>,
cx: &mut WindowContext,
) -> (gpui::LayoutId, Self::State) {
let mut menu_layout_id = None; let mut menu_layout_id = None;
let (menu, child_bounds) = if let Some(element_state) = element_state { // todo!()
(element_state.menu, element_state.child_bounds) // let (menu, child_bounds) = if let Some(element_state) = element_state {
} else { // (element_state.menu, element_state.child_bounds)
(Rc::default(), None) // } else {
}; // (Rc::default(), None)
// };
let menu: Rc<RefCell<Option<View<M>>>> = Rc::default();
let child_bounds = None;
let menu_element = menu.borrow_mut().as_mut().map(|menu| { let menu_element = menu.borrow_mut().as_mut().map(|menu| {
let mut overlay = overlay().snap_to_window().anchor(self.anchor); let mut overlay = overlay().snap_to_window().anchor(self.anchor);
@ -184,7 +183,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
fn paint( fn paint(
&mut self, &mut self,
_: Bounds<gpui::Pixels>, _: Bounds<gpui::Pixels>,
element_state: &mut Self::State, element_state: &mut Self::FrameState,
cx: &mut WindowContext, cx: &mut WindowContext,
) { ) {
if let Some(mut child) = element_state.child_element.take() { if let Some(mut child) = element_state.child_element.take() {

View file

@ -58,18 +58,17 @@ pub struct MenuHandleState<M> {
} }
impl<M: ManagedView> Element for RightClickMenu<M> { impl<M: ManagedView> Element for RightClickMenu<M> {
type State = MenuHandleState<M>; type FrameState = MenuHandleState<M>;
fn layout( fn layout(&mut self, cx: &mut WindowContext) -> (gpui::LayoutId, Self::FrameState) {
&mut self, // todo!()
element_state: Option<Self::State>, // let (menu, position) = if let Some(element_state) = element_state {
cx: &mut WindowContext, // (element_state.menu, element_state.position)
) -> (gpui::LayoutId, Self::State) { // } else {
let (menu, position) = if let Some(element_state) = element_state { // (Rc::default(), Rc::default())
(element_state.menu, element_state.position) // };
} else { let menu: Rc<RefCell<Option<View<M>>>> = Rc::default();
(Rc::default(), Rc::default()) let position: Rc<RefCell<Point<Pixels>>> = Rc::default();
};
let mut menu_layout_id = None; let mut menu_layout_id = None;
@ -114,7 +113,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
fn paint( fn paint(
&mut self, &mut self,
bounds: Bounds<gpui::Pixels>, bounds: Bounds<gpui::Pixels>,
element_state: &mut Self::State, element_state: &mut Self::FrameState,
cx: &mut WindowContext, cx: &mut WindowContext,
) { ) {
if let Some(mut child) = element_state.child_element.take() { if let Some(mut child) = element_state.child_element.take() {

View file

@ -756,25 +756,25 @@ mod element {
} }
impl Element for PaneAxisElement { impl Element for PaneAxisElement {
type State = Rc<RefCell<Option<usize>>>; type FrameState = Rc<RefCell<Option<usize>>>;
fn layout( fn layout(
&mut self, &mut self,
state: Option<Self::State>,
cx: &mut ui::prelude::WindowContext, cx: &mut ui::prelude::WindowContext,
) -> (gpui::LayoutId, Self::State) { ) -> (gpui::LayoutId, Self::FrameState) {
let mut style = Style::default(); let mut style = Style::default();
style.size.width = relative(1.).into(); style.size.width = relative(1.).into();
style.size.height = relative(1.).into(); style.size.height = relative(1.).into();
let layout_id = cx.request_layout(&style, None); let layout_id = cx.request_layout(&style, None);
let dragged_pane = state.unwrap_or_else(|| Rc::new(RefCell::new(None))); // todo!()
let dragged_pane = Rc::new(RefCell::new(None));
(layout_id, dragged_pane) (layout_id, dragged_pane)
} }
fn paint( fn paint(
&mut self, &mut self,
bounds: gpui::Bounds<ui::prelude::Pixels>, bounds: gpui::Bounds<ui::prelude::Pixels>,
state: &mut Self::State, state: &mut Self::FrameState,
cx: &mut ui::prelude::WindowContext, cx: &mut ui::prelude::WindowContext,
) { ) {
let flexes = self.flexes.lock().clone(); let flexes = self.flexes.lock().clone();