This commit is contained in:
Antonio Scandurra 2023-12-18 14:55:59 +01:00
parent 4e544545d1
commit 2330256741
16 changed files with 736 additions and 829 deletions

View file

@ -889,7 +889,7 @@ impl EditorElement {
}
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;
for fold in snapshot.folds_in_range(layout.visible_anchor_range.clone()) {
let fold_range = fold.range.clone();
@ -938,7 +938,7 @@ impl EditorElement {
fold_bounds.size,
cx,
|fold_element_state, cx| {
if fold_element_state.is_active() {
if fold_element_state.is_active {
cx.theme().colors().ghost_element_active
} else if fold_bounds.contains(&cx.mouse_position()) {
cx.theme().colors().ghost_element_hover
@ -1996,7 +1996,7 @@ impl EditorElement {
.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(
start_row..end_row,
&snapshot,
@ -2081,7 +2081,7 @@ impl EditorElement {
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(
fold_statuses,
&style,
@ -2734,13 +2734,9 @@ enum Invisible {
}
impl Element for EditorElement {
type State = ();
type FrameState = ();
fn layout(
&mut self,
element_state: Option<Self::State>,
cx: &mut gpui::WindowContext,
) -> (gpui::LayoutId, Self::State) {
fn layout(&mut self, cx: &mut gpui::WindowContext) -> (gpui::LayoutId, Self::FrameState) {
self.editor.update(cx, |editor, cx| {
editor.set_style(self.style.clone(), cx);
@ -2788,7 +2784,7 @@ impl Element for EditorElement {
fn paint(
&mut self,
bounds: Bounds<gpui::Pixels>,
element_state: &mut Self::State,
_element_state: &mut Self::FrameState,
cx: &mut gpui::WindowContext,
) {
let editor = self.editor.clone();
@ -2823,7 +2819,7 @@ impl Element for EditorElement {
self.paint_mouse_listeners(bounds, gutter_bounds, text_bounds, &layout, cx);
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);
});
}

View file

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

View file

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

View file

@ -1,9 +1,10 @@
use crate::{
point, px, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, BorrowAppContext,
BorrowWindow, Bounds, ClickEvent, DispatchPhase, Element, ElementId, FocusHandle, IntoElement,
KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent, MouseMoveEvent,
MouseUpEvent, ParentElement, Pixels, Point, Render, ScrollWheelEvent, SharedString, Size,
StackingOrder, Style, StyleRefinement, Styled, Task, View, Visibility, WindowContext,
BorrowWindow, Bounds, ClickEvent, DispatchPhase, Element, ElementId, FocusHandle,
GlobalElementId, IntoElement, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton,
MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Render,
ScrollWheelEvent, SharedString, Size, StackingOrder, Style, StyleRefinement, Styled, Task,
View, Visibility, WindowContext,
};
use collections::HashMap;
@ -704,33 +705,29 @@ impl ParentElement for Div {
}
impl Element for Div {
type State = DivState;
type FrameState = DivState;
fn layout(
&mut self,
element_state: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
let mut child_layout_ids = SmallVec::new();
let (layout_id, interactive_state) = self.interactivity.layout(
element_state.map(|s| s.interactive_state),
cx,
|style, cx| {
cx.with_text_style(style.text_style().cloned(), |cx| {
child_layout_ids = self
.children
.iter_mut()
.map(|child| child.layout(cx))
.collect::<SmallVec<_>>();
cx.request_layout(&style, child_layout_ids.iter().copied())
})
},
);
let mut is_active = false;
let layout_id = self.interactivity.layout(cx, |style, element_state, cx| {
cx.with_text_style(style.text_style().cloned(), |cx| {
child_layout_ids = self
.children
.iter_mut()
.map(|child| child.layout(cx))
.collect::<SmallVec<_>>();
is_active = element_state.as_ref().map_or(false, |(_, element_state)| {
element_state.pending_mouse_down.is_some()
});
cx.request_layout(&style, child_layout_ids.iter().copied())
})
});
(
layout_id,
DivState {
interactive_state,
child_layout_ids,
is_active,
},
)
}
@ -738,7 +735,7 @@ impl Element for Div {
fn paint(
&mut self,
bounds: Bounds<Pixels>,
element_state: &mut Self::State,
element_state: &mut Self::FrameState,
cx: &mut WindowContext,
) {
let mut child_min = point(Pixels::MAX, Pixels::MAX);
@ -774,12 +771,8 @@ impl Element for Div {
(child_max - child_min).into()
};
self.interactivity.paint(
bounds,
content_size,
&mut element_state.interactive_state,
cx,
|style, scroll_offset, cx| {
self.interactivity
.paint(bounds, content_size, cx, |style, _, scroll_offset, cx| {
let z_index = style.z_index.unwrap_or(0);
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 {
child_layout_ids: SmallVec<[LayoutId; 2]>,
interactive_state: InteractiveElementState,
}
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 is_active: bool,
}
pub struct Interactivity {
@ -879,43 +862,70 @@ impl InteractiveBounds {
impl Interactivity {
pub fn layout(
&mut self,
element_state: Option<InteractiveElementState>,
cx: &mut WindowContext,
f: impl FnOnce(Style, &mut WindowContext) -> LayoutId,
) -> (LayoutId, InteractiveElementState) {
let mut element_state = element_state.unwrap_or_default();
f: impl FnOnce(
Style,
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 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 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) = this.scroll_handle.as_ref() {
element_state.scroll_offset = Some(scroll_handle.0.borrow().offset.clone());
}
}
if let Some(scroll_handle) = self.scroll_handle.as_ref() {
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)
f(style, element_state, cx)
})
}
pub fn paint(
&mut self,
bounds: Bounds<Pixels>,
content_size: Size<Pixels>,
element_state: &mut InteractiveElementState,
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 {
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.
// 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({
let interactive_bounds = interactive_bounds.clone();
move |event: &MouseDownEvent, phase, cx| {
@ -1164,263 +1174,301 @@ impl Interactivity {
}
}
let click_listeners = mem::take(&mut self.click_listeners);
let mut drag_listener = mem::take(&mut self.drag_listener);
if let Some((global_element_id, element_state)) = element_state.as_mut() {
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() {
let pending_mouse_down = element_state
.pending_mouse_down
.get_or_insert_with(Default::default)
.clone();
let mouse_down = pending_mouse_down.borrow().clone();
if let Some(mouse_down) = mouse_down {
if drag_listener.is_some() {
let active_state = element_state
.clicked_state
.get_or_insert_with(Default::default)
.clone();
let interactive_bounds = interactive_bounds.clone();
if !click_listeners.is_empty() || drag_listener.is_some() {
let global_element_id = global_element_id.clone();
if let Some(mouse_down) = element_state.pending_mouse_down.clone() {
if drag_listener.is_some() {
let interactive_bounds = interactive_bounds.clone();
let global_element_id = global_element_id.clone();
cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
if cx.active_drag.is_some() {
if phase == DispatchPhase::Capture {
cx.notify();
}
} 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 self callback");
cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
if cx.active_drag.is_some() {
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();
}
},
);
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.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 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,
});
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);
}
}
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.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()
}
if let Some(group) = self.group.clone() {
GroupBounds::push(group, bounds, cx);
}
}
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() {
GroupBounds::push(group, bounds, cx);
}
let scroll_offset = element_state
.scroll_offset
.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_up_listeners = mem::take(&mut self.key_up_listeners);
let action_listeners = mem::take(&mut self.action_listeners);
cx.with_key_dispatch(
self.key_context.clone(),
element_state.focus_handle.clone(),
self.tracked_focus_handle.clone(),
|_, cx| {
for listener in key_down_listeners {
cx.on_key_event(move |event: &KeyDownEvent, phase, cx| {
@ -1438,7 +1486,7 @@ impl Interactivity {
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(
&self,
&mut self,
bounds: Option<Bounds<Pixels>>,
element_state: &mut InteractiveElementState,
cx: &mut WindowContext,
) -> Style {
let mut style = Style::default();
@ -1521,21 +1590,21 @@ impl Interactivity {
}
}
let clicked_state = element_state
.clicked_state
.get_or_insert_with(Default::default)
.borrow();
if clicked_state.group {
if let Some(group) = self.group_active_style.as_ref() {
style.refine(&group.style)
}
}
self.with_element_state(cx, |this, element_state, _cx| {
if let Some((_, element_state)) = element_state {
if element_state.clicked_state.group {
if let Some(group) = this.group_active_style.as_ref() {
style.refine(&group.style)
}
}
if let Some(active_style) = self.active_style.as_ref() {
if clicked_state.element {
style.refine(active_style)
if let Some(active_style) = this.active_style.as_ref() {
if element_state.clicked_state.element {
style.refine(active_style)
}
}
}
}
})
});
style
@ -1583,11 +1652,11 @@ impl Default for Interactivity {
#[derive(Default)]
pub struct InteractiveElementState {
pub focus_handle: Option<FocusHandle>,
pub clicked_state: Option<Rc<RefCell<ElementClickedState>>>,
pub hover_state: Option<Rc<RefCell<bool>>>,
pub pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
pub clicked_state: ElementClickedState,
pub hover_state: bool,
pub pending_mouse_down: Option<MouseDownEvent>,
pub scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
pub active_tooltip: Option<Rc<RefCell<Option<ActiveTooltip>>>>,
pub active_tooltip: Option<ActiveTooltip>,
}
pub struct ActiveTooltip {
@ -1663,17 +1732,18 @@ impl<E> Element for Focusable<E>
where
E: Element,
{
type State = E::State;
type FrameState = E::FrameState;
fn layout(
&mut self,
state: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
self.element.layout(state, cx)
fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
self.element.layout(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)
}
}
@ -1737,17 +1807,18 @@ impl<E> Element for Stateful<E>
where
E: Element,
{
type State = E::State;
type FrameState = E::FrameState;
fn layout(
&mut self,
state: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
self.element.layout(state, cx)
fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
self.element.layout(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)
}
}

View file

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

View file

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

View file

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

View file

@ -24,28 +24,25 @@ impl Svg {
}
impl Element for Svg {
type State = InteractiveElementState;
type FrameState = ();
fn layout(
&mut self,
element_state: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
self.interactivity.layout(element_state, cx, |style, cx| {
cx.request_layout(&style, None)
})
fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
let layout_id = self
.interactivity
.layout(cx, |style, _, cx| cx.request_layout(&style, None));
(layout_id, ())
}
fn paint(
&mut self,
bounds: Bounds<Pixels>,
element_state: &mut Self::State,
element_state: &mut Self::FrameState,
cx: &mut WindowContext,
) where
Self: Sized,
{
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) {
cx.paint_svg(bounds, path.clone(), color).log_err();
}

View file

@ -1,22 +1,18 @@
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,
WhiteSpace, WindowContext, WrappedLine,
};
use anyhow::anyhow;
use parking_lot::{Mutex, MutexGuard};
use smallvec::SmallVec;
use std::{cell::Cell, mem, ops::Range, rc::Rc, sync::Arc};
use std::{mem, ops::Range, sync::Arc};
use util::ResultExt;
impl Element for &'static str {
type State = TextState;
type FrameState = TextState;
fn layout(
&mut self,
_: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
let mut state = TextState::default();
let layout_id = state.layout(SharedString::from(*self), None, cx);
(layout_id, state)
@ -40,13 +36,9 @@ impl IntoElement for &'static str {
}
impl Element for SharedString {
type State = TextState;
type FrameState = TextState;
fn layout(
&mut self,
_: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
let mut state = TextState::default();
let layout_id = state.layout(self.clone(), None, cx);
(layout_id, state)
@ -116,19 +108,20 @@ impl StyledText {
}
impl Element for StyledText {
type State = TextState;
type FrameState = TextState;
fn layout(
&mut self,
_: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
let mut state = TextState::default();
let layout_id = state.layout(self.text.clone(), self.runs.take(), cx);
(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)
}
}
@ -295,9 +288,9 @@ struct InteractiveTextClickEvent {
mouse_up_index: usize,
}
#[derive(Default)]
pub struct InteractiveTextState {
text_state: TextState,
mouse_down_index: Rc<Cell<Option<usize>>>,
mouse_down_index: Option<usize>,
}
impl InteractiveText {
@ -329,86 +322,93 @@ impl InteractiveText {
}
impl Element for InteractiveText {
type State = InteractiveTextState;
type FrameState = TextState;
fn layout(
&mut self,
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 layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
self.text.layout(cx)
}
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
if let Some(click_listener) = self.click_listener.take() {
if let Some(ix) = state
.text_state
.index_for_position(bounds, cx.mouse_position())
{
if self
.clickable_ranges
.iter()
.any(|range| range.contains(&ix))
{
cx.set_cursor_style(crate::CursorStyle::PointingHand)
}
}
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)
fn paint(
&mut self,
bounds: Bounds<Pixels>,
text_state: &mut Self::FrameState,
cx: &mut WindowContext,
) {
cx.with_element_id(Some(self.element_id.clone()), |global_element_id, cx| {
let global_element_id = global_element_id.unwrap();
cx.with_element_state::<InteractiveTextState, _>(
&global_element_id,
|element_state, cx| {
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())
{
click_listener(
&clickable_ranges,
InteractiveTextClickEvent {
mouse_down_index,
mouse_up_index,
},
cx,
)
if self
.clickable_ranges
.iter()
.any(|range| range.contains(&ix))
{
cx.set_cursor_style(crate::CursorStyle::PointingHand)
}
}
mouse_down.take();
cx.notify();
}
});
} else {
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)
{
mouse_down.set(Some(mouse_down_index));
cx.notify();
let text_state = text_state.clone();
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);
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(
&clickable_ranges,
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::{
point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, ContentMask, Element,
ElementId, InteractiveElement, InteractiveElementState, Interactivity, IntoElement, LayoutId,
Pixels, Point, Render, Size, StyleRefinement, Styled, View, ViewContext, WindowContext,
ElementId, InteractiveElement, Interactivity, IntoElement, LayoutId, Pixels, Point, Render,
Size, StyleRefinement, Styled, View, ViewContext, WindowContext,
};
use smallvec::SmallVec;
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 {
type State = UniformListState;
type FrameState = Size<Pixels>;
fn layout(
&mut self,
state: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
let max_items = self.item_count;
let item_size = state
.as_ref()
.map(|s| s.item_size)
.unwrap_or_else(|| self.measure_item(None, cx));
let item_size = self.measure_item(None, cx);
let layout_id = self.interactivity.layout(cx, |style, _, cx| {
cx.request_measured_layout(style, move |known_dimensions, available_space, _cx| {
let desired_height = item_size.height * max_items;
let width = known_dimensions
.width
.unwrap_or(match available_space.width {
AvailableSpace::Definite(x) => x,
AvailableSpace::MinContent | AvailableSpace::MaxContent => item_size.width,
});
let height = match available_space.height {
AvailableSpace::Definite(height) => desired_height.min(height),
AvailableSpace::MinContent | AvailableSpace::MaxContent => desired_height,
};
size(width, height)
})
});
let (layout_id, interactive) =
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)
(layout_id, item_size)
}
fn paint(
&mut self,
bounds: Bounds<crate::Pixels>,
element_state: &mut Self::State,
item_size: &mut Self::FrameState,
cx: &mut WindowContext,
) {
let style =
self.interactivity
.compute_style(Some(bounds), &mut element_state.interactive, cx);
let item_size = *item_size;
let style = self.interactivity.compute_style(Some(bounds), cx);
let border = style.border_widths.to_pixels(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),
);
let item_size = element_state.item_size;
let content_size = Size {
width: padded_bounds.size.width,
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;
self.interactivity.paint(
bounds,
content_size,
&mut element_state.interactive,
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 padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());

View file

@ -79,19 +79,15 @@ impl<V: 'static> View<V> {
}
impl<V: Render> Element for View<V> {
type State = Option<AnyElement>;
type FrameState = Option<AnyElement>;
fn layout(
&mut self,
_state: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
let mut element = self.update(cx, |view, cx| view.render(cx).into_any());
let layout_id = element.layout(cx);
(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);
}
}
@ -227,18 +223,14 @@ impl<V: Render> From<View<V>> for AnyView {
}
impl Element for AnyView {
type State = Option<AnyElement>;
type FrameState = Option<AnyElement>;
fn layout(
&mut self,
_state: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
let (layout_id, state) = (self.layout)(self, cx);
(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!(
state.is_some(),
"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>(
&mut self,
id: Option<impl Into<ElementId>>,
f: impl FnOnce(&mut Self) -> R,
f: impl FnOnce(Option<GlobalElementId>, &mut Self) -> R,
) -> R {
if let Some(id) = id.map(Into::into) {
let window = self.window_mut();
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();
window.element_id_stack.pop();
result
} else {
f(self)
f(None, self)
}
}
@ -2071,90 +2072,67 @@ pub trait BorrowWindow: BorrowMut<Window> + BorrowMut<AppContext> {
/// when drawing the next frame.
fn with_element_state<S, R>(
&mut self,
id: ElementId,
f: impl FnOnce(Option<S>, &mut Self) -> (R, S),
global_element_id: &GlobalElementId,
f: impl FnOnce(&mut Option<Box<S>>, &mut Self) -> R,
) -> R
where
S: 'static,
{
self.with_element_id(Some(id), |cx| {
let global_id = cx.window().element_id_stack.clone();
if let Some(any) = cx
let mut state = self
.window_mut()
.next_frame
.element_states
.remove(&global_id)
.remove(global_element_id)
.or_else(|| {
cx.window_mut()
self.window_mut()
.rendered_frame
.element_states
.remove(&global_id)
})
{
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!(
"invalid element state type for id, requested_type {:?}, actual type: {:?}",
std::any::type_name::<S>(),
type_name
)
}
#[cfg(not(debug_assertions))]
{
anyhow!(
"invalid element state type for id, requested_type {:?}",
std::any::type_name::<S>(),
)
}
})
.unwrap();
// Actual: Option<AnyElement> <- View
// Requested: () <- AnyElemet
let state = state_box
.take()
.expect("element state is already on the stack");
let (result, state) = f(Some(state), cx);
state_box.replace(state);
cx.window_mut()
.next_frame
.element_states
.insert(global_id, ElementStateBox {
inner: state_box,
.remove(global_element_id)
}).map(|any| {
let ElementStateBox {
inner,
#[cfg(debug_assertions)]
type_name
});
result
} else {
let (result, state) = f(None, cx);
cx.window_mut()
.next_frame
.element_states
.insert(global_id,
ElementStateBox {
inner: Box::new(Some(state)),
} = any;
inner
.downcast::<S>()
.map_err(|_| {
#[cfg(debug_assertions)]
type_name: std::any::type_name::<S>()
}
{
anyhow!(
"invalid element state type for id, requested_type {:?}, actual type: {:?}",
std::any::type_name::<S>(),
type_name
)
}
);
result
}
})
#[cfg(not(debug_assertions))]
{
anyhow!(
"invalid element state type for id, requested_type {:?}",
std::any::type_name::<S>(),
)
}
})
.unwrap()
});
let result = f(&mut state, self);
if let Some(state) = state {
self.window_mut().next_frame.element_states.insert(
global_element_id.clone(),
ElementStateBox {
inner: state,
#[cfg(debug_assertions)]
type_name: std::any::type_name::<S>(),
},
);
}
result
}
/// Obtain the current content mask.

View file

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

View file

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

View file

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