Compare commits
3 commits
main
...
element-st
Author | SHA1 | Date | |
---|---|---|---|
![]() |
676f6f490e | ||
![]() |
be3c0ff419 | ||
![]() |
2330256741 |
17 changed files with 732 additions and 810 deletions
|
@ -889,7 +889,7 @@ impl EditorElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
let fold_corner_radius = 0.15 * layout.position_map.line_height;
|
let fold_corner_radius = 0.15 * layout.position_map.line_height;
|
||||||
cx.with_element_id(Some("folds"), |cx| {
|
cx.with_element_id(Some("folds"), |_, cx| {
|
||||||
let snapshot = &layout.position_map.snapshot;
|
let snapshot = &layout.position_map.snapshot;
|
||||||
for fold in snapshot.folds_in_range(layout.visible_anchor_range.clone()) {
|
for fold in snapshot.folds_in_range(layout.visible_anchor_range.clone()) {
|
||||||
let fold_range = fold.range.clone();
|
let fold_range = fold.range.clone();
|
||||||
|
@ -938,7 +938,7 @@ impl EditorElement {
|
||||||
fold_bounds.size,
|
fold_bounds.size,
|
||||||
cx,
|
cx,
|
||||||
|fold_element_state, cx| {
|
|fold_element_state, cx| {
|
||||||
if fold_element_state.is_active() {
|
if fold_element_state.is_active {
|
||||||
cx.theme().colors().ghost_element_active
|
cx.theme().colors().ghost_element_active
|
||||||
} else if fold_bounds.contains(&cx.mouse_position()) {
|
} else if fold_bounds.contains(&cx.mouse_position()) {
|
||||||
cx.theme().colors().ghost_element_hover
|
cx.theme().colors().ghost_element_hover
|
||||||
|
@ -1996,7 +1996,7 @@ impl EditorElement {
|
||||||
.width;
|
.width;
|
||||||
let scroll_width = longest_line_width.max(max_visible_line_width) + overscroll.width;
|
let scroll_width = longest_line_width.max(max_visible_line_width) + overscroll.width;
|
||||||
|
|
||||||
let (scroll_width, blocks) = cx.with_element_id(Some("editor_blocks"), |cx| {
|
let (scroll_width, blocks) = cx.with_element_id(Some("editor_blocks"), |_, cx| {
|
||||||
self.layout_blocks(
|
self.layout_blocks(
|
||||||
start_row..end_row,
|
start_row..end_row,
|
||||||
&snapshot,
|
&snapshot,
|
||||||
|
@ -2081,7 +2081,7 @@ impl EditorElement {
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut fold_indicators = cx.with_element_id(Some("gutter_fold_indicators"), |cx| {
|
let mut fold_indicators = cx.with_element_id(Some("gutter_fold_indicators"), |_, cx| {
|
||||||
editor.render_fold_indicators(
|
editor.render_fold_indicators(
|
||||||
fold_statuses,
|
fold_statuses,
|
||||||
&style,
|
&style,
|
||||||
|
@ -2734,13 +2734,9 @@ enum Invisible {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for EditorElement {
|
impl Element for EditorElement {
|
||||||
type State = ();
|
type FrameState = ();
|
||||||
|
|
||||||
fn layout(
|
fn layout(&mut self, cx: &mut gpui::WindowContext) -> (gpui::LayoutId, Self::FrameState) {
|
||||||
&mut self,
|
|
||||||
element_state: Option<Self::State>,
|
|
||||||
cx: &mut gpui::WindowContext,
|
|
||||||
) -> (gpui::LayoutId, Self::State) {
|
|
||||||
self.editor.update(cx, |editor, cx| {
|
self.editor.update(cx, |editor, cx| {
|
||||||
editor.set_style(self.style.clone(), cx);
|
editor.set_style(self.style.clone(), cx);
|
||||||
|
|
||||||
|
@ -2788,7 +2784,7 @@ impl Element for EditorElement {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<gpui::Pixels>,
|
bounds: Bounds<gpui::Pixels>,
|
||||||
element_state: &mut Self::State,
|
_element_state: &mut Self::FrameState,
|
||||||
cx: &mut gpui::WindowContext,
|
cx: &mut gpui::WindowContext,
|
||||||
) {
|
) {
|
||||||
let editor = self.editor.clone();
|
let editor = self.editor.clone();
|
||||||
|
@ -2823,7 +2819,7 @@ impl Element for EditorElement {
|
||||||
self.paint_mouse_listeners(bounds, gutter_bounds, text_bounds, &layout, cx);
|
self.paint_mouse_listeners(bounds, gutter_bounds, text_bounds, &layout, cx);
|
||||||
|
|
||||||
if !layout.blocks.is_empty() {
|
if !layout.blocks.is_empty() {
|
||||||
cx.with_element_id(Some("editor_blocks"), |cx| {
|
cx.with_element_id(Some("editor_blocks"), |_, cx| {
|
||||||
self.paint_blocks(bounds, &mut layout, cx);
|
self.paint_blocks(bounds, &mut layout, cx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use derive_more::{Deref, DerefMut};
|
use derive_more::{Deref, DerefMut};
|
||||||
pub(crate) use smallvec::SmallVec;
|
pub(crate) use smallvec::SmallVec;
|
||||||
use std::{any::Any, fmt::Debug};
|
use std::{any::Any, fmt::Debug, mem};
|
||||||
|
|
||||||
pub trait Render: 'static + Sized {
|
pub trait Render: 'static + Sized {
|
||||||
type Element: Element + 'static;
|
type Element: Element + 'static;
|
||||||
|
@ -28,30 +28,20 @@ pub trait IntoElement: Sized {
|
||||||
origin: Point<Pixels>,
|
origin: Point<Pixels>,
|
||||||
available_space: Size<T>,
|
available_space: Size<T>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
f: impl FnOnce(&mut <Self::Element as Element>::State, &mut WindowContext) -> R,
|
f: impl FnOnce(&mut <Self::Element as Element>::FrameState, &mut WindowContext) -> R,
|
||||||
) -> R
|
) -> R
|
||||||
where
|
where
|
||||||
T: Clone + Default + Debug + Into<AvailableSpace>,
|
T: Clone + Default + Debug + Into<AvailableSpace>,
|
||||||
{
|
{
|
||||||
let element = self.into_element();
|
let element = self.into_element();
|
||||||
let element_id = element.element_id();
|
|
||||||
let element = DrawableElement {
|
let element = DrawableElement {
|
||||||
element: Some(element),
|
element: Some(element),
|
||||||
phase: ElementDrawPhase::Start,
|
phase: ElementDrawPhase::Start,
|
||||||
};
|
};
|
||||||
|
|
||||||
let frame_state =
|
let mut frame_state =
|
||||||
DrawableElement::draw(element, origin, available_space.map(Into::into), cx);
|
DrawableElement::draw(element, origin, available_space.map(Into::into), cx);
|
||||||
|
f(&mut frame_state, cx)
|
||||||
if let Some(mut frame_state) = frame_state {
|
|
||||||
f(&mut frame_state, cx)
|
|
||||||
} else {
|
|
||||||
cx.with_element_state(element_id.unwrap(), |element_state, cx| {
|
|
||||||
let mut element_state = element_state.unwrap();
|
|
||||||
let result = f(&mut element_state, cx);
|
|
||||||
(result, element_state)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map<U>(self, f: impl FnOnce(Self) -> U) -> U
|
fn map<U>(self, f: impl FnOnce(Self) -> U) -> U
|
||||||
|
@ -84,15 +74,16 @@ pub trait IntoElement: Sized {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Element: 'static + IntoElement {
|
pub trait Element: 'static + IntoElement {
|
||||||
type State: 'static;
|
type FrameState: 'static;
|
||||||
|
|
||||||
fn layout(
|
fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState);
|
||||||
|
|
||||||
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: Option<Self::State>,
|
bounds: Bounds<Pixels>,
|
||||||
|
state: &mut Self::FrameState,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) -> (LayoutId, Self::State);
|
);
|
||||||
|
|
||||||
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext);
|
|
||||||
|
|
||||||
fn into_any(self) -> AnyElement {
|
fn into_any(self) -> AnyElement {
|
||||||
AnyElement::new(self)
|
AnyElement::new(self)
|
||||||
|
@ -111,7 +102,7 @@ pub struct Component<C> {
|
||||||
|
|
||||||
pub struct CompositeElementState<C: RenderOnce> {
|
pub struct CompositeElementState<C: RenderOnce> {
|
||||||
rendered_element: Option<<C::Rendered as IntoElement>::Element>,
|
rendered_element: Option<<C::Rendered as IntoElement>::Element>,
|
||||||
rendered_element_state: Option<<<C::Rendered as IntoElement>::Element as Element>::State>,
|
rendered_element_state: Option<<<C::Rendered as IntoElement>::Element as Element>::FrameState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C> Component<C> {
|
impl<C> Component<C> {
|
||||||
|
@ -123,48 +114,30 @@ impl<C> Component<C> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: RenderOnce> Element for Component<C> {
|
impl<C: RenderOnce> Element for Component<C> {
|
||||||
type State = CompositeElementState<C>;
|
type FrameState = CompositeElementState<C>;
|
||||||
|
|
||||||
fn layout(
|
fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
|
||||||
&mut self,
|
|
||||||
state: Option<Self::State>,
|
|
||||||
cx: &mut WindowContext,
|
|
||||||
) -> (LayoutId, Self::State) {
|
|
||||||
let mut element = self.component.take().unwrap().render(cx).into_element();
|
let mut element = self.component.take().unwrap().render(cx).into_element();
|
||||||
if let Some(element_id) = element.element_id() {
|
let (layout_id, state) = element.layout(cx);
|
||||||
let layout_id =
|
let state = CompositeElementState {
|
||||||
cx.with_element_state(element_id, |state, cx| element.layout(state, cx));
|
rendered_element: Some(element),
|
||||||
let state = CompositeElementState {
|
rendered_element_state: Some(state),
|
||||||
rendered_element: Some(element),
|
};
|
||||||
rendered_element_state: None,
|
(layout_id, state)
|
||||||
};
|
|
||||||
(layout_id, state)
|
|
||||||
} else {
|
|
||||||
let (layout_id, state) =
|
|
||||||
element.layout(state.and_then(|s| s.rendered_element_state), cx);
|
|
||||||
let state = CompositeElementState {
|
|
||||||
rendered_element: Some(element),
|
|
||||||
rendered_element_state: Some(state),
|
|
||||||
};
|
|
||||||
(layout_id, state)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
|
fn paint(
|
||||||
|
&mut self,
|
||||||
|
bounds: Bounds<Pixels>,
|
||||||
|
state: &mut Self::FrameState,
|
||||||
|
cx: &mut WindowContext,
|
||||||
|
) {
|
||||||
let mut element = state.rendered_element.take().unwrap();
|
let mut element = state.rendered_element.take().unwrap();
|
||||||
if let Some(element_id) = element.element_id() {
|
element.paint(
|
||||||
cx.with_element_state(element_id, |element_state, cx| {
|
bounds,
|
||||||
let mut element_state = element_state.unwrap();
|
&mut state.rendered_element_state.as_mut().unwrap(),
|
||||||
element.paint(bounds, &mut element_state, cx);
|
cx,
|
||||||
((), element_state)
|
);
|
||||||
});
|
|
||||||
} else {
|
|
||||||
element.paint(
|
|
||||||
bounds,
|
|
||||||
&mut state.rendered_element_state.as_mut().unwrap(),
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,7 +200,7 @@ trait ElementObject {
|
||||||
|
|
||||||
pub struct DrawableElement<E: Element> {
|
pub struct DrawableElement<E: Element> {
|
||||||
element: Option<E>,
|
element: Option<E>,
|
||||||
phase: ElementDrawPhase<E::State>,
|
phase: ElementDrawPhase<E::FrameState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -236,12 +209,12 @@ enum ElementDrawPhase<S> {
|
||||||
Start,
|
Start,
|
||||||
LayoutRequested {
|
LayoutRequested {
|
||||||
layout_id: LayoutId,
|
layout_id: LayoutId,
|
||||||
frame_state: Option<S>,
|
frame_state: S,
|
||||||
},
|
},
|
||||||
LayoutComputed {
|
LayoutComputed {
|
||||||
layout_id: LayoutId,
|
layout_id: LayoutId,
|
||||||
available_space: Size<AvailableSpace>,
|
available_space: Size<AvailableSpace>,
|
||||||
frame_state: Option<S>,
|
frame_state: S,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,17 +232,7 @@ impl<E: Element> DrawableElement<E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(&mut self, cx: &mut WindowContext) -> LayoutId {
|
fn layout(&mut self, cx: &mut WindowContext) -> LayoutId {
|
||||||
let (layout_id, frame_state) = if let Some(id) = self.element.as_ref().unwrap().element_id()
|
let (layout_id, frame_state) = self.element.as_mut().unwrap().layout(cx);
|
||||||
{
|
|
||||||
let layout_id = cx.with_element_state(id, |element_state, cx| {
|
|
||||||
self.element.as_mut().unwrap().layout(element_state, cx)
|
|
||||||
});
|
|
||||||
(layout_id, None)
|
|
||||||
} else {
|
|
||||||
let (layout_id, frame_state) = self.element.as_mut().unwrap().layout(None, cx);
|
|
||||||
(layout_id, Some(frame_state))
|
|
||||||
};
|
|
||||||
|
|
||||||
self.phase = ElementDrawPhase::LayoutRequested {
|
self.phase = ElementDrawPhase::LayoutRequested {
|
||||||
layout_id,
|
layout_id,
|
||||||
frame_state,
|
frame_state,
|
||||||
|
@ -277,42 +240,23 @@ impl<E: Element> DrawableElement<E> {
|
||||||
layout_id
|
layout_id
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(mut self, cx: &mut WindowContext) -> Option<E::State> {
|
fn paint(mut self, cx: &mut WindowContext) -> E::FrameState {
|
||||||
match self.phase {
|
match self.phase {
|
||||||
ElementDrawPhase::LayoutRequested {
|
ElementDrawPhase::LayoutRequested {
|
||||||
layout_id,
|
layout_id,
|
||||||
frame_state,
|
mut frame_state,
|
||||||
}
|
}
|
||||||
| ElementDrawPhase::LayoutComputed {
|
| ElementDrawPhase::LayoutComputed {
|
||||||
layout_id,
|
layout_id,
|
||||||
frame_state,
|
mut frame_state,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let bounds = cx.layout_bounds(layout_id);
|
let bounds = cx.layout_bounds(layout_id);
|
||||||
|
self.element
|
||||||
if let Some(mut frame_state) = frame_state {
|
.take()
|
||||||
self.element
|
.unwrap()
|
||||||
.take()
|
.paint(bounds, &mut frame_state, cx);
|
||||||
.unwrap()
|
frame_state
|
||||||
.paint(bounds, &mut frame_state, cx);
|
|
||||||
Some(frame_state)
|
|
||||||
} else {
|
|
||||||
let element_id = self
|
|
||||||
.element
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.element_id()
|
|
||||||
.expect("if we don't have frame state, we should have element state");
|
|
||||||
cx.with_element_state(element_id, |element_state, cx| {
|
|
||||||
let mut element_state = element_state.unwrap();
|
|
||||||
self.element
|
|
||||||
.take()
|
|
||||||
.unwrap()
|
|
||||||
.paint(bounds, &mut element_state, cx);
|
|
||||||
((), element_state)
|
|
||||||
});
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => panic!("must call layout before paint"),
|
_ => panic!("must call layout before paint"),
|
||||||
|
@ -328,30 +272,34 @@ impl<E: Element> DrawableElement<E> {
|
||||||
self.layout(cx);
|
self.layout(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
let layout_id = match &mut self.phase {
|
let layout_id = match mem::take(&mut self.phase) {
|
||||||
ElementDrawPhase::LayoutRequested {
|
ElementDrawPhase::LayoutRequested {
|
||||||
layout_id,
|
layout_id,
|
||||||
frame_state,
|
frame_state,
|
||||||
} => {
|
} => {
|
||||||
cx.compute_layout(*layout_id, available_space);
|
cx.compute_layout(layout_id, available_space);
|
||||||
let layout_id = *layout_id;
|
|
||||||
self.phase = ElementDrawPhase::LayoutComputed {
|
self.phase = ElementDrawPhase::LayoutComputed {
|
||||||
layout_id,
|
layout_id,
|
||||||
available_space,
|
available_space,
|
||||||
frame_state: frame_state.take(),
|
frame_state,
|
||||||
};
|
};
|
||||||
layout_id
|
layout_id
|
||||||
}
|
}
|
||||||
ElementDrawPhase::LayoutComputed {
|
ElementDrawPhase::LayoutComputed {
|
||||||
layout_id,
|
layout_id,
|
||||||
available_space: prev_available_space,
|
available_space: prev_available_space,
|
||||||
..
|
frame_state,
|
||||||
} => {
|
} => {
|
||||||
if available_space != *prev_available_space {
|
if available_space != prev_available_space {
|
||||||
cx.compute_layout(*layout_id, available_space);
|
cx.compute_layout(layout_id, available_space);
|
||||||
*prev_available_space = available_space;
|
|
||||||
}
|
}
|
||||||
*layout_id
|
self.phase = ElementDrawPhase::LayoutComputed {
|
||||||
|
layout_id,
|
||||||
|
available_space,
|
||||||
|
frame_state,
|
||||||
|
};
|
||||||
|
|
||||||
|
layout_id
|
||||||
}
|
}
|
||||||
_ => panic!("cannot measure after painting"),
|
_ => panic!("cannot measure after painting"),
|
||||||
};
|
};
|
||||||
|
@ -364,7 +312,7 @@ impl<E: Element> DrawableElement<E> {
|
||||||
origin: Point<Pixels>,
|
origin: Point<Pixels>,
|
||||||
available_space: Size<AvailableSpace>,
|
available_space: Size<AvailableSpace>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) -> Option<E::State> {
|
) -> E::FrameState {
|
||||||
self.measure(available_space, cx);
|
self.measure(available_space, cx);
|
||||||
cx.with_absolute_element_offset(origin, |cx| self.paint(cx))
|
cx.with_absolute_element_offset(origin, |cx| self.paint(cx))
|
||||||
}
|
}
|
||||||
|
@ -373,7 +321,7 @@ impl<E: Element> DrawableElement<E> {
|
||||||
impl<E> ElementObject for Option<DrawableElement<E>>
|
impl<E> ElementObject for Option<DrawableElement<E>>
|
||||||
where
|
where
|
||||||
E: Element,
|
E: Element,
|
||||||
E::State: 'static,
|
E::FrameState: 'static,
|
||||||
{
|
{
|
||||||
fn element_id(&self) -> Option<ElementId> {
|
fn element_id(&self) -> Option<ElementId> {
|
||||||
self.as_ref().unwrap().element_id()
|
self.as_ref().unwrap().element_id()
|
||||||
|
@ -411,7 +359,7 @@ impl AnyElement {
|
||||||
pub fn new<E>(element: E) -> Self
|
pub fn new<E>(element: E) -> Self
|
||||||
where
|
where
|
||||||
E: 'static + Element,
|
E: 'static + Element,
|
||||||
E::State: Any,
|
E::FrameState: Any,
|
||||||
{
|
{
|
||||||
let element = frame_alloc(|| Some(DrawableElement::new(element)))
|
let element = frame_alloc(|| Some(DrawableElement::new(element)))
|
||||||
.map(|element| element as &mut dyn ElementObject);
|
.map(|element| element as &mut dyn ElementObject);
|
||||||
|
@ -451,18 +399,14 @@ impl AnyElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for AnyElement {
|
impl Element for AnyElement {
|
||||||
type State = ();
|
type FrameState = ();
|
||||||
|
|
||||||
fn layout(
|
fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
|
||||||
&mut self,
|
|
||||||
_: Option<Self::State>,
|
|
||||||
cx: &mut WindowContext,
|
|
||||||
) -> (LayoutId, Self::State) {
|
|
||||||
let layout_id = self.layout(cx);
|
let layout_id = self.layout(cx);
|
||||||
(layout_id, ())
|
(layout_id, ())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(&mut self, _: Bounds<Pixels>, _: &mut Self::State, cx: &mut WindowContext) {
|
fn paint(&mut self, _: Bounds<Pixels>, _: &mut Self::FrameState, cx: &mut WindowContext) {
|
||||||
self.paint(cx)
|
self.paint(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -499,20 +443,16 @@ impl IntoElement for () {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for () {
|
impl Element for () {
|
||||||
type State = ();
|
type FrameState = ();
|
||||||
|
|
||||||
fn layout(
|
fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
|
||||||
&mut self,
|
|
||||||
_state: Option<Self::State>,
|
|
||||||
cx: &mut WindowContext,
|
|
||||||
) -> (LayoutId, Self::State) {
|
|
||||||
(cx.request_layout(&crate::Style::default(), None), ())
|
(cx.request_layout(&crate::Style::default(), None), ())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_bounds: Bounds<Pixels>,
|
_bounds: Bounds<Pixels>,
|
||||||
_state: &mut Self::State,
|
_state: &mut Self::FrameState,
|
||||||
_cx: &mut WindowContext,
|
_cx: &mut WindowContext,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,13 +27,9 @@ impl IntoElement for Canvas {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for Canvas {
|
impl Element for Canvas {
|
||||||
type State = Style;
|
type FrameState = Style;
|
||||||
|
|
||||||
fn layout(
|
fn layout(&mut self, cx: &mut WindowContext) -> (crate::LayoutId, Self::FrameState) {
|
||||||
&mut self,
|
|
||||||
_: Option<Self::State>,
|
|
||||||
cx: &mut WindowContext,
|
|
||||||
) -> (crate::LayoutId, Self::State) {
|
|
||||||
let mut style = Style::default();
|
let mut style = Style::default();
|
||||||
style.refine(&self.style);
|
style.refine(&self.style);
|
||||||
let layout_id = cx.request_layout(&style, []);
|
let layout_id = cx.request_layout(&style, []);
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
point, px, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, BorrowAppContext,
|
point, px, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, BorrowAppContext,
|
||||||
BorrowWindow, Bounds, ClickEvent, DispatchPhase, Element, ElementId, FocusHandle, IntoElement,
|
BorrowWindow, Bounds, ClickEvent, DispatchPhase, Element, ElementId, FocusHandle,
|
||||||
KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent, MouseMoveEvent,
|
GlobalElementId, IntoElement, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton,
|
||||||
MouseUpEvent, ParentElement, Pixels, Point, Render, ScrollWheelEvent, SharedString, Size,
|
MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Render,
|
||||||
StackingOrder, Style, StyleRefinement, Styled, Task, View, Visibility, WindowContext,
|
ScrollWheelEvent, SharedString, Size, StackingOrder, Style, StyleRefinement, Styled, Task,
|
||||||
|
View, Visibility, WindowContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
|
@ -704,33 +705,29 @@ impl ParentElement for Div {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for Div {
|
impl Element for Div {
|
||||||
type State = DivState;
|
type FrameState = DivState;
|
||||||
|
|
||||||
fn layout(
|
fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
|
||||||
&mut self,
|
|
||||||
element_state: Option<Self::State>,
|
|
||||||
cx: &mut WindowContext,
|
|
||||||
) -> (LayoutId, Self::State) {
|
|
||||||
let mut child_layout_ids = SmallVec::new();
|
let mut child_layout_ids = SmallVec::new();
|
||||||
let (layout_id, interactive_state) = self.interactivity.layout(
|
let mut is_active = false;
|
||||||
element_state.map(|s| s.interactive_state),
|
let layout_id = self.interactivity.layout(cx, |style, element_state, cx| {
|
||||||
cx,
|
cx.with_text_style(style.text_style().cloned(), |cx| {
|
||||||
|style, cx| {
|
child_layout_ids = self
|
||||||
cx.with_text_style(style.text_style().cloned(), |cx| {
|
.children
|
||||||
child_layout_ids = self
|
.iter_mut()
|
||||||
.children
|
.map(|child| child.layout(cx))
|
||||||
.iter_mut()
|
.collect::<SmallVec<_>>();
|
||||||
.map(|child| child.layout(cx))
|
is_active = element_state.as_ref().map_or(false, |(_, element_state)| {
|
||||||
.collect::<SmallVec<_>>();
|
element_state.pending_mouse_down.is_some()
|
||||||
cx.request_layout(&style, child_layout_ids.iter().copied())
|
});
|
||||||
})
|
cx.request_layout(&style, child_layout_ids.iter().copied())
|
||||||
},
|
})
|
||||||
);
|
});
|
||||||
(
|
(
|
||||||
layout_id,
|
layout_id,
|
||||||
DivState {
|
DivState {
|
||||||
interactive_state,
|
|
||||||
child_layout_ids,
|
child_layout_ids,
|
||||||
|
is_active,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -738,7 +735,7 @@ impl Element for Div {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
element_state: &mut Self::State,
|
element_state: &mut Self::FrameState,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) {
|
) {
|
||||||
let mut child_min = point(Pixels::MAX, Pixels::MAX);
|
let mut child_min = point(Pixels::MAX, Pixels::MAX);
|
||||||
|
@ -774,12 +771,8 @@ impl Element for Div {
|
||||||
(child_max - child_min).into()
|
(child_max - child_min).into()
|
||||||
};
|
};
|
||||||
|
|
||||||
self.interactivity.paint(
|
self.interactivity
|
||||||
bounds,
|
.paint(bounds, content_size, cx, |style, _, scroll_offset, cx| {
|
||||||
content_size,
|
|
||||||
&mut element_state.interactive_state,
|
|
||||||
cx,
|
|
||||||
|style, scroll_offset, cx| {
|
|
||||||
let z_index = style.z_index.unwrap_or(0);
|
let z_index = style.z_index.unwrap_or(0);
|
||||||
|
|
||||||
cx.with_z_index(z_index, |cx| {
|
cx.with_z_index(z_index, |cx| {
|
||||||
|
@ -795,8 +788,7 @@ impl Element for Div {
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
},
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -814,16 +806,7 @@ impl IntoElement for Div {
|
||||||
|
|
||||||
pub struct DivState {
|
pub struct DivState {
|
||||||
child_layout_ids: SmallVec<[LayoutId; 2]>,
|
child_layout_ids: SmallVec<[LayoutId; 2]>,
|
||||||
interactive_state: InteractiveElementState,
|
pub is_active: bool,
|
||||||
}
|
|
||||||
|
|
||||||
impl DivState {
|
|
||||||
pub fn is_active(&self) -> bool {
|
|
||||||
self.interactive_state
|
|
||||||
.pending_mouse_down
|
|
||||||
.as_ref()
|
|
||||||
.map_or(false, |pending| pending.borrow().is_some())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Interactivity {
|
pub struct Interactivity {
|
||||||
|
@ -879,43 +862,70 @@ impl InteractiveBounds {
|
||||||
impl Interactivity {
|
impl Interactivity {
|
||||||
pub fn layout(
|
pub fn layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
element_state: Option<InteractiveElementState>,
|
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
f: impl FnOnce(Style, &mut WindowContext) -> LayoutId,
|
f: impl FnOnce(
|
||||||
) -> (LayoutId, InteractiveElementState) {
|
Style,
|
||||||
let mut element_state = element_state.unwrap_or_default();
|
Option<(&GlobalElementId, &mut InteractiveElementState)>,
|
||||||
|
&mut WindowContext,
|
||||||
|
) -> LayoutId,
|
||||||
|
) -> LayoutId {
|
||||||
|
let style = self.compute_style(None, cx);
|
||||||
|
self.with_element_state(cx, |this, mut element_state, cx| {
|
||||||
|
if let Some((_, element_state)) = element_state.as_mut() {
|
||||||
|
// Ensure a focus handle exists if we're focusable.
|
||||||
|
// If there's an explicit focus handle we're tracking, use that. Otherwise
|
||||||
|
// create a new handle and store it in the element state, which lives for as
|
||||||
|
// as frames contain an element with this id.
|
||||||
|
if this.focusable {
|
||||||
|
this.tracked_focus_handle.get_or_insert_with(|| {
|
||||||
|
element_state
|
||||||
|
.focus_handle
|
||||||
|
.get_or_insert_with(|| cx.focus_handle())
|
||||||
|
.clone()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure we store a focus handle in our element state if we're focusable.
|
if let Some(scroll_handle) = this.scroll_handle.as_ref() {
|
||||||
// If there's an explicit focus handle we're tracking, use that. Otherwise
|
element_state.scroll_offset = Some(scroll_handle.0.borrow().offset.clone());
|
||||||
// create a new handle and store it in the element state, which lives for as
|
}
|
||||||
// as frames contain an element with this id.
|
}
|
||||||
if self.focusable {
|
|
||||||
element_state.focus_handle.get_or_insert_with(|| {
|
|
||||||
self.tracked_focus_handle
|
|
||||||
.clone()
|
|
||||||
.unwrap_or_else(|| cx.focus_handle())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(scroll_handle) = self.scroll_handle.as_ref() {
|
f(style, element_state, cx)
|
||||||
element_state.scroll_offset = Some(scroll_handle.0.borrow().offset.clone());
|
})
|
||||||
}
|
|
||||||
|
|
||||||
let style = self.compute_style(None, &mut element_state, cx);
|
|
||||||
let layout_id = f(style, cx);
|
|
||||||
(layout_id, element_state)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn paint(
|
pub fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
content_size: Size<Pixels>,
|
content_size: Size<Pixels>,
|
||||||
element_state: &mut InteractiveElementState,
|
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
f: impl FnOnce(Style, Point<Pixels>, &mut WindowContext),
|
f: impl FnOnce(
|
||||||
|
Style,
|
||||||
|
Option<(&GlobalElementId, &mut InteractiveElementState)>,
|
||||||
|
Point<Pixels>,
|
||||||
|
&mut WindowContext,
|
||||||
|
),
|
||||||
) {
|
) {
|
||||||
let style = self.compute_style(Some(bounds), element_state, cx);
|
let style = self.compute_style(Some(bounds), cx);
|
||||||
|
self.with_element_state(cx, |this, element_state, cx| {
|
||||||
|
this.paint_internal(style, bounds, content_size, element_state, cx, f)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paint_internal(
|
||||||
|
&mut self,
|
||||||
|
style: Style,
|
||||||
|
bounds: Bounds<Pixels>,
|
||||||
|
content_size: Size<Pixels>,
|
||||||
|
mut element_state: Option<(&GlobalElementId, &mut InteractiveElementState)>,
|
||||||
|
cx: &mut WindowContext,
|
||||||
|
f: impl FnOnce(
|
||||||
|
Style,
|
||||||
|
Option<(&GlobalElementId, &mut InteractiveElementState)>,
|
||||||
|
Point<Pixels>,
|
||||||
|
&mut WindowContext,
|
||||||
|
),
|
||||||
|
) {
|
||||||
if style.visibility == Visibility::Hidden {
|
if style.visibility == Visibility::Hidden {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1052,10 +1062,10 @@ impl Interactivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this element can be focused, register a mouse down listener
|
// If self element can be focused, register a mouse down listener
|
||||||
// that will automatically transfer focus when hitting the element.
|
// that will automatically transfer focus when hitting the element.
|
||||||
// This behavior can be suppressed by using `cx.prevent_default()`.
|
// This behavior can be suppressed by using `cx.prevent_default()`.
|
||||||
if let Some(focus_handle) = element_state.focus_handle.clone() {
|
if let Some(focus_handle) = self.tracked_focus_handle.clone() {
|
||||||
cx.on_mouse_event({
|
cx.on_mouse_event({
|
||||||
let interactive_bounds = interactive_bounds.clone();
|
let interactive_bounds = interactive_bounds.clone();
|
||||||
move |event: &MouseDownEvent, phase, cx| {
|
move |event: &MouseDownEvent, phase, cx| {
|
||||||
|
@ -1164,263 +1174,301 @@ impl Interactivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let click_listeners = mem::take(&mut self.click_listeners);
|
if let Some((global_element_id, element_state)) = element_state.as_mut() {
|
||||||
let mut drag_listener = mem::take(&mut self.drag_listener);
|
let click_listeners = mem::take(&mut self.click_listeners);
|
||||||
|
let mut drag_listener = mem::take(&mut self.drag_listener);
|
||||||
|
|
||||||
if !click_listeners.is_empty() || drag_listener.is_some() {
|
if !click_listeners.is_empty() || drag_listener.is_some() {
|
||||||
let pending_mouse_down = element_state
|
let global_element_id = global_element_id.clone();
|
||||||
.pending_mouse_down
|
if let Some(mouse_down) = element_state.pending_mouse_down.clone() {
|
||||||
.get_or_insert_with(Default::default)
|
if drag_listener.is_some() {
|
||||||
.clone();
|
let interactive_bounds = interactive_bounds.clone();
|
||||||
let mouse_down = pending_mouse_down.borrow().clone();
|
let global_element_id = global_element_id.clone();
|
||||||
if let Some(mouse_down) = mouse_down {
|
cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
|
||||||
if drag_listener.is_some() {
|
if cx.active_drag.is_some() {
|
||||||
let active_state = element_state
|
if phase == DispatchPhase::Capture {
|
||||||
.clicked_state
|
cx.notify();
|
||||||
.get_or_insert_with(Default::default)
|
}
|
||||||
.clone();
|
} else if phase == DispatchPhase::Bubble
|
||||||
let interactive_bounds = interactive_bounds.clone();
|
&& interactive_bounds.visibly_contains(&event.position, cx)
|
||||||
|
&& (event.position - mouse_down.position).magnitude()
|
||||||
|
> DRAG_THRESHOLD
|
||||||
|
{
|
||||||
|
let (drag_value, drag_listener) = drag_listener
|
||||||
|
.take()
|
||||||
|
.expect("The notify below should invalidate self callback");
|
||||||
|
|
||||||
cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
|
cx.with_element_state::<InteractiveElementState, _>(
|
||||||
if cx.active_drag.is_some() {
|
&global_element_id,
|
||||||
if phase == DispatchPhase::Capture {
|
|element_state, _cx| {
|
||||||
|
if let Some(element_state) = element_state {
|
||||||
|
element_state.clicked_state =
|
||||||
|
ElementClickedState::default();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let cursor_offset = event.position - bounds.origin;
|
||||||
|
let drag = (drag_listener)(drag_value.as_ref(), cx);
|
||||||
|
cx.active_drag = Some(AnyDrag {
|
||||||
|
view: drag,
|
||||||
|
value: drag_value,
|
||||||
|
cursor_offset,
|
||||||
|
});
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
cx.stop_propagation();
|
||||||
}
|
}
|
||||||
} else if phase == DispatchPhase::Bubble
|
});
|
||||||
&& interactive_bounds.visibly_contains(&event.position, cx)
|
}
|
||||||
&& (event.position - mouse_down.position).magnitude() > DRAG_THRESHOLD
|
|
||||||
{
|
|
||||||
let (drag_value, drag_listener) = drag_listener
|
|
||||||
.take()
|
|
||||||
.expect("The notify below should invalidate this callback");
|
|
||||||
|
|
||||||
*active_state.borrow_mut() = ElementClickedState::default();
|
let interactive_bounds = interactive_bounds.clone();
|
||||||
let cursor_offset = event.position - bounds.origin;
|
cx.on_mouse_event(move |event: &MouseUpEvent, phase, cx| {
|
||||||
let drag = (drag_listener)(drag_value.as_ref(), cx);
|
if phase == DispatchPhase::Bubble
|
||||||
cx.active_drag = Some(AnyDrag {
|
&& interactive_bounds.visibly_contains(&event.position, cx)
|
||||||
view: drag,
|
{
|
||||||
value: drag_value,
|
let mouse_click = ClickEvent {
|
||||||
cursor_offset,
|
down: mouse_down.clone(),
|
||||||
});
|
up: event.clone(),
|
||||||
|
};
|
||||||
|
for listener in &click_listeners {
|
||||||
|
listener(&mouse_click, cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cx.with_element_state::<InteractiveElementState, _>(
|
||||||
|
&global_element_id,
|
||||||
|
|element_state, _cx| {
|
||||||
|
if let Some(element_state) = element_state {
|
||||||
|
element_state.pending_mouse_down = None;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
cx.notify();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let interactive_bounds = interactive_bounds.clone();
|
||||||
|
cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
|
||||||
|
if phase == DispatchPhase::Bubble
|
||||||
|
&& event.button == MouseButton::Left
|
||||||
|
&& interactive_bounds.visibly_contains(&event.position, cx)
|
||||||
|
{
|
||||||
|
cx.with_element_state::<InteractiveElementState, _>(
|
||||||
|
&global_element_id,
|
||||||
|
|element_state, _cx| {
|
||||||
|
if let Some(element_state) = element_state {
|
||||||
|
element_state.pending_mouse_down = Some(event.clone());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(hover_listener) = self.hover_listener.take() {
|
||||||
|
let interactive_bounds = interactive_bounds.clone();
|
||||||
|
let global_element_id = global_element_id.clone();
|
||||||
|
cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
|
||||||
|
if phase != DispatchPhase::Bubble {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cx.with_element_state::<InteractiveElementState, _>(
|
||||||
|
&global_element_id,
|
||||||
|
|element_state, cx| {
|
||||||
|
if let Some(element_state) = element_state {
|
||||||
|
let is_hovered = interactive_bounds
|
||||||
|
.visibly_contains(&event.position, cx)
|
||||||
|
&& element_state.pending_mouse_down.is_none();
|
||||||
|
if is_hovered != element_state.hover_state {
|
||||||
|
element_state.hover_state = is_hovered;
|
||||||
|
hover_listener(&is_hovered, cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(tooltip_builder) = self.tooltip_builder.take() {
|
||||||
|
let interactive_bounds = interactive_bounds.clone();
|
||||||
|
|
||||||
|
cx.on_mouse_event({
|
||||||
|
let global_element_id = global_element_id.clone();
|
||||||
|
move |event: &MouseMoveEvent, phase, cx| {
|
||||||
|
cx.with_element_state::<InteractiveElementState, _>(
|
||||||
|
&global_element_id,
|
||||||
|
|element_state, cx| {
|
||||||
|
if let Some(element_state) = element_state {
|
||||||
|
let is_hovered = interactive_bounds
|
||||||
|
.visibly_contains(&event.position, cx)
|
||||||
|
&& element_state.pending_mouse_down.is_none();
|
||||||
|
if !is_hovered {
|
||||||
|
element_state.active_tooltip = None;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if phase != DispatchPhase::Bubble {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if element_state.active_tooltip.is_none() {
|
||||||
|
let task = cx.spawn({
|
||||||
|
let global_element_id = global_element_id.clone();
|
||||||
|
let tooltip_builder = tooltip_builder.clone();
|
||||||
|
move |mut cx| async move {
|
||||||
|
cx.background_executor().timer(TOOLTIP_DELAY).await;
|
||||||
|
cx.update(|_, cx| {
|
||||||
|
cx.with_element_state::<InteractiveElementState, _>(
|
||||||
|
&global_element_id,
|
||||||
|
|element_state, cx| {
|
||||||
|
if let Some(element_state) = element_state {
|
||||||
|
element_state.active_tooltip = Some(
|
||||||
|
ActiveTooltip {
|
||||||
|
tooltip: Some(AnyTooltip {
|
||||||
|
view: tooltip_builder(cx),
|
||||||
|
cursor_offset: cx.mouse_position(),
|
||||||
|
}),
|
||||||
|
_task: None,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
element_state.active_tooltip = Some(ActiveTooltip {
|
||||||
|
tooltip: None,
|
||||||
|
_task: Some(task),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let global_element_id = global_element_id.clone();
|
||||||
|
cx.on_mouse_event(move |_: &MouseDownEvent, _, cx| {
|
||||||
|
cx.with_element_state::<InteractiveElementState, _>(
|
||||||
|
&global_element_id,
|
||||||
|
|element_state, _cx| {
|
||||||
|
if let Some(element_state) = element_state {
|
||||||
|
element_state.active_tooltip = None;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(active_tooltip) = element_state.active_tooltip.as_ref() {
|
||||||
|
if active_tooltip.tooltip.is_some() {
|
||||||
|
cx.active_tooltip = active_tooltip.tooltip.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if element_state.clicked_state.is_clicked() {
|
||||||
|
let global_element_id = global_element_id.clone();
|
||||||
|
cx.on_mouse_event(move |_: &MouseUpEvent, phase, cx| {
|
||||||
|
if phase == DispatchPhase::Capture {
|
||||||
|
cx.with_element_state::<InteractiveElementState, _>(
|
||||||
|
&global_element_id,
|
||||||
|
|element_state, cx| {
|
||||||
|
if let Some(element_state) = element_state {
|
||||||
|
element_state.clicked_state = ElementClickedState::default();
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let active_group_bounds = self
|
||||||
|
.group_active_style
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|group_active| GroupBounds::get(&group_active.group, cx));
|
||||||
|
let interactive_bounds = interactive_bounds.clone();
|
||||||
|
let global_element_id = global_element_id.clone();
|
||||||
|
cx.on_mouse_event(move |down: &MouseDownEvent, phase, cx| {
|
||||||
|
if phase == DispatchPhase::Bubble && !cx.default_prevented() {
|
||||||
|
let group = active_group_bounds
|
||||||
|
.map_or(false, |bounds| bounds.contains(&down.position));
|
||||||
|
let element = interactive_bounds.visibly_contains(&down.position, cx);
|
||||||
|
if group || element {
|
||||||
|
cx.with_element_state::<InteractiveElementState, _>(
|
||||||
|
&global_element_id,
|
||||||
|
|element_state, cx| {
|
||||||
|
if let Some(element_state) = element_state {
|
||||||
|
element_state.clicked_state =
|
||||||
|
ElementClickedState { group, element };
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let overflow = style.overflow;
|
||||||
|
if overflow.x == Overflow::Scroll || overflow.y == Overflow::Scroll {
|
||||||
|
if let Some(scroll_handle) = &self.scroll_handle {
|
||||||
|
scroll_handle.0.borrow_mut().overflow = overflow;
|
||||||
|
}
|
||||||
|
|
||||||
|
let scroll_offset = element_state
|
||||||
|
.scroll_offset
|
||||||
|
.get_or_insert_with(Rc::default)
|
||||||
|
.clone();
|
||||||
|
let line_height = cx.line_height();
|
||||||
|
let scroll_max = (content_size - bounds.size).max(&Size::default());
|
||||||
|
let interactive_bounds = interactive_bounds.clone();
|
||||||
|
|
||||||
|
cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
|
||||||
|
if phase == DispatchPhase::Bubble
|
||||||
|
&& interactive_bounds.visibly_contains(&event.position, cx)
|
||||||
|
{
|
||||||
|
let mut scroll_offset = scroll_offset.borrow_mut();
|
||||||
|
let old_scroll_offset = *scroll_offset;
|
||||||
|
let delta = event.delta.pixel_delta(line_height);
|
||||||
|
|
||||||
|
if overflow.x == Overflow::Scroll {
|
||||||
|
scroll_offset.x =
|
||||||
|
(scroll_offset.x + delta.x).clamp(-scroll_max.width, px(0.));
|
||||||
|
}
|
||||||
|
|
||||||
|
if overflow.y == Overflow::Scroll {
|
||||||
|
scroll_offset.y =
|
||||||
|
(scroll_offset.y + delta.y).clamp(-scroll_max.height, px(0.));
|
||||||
|
}
|
||||||
|
|
||||||
|
if *scroll_offset != old_scroll_offset {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
cx.stop_propagation();
|
cx.stop_propagation();
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let interactive_bounds = interactive_bounds.clone();
|
|
||||||
cx.on_mouse_event(move |event: &MouseUpEvent, phase, cx| {
|
|
||||||
if phase == DispatchPhase::Bubble
|
|
||||||
&& interactive_bounds.visibly_contains(&event.position, cx)
|
|
||||||
{
|
|
||||||
let mouse_click = ClickEvent {
|
|
||||||
down: mouse_down.clone(),
|
|
||||||
up: event.clone(),
|
|
||||||
};
|
|
||||||
for listener in &click_listeners {
|
|
||||||
listener(&mouse_click, cx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*pending_mouse_down.borrow_mut() = None;
|
|
||||||
cx.notify();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
let interactive_bounds = interactive_bounds.clone();
|
|
||||||
cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
|
|
||||||
if phase == DispatchPhase::Bubble
|
|
||||||
&& event.button == MouseButton::Left
|
|
||||||
&& interactive_bounds.visibly_contains(&event.position, cx)
|
|
||||||
{
|
|
||||||
*pending_mouse_down.borrow_mut() = Some(event.clone());
|
|
||||||
cx.notify();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(hover_listener) = self.hover_listener.take() {
|
|
||||||
let was_hovered = element_state
|
|
||||||
.hover_state
|
|
||||||
.get_or_insert_with(Default::default)
|
|
||||||
.clone();
|
|
||||||
let has_mouse_down = element_state
|
|
||||||
.pending_mouse_down
|
|
||||||
.get_or_insert_with(Default::default)
|
|
||||||
.clone();
|
|
||||||
let interactive_bounds = interactive_bounds.clone();
|
|
||||||
|
|
||||||
cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
|
|
||||||
if phase != DispatchPhase::Bubble {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let is_hovered = interactive_bounds.visibly_contains(&event.position, cx)
|
|
||||||
&& has_mouse_down.borrow().is_none();
|
|
||||||
let mut was_hovered = was_hovered.borrow_mut();
|
|
||||||
|
|
||||||
if is_hovered != was_hovered.clone() {
|
|
||||||
*was_hovered = is_hovered;
|
|
||||||
drop(was_hovered);
|
|
||||||
|
|
||||||
hover_listener(&is_hovered, cx);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(tooltip_builder) = self.tooltip_builder.take() {
|
|
||||||
let active_tooltip = element_state
|
|
||||||
.active_tooltip
|
|
||||||
.get_or_insert_with(Default::default)
|
|
||||||
.clone();
|
|
||||||
let pending_mouse_down = element_state
|
|
||||||
.pending_mouse_down
|
|
||||||
.get_or_insert_with(Default::default)
|
|
||||||
.clone();
|
|
||||||
let interactive_bounds = interactive_bounds.clone();
|
|
||||||
|
|
||||||
cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
|
|
||||||
let is_hovered = interactive_bounds.visibly_contains(&event.position, cx)
|
|
||||||
&& pending_mouse_down.borrow().is_none();
|
|
||||||
if !is_hovered {
|
|
||||||
active_tooltip.borrow_mut().take();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if phase != DispatchPhase::Bubble {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if active_tooltip.borrow().is_none() {
|
|
||||||
let task = cx.spawn({
|
|
||||||
let active_tooltip = active_tooltip.clone();
|
|
||||||
let tooltip_builder = tooltip_builder.clone();
|
|
||||||
|
|
||||||
move |mut cx| async move {
|
|
||||||
cx.background_executor().timer(TOOLTIP_DELAY).await;
|
|
||||||
cx.update(|_, cx| {
|
|
||||||
active_tooltip.borrow_mut().replace(ActiveTooltip {
|
|
||||||
tooltip: Some(AnyTooltip {
|
|
||||||
view: tooltip_builder(cx),
|
|
||||||
cursor_offset: cx.mouse_position(),
|
|
||||||
}),
|
|
||||||
_task: None,
|
|
||||||
});
|
|
||||||
cx.notify();
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
active_tooltip.borrow_mut().replace(ActiveTooltip {
|
|
||||||
tooltip: None,
|
|
||||||
_task: Some(task),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let active_tooltip = element_state
|
|
||||||
.active_tooltip
|
|
||||||
.get_or_insert_with(Default::default)
|
|
||||||
.clone();
|
|
||||||
cx.on_mouse_event(move |_: &MouseDownEvent, _, _| {
|
|
||||||
active_tooltip.borrow_mut().take();
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(active_tooltip) = element_state
|
|
||||||
.active_tooltip
|
|
||||||
.get_or_insert_with(Default::default)
|
|
||||||
.borrow()
|
|
||||||
.as_ref()
|
|
||||||
{
|
|
||||||
if active_tooltip.tooltip.is_some() {
|
|
||||||
cx.active_tooltip = active_tooltip.tooltip.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let active_state = element_state
|
|
||||||
.clicked_state
|
|
||||||
.get_or_insert_with(Default::default)
|
|
||||||
.clone();
|
|
||||||
if active_state.borrow().is_clicked() {
|
|
||||||
cx.on_mouse_event(move |_: &MouseUpEvent, phase, cx| {
|
|
||||||
if phase == DispatchPhase::Capture {
|
|
||||||
*active_state.borrow_mut() = ElementClickedState::default();
|
|
||||||
cx.notify();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
let active_group_bounds = self
|
|
||||||
.group_active_style
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|group_active| GroupBounds::get(&group_active.group, cx));
|
|
||||||
let interactive_bounds = interactive_bounds.clone();
|
|
||||||
cx.on_mouse_event(move |down: &MouseDownEvent, phase, cx| {
|
|
||||||
if phase == DispatchPhase::Bubble && !cx.default_prevented() {
|
|
||||||
let group =
|
|
||||||
active_group_bounds.map_or(false, |bounds| bounds.contains(&down.position));
|
|
||||||
let element = interactive_bounds.visibly_contains(&down.position, cx);
|
|
||||||
if group || element {
|
|
||||||
*active_state.borrow_mut() = ElementClickedState { group, element };
|
|
||||||
cx.notify();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let overflow = style.overflow;
|
|
||||||
if overflow.x == Overflow::Scroll || overflow.y == Overflow::Scroll {
|
|
||||||
if let Some(scroll_handle) = &self.scroll_handle {
|
|
||||||
scroll_handle.0.borrow_mut().overflow = overflow;
|
|
||||||
}
|
|
||||||
|
|
||||||
let scroll_offset = element_state
|
|
||||||
.scroll_offset
|
|
||||||
.get_or_insert_with(Rc::default)
|
|
||||||
.clone();
|
|
||||||
let line_height = cx.line_height();
|
|
||||||
let scroll_max = (content_size - bounds.size).max(&Size::default());
|
|
||||||
let interactive_bounds = interactive_bounds.clone();
|
|
||||||
|
|
||||||
cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
|
|
||||||
if phase == DispatchPhase::Bubble
|
|
||||||
&& interactive_bounds.visibly_contains(&event.position, cx)
|
|
||||||
{
|
|
||||||
let mut scroll_offset = scroll_offset.borrow_mut();
|
|
||||||
let old_scroll_offset = *scroll_offset;
|
|
||||||
let delta = event.delta.pixel_delta(line_height);
|
|
||||||
|
|
||||||
if overflow.x == Overflow::Scroll {
|
|
||||||
scroll_offset.x =
|
|
||||||
(scroll_offset.x + delta.x).clamp(-scroll_max.width, px(0.));
|
|
||||||
}
|
|
||||||
|
|
||||||
if overflow.y == Overflow::Scroll {
|
|
||||||
scroll_offset.y =
|
|
||||||
(scroll_offset.y + delta.y).clamp(-scroll_max.height, px(0.));
|
|
||||||
}
|
|
||||||
|
|
||||||
if *scroll_offset != old_scroll_offset {
|
|
||||||
cx.notify();
|
|
||||||
cx.stop_propagation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(group) = self.group.clone() {
|
if let Some(group) = self.group.clone() {
|
||||||
GroupBounds::push(group, bounds, cx);
|
GroupBounds::push(group, bounds, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
let scroll_offset = element_state
|
let scroll_offset = element_state
|
||||||
.scroll_offset
|
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|scroll_offset| *scroll_offset.borrow());
|
.and_then(|(_, element_state)| Some(*element_state.scroll_offset.as_ref()?.borrow()));
|
||||||
|
|
||||||
let key_down_listeners = mem::take(&mut self.key_down_listeners);
|
let key_down_listeners = mem::take(&mut self.key_down_listeners);
|
||||||
let key_up_listeners = mem::take(&mut self.key_up_listeners);
|
let key_up_listeners = mem::take(&mut self.key_up_listeners);
|
||||||
let action_listeners = mem::take(&mut self.action_listeners);
|
let action_listeners = mem::take(&mut self.action_listeners);
|
||||||
cx.with_key_dispatch(
|
cx.with_key_dispatch(
|
||||||
self.key_context.clone(),
|
self.key_context.clone(),
|
||||||
element_state.focus_handle.clone(),
|
self.tracked_focus_handle.clone(),
|
||||||
|_, cx| {
|
|_, cx| {
|
||||||
for listener in key_down_listeners {
|
for listener in key_down_listeners {
|
||||||
cx.on_key_event(move |event: &KeyDownEvent, phase, cx| {
|
cx.on_key_event(move |event: &KeyDownEvent, phase, cx| {
|
||||||
|
@ -1438,7 +1486,7 @@ impl Interactivity {
|
||||||
cx.on_action(action_type, listener)
|
cx.on_action(action_type, listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
f(style, scroll_offset.unwrap_or_default(), cx)
|
f(style, element_state, scroll_offset.unwrap_or_default(), cx)
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1447,10 +1495,31 @@ impl Interactivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn with_element_state<R>(
|
||||||
|
&mut self,
|
||||||
|
cx: &mut WindowContext,
|
||||||
|
f: impl FnOnce(
|
||||||
|
&mut Self,
|
||||||
|
Option<(&GlobalElementId, &mut InteractiveElementState)>,
|
||||||
|
&mut WindowContext,
|
||||||
|
) -> R,
|
||||||
|
) -> R {
|
||||||
|
cx.with_element_id(self.element_id.clone(), |global_element_id, cx| {
|
||||||
|
if let Some(global_element_id) = global_element_id {
|
||||||
|
cx.with_element_state(&global_element_id, |element_state, cx| {
|
||||||
|
let mut element_state = element_state
|
||||||
|
.get_or_insert_with(|| Box::<InteractiveElementState>::default());
|
||||||
|
f(self, Some((&global_element_id, &mut element_state)), cx)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
f(self, None, cx)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn compute_style(
|
pub fn compute_style(
|
||||||
&self,
|
&mut self,
|
||||||
bounds: Option<Bounds<Pixels>>,
|
bounds: Option<Bounds<Pixels>>,
|
||||||
element_state: &mut InteractiveElementState,
|
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) -> Style {
|
) -> Style {
|
||||||
let mut style = Style::default();
|
let mut style = Style::default();
|
||||||
|
@ -1521,21 +1590,21 @@ impl Interactivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let clicked_state = element_state
|
self.with_element_state(cx, |this, element_state, _cx| {
|
||||||
.clicked_state
|
if let Some((_, element_state)) = element_state {
|
||||||
.get_or_insert_with(Default::default)
|
if element_state.clicked_state.group {
|
||||||
.borrow();
|
if let Some(group) = this.group_active_style.as_ref() {
|
||||||
if clicked_state.group {
|
style.refine(&group.style)
|
||||||
if let Some(group) = self.group_active_style.as_ref() {
|
}
|
||||||
style.refine(&group.style)
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(active_style) = self.active_style.as_ref() {
|
if let Some(active_style) = this.active_style.as_ref() {
|
||||||
if clicked_state.element {
|
if element_state.clicked_state.element {
|
||||||
style.refine(active_style)
|
style.refine(active_style)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
style
|
style
|
||||||
|
@ -1583,11 +1652,11 @@ impl Default for Interactivity {
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct InteractiveElementState {
|
pub struct InteractiveElementState {
|
||||||
pub focus_handle: Option<FocusHandle>,
|
pub focus_handle: Option<FocusHandle>,
|
||||||
pub clicked_state: Option<Rc<RefCell<ElementClickedState>>>,
|
pub clicked_state: ElementClickedState,
|
||||||
pub hover_state: Option<Rc<RefCell<bool>>>,
|
pub hover_state: bool,
|
||||||
pub pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
|
pub pending_mouse_down: Option<MouseDownEvent>,
|
||||||
pub scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
|
pub scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
|
||||||
pub active_tooltip: Option<Rc<RefCell<Option<ActiveTooltip>>>>,
|
pub active_tooltip: Option<ActiveTooltip>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ActiveTooltip {
|
pub struct ActiveTooltip {
|
||||||
|
@ -1663,17 +1732,18 @@ impl<E> Element for Focusable<E>
|
||||||
where
|
where
|
||||||
E: Element,
|
E: Element,
|
||||||
{
|
{
|
||||||
type State = E::State;
|
type FrameState = E::FrameState;
|
||||||
|
|
||||||
fn layout(
|
fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
|
||||||
&mut self,
|
self.element.layout(cx)
|
||||||
state: Option<Self::State>,
|
|
||||||
cx: &mut WindowContext,
|
|
||||||
) -> (LayoutId, Self::State) {
|
|
||||||
self.element.layout(state, cx)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
|
fn paint(
|
||||||
|
&mut self,
|
||||||
|
bounds: Bounds<Pixels>,
|
||||||
|
state: &mut Self::FrameState,
|
||||||
|
cx: &mut WindowContext,
|
||||||
|
) {
|
||||||
self.element.paint(bounds, state, cx)
|
self.element.paint(bounds, state, cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1737,17 +1807,18 @@ impl<E> Element for Stateful<E>
|
||||||
where
|
where
|
||||||
E: Element,
|
E: Element,
|
||||||
{
|
{
|
||||||
type State = E::State;
|
type FrameState = E::FrameState;
|
||||||
|
|
||||||
fn layout(
|
fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
|
||||||
&mut self,
|
self.element.layout(cx)
|
||||||
state: Option<Self::State>,
|
|
||||||
cx: &mut WindowContext,
|
|
||||||
) -> (LayoutId, Self::State) {
|
|
||||||
self.element.layout(state, cx)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
|
fn paint(
|
||||||
|
&mut self,
|
||||||
|
bounds: Bounds<Pixels>,
|
||||||
|
state: &mut Self::FrameState,
|
||||||
|
cx: &mut WindowContext,
|
||||||
|
) {
|
||||||
self.element.paint(bounds, state, cx)
|
self.element.paint(bounds, state, cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
point, size, Bounds, DevicePixels, Element, ImageData, InteractiveElement,
|
point, size, Bounds, DevicePixels, Element, ImageData, InteractiveElement, Interactivity,
|
||||||
InteractiveElementState, Interactivity, IntoElement, LayoutId, Pixels, SharedString, Size,
|
IntoElement, LayoutId, Pixels, SharedString, Size, StyleRefinement, Styled, WindowContext,
|
||||||
StyleRefinement, Styled, WindowContext,
|
|
||||||
};
|
};
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use media::core_video::CVImageBuffer;
|
use media::core_video::CVImageBuffer;
|
||||||
|
@ -69,30 +68,19 @@ impl Img {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for Img {
|
impl Element for Img {
|
||||||
type State = InteractiveElementState;
|
type FrameState = ();
|
||||||
|
|
||||||
fn layout(
|
fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
|
||||||
&mut self,
|
let layout_id = self
|
||||||
element_state: Option<Self::State>,
|
.interactivity
|
||||||
cx: &mut WindowContext,
|
.layout(cx, |style, _, cx| cx.request_layout(&style, []));
|
||||||
) -> (LayoutId, Self::State) {
|
(layout_id, ())
|
||||||
self.interactivity
|
|
||||||
.layout(element_state, cx, |style, cx| cx.request_layout(&style, []))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(
|
fn paint(&mut self, bounds: Bounds<Pixels>, _: &mut Self::FrameState, cx: &mut WindowContext) {
|
||||||
&mut self,
|
|
||||||
bounds: Bounds<Pixels>,
|
|
||||||
element_state: &mut Self::State,
|
|
||||||
cx: &mut WindowContext,
|
|
||||||
) {
|
|
||||||
let source = self.source.clone();
|
let source = self.source.clone();
|
||||||
self.interactivity.paint(
|
self.interactivity
|
||||||
bounds,
|
.paint(bounds, bounds.size, cx, |style, _, _, cx| {
|
||||||
bounds.size,
|
|
||||||
element_state,
|
|
||||||
cx,
|
|
||||||
|style, _scroll_offset, cx| {
|
|
||||||
let corner_radii = style.corner_radii.to_pixels(bounds.size, cx.rem_size());
|
let corner_radii = style.corner_radii.to_pixels(bounds.size, cx.rem_size());
|
||||||
cx.with_z_index(1, |cx| {
|
cx.with_z_index(1, |cx| {
|
||||||
match source {
|
match source {
|
||||||
|
@ -130,8 +118,7 @@ impl Element for Img {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -300,13 +300,9 @@ pub struct ListOffset {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for List {
|
impl Element for List {
|
||||||
type State = ();
|
type FrameState = ();
|
||||||
|
|
||||||
fn layout(
|
fn layout(&mut self, cx: &mut crate::WindowContext) -> (crate::LayoutId, Self::FrameState) {
|
||||||
&mut self,
|
|
||||||
_state: Option<Self::State>,
|
|
||||||
cx: &mut crate::WindowContext,
|
|
||||||
) -> (crate::LayoutId, Self::State) {
|
|
||||||
let mut style = Style::default();
|
let mut style = Style::default();
|
||||||
style.refine(&self.style);
|
style.refine(&self.style);
|
||||||
let layout_id = cx.with_text_style(style.text_style().cloned(), |cx| {
|
let layout_id = cx.with_text_style(style.text_style().cloned(), |cx| {
|
||||||
|
@ -318,7 +314,7 @@ impl Element for List {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: crate::Bounds<crate::Pixels>,
|
bounds: crate::Bounds<crate::Pixels>,
|
||||||
_state: &mut Self::State,
|
_state: &mut Self::FrameState,
|
||||||
cx: &mut crate::WindowContext,
|
cx: &mut crate::WindowContext,
|
||||||
) {
|
) {
|
||||||
let state = &mut *self.state.0.borrow_mut();
|
let state = &mut *self.state.0.borrow_mut();
|
||||||
|
|
|
@ -58,13 +58,9 @@ impl ParentElement for Overlay {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for Overlay {
|
impl Element for Overlay {
|
||||||
type State = OverlayState;
|
type FrameState = OverlayState;
|
||||||
|
|
||||||
fn layout(
|
fn layout(&mut self, cx: &mut WindowContext) -> (crate::LayoutId, Self::FrameState) {
|
||||||
&mut self,
|
|
||||||
_: Option<Self::State>,
|
|
||||||
cx: &mut WindowContext,
|
|
||||||
) -> (crate::LayoutId, Self::State) {
|
|
||||||
let child_layout_ids = self
|
let child_layout_ids = self
|
||||||
.children
|
.children
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
|
@ -83,7 +79,7 @@ impl Element for Overlay {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: crate::Bounds<crate::Pixels>,
|
bounds: crate::Bounds<crate::Pixels>,
|
||||||
element_state: &mut Self::State,
|
element_state: &mut Self::FrameState,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) {
|
) {
|
||||||
if element_state.child_layout_ids.is_empty() {
|
if element_state.child_layout_ids.is_empty() {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
Bounds, Element, ElementId, InteractiveElement, InteractiveElementState, Interactivity,
|
Bounds, Element, ElementId, InteractiveElement, Interactivity, IntoElement, LayoutId, Pixels,
|
||||||
IntoElement, LayoutId, Pixels, SharedString, StyleRefinement, Styled, WindowContext,
|
SharedString, StyleRefinement, Styled, WindowContext,
|
||||||
};
|
};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
|
@ -24,28 +24,21 @@ impl Svg {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for Svg {
|
impl Element for Svg {
|
||||||
type State = InteractiveElementState;
|
type FrameState = ();
|
||||||
|
|
||||||
fn layout(
|
fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
|
||||||
&mut self,
|
let layout_id = self
|
||||||
element_state: Option<Self::State>,
|
.interactivity
|
||||||
cx: &mut WindowContext,
|
.layout(cx, |style, _, cx| cx.request_layout(&style, None));
|
||||||
) -> (LayoutId, Self::State) {
|
(layout_id, ())
|
||||||
self.interactivity.layout(element_state, cx, |style, cx| {
|
|
||||||
cx.request_layout(&style, None)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(
|
fn paint(&mut self, bounds: Bounds<Pixels>, _: &mut Self::FrameState, cx: &mut WindowContext)
|
||||||
&mut self,
|
where
|
||||||
bounds: Bounds<Pixels>,
|
|
||||||
element_state: &mut Self::State,
|
|
||||||
cx: &mut WindowContext,
|
|
||||||
) where
|
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
self.interactivity
|
self.interactivity
|
||||||
.paint(bounds, bounds.size, element_state, cx, |style, _, cx| {
|
.paint(bounds, bounds.size, cx, |style, _, _, cx| {
|
||||||
if let Some((path, color)) = self.path.as_ref().zip(style.text.color) {
|
if let Some((path, color)) = self.path.as_ref().zip(style.text.color) {
|
||||||
cx.paint_svg(bounds, path.clone(), color).log_err();
|
cx.paint_svg(bounds, path.clone(), color).log_err();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,18 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
Bounds, DispatchPhase, Element, ElementId, HighlightStyle, IntoElement, LayoutId,
|
BorrowWindow, Bounds, DispatchPhase, Element, ElementId, HighlightStyle, IntoElement, LayoutId,
|
||||||
MouseDownEvent, MouseUpEvent, Pixels, Point, SharedString, Size, TextRun, TextStyle,
|
MouseDownEvent, MouseUpEvent, Pixels, Point, SharedString, Size, TextRun, TextStyle,
|
||||||
WhiteSpace, WindowContext, WrappedLine,
|
WhiteSpace, WindowContext, WrappedLine,
|
||||||
};
|
};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use parking_lot::{Mutex, MutexGuard};
|
use parking_lot::{Mutex, MutexGuard};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::{cell::Cell, mem, ops::Range, rc::Rc, sync::Arc};
|
use std::{mem, ops::Range, sync::Arc};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
impl Element for &'static str {
|
impl Element for &'static str {
|
||||||
type State = TextState;
|
type FrameState = TextState;
|
||||||
|
|
||||||
fn layout(
|
fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
|
||||||
&mut self,
|
|
||||||
_: Option<Self::State>,
|
|
||||||
cx: &mut WindowContext,
|
|
||||||
) -> (LayoutId, Self::State) {
|
|
||||||
let mut state = TextState::default();
|
let mut state = TextState::default();
|
||||||
let layout_id = state.layout(SharedString::from(*self), None, cx);
|
let layout_id = state.layout(SharedString::from(*self), None, cx);
|
||||||
(layout_id, state)
|
(layout_id, state)
|
||||||
|
@ -40,13 +36,9 @@ impl IntoElement for &'static str {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for SharedString {
|
impl Element for SharedString {
|
||||||
type State = TextState;
|
type FrameState = TextState;
|
||||||
|
|
||||||
fn layout(
|
fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
|
||||||
&mut self,
|
|
||||||
_: Option<Self::State>,
|
|
||||||
cx: &mut WindowContext,
|
|
||||||
) -> (LayoutId, Self::State) {
|
|
||||||
let mut state = TextState::default();
|
let mut state = TextState::default();
|
||||||
let layout_id = state.layout(self.clone(), None, cx);
|
let layout_id = state.layout(self.clone(), None, cx);
|
||||||
(layout_id, state)
|
(layout_id, state)
|
||||||
|
@ -116,19 +108,20 @@ impl StyledText {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for StyledText {
|
impl Element for StyledText {
|
||||||
type State = TextState;
|
type FrameState = TextState;
|
||||||
|
|
||||||
fn layout(
|
fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
|
||||||
&mut self,
|
|
||||||
_: Option<Self::State>,
|
|
||||||
cx: &mut WindowContext,
|
|
||||||
) -> (LayoutId, Self::State) {
|
|
||||||
let mut state = TextState::default();
|
let mut state = TextState::default();
|
||||||
let layout_id = state.layout(self.text.clone(), self.runs.take(), cx);
|
let layout_id = state.layout(self.text.clone(), self.runs.take(), cx);
|
||||||
(layout_id, state)
|
(layout_id, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
|
fn paint(
|
||||||
|
&mut self,
|
||||||
|
bounds: Bounds<Pixels>,
|
||||||
|
state: &mut Self::FrameState,
|
||||||
|
cx: &mut WindowContext,
|
||||||
|
) {
|
||||||
state.paint(bounds, &self.text, cx)
|
state.paint(bounds, &self.text, cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -295,9 +288,9 @@ struct InteractiveTextClickEvent {
|
||||||
mouse_up_index: usize,
|
mouse_up_index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
pub struct InteractiveTextState {
|
pub struct InteractiveTextState {
|
||||||
text_state: TextState,
|
mouse_down_index: Option<usize>,
|
||||||
mouse_down_index: Rc<Cell<Option<usize>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InteractiveText {
|
impl InteractiveText {
|
||||||
|
@ -329,86 +322,93 @@ impl InteractiveText {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for InteractiveText {
|
impl Element for InteractiveText {
|
||||||
type State = InteractiveTextState;
|
type FrameState = TextState;
|
||||||
|
|
||||||
fn layout(
|
fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
|
||||||
&mut self,
|
self.text.layout(cx)
|
||||||
state: Option<Self::State>,
|
|
||||||
cx: &mut WindowContext,
|
|
||||||
) -> (LayoutId, Self::State) {
|
|
||||||
if let Some(InteractiveTextState {
|
|
||||||
mouse_down_index, ..
|
|
||||||
}) = state
|
|
||||||
{
|
|
||||||
let (layout_id, text_state) = self.text.layout(None, cx);
|
|
||||||
let element_state = InteractiveTextState {
|
|
||||||
text_state,
|
|
||||||
mouse_down_index,
|
|
||||||
};
|
|
||||||
(layout_id, element_state)
|
|
||||||
} else {
|
|
||||||
let (layout_id, text_state) = self.text.layout(None, cx);
|
|
||||||
let element_state = InteractiveTextState {
|
|
||||||
text_state,
|
|
||||||
mouse_down_index: Rc::default(),
|
|
||||||
};
|
|
||||||
(layout_id, element_state)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
|
fn paint(
|
||||||
if let Some(click_listener) = self.click_listener.take() {
|
&mut self,
|
||||||
if let Some(ix) = state
|
bounds: Bounds<Pixels>,
|
||||||
.text_state
|
text_state: &mut Self::FrameState,
|
||||||
.index_for_position(bounds, cx.mouse_position())
|
cx: &mut WindowContext,
|
||||||
{
|
) {
|
||||||
if self
|
cx.with_element_id(Some(self.element_id.clone()), |global_element_id, cx| {
|
||||||
.clickable_ranges
|
let global_element_id = global_element_id.unwrap();
|
||||||
.iter()
|
cx.with_element_state::<InteractiveTextState, _>(
|
||||||
.any(|range| range.contains(&ix))
|
&global_element_id,
|
||||||
{
|
|element_state, cx| {
|
||||||
cx.set_cursor_style(crate::CursorStyle::PointingHand)
|
let element_state = element_state.get_or_insert_with(Default::default);
|
||||||
}
|
if let Some(click_listener) = self.click_listener.take() {
|
||||||
}
|
if let Some(ix) = text_state.index_for_position(bounds, cx.mouse_position())
|
||||||
|
|
||||||
let text_state = state.text_state.clone();
|
|
||||||
let mouse_down = state.mouse_down_index.clone();
|
|
||||||
if let Some(mouse_down_index) = mouse_down.get() {
|
|
||||||
let clickable_ranges = mem::take(&mut self.clickable_ranges);
|
|
||||||
cx.on_mouse_event(move |event: &MouseUpEvent, phase, cx| {
|
|
||||||
if phase == DispatchPhase::Bubble {
|
|
||||||
if let Some(mouse_up_index) =
|
|
||||||
text_state.index_for_position(bounds, event.position)
|
|
||||||
{
|
{
|
||||||
click_listener(
|
if self
|
||||||
&clickable_ranges,
|
.clickable_ranges
|
||||||
InteractiveTextClickEvent {
|
.iter()
|
||||||
mouse_down_index,
|
.any(|range| range.contains(&ix))
|
||||||
mouse_up_index,
|
{
|
||||||
},
|
cx.set_cursor_style(crate::CursorStyle::PointingHand)
|
||||||
cx,
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mouse_down.take();
|
let text_state = text_state.clone();
|
||||||
cx.notify();
|
if let Some(mouse_down_index) = element_state.mouse_down_index {
|
||||||
}
|
let global_element_id = global_element_id.clone();
|
||||||
});
|
let clickable_ranges = mem::take(&mut self.clickable_ranges);
|
||||||
} else {
|
cx.on_mouse_event(move |event: &MouseUpEvent, phase, cx| {
|
||||||
cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
|
if phase == DispatchPhase::Bubble {
|
||||||
if phase == DispatchPhase::Bubble {
|
if let Some(mouse_up_index) =
|
||||||
if let Some(mouse_down_index) =
|
text_state.index_for_position(bounds, event.position)
|
||||||
text_state.index_for_position(bounds, event.position)
|
{
|
||||||
{
|
click_listener(
|
||||||
mouse_down.set(Some(mouse_down_index));
|
&clickable_ranges,
|
||||||
cx.notify();
|
InteractiveTextClickEvent {
|
||||||
|
mouse_down_index,
|
||||||
|
mouse_up_index,
|
||||||
|
},
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
cx.with_element_state::<InteractiveTextState, _>(
|
||||||
|
&global_element_id,
|
||||||
|
|element_state, cx| {
|
||||||
|
if let Some(element_state) = element_state {
|
||||||
|
element_state.mouse_down_index = None;
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let global_element_id = global_element_id.clone();
|
||||||
|
cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
|
||||||
|
if phase == DispatchPhase::Bubble {
|
||||||
|
if let Some(mouse_down_index) =
|
||||||
|
text_state.index_for_position(bounds, event.position)
|
||||||
|
{
|
||||||
|
cx.with_element_state::<InteractiveTextState, _>(
|
||||||
|
&global_element_id,
|
||||||
|
|element_state, cx| {
|
||||||
|
if let Some(element_state) = element_state {
|
||||||
|
element_state.mouse_down_index =
|
||||||
|
Some(mouse_down_index);
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
}
|
)
|
||||||
}
|
});
|
||||||
|
|
||||||
self.text.paint(bounds, &mut state.text_state, cx)
|
self.text.paint(bounds, text_state, cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, ContentMask, Element,
|
point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, ContentMask, Element,
|
||||||
ElementId, InteractiveElement, InteractiveElementState, Interactivity, IntoElement, LayoutId,
|
ElementId, InteractiveElement, Interactivity, IntoElement, LayoutId, Pixels, Point, Render,
|
||||||
Pixels, Point, Render, Size, StyleRefinement, Styled, View, ViewContext, WindowContext,
|
Size, StyleRefinement, Styled, View, ViewContext, WindowContext,
|
||||||
};
|
};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
|
use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
|
||||||
|
@ -99,70 +99,40 @@ impl Styled for UniformList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct UniformListState {
|
|
||||||
interactive: InteractiveElementState,
|
|
||||||
item_size: Size<Pixels>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Element for UniformList {
|
impl Element for UniformList {
|
||||||
type State = UniformListState;
|
type FrameState = Size<Pixels>;
|
||||||
|
|
||||||
fn layout(
|
fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
|
||||||
&mut self,
|
|
||||||
state: Option<Self::State>,
|
|
||||||
cx: &mut WindowContext,
|
|
||||||
) -> (LayoutId, Self::State) {
|
|
||||||
let max_items = self.item_count;
|
let max_items = self.item_count;
|
||||||
let item_size = state
|
let item_size = self.measure_item(None, cx);
|
||||||
.as_ref()
|
let layout_id = self.interactivity.layout(cx, |style, _, cx| {
|
||||||
.map(|s| s.item_size)
|
cx.request_measured_layout(style, move |known_dimensions, available_space, _cx| {
|
||||||
.unwrap_or_else(|| self.measure_item(None, cx));
|
let desired_height = item_size.height * max_items;
|
||||||
|
let width = known_dimensions
|
||||||
|
.width
|
||||||
|
.unwrap_or(match available_space.width {
|
||||||
|
AvailableSpace::Definite(x) => x,
|
||||||
|
AvailableSpace::MinContent | AvailableSpace::MaxContent => item_size.width,
|
||||||
|
});
|
||||||
|
let height = match available_space.height {
|
||||||
|
AvailableSpace::Definite(height) => desired_height.min(height),
|
||||||
|
AvailableSpace::MinContent | AvailableSpace::MaxContent => desired_height,
|
||||||
|
};
|
||||||
|
size(width, height)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
let (layout_id, interactive) =
|
(layout_id, item_size)
|
||||||
self.interactivity
|
|
||||||
.layout(state.map(|s| s.interactive), cx, |style, cx| {
|
|
||||||
cx.request_measured_layout(
|
|
||||||
style,
|
|
||||||
move |known_dimensions, available_space, _cx| {
|
|
||||||
let desired_height = item_size.height * max_items;
|
|
||||||
let width =
|
|
||||||
known_dimensions
|
|
||||||
.width
|
|
||||||
.unwrap_or(match available_space.width {
|
|
||||||
AvailableSpace::Definite(x) => x,
|
|
||||||
AvailableSpace::MinContent | AvailableSpace::MaxContent => {
|
|
||||||
item_size.width
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let height = match available_space.height {
|
|
||||||
AvailableSpace::Definite(height) => desired_height.min(height),
|
|
||||||
AvailableSpace::MinContent | AvailableSpace::MaxContent => {
|
|
||||||
desired_height
|
|
||||||
}
|
|
||||||
};
|
|
||||||
size(width, height)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let element_state = UniformListState {
|
|
||||||
interactive,
|
|
||||||
item_size,
|
|
||||||
};
|
|
||||||
|
|
||||||
(layout_id, element_state)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<crate::Pixels>,
|
bounds: Bounds<crate::Pixels>,
|
||||||
element_state: &mut Self::State,
|
item_size: &mut Self::FrameState,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) {
|
) {
|
||||||
let style =
|
let item_size = *item_size;
|
||||||
self.interactivity
|
let style = self.interactivity.compute_style(Some(bounds), cx);
|
||||||
.compute_style(Some(bounds), &mut element_state.interactive, cx);
|
|
||||||
let border = style.border_widths.to_pixels(cx.rem_size());
|
let border = style.border_widths.to_pixels(cx.rem_size());
|
||||||
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
|
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
|
||||||
|
|
||||||
|
@ -172,26 +142,24 @@ impl Element for UniformList {
|
||||||
- point(border.right + padding.right, border.bottom + padding.bottom),
|
- point(border.right + padding.right, border.bottom + padding.bottom),
|
||||||
);
|
);
|
||||||
|
|
||||||
let item_size = element_state.item_size;
|
|
||||||
let content_size = Size {
|
let content_size = Size {
|
||||||
width: padded_bounds.size.width,
|
width: padded_bounds.size.width,
|
||||||
height: item_size.height * self.item_count + padding.top + padding.bottom,
|
height: item_size.height * self.item_count + padding.top + padding.bottom,
|
||||||
};
|
};
|
||||||
|
|
||||||
let shared_scroll_offset = element_state
|
|
||||||
.interactive
|
|
||||||
.scroll_offset
|
|
||||||
.get_or_insert_with(Rc::default)
|
|
||||||
.clone();
|
|
||||||
|
|
||||||
let item_height = self.measure_item(Some(padded_bounds.size.width), cx).height;
|
let item_height = self.measure_item(Some(padded_bounds.size.width), cx).height;
|
||||||
|
|
||||||
self.interactivity.paint(
|
self.interactivity.paint(
|
||||||
bounds,
|
bounds,
|
||||||
content_size,
|
content_size,
|
||||||
&mut element_state.interactive,
|
|
||||||
cx,
|
cx,
|
||||||
|style, mut scroll_offset, cx| {
|
|style, element_state, mut scroll_offset, cx| {
|
||||||
|
let (_, element_state) = element_state.unwrap();
|
||||||
|
let shared_scroll_offset = element_state
|
||||||
|
.scroll_offset
|
||||||
|
.get_or_insert_with(Rc::default)
|
||||||
|
.clone();
|
||||||
|
|
||||||
let border = style.border_widths.to_pixels(cx.rem_size());
|
let border = style.border_widths.to_pixels(cx.rem_size());
|
||||||
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
|
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
|
||||||
|
|
||||||
|
|
|
@ -1465,7 +1465,9 @@ extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let window_state = get_window_state(this);
|
let window_state = get_window_state(this);
|
||||||
let mut draw = window_state.lock().draw.take().unwrap();
|
let mut draw = window_state.lock().draw.take().unwrap();
|
||||||
|
let t0 = std::time::Instant::now();
|
||||||
let scene = draw().log_err();
|
let scene = draw().log_err();
|
||||||
|
eprintln!("window draw: {:?}", t0.elapsed());
|
||||||
let mut window_state = window_state.lock();
|
let mut window_state = window_state.lock();
|
||||||
window_state.draw = Some(draw);
|
window_state.draw = Some(draw);
|
||||||
if let Some(scene) = scene {
|
if let Some(scene) = scene {
|
||||||
|
|
|
@ -79,19 +79,15 @@ impl<V: 'static> View<V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: Render> Element for View<V> {
|
impl<V: Render> Element for View<V> {
|
||||||
type State = Option<AnyElement>;
|
type FrameState = Option<AnyElement>;
|
||||||
|
|
||||||
fn layout(
|
fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
|
||||||
&mut self,
|
|
||||||
_state: Option<Self::State>,
|
|
||||||
cx: &mut WindowContext,
|
|
||||||
) -> (LayoutId, Self::State) {
|
|
||||||
let mut element = self.update(cx, |view, cx| view.render(cx).into_any());
|
let mut element = self.update(cx, |view, cx| view.render(cx).into_any());
|
||||||
let layout_id = element.layout(cx);
|
let layout_id = element.layout(cx);
|
||||||
(layout_id, Some(element))
|
(layout_id, Some(element))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut WindowContext) {
|
fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::FrameState, cx: &mut WindowContext) {
|
||||||
element.take().unwrap().paint(cx);
|
element.take().unwrap().paint(cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -227,18 +223,14 @@ impl<V: Render> From<View<V>> for AnyView {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for AnyView {
|
impl Element for AnyView {
|
||||||
type State = Option<AnyElement>;
|
type FrameState = Option<AnyElement>;
|
||||||
|
|
||||||
fn layout(
|
fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::FrameState) {
|
||||||
&mut self,
|
|
||||||
_state: Option<Self::State>,
|
|
||||||
cx: &mut WindowContext,
|
|
||||||
) -> (LayoutId, Self::State) {
|
|
||||||
let (layout_id, state) = (self.layout)(self, cx);
|
let (layout_id, state) = (self.layout)(self, cx);
|
||||||
(layout_id, Some(state))
|
(layout_id, Some(state))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(&mut self, _: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
|
fn paint(&mut self, _: Bounds<Pixels>, state: &mut Self::FrameState, cx: &mut WindowContext) {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
state.is_some(),
|
state.is_some(),
|
||||||
"state is None. Did you include an AnyView twice in the tree?"
|
"state is None. Did you include an AnyView twice in the tree?"
|
||||||
|
|
|
@ -1977,17 +1977,18 @@ pub trait BorrowWindow: BorrowMut<Window> + BorrowMut<AppContext> {
|
||||||
fn with_element_id<R>(
|
fn with_element_id<R>(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: Option<impl Into<ElementId>>,
|
id: Option<impl Into<ElementId>>,
|
||||||
f: impl FnOnce(&mut Self) -> R,
|
f: impl FnOnce(Option<GlobalElementId>, &mut Self) -> R,
|
||||||
) -> R {
|
) -> R {
|
||||||
if let Some(id) = id.map(Into::into) {
|
if let Some(id) = id.map(Into::into) {
|
||||||
let window = self.window_mut();
|
let window = self.window_mut();
|
||||||
window.element_id_stack.push(id.into());
|
window.element_id_stack.push(id.into());
|
||||||
let result = f(self);
|
let global_element_id = window.element_id_stack.clone();
|
||||||
|
let result = f(Some(global_element_id), self);
|
||||||
let window: &mut Window = self.borrow_mut();
|
let window: &mut Window = self.borrow_mut();
|
||||||
window.element_id_stack.pop();
|
window.element_id_stack.pop();
|
||||||
result
|
result
|
||||||
} else {
|
} else {
|
||||||
f(self)
|
f(None, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2071,36 +2072,40 @@ pub trait BorrowWindow: BorrowMut<Window> + BorrowMut<AppContext> {
|
||||||
/// when drawing the next frame.
|
/// when drawing the next frame.
|
||||||
fn with_element_state<S, R>(
|
fn with_element_state<S, R>(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: ElementId,
|
global_element_id: &GlobalElementId,
|
||||||
f: impl FnOnce(Option<S>, &mut Self) -> (R, S),
|
f: impl FnOnce(&mut Option<Box<S>>, &mut Self) -> R,
|
||||||
) -> R
|
) -> R
|
||||||
where
|
where
|
||||||
S: 'static,
|
S: 'static,
|
||||||
{
|
{
|
||||||
self.with_element_id(Some(id), |cx| {
|
let state = if self.window_mut().drawing {
|
||||||
let global_id = cx.window().element_id_stack.clone();
|
self.window_mut()
|
||||||
|
|
||||||
if let Some(any) = cx
|
|
||||||
.window_mut()
|
|
||||||
.next_frame
|
.next_frame
|
||||||
.element_states
|
.element_states
|
||||||
.remove(&global_id)
|
.remove(global_element_id)
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
cx.window_mut()
|
self.window_mut()
|
||||||
.rendered_frame
|
.rendered_frame
|
||||||
.element_states
|
.element_states
|
||||||
.remove(&global_id)
|
.remove(global_element_id)
|
||||||
})
|
})
|
||||||
{
|
} else {
|
||||||
|
self.window_mut()
|
||||||
|
.rendered_frame
|
||||||
|
.element_states
|
||||||
|
.remove(global_element_id)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut state = state
|
||||||
|
.map(|any| {
|
||||||
let ElementStateBox {
|
let ElementStateBox {
|
||||||
inner,
|
inner,
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
type_name
|
type_name,
|
||||||
} = any;
|
} = any;
|
||||||
// Using the extra inner option to avoid needing to reallocate a new box.
|
inner
|
||||||
let mut state_box = inner
|
.downcast::<S>()
|
||||||
.downcast::<Option<S>>()
|
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
{
|
{
|
||||||
|
@ -2119,42 +2124,32 @@ pub trait BorrowWindow: BorrowMut<Window> + BorrowMut<AppContext> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
// Actual: Option<AnyElement> <- View
|
let result = f(&mut state, self);
|
||||||
// Requested: () <- AnyElemet
|
|
||||||
let state = state_box
|
if let Some(state) = state {
|
||||||
.take()
|
let state = ElementStateBox {
|
||||||
.expect("element state is already on the stack");
|
inner: state,
|
||||||
let (result, state) = f(Some(state), cx);
|
|
||||||
state_box.replace(state);
|
#[cfg(debug_assertions)]
|
||||||
cx.window_mut()
|
type_name: std::any::type_name::<S>(),
|
||||||
|
};
|
||||||
|
if self.window().drawing {
|
||||||
|
self.window_mut()
|
||||||
.next_frame
|
.next_frame
|
||||||
.element_states
|
.element_states
|
||||||
.insert(global_id, ElementStateBox {
|
.insert(global_element_id.clone(), state);
|
||||||
inner: state_box,
|
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
type_name
|
|
||||||
});
|
|
||||||
result
|
|
||||||
} else {
|
} else {
|
||||||
let (result, state) = f(None, cx);
|
self.window_mut()
|
||||||
cx.window_mut()
|
.rendered_frame
|
||||||
.next_frame
|
|
||||||
.element_states
|
.element_states
|
||||||
.insert(global_id,
|
.insert(global_element_id.clone(), state);
|
||||||
ElementStateBox {
|
|
||||||
inner: Box::new(Some(state)),
|
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
type_name: std::any::type_name::<S>()
|
|
||||||
}
|
|
||||||
|
|
||||||
);
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Obtain the current content mask.
|
/// Obtain the current content mask.
|
||||||
|
|
|
@ -2,10 +2,10 @@ use editor::{Cursor, HighlightedRange, HighlightedRangeLine};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
black, div, fill, point, px, red, relative, AnyElement, AsyncWindowContext, AvailableSpace,
|
black, div, fill, point, px, red, relative, AnyElement, AsyncWindowContext, AvailableSpace,
|
||||||
Bounds, DispatchPhase, Element, ElementId, ExternalPaths, FocusHandle, Font, FontStyle,
|
Bounds, DispatchPhase, Element, ElementId, ExternalPaths, FocusHandle, Font, FontStyle,
|
||||||
FontWeight, HighlightStyle, Hsla, InteractiveElement, InteractiveElementState, Interactivity,
|
FontWeight, HighlightStyle, Hsla, InteractiveElement, Interactivity, IntoElement, LayoutId,
|
||||||
IntoElement, LayoutId, Model, ModelContext, ModifiersChangedEvent, MouseButton, Pixels,
|
Model, ModelContext, ModifiersChangedEvent, MouseButton, Pixels, PlatformInputHandler, Point,
|
||||||
PlatformInputHandler, Point, Rgba, ShapedLine, Size, StatefulInteractiveElement, Styled,
|
Rgba, ShapedLine, Size, StatefulInteractiveElement, Styled, TextRun, TextStyle, TextSystem,
|
||||||
TextRun, TextStyle, TextSystem, UnderlineStyle, WhiteSpace, WindowContext,
|
UnderlineStyle, WhiteSpace, WindowContext,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use language::CursorShape;
|
use language::CursorShape;
|
||||||
|
@ -759,30 +759,22 @@ impl TerminalElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for TerminalElement {
|
impl Element for TerminalElement {
|
||||||
type State = InteractiveElementState;
|
type FrameState = ();
|
||||||
|
|
||||||
fn layout(
|
fn layout(&mut self, cx: &mut WindowContext<'_>) -> (LayoutId, Self::FrameState) {
|
||||||
&mut self,
|
let layout_id = self.interactivity.layout(cx, |mut style, _, cx| {
|
||||||
element_state: Option<Self::State>,
|
style.size.width = relative(1.).into();
|
||||||
cx: &mut WindowContext<'_>,
|
style.size.height = relative(1.).into();
|
||||||
) -> (LayoutId, Self::State) {
|
cx.request_layout(&style, None)
|
||||||
let (layout_id, interactive_state) =
|
});
|
||||||
self.interactivity
|
|
||||||
.layout(element_state, cx, |mut style, cx| {
|
|
||||||
style.size.width = relative(1.).into();
|
|
||||||
style.size.height = relative(1.).into();
|
|
||||||
let layout_id = cx.request_layout(&style, None);
|
|
||||||
|
|
||||||
layout_id
|
(layout_id, ())
|
||||||
});
|
|
||||||
|
|
||||||
(layout_id, interactive_state)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
state: &mut Self::State,
|
state: &mut Self::FrameState,
|
||||||
cx: &mut WindowContext<'_>,
|
cx: &mut WindowContext<'_>,
|
||||||
) {
|
) {
|
||||||
let mut layout = self.compute_layout(bounds, cx);
|
let mut layout = self.compute_layout(bounds, cx);
|
||||||
|
@ -812,7 +804,7 @@ impl Element for TerminalElement {
|
||||||
// })
|
// })
|
||||||
|
|
||||||
let mut interactivity = mem::take(&mut self.interactivity);
|
let mut interactivity = mem::take(&mut self.interactivity);
|
||||||
interactivity.paint(bounds, bounds.size, state, cx, |_, _, cx| {
|
interactivity.paint(bounds, bounds.size, cx, |_, _, _, cx| {
|
||||||
cx.handle_input(&self.focus, terminal_input_handler);
|
cx.handle_input(&self.focus, terminal_input_handler);
|
||||||
|
|
||||||
self.register_key_listeners(cx);
|
self.register_key_listeners(cx);
|
||||||
|
|
|
@ -126,20 +126,19 @@ pub struct PopoverMenuState<M> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M: ManagedView> Element for PopoverMenu<M> {
|
impl<M: ManagedView> Element for PopoverMenu<M> {
|
||||||
type State = PopoverMenuState<M>;
|
type FrameState = PopoverMenuState<M>;
|
||||||
|
|
||||||
fn layout(
|
fn layout(&mut self, cx: &mut WindowContext) -> (gpui::LayoutId, Self::FrameState) {
|
||||||
&mut self,
|
|
||||||
element_state: Option<Self::State>,
|
|
||||||
cx: &mut WindowContext,
|
|
||||||
) -> (gpui::LayoutId, Self::State) {
|
|
||||||
let mut menu_layout_id = None;
|
let mut menu_layout_id = None;
|
||||||
|
|
||||||
let (menu, child_bounds) = if let Some(element_state) = element_state {
|
// todo!()
|
||||||
(element_state.menu, element_state.child_bounds)
|
// let (menu, child_bounds) = if let Some(element_state) = element_state {
|
||||||
} else {
|
// (element_state.menu, element_state.child_bounds)
|
||||||
(Rc::default(), None)
|
// } else {
|
||||||
};
|
// (Rc::default(), None)
|
||||||
|
// };
|
||||||
|
let menu: Rc<RefCell<Option<View<M>>>> = Rc::default();
|
||||||
|
let child_bounds = None;
|
||||||
|
|
||||||
let menu_element = menu.borrow_mut().as_mut().map(|menu| {
|
let menu_element = menu.borrow_mut().as_mut().map(|menu| {
|
||||||
let mut overlay = overlay().snap_to_window().anchor(self.anchor);
|
let mut overlay = overlay().snap_to_window().anchor(self.anchor);
|
||||||
|
@ -184,7 +183,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: Bounds<gpui::Pixels>,
|
_: Bounds<gpui::Pixels>,
|
||||||
element_state: &mut Self::State,
|
element_state: &mut Self::FrameState,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) {
|
) {
|
||||||
if let Some(mut child) = element_state.child_element.take() {
|
if let Some(mut child) = element_state.child_element.take() {
|
||||||
|
|
|
@ -58,18 +58,17 @@ pub struct MenuHandleState<M> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M: ManagedView> Element for RightClickMenu<M> {
|
impl<M: ManagedView> Element for RightClickMenu<M> {
|
||||||
type State = MenuHandleState<M>;
|
type FrameState = MenuHandleState<M>;
|
||||||
|
|
||||||
fn layout(
|
fn layout(&mut self, cx: &mut WindowContext) -> (gpui::LayoutId, Self::FrameState) {
|
||||||
&mut self,
|
// todo!()
|
||||||
element_state: Option<Self::State>,
|
// let (menu, position) = if let Some(element_state) = element_state {
|
||||||
cx: &mut WindowContext,
|
// (element_state.menu, element_state.position)
|
||||||
) -> (gpui::LayoutId, Self::State) {
|
// } else {
|
||||||
let (menu, position) = if let Some(element_state) = element_state {
|
// (Rc::default(), Rc::default())
|
||||||
(element_state.menu, element_state.position)
|
// };
|
||||||
} else {
|
let menu: Rc<RefCell<Option<View<M>>>> = Rc::default();
|
||||||
(Rc::default(), Rc::default())
|
let position: Rc<RefCell<Point<Pixels>>> = Rc::default();
|
||||||
};
|
|
||||||
|
|
||||||
let mut menu_layout_id = None;
|
let mut menu_layout_id = None;
|
||||||
|
|
||||||
|
@ -114,7 +113,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<gpui::Pixels>,
|
bounds: Bounds<gpui::Pixels>,
|
||||||
element_state: &mut Self::State,
|
element_state: &mut Self::FrameState,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) {
|
) {
|
||||||
if let Some(mut child) = element_state.child_element.take() {
|
if let Some(mut child) = element_state.child_element.take() {
|
||||||
|
|
|
@ -756,25 +756,25 @@ mod element {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for PaneAxisElement {
|
impl Element for PaneAxisElement {
|
||||||
type State = Rc<RefCell<Option<usize>>>;
|
type FrameState = Rc<RefCell<Option<usize>>>;
|
||||||
|
|
||||||
fn layout(
|
fn layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: Option<Self::State>,
|
|
||||||
cx: &mut ui::prelude::WindowContext,
|
cx: &mut ui::prelude::WindowContext,
|
||||||
) -> (gpui::LayoutId, Self::State) {
|
) -> (gpui::LayoutId, Self::FrameState) {
|
||||||
let mut style = Style::default();
|
let mut style = Style::default();
|
||||||
style.size.width = relative(1.).into();
|
style.size.width = relative(1.).into();
|
||||||
style.size.height = relative(1.).into();
|
style.size.height = relative(1.).into();
|
||||||
let layout_id = cx.request_layout(&style, None);
|
let layout_id = cx.request_layout(&style, None);
|
||||||
let dragged_pane = state.unwrap_or_else(|| Rc::new(RefCell::new(None)));
|
// todo!()
|
||||||
|
let dragged_pane = Rc::new(RefCell::new(None));
|
||||||
(layout_id, dragged_pane)
|
(layout_id, dragged_pane)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: gpui::Bounds<ui::prelude::Pixels>,
|
bounds: gpui::Bounds<ui::prelude::Pixels>,
|
||||||
state: &mut Self::State,
|
state: &mut Self::FrameState,
|
||||||
cx: &mut ui::prelude::WindowContext,
|
cx: &mut ui::prelude::WindowContext,
|
||||||
) {
|
) {
|
||||||
let flexes = self.flexes.lock().clone();
|
let flexes = self.flexes.lock().clone();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue