mirror of
https://github.com/zed-industries/zed.git
synced 2025-02-03 08:54:04 +00:00
WIP
This commit is contained in:
parent
4e544545d1
commit
2330256741
16 changed files with 736 additions and 829 deletions
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
) {
|
||||
}
|
||||
|
|
|
@ -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, []);
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
|||
}
|
||||
};
|
||||
});
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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?"
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue