Compare commits

...

16 Commits

Author SHA1 Message Date
Antonio Scandurra
2205430814 WIP 2024-04-19 18:04:04 +02:00
Antonio Scandurra
fa410ec150 WIP: start on EditorElement::after_layout 2024-04-19 14:14:21 +02:00
Antonio Scandurra
504ea1aaea Merge remote-tracking branch 'origin/main' into before-paint 2024-04-19 10:33:18 +02:00
Antonio Scandurra
d75f1e64b8 Merge remote-tracking branch 'origin/main' into before-paint 2024-04-18 18:39:25 +02:00
Antonio Scandurra
1358758226 Fix errors in editor.rs 2024-04-18 18:39:19 +02:00
Antonio Scandurra
9de4dc12dd Implement PaneAxisElement::after_layout 2024-04-18 18:34:15 +02:00
Antonio Scandurra
7ae13c9753 Implement DisconnectedOverlay::after_layout 2024-04-18 17:31:22 +02:00
Antonio Scandurra
d4bae0475f Add RightClickMenu::after_layout 2024-04-18 15:57:48 +02:00
Antonio Scandurra
72a4e525d7 Add PopoverMenu::after_layout 2024-04-18 15:47:40 +02:00
Antonio Scandurra
15f4e93102 Re-enable view caching 2024-04-18 15:41:04 +02:00
Antonio Scandurra
dbfb588e3e Wire up List::after_layout
Note that we're still not making use of the new focus target bounds returned by
the child elements.
2024-04-18 14:34:27 +02:00
Antonio Scandurra
8ead209099 Implement UniformList::after_layout
The list doesn't really have a mechanism for scrolling to the focused
element right now, so we should consider adding it before merging this
branch.
2024-04-18 11:37:31 +02:00
Antonio Scandurra
eb14932950 WIP: start migrating UniformList 2024-04-17 17:58:23 +02:00
Antonio Scandurra
d3756cdab3 WIP
Co-Authored-By: Nathan <nathan@zed.dev>
2024-04-17 16:52:46 +02:00
Antonio Scandurra
d4c54f3479 WIP: Start introducing a new after_layout pass 2024-04-17 12:47:59 +02:00
Antonio Scandurra
c024526db2 Rename after_layout to before_paint 2024-04-17 12:14:14 +02:00
29 changed files with 1673 additions and 597 deletions

View File

@@ -1094,31 +1094,37 @@ impl AssistantPanel {
let view = cx.view().clone();
let scroll_handle = self.saved_conversations_scroll_handle.clone();
let conversation_count = self.saved_conversations.len();
canvas(
move |bounds, cx| {
let mut saved_conversations = uniform_list(
view,
"saved_conversations",
conversation_count,
|this, range, cx| {
range
.map(|ix| this.render_saved_conversation(ix, cx))
.collect()
},
)
.track_scroll(scroll_handle)
.into_any_element();
saved_conversations.layout(
bounds.origin,
bounds.size.map(AvailableSpace::Definite),
cx,
);
saved_conversations
},
|_bounds, mut saved_conversations, cx| saved_conversations.paint(cx),
)
.size_full()
.into_any_element()
todo!("replace canvas")
// canvas(
// move |_, cx| {
// let saved_conversations = uniform_list(
// view.clone(),
// "saved_conversations",
// conversation_count,
// |this, range, cx| {
// range
// .map(|ix| this.render_saved_conversation(ix, cx))
// .collect()
// },
// )
// .track_scroll(scroll_handle.clone())
// .into_any_element();
// saved_conversations.layout(absolute_offset, available_space, cx)
// // compute layout for saved conversations
// saved_conversations
// },
// move |bounds, saved_conversations, cx| {
// saved_conversations.layout(
// bounds.origin,
// bounds.size.map(AvailableSpace::Definite),
// cx,
// );
// saved_conversations
// },
// |_bounds, mut saved_conversations, cx| saved_conversations.paint(cx),
// )
// .size_full()
// .into_any_element()
} else if let Some(editor) = self.active_conversation_editor() {
let editor = editor.clone();
let conversation = editor.read(cx).conversation.clone();

View File

@@ -2834,34 +2834,31 @@ fn render_tree_branch(is_last: bool, overdraw: bool, cx: &mut WindowContext) ->
let thickness = px(1.);
let color = cx.theme().colors().text;
canvas(
|_, _| {},
move |bounds, _, cx| {
let start_x = (bounds.left() + bounds.right() - thickness) / 2.;
let start_y = (bounds.top() + bounds.bottom() - thickness) / 2.;
let right = bounds.right();
let top = bounds.top();
canvas(move |bounds, cx| {
let start_x = (bounds.left() + bounds.right() - thickness) / 2.;
let start_y = (bounds.top() + bounds.bottom() - thickness) / 2.;
let right = bounds.right();
let top = bounds.top();
cx.paint_quad(fill(
Bounds::from_corners(
point(start_x, top),
point(
start_x + thickness,
if is_last {
start_y
} else {
bounds.bottom() + if overdraw { px(1.) } else { px(0.) }
},
),
cx.paint_quad(fill(
Bounds::from_corners(
point(start_x, top),
point(
start_x + thickness,
if is_last {
start_y
} else {
bounds.bottom() + if overdraw { px(1.) } else { px(0.) }
},
),
color,
));
cx.paint_quad(fill(
Bounds::from_corners(point(start_x, start_y), point(right, start_y + thickness)),
color,
));
},
)
),
color,
));
cx.paint_quad(fill(
Bounds::from_corners(point(start_x, start_y), point(right, start_y + thickness)),
color,
));
})
.w(width)
.h(line_height)
}

View File

@@ -318,26 +318,23 @@ impl Render for CollabTitlebarItem {
}
fn render_color_ribbon(color: Hsla) -> impl Element {
canvas(
move |_, _| {},
move |bounds, _, cx| {
let height = bounds.size.height;
let horizontal_offset = height;
let vertical_offset = px(height.0 / 2.0);
let mut path = Path::new(bounds.lower_left());
path.curve_to(
bounds.origin + point(horizontal_offset, vertical_offset),
bounds.origin + point(px(0.0), vertical_offset),
);
path.line_to(bounds.upper_right() + point(-horizontal_offset, vertical_offset));
path.curve_to(
bounds.lower_right(),
bounds.upper_right() + point(px(0.0), vertical_offset),
);
path.line_to(bounds.lower_left());
cx.paint_path(path, color);
},
)
canvas(move |bounds, cx| {
let height = bounds.size.height;
let horizontal_offset = height;
let vertical_offset = px(height.0 / 2.0);
let mut path = Path::new(bounds.lower_left());
path.curve_to(
bounds.origin + point(horizontal_offset, vertical_offset),
bounds.origin + point(px(0.0), vertical_offset),
);
path.line_to(bounds.upper_right() + point(-horizontal_offset, vertical_offset));
path.curve_to(
bounds.lower_right(),
bounds.upper_right() + point(px(0.0), vertical_offset),
);
path.line_to(bounds.lower_left());
cx.paint_path(path, color);
})
.h_1()
.w_full()
}

View File

@@ -476,8 +476,8 @@ pub struct Editor {
+ Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
>,
>,
last_bounds: Option<Bounds<Pixels>>,
expect_bounds_change: Option<Bounds<Pixels>>,
last_layout_bounds: Option<Bounds<Pixels>>,
expect_layout_bounds_change: Option<Bounds<Pixels>>,
}
#[derive(Clone)]
@@ -1492,8 +1492,8 @@ impl Editor {
inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
gutter_hovered: false,
pixel_position_of_newest_cursor: None,
last_bounds: None,
expect_bounds_change: None,
last_layout_bounds: None,
expect_layout_bounds_change: None,
gutter_width: Default::default(),
style: None,
show_cursor_names: false,
@@ -10778,7 +10778,8 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, _is_valid: bool) -> Ren
let icon_size = buttons(&diagnostic, cx.block_id)
.into_any_element()
.measure(AvailableSpace::min_size(), cx);
.layout(AvailableSpace::min_size(), cx)
.size;
h_flex()
.id(cx.block_id)

File diff suppressed because it is too large Load Diff

View File

@@ -1173,7 +1173,7 @@ impl SearchableItem for Editor {
}
fn search_bar_visibility_changed(&mut self, _visible: bool, _cx: &mut ViewContext<Self>) {
self.expect_bounds_change = self.last_bounds;
self.expect_layout_bounds_change = self.last_layout_bounds;
}
}

View File

@@ -72,7 +72,7 @@ impl Editor {
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let mut scroll_position = self.scroll_manager.scroll_position(&display_map);
let original_y = scroll_position.y;
if let Some(last_bounds) = self.expect_bounds_change.take() {
if let Some(last_bounds) = self.expect_layout_bounds_change.take() {
if scroll_position.y != 0. {
scroll_position.y += (bounds.top() - last_bounds.top()) / line_height;
if scroll_position.y < 0. {

View File

@@ -936,24 +936,25 @@ impl Render for ExtensionsPage {
let view = cx.view().clone();
let scroll_handle = self.list.clone();
this.child(
canvas(
move |bounds, cx| {
let mut list = uniform_list::<_, ExtensionCard, _>(
view,
"entries",
count,
Self::render_extensions,
)
.size_full()
.pb_4()
.track_scroll(scroll_handle)
.into_any_element();
list.layout(bounds.origin, bounds.size.into(), cx);
list
},
|_bounds, mut list, cx| list.paint(cx),
)
.size_full(),
// canvas(
// move |bounds, cx| {
// let mut list = uniform_list::<_, ExtensionCard, _>(
// view,
// "entries",
// count,
// Self::render_extensions,
// )
// .size_full()
// .pb_4()
// .track_scroll(scroll_handle)
// .into_any_element();
// list.layout(bounds.origin, bounds.size.into(), cx);
// list
// },
// |_bounds, mut list, cx| list.paint(cx),
// )
// .size_full(),
todo!("replace canvas"),
)
}))
}

View File

@@ -734,7 +734,8 @@ impl VisualTestContext {
self.update(|cx| {
cx.with_element_context(|cx| {
let mut element = f(cx);
element.layout(origin, space, cx);
element.layout(space, cx);
cx.with_element_offset(origin, |cx| element.before_paint(cx));
element.paint(cx);
});

View File

@@ -33,7 +33,7 @@
use crate::{
util::FluentBuilder, ArenaBox, AvailableSpace, Bounds, DispatchNodeId, ElementContext,
ElementId, LayoutId, Pixels, Point, Size, ViewContext, WindowContext, ELEMENT_ARENA,
ElementId, LayoutId, Pixels, Size, ViewContext, WindowContext, ELEMENT_ARENA,
};
use derive_more::{Deref, DerefMut};
pub(crate) use smallvec::SmallVec;
@@ -45,33 +45,46 @@ use std::{any::Any, fmt::Debug, mem, ops::DerefMut};
/// for more details.
pub trait Element: 'static + IntoElement {
/// The type of state returned from [`Element::before_layout`]. A mutable reference to this state is subsequently
/// provided to [`Element::after_layout`] and [`Element::paint`].
/// provided to [`Element::after_layout`], [`Element::before_paint`] and [`Element::paint`].
type BeforeLayout: 'static;
/// The type of state returned from [`Element::after_layout`]. A mutable reference to this state is subsequently
/// provided to [`Element::paint`].
/// provided to [`Element::before_paint`] and [`Element::paint`].
type AfterLayout: 'static;
/// The type of state returned from [`Element::before_paint`]. A mutable reference to this state is subsequently
/// provided to [`Element::paint`].
type BeforePaint: 'static;
/// Before an element can be painted, we need to know where it's going to be and how big it is.
/// Use this method to request a layout from Taffy and initialize the element's state.
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout);
/// After laying out an element, we need to commit its bounds to the current frame for hitbox
/// purposes. The state argument is the same state that was returned from [`Element::before_layout()`].
/// todo!()
fn after_layout(
&mut self,
bounds: Bounds<Pixels>,
before_layout: &mut Self::BeforeLayout,
cx: &mut ElementContext,
) -> Self::AfterLayout;
) -> (Option<Bounds<Pixels>>, Self::AfterLayout);
/// Before painting an element, we need to commit its bounds to the current frame for hitbox
/// purposes.
fn before_paint(
&mut self,
bounds: Bounds<Pixels>,
before_layout: &mut Self::BeforeLayout,
after_layout: &mut Self::AfterLayout,
cx: &mut ElementContext,
) -> Self::BeforePaint;
/// Once layout has been completed, this method will be called to paint the element to the screen.
/// The state argument is the same state that was returned from [`Element::before_layout()`].
fn paint(
&mut self,
bounds: Bounds<Pixels>,
before_layout: &mut Self::BeforeLayout,
after_layout: &mut Self::AfterLayout,
before_paint: &mut Self::BeforePaint,
cx: &mut ElementContext,
);
@@ -163,6 +176,7 @@ impl<C: RenderOnce> Component<C> {
impl<C: RenderOnce> Element for Component<C> {
type BeforeLayout = AnyElement;
type AfterLayout = ();
type BeforePaint = ();
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
let mut element = self
@@ -176,12 +190,23 @@ impl<C: RenderOnce> Element for Component<C> {
}
fn after_layout(
&mut self,
_bounds: Bounds<Pixels>,
element: &mut Self::BeforeLayout,
cx: &mut ElementContext,
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
let bounds = element.after_layout(cx);
(bounds, ())
}
fn before_paint(
&mut self,
_: Bounds<Pixels>,
element: &mut AnyElement,
_: &mut Self::AfterLayout,
cx: &mut ElementContext,
) {
element.after_layout(cx);
element.before_paint(cx);
}
fn paint(
@@ -189,6 +214,7 @@ impl<C: RenderOnce> Element for Component<C> {
_: Bounds<Pixels>,
element: &mut Self::BeforeLayout,
_: &mut Self::AfterLayout,
_: &mut Self::BeforePaint,
cx: &mut ElementContext,
) {
element.paint(cx)
@@ -212,46 +238,59 @@ trait ElementObject {
fn before_layout(&mut self, cx: &mut ElementContext) -> LayoutId;
fn after_layout(&mut self, cx: &mut ElementContext);
fn after_layout(&mut self, cx: &mut ElementContext) -> Option<Bounds<Pixels>>;
fn before_paint(&mut self, cx: &mut ElementContext);
fn paint(&mut self, cx: &mut ElementContext);
fn measure(
fn layout(
&mut self,
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
) -> Size<Pixels>;
) -> ElementMeasurement;
}
/// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window.
pub struct Drawable<E: Element> {
/// The drawn element.
pub element: E,
phase: ElementDrawPhase<E::BeforeLayout, E::AfterLayout>,
phase: ElementDrawPhase<E::BeforeLayout, E::AfterLayout, E::BeforePaint>,
}
#[derive(Default)]
enum ElementDrawPhase<BeforeLayout, AfterLayout> {
enum ElementDrawPhase<BeforeLayout, AfterLayout, BeforePaint> {
#[default]
Start,
BeforeLayout {
layout_id: LayoutId,
before_layout: BeforeLayout,
},
LayoutComputed {
layout_id: LayoutId,
available_space: Size<AvailableSpace>,
before_layout: BeforeLayout,
},
AfterLayout {
layout_id: LayoutId,
available_space: Option<Size<AvailableSpace>>,
before_layout: BeforeLayout,
after_layout: AfterLayout,
},
BeforePaint {
node_id: DispatchNodeId,
bounds: Bounds<Pixels>,
before_layout: BeforeLayout,
after_layout: AfterLayout,
before_paint: BeforePaint,
},
Painted,
}
/// todo!()
#[derive(Default)]
pub struct ElementMeasurement {
/// The size of the element.
pub size: Size<Pixels>,
/// The bounds for the focus target inside of the measured element.
pub focus_target_bounds: Option<Bounds<Pixels>>,
}
/// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window.
impl<E: Element> Drawable<E> {
fn new(element: E) -> Self {
@@ -275,92 +314,133 @@ impl<E: Element> Drawable<E> {
}
}
fn after_layout(&mut self, cx: &mut ElementContext) {
fn after_layout(&mut self, cx: &mut ElementContext) -> Option<Bounds<Pixels>> {
match mem::take(&mut self.phase) {
ElementDrawPhase::BeforeLayout {
layout_id,
mut before_layout,
}
| ElementDrawPhase::LayoutComputed {
layout_id,
mut before_layout,
..
} => {
let bounds = cx.layout_bounds(layout_id);
let node_id = cx.window.next_frame.dispatch_tree.push_node();
let after_layout = self.element.after_layout(bounds, &mut before_layout, cx);
let (focus_target_bounds, after_layout) =
self.element.after_layout(bounds, &mut before_layout, cx);
self.phase = ElementDrawPhase::AfterLayout {
node_id,
bounds,
layout_id,
available_space: None,
before_layout,
after_layout,
};
cx.window.next_frame.dispatch_tree.pop_node();
focus_target_bounds
}
_ => panic!("must call before_layout before after_layout"),
}
}
fn paint(&mut self, cx: &mut ElementContext) -> E::BeforeLayout {
fn before_paint(&mut self, cx: &mut ElementContext) {
match mem::take(&mut self.phase) {
ElementDrawPhase::AfterLayout {
node_id,
bounds,
layout_id,
mut before_layout,
mut after_layout,
..
} => {
cx.window.next_frame.dispatch_tree.set_active_node(node_id);
self.element
.paint(bounds, &mut before_layout, &mut after_layout, cx);
self.phase = ElementDrawPhase::Painted;
before_layout
let bounds = cx.layout_bounds(layout_id);
let node_id = cx.window.next_frame.dispatch_tree.push_node();
let before_paint =
self.element
.before_paint(bounds, &mut before_layout, &mut after_layout, cx);
self.phase = ElementDrawPhase::BeforePaint {
node_id,
bounds,
before_layout,
after_layout,
before_paint,
};
cx.window.next_frame.dispatch_tree.pop_node();
}
_ => panic!("must call after_layout before paint"),
_ => panic!("must call after_layout before before_paint"),
}
}
fn measure(
fn paint(&mut self, cx: &mut ElementContext) -> E::BeforeLayout {
match mem::take(&mut self.phase) {
ElementDrawPhase::BeforePaint {
node_id,
bounds,
mut before_layout,
mut after_layout,
mut before_paint,
..
} => {
cx.window.next_frame.dispatch_tree.set_active_node(node_id);
self.element.paint(
bounds,
&mut before_layout,
&mut after_layout,
&mut before_paint,
cx,
);
self.phase = ElementDrawPhase::Painted;
before_layout
}
_ => panic!("must call before_paint before paint"),
}
}
fn layout(
&mut self,
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
) -> Size<Pixels> {
) -> ElementMeasurement {
if matches!(&self.phase, ElementDrawPhase::Start) {
self.before_layout(cx);
}
let layout_id = match mem::take(&mut self.phase) {
match mem::take(&mut self.phase) {
ElementDrawPhase::BeforeLayout {
layout_id,
before_layout,
mut before_layout,
} => {
cx.compute_layout(layout_id, available_space);
self.phase = ElementDrawPhase::LayoutComputed {
let bounds = cx.layout_bounds(layout_id);
let (focus_target_bounds, after_layout) =
self.element.after_layout(bounds, &mut before_layout, cx);
self.phase = ElementDrawPhase::AfterLayout {
layout_id,
available_space,
available_space: Some(available_space),
before_layout,
after_layout,
};
layout_id
ElementMeasurement {
size: bounds.size,
focus_target_bounds,
}
}
ElementDrawPhase::LayoutComputed {
ElementDrawPhase::AfterLayout {
layout_id,
available_space: prev_available_space,
before_layout,
mut before_layout,
..
} => {
if available_space != prev_available_space {
if Some(available_space) != prev_available_space {
cx.compute_layout(layout_id, available_space);
}
self.phase = ElementDrawPhase::LayoutComputed {
let bounds = cx.layout_bounds(layout_id);
let (focus_target_bounds, after_layout) =
self.element.after_layout(bounds, &mut before_layout, cx);
self.phase = ElementDrawPhase::AfterLayout {
layout_id,
available_space,
available_space: Some(available_space),
before_layout,
after_layout,
};
layout_id
ElementMeasurement {
size: bounds.size,
focus_target_bounds,
}
}
_ => panic!("cannot measure after painting"),
};
cx.layout_bounds(layout_id).size
_ => panic!("cannot layout after painting"),
}
}
}
@@ -377,20 +457,24 @@ where
Drawable::before_layout(self, cx)
}
fn after_layout(&mut self, cx: &mut ElementContext) {
Drawable::after_layout(self, cx);
fn after_layout(&mut self, cx: &mut ElementContext) -> Option<Bounds<Pixels>> {
Drawable::after_layout(self, cx)
}
fn before_paint(&mut self, cx: &mut ElementContext) {
Drawable::before_paint(self, cx);
}
fn paint(&mut self, cx: &mut ElementContext) {
Drawable::paint(self, cx);
}
fn measure(
fn layout(
&mut self,
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
) -> Size<Pixels> {
Drawable::measure(self, available_space, cx)
) -> ElementMeasurement {
Drawable::layout(self, available_space, cx)
}
}
@@ -420,41 +504,35 @@ impl AnyElement {
self.0.before_layout(cx)
}
/// Commits the element bounds of this [AnyElement] for hitbox purposes.
pub fn after_layout(&mut self, cx: &mut ElementContext) {
/// todo!()
pub fn after_layout(&mut self, cx: &mut ElementContext) -> Option<Bounds<Pixels>> {
self.0.after_layout(cx)
}
/// Commits the element bounds of this [AnyElement] for hitbox purposes.
pub fn before_paint(&mut self, cx: &mut ElementContext) {
self.0.before_paint(cx)
}
/// Paints the element stored in this `AnyElement`.
pub fn paint(&mut self, cx: &mut ElementContext) {
self.0.paint(cx)
}
/// Initializes this element and performs layout within the given available space to determine its size.
pub fn measure(
&mut self,
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
) -> Size<Pixels> {
self.0.measure(available_space, cx)
}
/// Initializes this element, performs layout if needed and commits its bounds for hitbox purposes.
pub fn layout(
&mut self,
absolute_offset: Point<Pixels>,
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
) -> Size<Pixels> {
let size = self.measure(available_space, cx);
cx.with_absolute_element_offset(absolute_offset, |cx| self.after_layout(cx));
size
) -> ElementMeasurement {
self.0.layout(available_space, cx)
}
}
impl Element for AnyElement {
type BeforeLayout = ();
type AfterLayout = ();
type BeforePaint = ();
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
let layout_id = self.before_layout(cx);
@@ -466,8 +544,19 @@ impl Element for AnyElement {
_: Bounds<Pixels>,
_: &mut Self::BeforeLayout,
cx: &mut ElementContext,
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
let bounds = self.after_layout(cx);
(bounds, ())
}
fn before_paint(
&mut self,
_: Bounds<Pixels>,
_: &mut Self::BeforeLayout,
_: &mut Self::AfterLayout,
cx: &mut ElementContext,
) {
self.after_layout(cx)
self.before_paint(cx)
}
fn paint(
@@ -475,6 +564,7 @@ impl Element for AnyElement {
_: Bounds<Pixels>,
_: &mut Self::BeforeLayout,
_: &mut Self::AfterLayout,
_: &mut Self::BeforePaint,
cx: &mut ElementContext,
) {
self.paint(cx)
@@ -507,6 +597,7 @@ impl IntoElement for Empty {
impl Element for Empty {
type BeforeLayout = ();
type AfterLayout = ();
type BeforePaint = ();
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
(cx.request_layout(&crate::Style::default(), None), ())
@@ -515,7 +606,17 @@ impl Element for Empty {
fn after_layout(
&mut self,
_bounds: Bounds<Pixels>,
_state: &mut Self::BeforeLayout,
_before_layout: &mut Self::BeforeLayout,
_cx: &mut ElementContext,
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
(None, ())
}
fn before_paint(
&mut self,
_bounds: Bounds<Pixels>,
_before_layout: &mut Self::BeforeLayout,
_after_layout: &mut Self::AfterLayout,
_cx: &mut ElementContext,
) {
}
@@ -525,6 +626,7 @@ impl Element for Empty {
_bounds: Bounds<Pixels>,
_before_layout: &mut Self::BeforeLayout,
_after_layout: &mut Self::AfterLayout,
_before_paint: &mut Self::BeforePaint,
_cx: &mut ElementContext,
) {
}

View File

@@ -71,6 +71,7 @@ impl ParentElement for Anchored {
impl Element for Anchored {
type BeforeLayout = AnchoredState;
type AfterLayout = ();
type BeforePaint = ();
fn before_layout(&mut self, cx: &mut ElementContext) -> (crate::LayoutId, Self::BeforeLayout) {
let child_layout_ids = self
@@ -91,9 +92,27 @@ impl Element for Anchored {
}
fn after_layout(
&mut self,
_bounds: Bounds<Pixels>,
_before_layout: &mut Self::BeforeLayout,
cx: &mut ElementContext,
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
let mut focus_target_bounds = None;
for child in &mut self.children {
if let Some(child_focus_target_bounds) = child.after_layout(cx) {
if focus_target_bounds.is_none() {
focus_target_bounds = Some(child_focus_target_bounds);
}
}
}
(focus_target_bounds, ())
}
fn before_paint(
&mut self,
bounds: Bounds<Pixels>,
before_layout: &mut Self::BeforeLayout,
_after_layout: &mut Self::AfterLayout,
cx: &mut ElementContext,
) {
if before_layout.child_layout_ids.is_empty() {
@@ -167,7 +186,7 @@ impl Element for Anchored {
cx.with_element_offset(offset, |cx| {
for child in &mut self.children {
child.after_layout(cx);
child.before_paint(cx);
}
})
}
@@ -177,6 +196,7 @@ impl Element for Anchored {
_bounds: crate::Bounds<crate::Pixels>,
_before_layout: &mut Self::BeforeLayout,
_after_layout: &mut Self::AfterLayout,
_before_paint: &mut Self::BeforePaint,
cx: &mut ElementContext,
) {
for child in &mut self.children {

View File

@@ -1,6 +1,6 @@
use std::time::{Duration, Instant};
use crate::{AnyElement, Element, ElementId, IntoElement};
use crate::{AnyElement, Bounds, Element, ElementId, IntoElement, Pixels};
pub use easing::*;
@@ -86,8 +86,8 @@ struct AnimationState {
impl<E: IntoElement + 'static> Element for AnimationElement<E> {
type BeforeLayout = AnyElement;
type AfterLayout = ();
type BeforePaint = ();
fn before_layout(
&mut self,
@@ -136,18 +136,29 @@ impl<E: IntoElement + 'static> Element for AnimationElement<E> {
fn after_layout(
&mut self,
_bounds: crate::Bounds<crate::Pixels>,
_bounds: Bounds<Pixels>,
element: &mut Self::BeforeLayout,
cx: &mut crate::ElementContext,
) -> Self::AfterLayout {
element.after_layout(cx);
) -> (Option<Bounds<Pixels>>, ()) {
(element.after_layout(cx), ())
}
fn before_paint(
&mut self,
_bounds: Bounds<Pixels>,
element: &mut Self::BeforeLayout,
_after_layout: &mut Self::AfterLayout,
cx: &mut crate::ElementContext,
) -> Self::BeforePaint {
element.before_paint(cx);
}
fn paint(
&mut self,
_bounds: crate::Bounds<crate::Pixels>,
_bounds: Bounds<Pixels>,
element: &mut Self::BeforeLayout,
_: &mut Self::AfterLayout,
_after_layout: &mut Self::AfterLayout,
_before_paint: &mut Self::BeforePaint,
cx: &mut crate::ElementContext,
) {
element.paint(cx);

View File

@@ -4,12 +4,8 @@ use crate::{Bounds, Element, ElementContext, IntoElement, Pixels, Style, StyleRe
/// Construct a canvas element with the given paint callback.
/// Useful for adding short term custom drawing to a view.
pub fn canvas<T>(
after_layout: impl 'static + FnOnce(Bounds<Pixels>, &mut ElementContext) -> T,
paint: impl 'static + FnOnce(Bounds<Pixels>, T, &mut ElementContext),
) -> Canvas<T> {
pub fn canvas(paint: impl 'static + FnOnce(Bounds<Pixels>, &mut ElementContext)) -> Canvas {
Canvas {
after_layout: Some(Box::new(after_layout)),
paint: Some(Box::new(paint)),
style: StyleRefinement::default(),
}
@@ -17,13 +13,12 @@ pub fn canvas<T>(
/// A canvas element, meant for accessing the low level paint API without defining a whole
/// custom element
pub struct Canvas<T> {
after_layout: Option<Box<dyn FnOnce(Bounds<Pixels>, &mut ElementContext) -> T>>,
paint: Option<Box<dyn FnOnce(Bounds<Pixels>, T, &mut ElementContext)>>,
pub struct Canvas {
paint: Option<Box<dyn FnOnce(Bounds<Pixels>, &mut ElementContext)>>,
style: StyleRefinement,
}
impl<T: 'static> IntoElement for Canvas<T> {
impl IntoElement for Canvas {
type Element = Self;
fn into_element(self) -> Self::Element {
@@ -31,9 +26,10 @@ impl<T: 'static> IntoElement for Canvas<T> {
}
}
impl<T: 'static> Element for Canvas<T> {
impl Element for Canvas {
type BeforeLayout = Style;
type AfterLayout = Option<T>;
type AfterLayout = ();
type BeforePaint = ();
fn before_layout(&mut self, cx: &mut ElementContext) -> (crate::LayoutId, Self::BeforeLayout) {
let mut style = Style::default();
@@ -44,28 +40,35 @@ impl<T: 'static> Element for Canvas<T> {
fn after_layout(
&mut self,
bounds: Bounds<Pixels>,
_bounds: Bounds<Pixels>,
_before_layout: &mut Self::BeforeLayout,
_cx: &mut ElementContext,
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
(None, ())
}
fn before_paint(
&mut self,
_bounds: Bounds<Pixels>,
_before_layout: &mut Style,
cx: &mut ElementContext,
) -> Option<T> {
Some(self.after_layout.take().unwrap()(bounds, cx))
_after_layout: &mut Self::AfterLayout,
_cx: &mut ElementContext,
) {
}
fn paint(
&mut self,
bounds: Bounds<Pixels>,
style: &mut Style,
after_layout: &mut Self::AfterLayout,
_after_layout: &mut Self::AfterLayout,
_before_paint: &mut Self::BeforePaint,
cx: &mut ElementContext,
) {
let after_layout = after_layout.take().unwrap();
style.paint(bounds, cx, |cx| {
(self.paint.take().unwrap())(bounds, after_layout, cx)
});
style.paint(bounds, cx, |cx| (self.paint.take().unwrap())(bounds, cx));
}
}
impl<T> Styled for Canvas<T> {
impl Styled for Canvas {
fn style(&mut self) -> &mut crate::StyleRefinement {
&mut self.style
}

View File

@@ -28,6 +28,7 @@ impl Deferred {
impl Element for Deferred {
type BeforeLayout = ();
type AfterLayout = ();
type BeforePaint = ();
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, ()) {
let layout_id = self.child.as_mut().unwrap().before_layout(cx);
@@ -39,6 +40,17 @@ impl Element for Deferred {
_bounds: Bounds<Pixels>,
_before_layout: &mut Self::BeforeLayout,
cx: &mut ElementContext,
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
let bounds = self.child.as_mut().unwrap().after_layout(cx);
(bounds, ())
}
fn before_paint(
&mut self,
_bounds: Bounds<Pixels>,
_before_layout: &mut Self::BeforeLayout,
_after_layout: &mut Self::AfterLayout,
cx: &mut ElementContext,
) {
let child = self.child.take().unwrap();
let element_offset = cx.element_offset();
@@ -50,6 +62,7 @@ impl Element for Deferred {
_bounds: Bounds<Pixels>,
_before_layout: &mut Self::BeforeLayout,
_after_layout: &mut Self::AfterLayout,
_before_paint: &mut Self::BeforePaint,
_cx: &mut ElementContext,
) {
}

View File

@@ -1121,7 +1121,8 @@ impl ParentElement for Div {
impl Element for Div {
type BeforeLayout = DivFrameState;
type AfterLayout = Option<Hitbox>;
type AfterLayout = ();
type BeforePaint = Option<Hitbox>;
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
let mut child_layout_ids = SmallVec::new();
@@ -1143,7 +1144,7 @@ impl Element for Div {
bounds: Bounds<Pixels>,
before_layout: &mut Self::BeforeLayout,
cx: &mut ElementContext,
) -> Option<Hitbox> {
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
let mut child_min = point(Pixels::MAX, Pixels::MAX);
let mut child_max = Point::default();
let content_size = if before_layout.child_layout_ids.is_empty() {
@@ -1177,25 +1178,49 @@ impl Element for Div {
(child_max - child_min).into()
};
self.interactivity.after_layout(
let focus_target_bounds = self.interactivity.after_layout(
bounds,
content_size,
cx,
|_style, scroll_offset, hitbox, cx| {
|_, scroll_offset, mut focus_target_bounds, cx| {
cx.with_element_offset(scroll_offset, |cx| {
for child in &mut self.children {
child.after_layout(cx);
if let Some(child_focus_bounds) = child.after_layout(cx) {
focus_target_bounds = Some(child_focus_bounds);
}
}
focus_target_bounds
})
},
);
(focus_target_bounds, ())
}
fn before_paint(
&mut self,
bounds: Bounds<Pixels>,
_before_layout: &mut Self::BeforeLayout,
_after_layout: &mut Self::AfterLayout,
cx: &mut ElementContext,
) -> Option<Hitbox> {
self.interactivity
.before_paint(bounds, cx, |_style, scroll_offset, hitbox, cx| {
cx.with_element_offset(scroll_offset, |cx| {
for child in &mut self.children {
child.before_paint(cx);
}
});
hitbox
},
)
})
}
fn paint(
&mut self,
bounds: Bounds<Pixels>,
_before_layout: &mut Self::BeforeLayout,
_after_layout: &mut Self::AfterLayout,
hitbox: &mut Option<Hitbox>,
cx: &mut ElementContext,
) {
@@ -1336,13 +1361,13 @@ impl Interactivity {
)
}
/// Commit the bounds of this element according to this interactivity state's configured styles.
/// todo!()
pub fn after_layout<R>(
&mut self,
bounds: Bounds<Pixels>,
content_size: Size<Pixels>,
cx: &mut ElementContext,
f: impl FnOnce(&Style, Point<Pixels>, Option<Hitbox>, &mut ElementContext) -> R,
f: impl FnOnce(&Style, Point<Pixels>, Option<Bounds<Pixels>>, &mut ElementContext) -> R,
) -> R {
self.content_size = content_size;
cx.with_element_state::<InteractiveElementState, _>(
@@ -1367,6 +1392,40 @@ impl Interactivity {
}
}
cx.with_text_style(style.text_style().cloned(), |cx| {
cx.with_content_mask(style.overflow_mask(bounds, cx.rem_size()), |cx| {
let scroll_offset = self.clamp_scroll_position(bounds, &style, cx);
let focus_target_bounds =
self.tracked_focus_handle.as_ref().and_then(|focus_handle| {
if focus_handle.is_focused(cx) {
Some(bounds)
} else {
None
}
});
let result = f(&style, scroll_offset, focus_target_bounds, cx);
(result, element_state)
})
})
},
)
}
/// Commit the bounds of this element according to this interactivity state's configured styles.
pub fn before_paint<R>(
&mut self,
bounds: Bounds<Pixels>,
cx: &mut ElementContext,
f: impl FnOnce(&Style, Point<Pixels>, Option<Hitbox>, &mut ElementContext) -> R,
) -> R {
cx.with_element_state::<InteractiveElementState, _>(
self.element_id.clone(),
|element_state, cx| {
let mut element_state =
element_state.map(|element_state| element_state.unwrap_or_default());
let style = self.compute_style_internal(None, element_state.as_mut(), cx);
cx.with_text_style(style.text_style().cloned(), |cx| {
cx.with_content_mask(style.overflow_mask(bounds, cx.rem_size()), |cx| {
let hitbox = if self.should_insert_hitbox(&style) {
@@ -1375,7 +1434,11 @@ impl Interactivity {
None
};
let scroll_offset = self.clamp_scroll_position(bounds, &style, cx);
let scroll_offset = self
.scroll_offset
.as_ref()
.map(|scroll_offset| *scroll_offset.borrow())
.unwrap_or_default();
let result = f(&style, scroll_offset, hitbox, cx);
(result, element_state)
})
@@ -2263,6 +2326,7 @@ where
{
type BeforeLayout = E::BeforeLayout;
type AfterLayout = E::AfterLayout;
type BeforePaint = E::BeforePaint;
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
self.element.before_layout(cx)
@@ -2271,10 +2335,21 @@ where
fn after_layout(
&mut self,
bounds: Bounds<Pixels>,
state: &mut Self::BeforeLayout,
before_layout: &mut Self::BeforeLayout,
cx: &mut ElementContext,
) -> E::AfterLayout {
self.element.after_layout(bounds, state, cx)
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
self.element.after_layout(bounds, before_layout, cx)
}
fn before_paint(
&mut self,
bounds: Bounds<Pixels>,
before_layout: &mut Self::BeforeLayout,
after_layout: &mut Self::AfterLayout,
cx: &mut ElementContext,
) -> E::BeforePaint {
self.element
.before_paint(bounds, before_layout, after_layout, cx)
}
fn paint(
@@ -2282,9 +2357,11 @@ where
bounds: Bounds<Pixels>,
before_layout: &mut Self::BeforeLayout,
after_layout: &mut Self::AfterLayout,
before_paint: &mut Self::BeforePaint,
cx: &mut ElementContext,
) {
self.element.paint(bounds, before_layout, after_layout, cx)
self.element
.paint(bounds, before_layout, after_layout, before_paint, cx)
}
}
@@ -2346,6 +2423,7 @@ where
{
type BeforeLayout = E::BeforeLayout;
type AfterLayout = E::AfterLayout;
type BeforePaint = E::BeforePaint;
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
self.element.before_layout(cx)
@@ -2354,10 +2432,21 @@ where
fn after_layout(
&mut self,
bounds: Bounds<Pixels>,
state: &mut Self::BeforeLayout,
before_layout: &mut Self::BeforeLayout,
cx: &mut ElementContext,
) -> E::AfterLayout {
self.element.after_layout(bounds, state, cx)
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
self.element.after_layout(bounds, before_layout, cx)
}
fn before_paint(
&mut self,
bounds: Bounds<Pixels>,
before_layout: &mut Self::BeforeLayout,
after_layout: &mut Self::AfterLayout,
cx: &mut ElementContext,
) -> E::BeforePaint {
self.element
.before_paint(bounds, before_layout, after_layout, cx)
}
fn paint(
@@ -2365,9 +2454,11 @@ where
bounds: Bounds<Pixels>,
before_layout: &mut Self::BeforeLayout,
after_layout: &mut Self::AfterLayout,
before_paint: &mut Self::BeforePaint,
cx: &mut ElementContext,
) {
self.element.paint(bounds, before_layout, after_layout, cx);
self.element
.paint(bounds, before_layout, after_layout, before_paint, cx);
}
}

View File

@@ -230,7 +230,8 @@ impl Img {
impl Element for Img {
type BeforeLayout = ();
type AfterLayout = Option<Hitbox>;
type AfterLayout = ();
type BeforePaint = Option<Hitbox>;
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
let layout_id = self.interactivity.before_layout(cx, |mut style, cx| {
@@ -261,16 +262,29 @@ impl Element for Img {
bounds: Bounds<Pixels>,
_before_layout: &mut Self::BeforeLayout,
cx: &mut ElementContext,
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
self.interactivity
.after_layout(bounds, bounds.size, cx, |_, _, _, _| {});
(None, ())
}
fn before_paint(
&mut self,
bounds: Bounds<Pixels>,
_before_layout: &mut Self::BeforeLayout,
_after_layout: &mut Self::AfterLayout,
cx: &mut ElementContext,
) -> Option<Hitbox> {
self.interactivity
.after_layout(bounds, bounds.size, cx, |_, _, hitbox, _| hitbox)
.before_paint(bounds, cx, |_, _, hitbox, _| hitbox)
}
fn paint(
&mut self,
bounds: Bounds<Pixels>,
_: &mut Self::BeforeLayout,
hitbox: &mut Self::AfterLayout,
_before_layout: &mut Self::BeforeLayout,
_after_layout: &mut Self::AfterLayout,
hitbox: &mut Self::BeforePaint,
cx: &mut ElementContext,
) {
let source = self.source.clone();

View File

@@ -89,17 +89,28 @@ pub enum ListSizingBehavior {
Auto,
}
struct LayoutItemsResponse {
/// Layout information computed by the [List] element during `after_layout`.
pub struct ListLayout {
max_item_width: Pixels,
scroll_top: ListOffset,
available_item_space: Size<AvailableSpace>,
item_elements: VecDeque<AnyElement>,
items: VecDeque<MeasuredItem>,
focus_target: Option<FocusTarget>,
}
/// Frame state used by the [List] element after layout.
pub struct ListAfterLayoutState {
struct MeasuredItem {
element: AnyElement,
size: Size<Pixels>,
}
struct FocusTarget {
item_index: usize,
bounds_in_item: Bounds<Pixels>,
}
/// Frame state used by the [List] element during paint.
pub struct ListBeforePaintState {
hitbox: Hitbox,
layout: LayoutItemsResponse,
layout: ListLayout,
}
#[derive(Clone)]
@@ -376,13 +387,14 @@ impl StateInner {
available_height: Pixels,
padding: &Edges<Pixels>,
cx: &mut ElementContext,
) -> LayoutItemsResponse {
) -> ListLayout {
let old_items = self.items.clone();
let mut measured_items = VecDeque::new();
let mut item_elements = VecDeque::new();
let mut items = VecDeque::new();
let mut rendered_height = padding.top;
let mut max_item_width = px(0.);
let mut scroll_top = self.logical_scroll_top();
let mut focus_target = None;
let available_item_space = size(
available_width.map_or(AvailableSpace::MinContent, |width| {
@@ -411,10 +423,20 @@ impl StateInner {
// If we're within the visible area or the height wasn't cached, render and measure the item's element
if visible_height < available_height || size.is_none() {
let mut element = (self.render_item)(scroll_top.item_ix + ix, cx);
let element_size = element.measure(available_item_space, cx);
size = Some(element_size);
let element_measurement = element.layout(available_item_space, cx);
size = Some(element_measurement.size);
if visible_height < available_height {
item_elements.push_back(element);
if let Some(focus_target_bounds) = element_measurement.focus_target_bounds {
focus_target = Some(FocusTarget {
item_index: items.len(),
bounds_in_item: focus_target_bounds,
});
}
items.push_back(MeasuredItem {
element,
size: element_measurement.size,
});
}
}
@@ -435,11 +457,26 @@ impl StateInner {
cursor.prev(&());
if cursor.item().is_some() {
let mut element = (self.render_item)(cursor.start().0, cx);
let element_size = element.measure(available_item_space, cx);
let element_measurement = element.layout(available_item_space, cx);
rendered_height += element_size.height;
measured_items.push_front(ListItem::Rendered { size: element_size });
item_elements.push_front(element)
rendered_height += element_measurement.size.height;
measured_items.push_front(ListItem::Rendered {
size: element_measurement.size,
});
items.push_front(MeasuredItem {
element,
size: element_measurement.size,
});
if let Some(focus_target_bounds) = element_measurement.focus_target_bounds {
focus_target = Some(FocusTarget {
item_index: 0,
bounds_in_item: focus_target_bounds,
});
} else if let Some(focus_target) = focus_target.as_mut() {
focus_target.item_index += 1;
}
} else {
break;
}
@@ -474,7 +511,7 @@ impl StateInner {
*size
} else {
let mut element = (self.render_item)(cursor.start().0, cx);
element.measure(available_item_space, cx)
element.layout(available_item_space, cx).size
};
leading_overdraw += size.height;
@@ -493,11 +530,11 @@ impl StateInner {
self.items = new_items;
LayoutItemsResponse {
ListLayout {
max_item_width,
scroll_top,
available_item_space,
item_elements,
items,
focus_target,
}
}
}
@@ -523,7 +560,8 @@ pub struct ListOffset {
impl Element for List {
type BeforeLayout = ();
type AfterLayout = ListAfterLayoutState;
type AfterLayout = Option<ListLayout>;
type BeforePaint = ListBeforePaintState;
fn before_layout(
&mut self,
@@ -592,17 +630,15 @@ impl Element for List {
fn after_layout(
&mut self,
bounds: Bounds<Pixels>,
_: &mut Self::BeforeLayout,
_before_layout: &mut Self::BeforeLayout,
cx: &mut ElementContext,
) -> ListAfterLayoutState {
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
let state = &mut *self.state.0.borrow_mut();
state.reset = false;
let mut style = Style::default();
style.refine(&self.style);
let hitbox = cx.insert_hitbox(bounds, false);
// If the width of the list has changed, invalidate all cached item heights
if state.last_layout_bounds.map_or(true, |last_bounds| {
last_bounds.size.width != bounds.size.width
@@ -614,48 +650,77 @@ impl Element for List {
}
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
let mut layout_response =
state.layout_items(Some(bounds.size.width), bounds.size.height, &padding, cx);
let layout = state.layout_items(Some(bounds.size.width), bounds.size.height, &padding, cx);
let focus_target_bounds = layout.focus_target.as_ref().map(|focus_target| {
let mut item_origin =
bounds.origin + Point::new(px(0.), padding.top - layout.scroll_top.offset_in_item);
item_origin.y -= layout.scroll_top.offset_in_item;
for item in layout.items.iter().take(focus_target.item_index) {
item_origin.y += item.size.height;
}
Bounds::new(
item_origin + focus_target.bounds_in_item.origin,
focus_target.bounds_in_item.size,
)
});
(focus_target_bounds, Some(layout))
}
fn before_paint(
&mut self,
bounds: Bounds<Pixels>,
_: &mut Self::BeforeLayout,
layout: &mut Self::AfterLayout,
cx: &mut ElementContext,
) -> ListBeforePaintState {
let mut layout = layout.take().unwrap();
let state = &mut *self.state.0.borrow_mut();
state.reset = false;
let mut style = Style::default();
style.refine(&self.style);
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
let hitbox = cx.insert_hitbox(bounds, false);
// Only paint the visible items, if there is actually any space for them (taking padding into account)
if bounds.size.height > padding.top + padding.bottom {
// Paint the visible items
cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
let mut item_origin = bounds.origin + Point::new(px(0.), padding.top);
item_origin.y -= layout_response.scroll_top.offset_in_item;
for mut item_element in &mut layout_response.item_elements {
let item_size = item_element.measure(layout_response.available_item_space, cx);
item_element.layout(item_origin, layout_response.available_item_space, cx);
item_origin.y += item_size.height;
item_origin.y -= layout.scroll_top.offset_in_item;
for item in &mut layout.items {
cx.with_absolute_element_offset(item_origin, |cx| {
item.element.before_paint(cx)
});
item_origin.y += item.size.height;
}
});
}
state.last_layout_bounds = Some(bounds);
state.last_padding = Some(padding);
ListAfterLayoutState {
hitbox,
layout: layout_response,
}
ListBeforePaintState { hitbox, layout }
}
fn paint(
&mut self,
bounds: Bounds<crate::Pixels>,
_: &mut Self::BeforeLayout,
after_layout: &mut Self::AfterLayout,
_: &mut Self::AfterLayout,
before_paint: &mut Self::BeforePaint,
cx: &mut crate::ElementContext,
) {
cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
for item in &mut after_layout.layout.item_elements {
item.paint(cx);
for item in &mut before_paint.layout.items {
item.element.paint(cx);
}
});
let list_state = self.state.clone();
let height = bounds.size.height;
let scroll_top = after_layout.layout.scroll_top;
let hitbox_id = after_layout.hitbox.id;
let scroll_top = before_paint.layout.scroll_top;
let hitbox_id = before_paint.hitbox.id;
cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
if phase == DispatchPhase::Bubble && hitbox_id.is_hovered(cx) {
list_state.0.borrow_mut().scroll(

View File

@@ -38,7 +38,8 @@ impl Svg {
impl Element for Svg {
type BeforeLayout = ();
type AfterLayout = Option<Hitbox>;
type AfterLayout = ();
type BeforePaint = Option<Hitbox>;
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
let layout_id = self
@@ -52,15 +53,28 @@ impl Element for Svg {
bounds: Bounds<Pixels>,
_before_layout: &mut Self::BeforeLayout,
cx: &mut ElementContext,
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
self.interactivity
.after_layout(bounds, bounds.size, cx, |_, _, _, _| {});
(None, ())
}
fn before_paint(
&mut self,
bounds: Bounds<Pixels>,
_before_layout: &mut Self::BeforeLayout,
_after_layout: &mut Self::AfterLayout,
cx: &mut ElementContext,
) -> Option<Hitbox> {
self.interactivity
.after_layout(bounds, bounds.size, cx, |_, _, hitbox, _| hitbox)
.before_paint(bounds, cx, |_, _, hitbox, _| hitbox)
}
fn paint(
&mut self,
bounds: Bounds<Pixels>,
_before_layout: &mut Self::BeforeLayout,
_after_layout: &mut Self::AfterLayout,
hitbox: &mut Option<Hitbox>,
cx: &mut ElementContext,
) where

View File

@@ -19,6 +19,7 @@ use util::ResultExt;
impl Element for &'static str {
type BeforeLayout = TextState;
type AfterLayout = ();
type BeforePaint = ();
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
let mut state = TextState::default();
@@ -27,9 +28,19 @@ impl Element for &'static str {
}
fn after_layout(
&mut self,
_bounds: Bounds<Pixels>,
_before_layout: &mut Self::BeforeLayout,
_cx: &mut ElementContext,
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
(None, ())
}
fn before_paint(
&mut self,
_bounds: Bounds<Pixels>,
_text_state: &mut Self::BeforeLayout,
_: &mut Self::AfterLayout,
_cx: &mut ElementContext,
) {
}
@@ -38,6 +49,7 @@ impl Element for &'static str {
&mut self,
bounds: Bounds<Pixels>,
text_state: &mut TextState,
_: &mut Self::AfterLayout,
_: &mut (),
cx: &mut ElementContext,
) {
@@ -64,6 +76,7 @@ impl IntoElement for String {
impl Element for SharedString {
type BeforeLayout = TextState;
type AfterLayout = ();
type BeforePaint = ();
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
let mut state = TextState::default();
@@ -72,9 +85,19 @@ impl Element for SharedString {
}
fn after_layout(
&mut self,
_bounds: Bounds<Pixels>,
_before_layout: &mut Self::BeforeLayout,
_cx: &mut ElementContext,
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
(None, ())
}
fn before_paint(
&mut self,
_bounds: Bounds<Pixels>,
_text_state: &mut Self::BeforeLayout,
_after_layout: &mut Self::AfterLayout,
_cx: &mut ElementContext,
) {
}
@@ -83,7 +106,8 @@ impl Element for SharedString {
&mut self,
bounds: Bounds<Pixels>,
text_state: &mut Self::BeforeLayout,
_: &mut Self::AfterLayout,
_after_layout: &mut Self::AfterLayout,
_: &mut Self::BeforePaint,
cx: &mut ElementContext,
) {
let text_str: &str = self.as_ref();
@@ -150,6 +174,7 @@ impl StyledText {
impl Element for StyledText {
type BeforeLayout = TextState;
type AfterLayout = ();
type BeforePaint = ();
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
let mut state = TextState::default();
@@ -158,9 +183,19 @@ impl Element for StyledText {
}
fn after_layout(
&mut self,
_bounds: Bounds<Pixels>,
_before_layout: &mut Self::BeforeLayout,
_cx: &mut ElementContext,
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
(None, ())
}
fn before_paint(
&mut self,
_bounds: Bounds<Pixels>,
_state: &mut Self::BeforeLayout,
_: &mut Self::AfterLayout,
_cx: &mut ElementContext,
) {
}
@@ -170,6 +205,7 @@ impl Element for StyledText {
bounds: Bounds<Pixels>,
text_state: &mut Self::BeforeLayout,
_: &mut Self::AfterLayout,
_: &mut (),
cx: &mut ElementContext,
) {
text_state.paint(bounds, &self.text, cx)
@@ -403,16 +439,27 @@ impl InteractiveText {
impl Element for InteractiveText {
type BeforeLayout = TextState;
type AfterLayout = Hitbox;
type AfterLayout = ();
type BeforePaint = Hitbox;
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
self.text.before_layout(cx)
}
fn after_layout(
&mut self,
_bounds: Bounds<Pixels>,
_before_layout: &mut Self::BeforeLayout,
_cx: &mut ElementContext,
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
(None, ())
}
fn before_paint(
&mut self,
bounds: Bounds<Pixels>,
state: &mut Self::BeforeLayout,
_after_layout: &mut Self::AfterLayout,
cx: &mut ElementContext,
) -> Hitbox {
cx.with_element_state::<InteractiveTextState, _>(
@@ -430,7 +477,7 @@ impl Element for InteractiveText {
}
}
self.text.after_layout(bounds, state, cx);
self.text.before_paint(bounds, state, &mut (), cx);
let hitbox = cx.insert_hitbox(bounds, false);
(hitbox, interactive_state)
},
@@ -441,6 +488,7 @@ impl Element for InteractiveText {
&mut self,
bounds: Bounds<Pixels>,
text_state: &mut Self::BeforeLayout,
_after_layout: &mut Self::AfterLayout,
hitbox: &mut Hitbox,
cx: &mut ElementContext,
) {
@@ -577,7 +625,7 @@ impl Element for InteractiveText {
});
}
self.text.paint(bounds, text_state, &mut (), cx);
self.text.paint(bounds, text_state, &mut (), &mut (), cx);
((), Some(interactive_state))
},

View File

@@ -6,8 +6,9 @@
use crate::{
point, px, size, AnyElement, AvailableSpace, Bounds, ContentMask, Element, ElementContext,
ElementId, Hitbox, InteractiveElement, Interactivity, IntoElement, LayoutId, Pixels, Render,
ScrollHandle, Size, StyleRefinement, Styled, View, ViewContext, WindowContext,
ElementId, ElementMeasurement, Hitbox, InteractiveElement, Interactivity, IntoElement,
LayoutId, Pixels, Render, ScrollHandle, Size, StyleRefinement, Styled, View, ViewContext,
WindowContext,
};
use smallvec::SmallVec;
use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
@@ -70,8 +71,9 @@ pub struct UniformList {
/// Frame state used by the [UniformList].
pub struct UniformListFrameState {
item_size: Size<Pixels>,
item_height: Pixels,
items: SmallVec<[AnyElement; 32]>,
visible_range: Range<usize>,
}
/// A handle for controlling the scroll position of a uniform list.
@@ -105,19 +107,22 @@ impl Styled for UniformList {
impl Element for UniformList {
type BeforeLayout = UniformListFrameState;
type AfterLayout = Option<Hitbox>;
type AfterLayout = ();
type BeforePaint = Option<Hitbox>;
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
let max_items = self.item_count;
let item_size = self.measure_item(None, cx);
let item_measurement = self.measure_item(None, cx);
let layout_id = self.interactivity.before_layout(cx, |style, cx| {
cx.request_measured_layout(style, move |known_dimensions, available_space, _cx| {
let desired_height = item_size.height * max_items;
let desired_height = item_measurement.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,
AvailableSpace::MinContent | AvailableSpace::MaxContent => {
item_measurement.size.width
}
});
let height = match available_space.height {
@@ -131,8 +136,9 @@ impl Element for UniformList {
(
layout_id,
UniformListFrameState {
item_size,
item_height: Pixels::default(),
items: SmallVec::new(),
visible_range: 0..0,
},
)
}
@@ -140,28 +146,26 @@ impl Element for UniformList {
fn after_layout(
&mut self,
bounds: Bounds<Pixels>,
before_layout: &mut Self::BeforeLayout,
state: &mut Self::BeforeLayout,
cx: &mut ElementContext,
) -> Option<Hitbox> {
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
let style = self.interactivity.compute_style(None, cx);
let border = style.border_widths.to_pixels(cx.rem_size());
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
let padded_bounds = Bounds::from_corners(
bounds.origin + point(border.left + padding.left, border.top + padding.top),
bounds.lower_right()
- point(border.right + padding.right, border.bottom + padding.bottom),
let padded_size = size(
bounds.size.width - border.left - padding.left - border.right - padding.right,
bounds.size.height - border.top - padding.top - border.bottom - padding.bottom,
);
let item_height = self.measure_item(Some(padded_size.width), cx).size.height;
let content_size = Size {
width: padded_bounds.size.width,
height: before_layout.item_size.height * self.item_count + padding.top + padding.bottom,
width: padded_size.width,
height: item_height * self.item_count + padding.top + padding.bottom,
};
let shared_scroll_offset = self.interactivity.scroll_offset.clone().unwrap();
let item_height = self.measure_item(Some(padded_bounds.size.width), cx).height;
let shared_scroll_to_item = self
// todo!("add support for scrolling to the focused element?");
let deferred_scroll_to_item = self
.scroll_handle
.as_mut()
.and_then(|handle| handle.deferred_scroll_to_item.take());
@@ -170,73 +174,103 @@ impl Element for UniformList {
bounds,
content_size,
cx,
|style, mut scroll_offset, hitbox, cx| {
|style, mut scroll_offset, mut focus_target_bounds, cx| {
if self.item_count == 0 {
return (focus_target_bounds, ());
}
let border = style.border_widths.to_pixels(cx.rem_size());
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
let padded_size = size(
bounds.size.width - border.left - padding.left - border.right - padding.right,
bounds.size.height - border.top - padding.top - border.bottom - padding.bottom,
);
let content_height = item_height * self.item_count + padding.top + padding.bottom;
let min_scroll_offset = padded_size.height - content_height;
let is_scrolled = scroll_offset.y != px(0.);
if is_scrolled && scroll_offset.y < min_scroll_offset {
shared_scroll_offset.borrow_mut().y = min_scroll_offset;
scroll_offset.y = min_scroll_offset;
}
if let Some(ix) = deferred_scroll_to_item {
let list_height = padded_size.height;
let mut updated_scroll_offset = shared_scroll_offset.borrow_mut();
let item_top = item_height * ix + padding.top;
let item_bottom = item_top + item_height;
let scroll_top = -updated_scroll_offset.y;
if item_top < scroll_top + padding.top {
updated_scroll_offset.y = -(item_top) + padding.top;
} else if item_bottom > scroll_top + list_height - padding.bottom {
updated_scroll_offset.y = -(item_bottom - list_height) - padding.bottom;
}
scroll_offset = *updated_scroll_offset;
}
let first_visible_element_ix =
(-(scroll_offset.y + padding.top) / item_height).floor() as usize;
let last_visible_element_ix =
((-scroll_offset.y + padded_size.height) / item_height).ceil() as usize;
let visible_range =
first_visible_element_ix..cmp::min(last_visible_element_ix, self.item_count);
let mut items = (self.render_items)(visible_range.clone(), cx);
let available_space = size(
AvailableSpace::Definite(padded_size.width),
AvailableSpace::Definite(item_height),
);
for mut item in items {
let measurement = item.layout(available_space, cx);
if measurement.focus_target_bounds.is_some() {
focus_target_bounds = measurement.focus_target_bounds;
}
state.items.push(item);
}
state.item_height = item_height;
state.visible_range = visible_range;
(focus_target_bounds, ())
},
)
}
fn before_paint(
&mut self,
bounds: Bounds<Pixels>,
state: &mut Self::BeforeLayout,
_after_layout: &mut Self::AfterLayout,
cx: &mut ElementContext,
) -> Option<Hitbox> {
self.interactivity
.before_paint(bounds, cx, |style, scroll_offset, hitbox, cx| {
let border = style.border_widths.to_pixels(cx.rem_size());
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
let padded_bounds = Bounds::from_corners(
bounds.origin + point(border.left + padding.left, border.top),
bounds.lower_right() - point(border.right + padding.right, border.bottom),
);
if self.item_count > 0 {
let content_height =
item_height * self.item_count + padding.top + padding.bottom;
let min_scroll_offset = padded_bounds.size.height - content_height;
let is_scrolled = scroll_offset.y != px(0.);
if is_scrolled && scroll_offset.y < min_scroll_offset {
shared_scroll_offset.borrow_mut().y = min_scroll_offset;
scroll_offset.y = min_scroll_offset;
let content_mask = ContentMask { bounds };
cx.with_content_mask(Some(content_mask), |cx| {
for (item, ix) in state.items.iter_mut().zip(state.visible_range.clone()) {
let item_y = state.item_height * ix + scroll_offset.y + padding.top;
let item_origin = padded_bounds.origin + point(px(0.), item_y);
cx.with_absolute_element_offset(item_origin, |cx| item.before_paint(cx));
}
if let Some(ix) = shared_scroll_to_item {
let list_height = padded_bounds.size.height;
let mut updated_scroll_offset = shared_scroll_offset.borrow_mut();
let item_top = item_height * ix + padding.top;
let item_bottom = item_top + item_height;
let scroll_top = -updated_scroll_offset.y;
if item_top < scroll_top + padding.top {
updated_scroll_offset.y = -(item_top) + padding.top;
} else if item_bottom > scroll_top + list_height - padding.bottom {
updated_scroll_offset.y = -(item_bottom - list_height) - padding.bottom;
}
scroll_offset = *updated_scroll_offset;
}
let first_visible_element_ix =
(-(scroll_offset.y + padding.top) / item_height).floor() as usize;
let last_visible_element_ix = ((-scroll_offset.y + padded_bounds.size.height)
/ item_height)
.ceil() as usize;
let visible_range = first_visible_element_ix
..cmp::min(last_visible_element_ix, self.item_count);
let mut items = (self.render_items)(visible_range.clone(), cx);
let content_mask = ContentMask { bounds };
cx.with_content_mask(Some(content_mask), |cx| {
for (mut item, ix) in items.into_iter().zip(visible_range) {
let item_origin = padded_bounds.origin
+ point(px(0.), item_height * ix + scroll_offset.y + padding.top);
let available_space = size(
AvailableSpace::Definite(padded_bounds.size.width),
AvailableSpace::Definite(item_height),
);
item.layout(item_origin, available_space, cx);
before_layout.items.push(item);
}
});
}
});
hitbox
},
)
})
}
fn paint(
&mut self,
bounds: Bounds<crate::Pixels>,
before_layout: &mut Self::BeforeLayout,
_after_layout: &mut Self::AfterLayout,
hitbox: &mut Option<Hitbox>,
cx: &mut ElementContext,
) {
@@ -264,9 +298,13 @@ impl UniformList {
self
}
fn measure_item(&self, list_width: Option<Pixels>, cx: &mut ElementContext) -> Size<Pixels> {
fn measure_item(
&self,
list_width: Option<Pixels>,
cx: &mut ElementContext,
) -> ElementMeasurement {
if self.item_count == 0 {
return Size::default();
return ElementMeasurement::default();
}
let item_ix = cmp::min(self.item_to_measure_index, self.item_count - 1);
@@ -278,7 +316,7 @@ impl UniformList {
}),
AvailableSpace::MinContent,
);
item_to_measure.measure(available_space, cx)
item_to_measure.layout(available_space, cx)
}
/// Track and render scroll state of this list with reference to the given scroll handle.

View File

@@ -1,8 +1,8 @@
use crate::{
seal::Sealed, AfterLayoutIndex, AnyElement, AnyModel, AnyWeakModel, AppContext, Bounds,
ContentMask, Element, ElementContext, ElementId, Entity, EntityId, Flatten, FocusHandle,
FocusableView, IntoElement, LayoutId, Model, PaintIndex, Pixels, Render, Style,
StyleRefinement, TextStyle, ViewContext, VisualContext, WeakModel,
seal::Sealed, AfterLayoutIndex, AnyElement, AnyModel, AnyWeakModel, AppContext,
BeforePaintIndex, Bounds, ContentMask, Element, ElementContext, ElementId, Entity, EntityId,
Flatten, FocusHandle, FocusableView, IntoElement, LayoutId, Model, PaintIndex, Pixels, Render,
Style, StyleRefinement, TextStyle, ViewContext, VisualContext, WeakModel,
};
use anyhow::{Context, Result};
use refineable::Refineable;
@@ -24,13 +24,16 @@ impl<V> Sealed for View<V> {}
struct AnyViewState {
after_layout_range: Range<AfterLayoutIndex>,
before_paint_range: Range<BeforePaintIndex>,
paint_range: Range<PaintIndex>,
focus_target_bounds: Option<Bounds<Pixels>>,
cache_key: ViewCacheKey,
}
#[derive(Default)]
struct ViewCacheKey {
bounds: Bounds<Pixels>,
after_layout_bounds: Bounds<Pixels>,
before_paint_bounds: Bounds<Pixels>,
content_mask: ContentMask<Pixels>,
text_style: TextStyle,
}
@@ -92,6 +95,7 @@ impl<V: 'static> View<V> {
impl<V: Render> Element for View<V> {
type BeforeLayout = AnyElement;
type AfterLayout = ();
type BeforePaint = ();
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
@@ -102,14 +106,24 @@ impl<V: Render> Element for View<V> {
}
fn after_layout(
&mut self,
_bounds: Bounds<Pixels>,
element: &mut Self::BeforeLayout,
cx: &mut ElementContext,
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
(element.after_layout(cx), ())
}
fn before_paint(
&mut self,
_: Bounds<Pixels>,
element: &mut Self::BeforeLayout,
_: &mut Self::AfterLayout,
cx: &mut ElementContext,
) {
cx.set_view_id(self.entity_id());
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
element.after_layout(cx)
element.before_paint(cx)
})
}
@@ -118,6 +132,7 @@ impl<V: Render> Element for View<V> {
_: Bounds<Pixels>,
element: &mut Self::BeforeLayout,
_: &mut Self::AfterLayout,
_: &mut Self::BeforePaint,
cx: &mut ElementContext,
) {
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
@@ -277,7 +292,8 @@ impl<V: Render> From<View<V>> for AnyView {
impl Element for AnyView {
type BeforeLayout = Option<AnyElement>;
type AfterLayout = Option<AnyElement>;
type AfterLayout = ();
type BeforePaint = ();
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
if let Some(style) = self.cached_style.as_ref() {
@@ -299,20 +315,16 @@ impl Element for AnyView {
bounds: Bounds<Pixels>,
element: &mut Self::BeforeLayout,
cx: &mut ElementContext,
) -> Option<AnyElement> {
cx.set_view_id(self.entity_id());
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
if self.cached_style.is_some() {
cx.with_element_state::<AnyViewState, _>(
let focus_target_bounds = cx.with_element_state::<AnyViewState, _>(
Some(ElementId::View(self.entity_id())),
|element_state, cx| {
let mut element_state = element_state.unwrap();
let content_mask = cx.content_mask();
let text_style = cx.text_style();
if let Some(mut element_state) = element_state {
if element_state.cache_key.bounds == bounds
&& element_state.cache_key.content_mask == content_mask
if element_state.cache_key.after_layout_bounds == bounds
&& element_state.cache_key.text_style == text_style
&& !cx.window.dirty_views.contains(&self.entity_id())
&& !cx.window.refreshing
@@ -321,34 +333,106 @@ impl Element for AnyView {
cx.reuse_after_layout(element_state.after_layout_range.clone());
let after_layout_end = cx.after_layout_index();
element_state.after_layout_range = after_layout_start..after_layout_end;
return (None, Some(element_state));
return (element_state.focus_target_bounds, Some(element_state));
}
}
let after_layout_start = cx.after_layout_index();
let mut element = (self.render)(self, cx);
element.layout(bounds.origin, bounds.size.into(), cx);
let mut rendered_element = (self.render)(self, cx);
let element_measurement = rendered_element.layout(bounds.size.into(), cx);
*element = Some(rendered_element);
let focus_target_bounds =
element_measurement
.focus_target_bounds
.map(|focus_target_bounds| {
Bounds::new(
bounds.origin + focus_target_bounds.origin,
focus_target_bounds.size,
)
});
let after_layout_end = cx.after_layout_index();
(
Some(element),
Some(AnyViewState {
after_layout_range: after_layout_start..after_layout_end,
paint_range: PaintIndex::default()..PaintIndex::default(),
cache_key: ViewCacheKey {
bounds,
content_mask,
text_style,
},
}),
)
let view_state = AnyViewState {
after_layout_range: after_layout_start..after_layout_end,
before_paint_range: BeforePaintIndex::default()
..BeforePaintIndex::default(),
paint_range: PaintIndex::default()..PaintIndex::default(),
focus_target_bounds,
cache_key: ViewCacheKey {
after_layout_bounds: bounds,
text_style,
..Default::default()
},
};
(focus_target_bounds, Some(view_state))
},
);
(focus_target_bounds, ())
} else {
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
let bounds = element.as_mut().unwrap().after_layout(cx);
(bounds, ())
})
}
}
fn before_paint(
&mut self,
bounds: Bounds<Pixels>,
element: &mut Self::BeforeLayout,
_: &mut Self::AfterLayout,
cx: &mut ElementContext,
) {
cx.set_view_id(self.entity_id());
if self.cached_style.is_some() {
cx.with_element_state::<AnyViewState, _>(
Some(ElementId::View(self.entity_id())),
|element_state, cx| {
let mut element_state = element_state.unwrap().unwrap();
let content_mask = cx.content_mask();
if let Some(element) = element {
let before_paint_start = cx.before_paint_index();
cx.with_absolute_element_offset(bounds.origin, |cx| {
element.before_paint(cx)
});
let before_paint_end = cx.before_paint_index();
element_state.before_paint_range = before_paint_start..before_paint_end;
element_state.cache_key.before_paint_bounds = bounds;
element_state.cache_key.content_mask = content_mask;
} else if element_state.cache_key.before_paint_bounds == bounds
&& element_state.cache_key.content_mask == content_mask
{
let before_paint_start = cx.before_paint_index();
cx.reuse_before_paint(element_state.before_paint_range.clone());
let before_paint_end = cx.before_paint_index();
element_state.before_paint_range = before_paint_start..before_paint_end;
} else {
let mut rendered_element = (self.render)(self, cx);
let after_layout_start = cx.after_layout_index();
rendered_element.layout(bounds.size.into(), cx);
let after_layout_end = cx.after_layout_index();
let before_paint_start = cx.before_paint_index();
cx.with_absolute_element_offset(bounds.origin, |cx| {
rendered_element.before_paint(cx)
});
let before_paint_end = cx.before_paint_index();
element_state.after_layout_range = after_layout_start..after_layout_end;
element_state.before_paint_range = before_paint_start..before_paint_end;
element_state.cache_key.before_paint_bounds = bounds;
element_state.cache_key.content_mask = content_mask;
*element = Some(rendered_element);
}
((), Some(element_state))
},
)
} else {
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
let mut element = element.take().unwrap();
element.after_layout(cx);
Some(element)
element.as_mut().unwrap().before_paint(cx)
})
}
}
@@ -356,8 +440,9 @@ impl Element for AnyView {
fn paint(
&mut self,
_bounds: Bounds<Pixels>,
_: &mut Self::BeforeLayout,
element: &mut Self::AfterLayout,
element: &mut Self::BeforeLayout,
_: &mut Self::AfterLayout,
_: &mut Self::BeforePaint,
cx: &mut ElementContext,
) {
if self.cached_style.is_some() {

View File

@@ -121,7 +121,7 @@ pub(crate) struct DeferredDraw {
text_style_stack: Vec<TextStyleRefinement>,
element: Option<AnyElement>,
absolute_offset: Point<Pixels>,
layout_range: Range<AfterLayoutIndex>,
before_paint_range: Range<BeforePaintIndex>,
paint_range: Range<PaintIndex>,
}
@@ -146,6 +146,12 @@ pub(crate) struct Frame {
#[derive(Clone, Default)]
pub(crate) struct AfterLayoutIndex {
accessed_element_states_index: usize,
line_layout_index: LineLayoutIndex,
}
#[derive(Clone, Default)]
pub(crate) struct BeforePaintIndex {
hitboxes_index: usize,
tooltips_index: usize,
deferred_draws_index: usize,
@@ -399,7 +405,8 @@ impl<'a> ElementContext<'a> {
// Layout all root elements.
let mut root_element = self.window.root_view.as_ref().unwrap().clone().into_any();
root_element.layout(Point::default(), self.window.viewport_size.into(), self);
root_element.layout(self.window.viewport_size.into(), self);
root_element.before_paint(self);
let mut sorted_deferred_draws =
(0..self.window.next_frame.deferred_draws.len()).collect::<SmallVec<[_; 8]>>();
@@ -411,13 +418,15 @@ impl<'a> ElementContext<'a> {
let mut tooltip_element = None;
if let Some(prompt) = self.window.prompt.take() {
let mut element = prompt.view.any_view().into_any();
element.layout(Point::default(), self.window.viewport_size.into(), self);
element.layout(self.window.viewport_size.into(), self);
element.before_paint(self);
prompt_element = Some(element);
self.window.prompt = Some(prompt);
} else if let Some(active_drag) = self.app.active_drag.take() {
let mut element = active_drag.view.clone().into_any();
element.layout(AvailableSpace::min_size(), self);
let offset = self.mouse_position() - active_drag.cursor_offset;
element.layout(offset, AvailableSpace::min_size(), self);
self.with_element_offset(offset, |cx| element.before_paint(cx));
active_drag_element = Some(element);
self.app.active_drag = Some(active_drag);
} else {
@@ -446,7 +455,7 @@ impl<'a> ElementContext<'a> {
let tooltip_request = tooltip_request.unwrap();
let mut element = tooltip_request.tooltip.view.clone().into_any();
let mouse_position = tooltip_request.tooltip.mouse_position;
let tooltip_size = element.measure(AvailableSpace::min_size(), self);
let tooltip_size = element.layout(AvailableSpace::min_size(), self).size;
let mut tooltip_bounds = Bounds::new(mouse_position + point(px(1.), px(1.)), tooltip_size);
let window_bounds = Bounds {
@@ -478,7 +487,7 @@ impl<'a> ElementContext<'a> {
}
}
self.with_absolute_element_offset(tooltip_bounds.origin, |cx| element.after_layout(cx));
self.with_absolute_element_offset(tooltip_bounds.origin, |cx| element.before_paint(cx));
self.window.tooltip_bounds = Some(TooltipBounds {
id: tooltip_request.id,
@@ -500,16 +509,16 @@ impl<'a> ElementContext<'a> {
.dispatch_tree
.set_active_node(deferred_draw.parent_node);
let layout_start = self.after_layout_index();
let layout_start = self.before_paint_index();
if let Some(element) = deferred_draw.element.as_mut() {
self.with_absolute_element_offset(deferred_draw.absolute_offset, |cx| {
element.after_layout(cx)
element.before_paint(cx)
});
} else {
self.reuse_after_layout(deferred_draw.layout_range.clone());
self.reuse_before_paint(deferred_draw.before_paint_range.clone());
}
let layout_end = self.after_layout_index();
deferred_draw.layout_range = layout_start..layout_end;
let layout_end = self.before_paint_index();
deferred_draw.before_paint_range = layout_start..layout_end;
}
assert_eq!(
self.window.next_frame.deferred_draws.len(),
@@ -548,6 +557,26 @@ impl<'a> ElementContext<'a> {
pub(crate) fn after_layout_index(&self) -> AfterLayoutIndex {
AfterLayoutIndex {
accessed_element_states_index: self.window.next_frame.accessed_element_states.len(),
line_layout_index: self.window.text_system.layout_index(),
}
}
pub(crate) fn reuse_after_layout(&mut self, range: Range<AfterLayoutIndex>) {
let window = &mut self.window;
window.next_frame.accessed_element_states.extend(
window.rendered_frame.accessed_element_states[range.start.accessed_element_states_index
..range.end.accessed_element_states_index]
.iter()
.cloned(),
);
window
.text_system
.reuse_layouts(range.start.line_layout_index..range.end.line_layout_index);
}
pub(crate) fn before_paint_index(&self) -> BeforePaintIndex {
BeforePaintIndex {
hitboxes_index: self.window.next_frame.hitboxes.len(),
tooltips_index: self.window.next_frame.tooltip_requests.len(),
deferred_draws_index: self.window.next_frame.deferred_draws.len(),
@@ -557,7 +586,7 @@ impl<'a> ElementContext<'a> {
}
}
pub(crate) fn reuse_after_layout(&mut self, range: Range<AfterLayoutIndex>) {
pub(crate) fn reuse_before_paint(&mut self, range: Range<BeforePaintIndex>) {
let window = &mut self.window;
window.next_frame.hitboxes.extend(
window.rendered_frame.hitboxes[range.start.hitboxes_index..range.end.hitboxes_index]
@@ -595,7 +624,7 @@ impl<'a> ElementContext<'a> {
priority: deferred_draw.priority,
element: None,
absolute_offset: deferred_draw.absolute_offset,
layout_range: deferred_draw.layout_range.clone(),
before_paint_range: deferred_draw.before_paint_range.clone(),
paint_range: deferred_draw.paint_range.clone(),
}),
);
@@ -974,7 +1003,7 @@ impl<'a> ElementContext<'a> {
assert_eq!(
window.draw_phase,
DrawPhase::Layout,
"defer_draw can only be called during before_layout or after_layout"
"defer_draw can only be called during before_layout or before_paint"
);
let parent_node = window.next_frame.dispatch_tree.active_node_id().unwrap();
window.next_frame.deferred_draws.push(DeferredDraw {
@@ -984,7 +1013,7 @@ impl<'a> ElementContext<'a> {
priority,
element: Some(element),
absolute_offset,
layout_range: AfterLayoutIndex::default()..AfterLayoutIndex::default(),
before_paint_range: BeforePaintIndex::default()..BeforePaintIndex::default(),
paint_range: PaintIndex::default()..PaintIndex::default(),
});
}
@@ -1397,7 +1426,7 @@ impl<'a> ElementContext<'a> {
bounds
}
/// This method should be called during `after_layout`. You can use
/// This method should be called during `before_paint`. You can use
/// the returned [Hitbox] during `paint` or in an event handler
/// to determine whether the inserted hitbox was the topmost.
pub fn insert_hitbox(&mut self, bounds: Bounds<Pixels>, opaque: bool) -> Hitbox {

View File

@@ -155,7 +155,7 @@ impl FocusableView for ImageView {
impl Render for ImageView {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let checkered_background = |bounds: Bounds<Pixels>, _, cx: &mut ElementContext| {
let checkered_background = |bounds: Bounds<Pixels>, cx: &mut ElementContext| {
let square_size = 32.0;
let start_y = bounds.origin.y.0;
@@ -190,7 +190,7 @@ impl Render for ImageView {
}
};
let checkered_background = canvas(|_, _| (), checkered_background)
let checkered_background = canvas(checkered_background)
.border_2()
.border_color(cx.theme().styles.colors.border)
.size_full()

View File

@@ -363,14 +363,15 @@ impl Render for SyntaxTreeView {
.text_bg(cx.theme().colors().background).into_any_element();
rendered = rendered.child(
canvas(
move |bounds, cx| {
list.layout(bounds.origin, bounds.size.into(), cx);
list
},
|_, mut list, cx| list.paint(cx),
)
.size_full(),
// canvas(
// move |bounds, cx| {
// list.layout(bounds.origin, bounds.size.into(), cx);
// list
// },
// |_, mut list, cx| list.paint(cx),
// )
// .size_full(),
todo!("replace canvas"),
);
}

View File

@@ -542,7 +542,7 @@ impl TerminalElement {
impl Element for TerminalElement {
type BeforeLayout = ();
type AfterLayout = LayoutState;
type BeforePaint = LayoutState;
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
self.interactivity.occlude_mouse();
@@ -556,14 +556,14 @@ impl Element for TerminalElement {
(layout_id, ())
}
fn after_layout(
fn before_paint(
&mut self,
bounds: Bounds<Pixels>,
_: &mut Self::BeforeLayout,
cx: &mut ElementContext,
) -> Self::AfterLayout {
) -> Self::BeforePaint {
self.interactivity
.after_layout(bounds, bounds.size, cx, |_, _, hitbox, cx| {
.before_paint(bounds, bounds.size, cx, |_, _, hitbox, cx| {
let hitbox = hitbox.unwrap();
let settings = ThemeSettings::get_global(cx).clone();
@@ -776,7 +776,7 @@ impl Element for TerminalElement {
&mut self,
bounds: Bounds<Pixels>,
_: &mut Self::BeforeLayout,
layout: &mut Self::AfterLayout,
layout: &mut Self::BeforePaint,
cx: &mut ElementContext<'_>,
) {
cx.paint_quad(fill(bounds, layout.background_color));

View File

@@ -169,7 +169,8 @@ pub struct PopoverMenuFrameState {
impl<M: ManagedView> Element for PopoverMenu<M> {
type BeforeLayout = PopoverMenuFrameState;
type AfterLayout = Option<HitboxId>;
type AfterLayout = ();
type BeforePaint = Option<HitboxId>;
fn before_layout(&mut self, cx: &mut ElementContext) -> (gpui::LayoutId, Self::BeforeLayout) {
self.with_element_state(cx, |this, element_state, cx| {
@@ -219,14 +220,40 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
_bounds: Bounds<Pixels>,
before_layout: &mut Self::BeforeLayout,
cx: &mut ElementContext,
) -> Option<HitboxId> {
self.with_element_state(cx, |_this, element_state, cx| {
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
cx.with_element_id(Some(self.id.clone()), |cx| {
let mut focus_target_bounds = None;
if let Some(child) = before_layout.child_element.as_mut() {
child.after_layout(cx);
if let Some(child_focus_target_bounds) = child.after_layout(cx) {
focus_target_bounds = Some(child_focus_target_bounds);
}
}
if let Some(menu) = before_layout.menu_element.as_mut() {
menu.after_layout(cx);
if let Some(menu_focus_target_bounds) = menu.after_layout(cx) {
focus_target_bounds = Some(menu_focus_target_bounds);
}
}
(focus_target_bounds, ())
})
}
fn before_paint(
&mut self,
_bounds: Bounds<Pixels>,
before_layout: &mut Self::BeforeLayout,
_after_layout: &mut Self::AfterLayout,
cx: &mut ElementContext,
) -> Option<HitboxId> {
self.with_element_state(cx, |_this, element_state, cx| {
if let Some(child) = before_layout.child_element.as_mut() {
child.before_paint(cx);
}
if let Some(menu) = before_layout.menu_element.as_mut() {
menu.before_paint(cx);
}
before_layout.child_layout_id.map(|layout_id| {
@@ -241,6 +268,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
&mut self,
_: Bounds<gpui::Pixels>,
before_layout: &mut Self::BeforeLayout,
_after_layout: &mut Self::AfterLayout,
child_hitbox: &mut Option<HitboxId>,
cx: &mut ElementContext,
) {

View File

@@ -97,7 +97,8 @@ pub struct MenuHandleFrameState {
impl<M: ManagedView> Element for RightClickMenu<M> {
type BeforeLayout = MenuHandleFrameState;
type AfterLayout = Hitbox;
type AfterLayout = ();
type BeforePaint = Hitbox;
fn before_layout(&mut self, cx: &mut ElementContext) -> (gpui::LayoutId, Self::BeforeLayout) {
self.with_element_state(cx, |this, element_state, cx| {
@@ -144,20 +145,46 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
}
fn after_layout(
&mut self,
_bounds: Bounds<Pixels>,
before_layout: &mut Self::BeforeLayout,
cx: &mut ElementContext,
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
cx.with_element_id(Some(self.id.clone()), |cx| {
let mut focus_target_bounds = None;
if let Some(child) = before_layout.child_element.as_mut() {
if let Some(child_focus_target_bounds) = child.after_layout(cx) {
focus_target_bounds = Some(child_focus_target_bounds);
}
}
if let Some(menu) = before_layout.menu_element.as_mut() {
if let Some(menu_focus_target_bounds) = menu.after_layout(cx) {
focus_target_bounds = Some(menu_focus_target_bounds);
}
}
(focus_target_bounds, ())
})
}
fn before_paint(
&mut self,
bounds: Bounds<Pixels>,
before_layout: &mut Self::BeforeLayout,
_after_layout: &mut Self::AfterLayout,
cx: &mut ElementContext,
) -> Hitbox {
cx.with_element_id(Some(self.id.clone()), |cx| {
let hitbox = cx.insert_hitbox(bounds, false);
if let Some(child) = before_layout.child_element.as_mut() {
child.after_layout(cx);
child.before_paint(cx);
}
if let Some(menu) = before_layout.menu_element.as_mut() {
menu.after_layout(cx);
menu.before_paint(cx);
}
hitbox
@@ -168,7 +195,8 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
&mut self,
_bounds: Bounds<gpui::Pixels>,
before_layout: &mut Self::BeforeLayout,
hitbox: &mut Self::AfterLayout,
_after_layout: &mut Self::AfterLayout,
hitbox: &mut Self::BeforePaint,
cx: &mut ElementContext,
) {
self.with_element_state(cx, |this, element_state, cx| {

View File

@@ -649,7 +649,7 @@ mod element {
children: Vec<PaneAxisChildLayout>,
}
struct PaneAxisChildLayout {
pub struct PaneAxisChildLayout {
bounds: Bounds<Pixels>,
element: AnyElement,
handle: Option<PaneAxisHandleLayout>,
@@ -793,7 +793,8 @@ mod element {
impl Element for PaneAxisElement {
type BeforeLayout = ();
type AfterLayout = PaneAxisLayout;
type AfterLayout = Vec<PaneAxisChildLayout>;
type BeforePaint = PaneAxisLayout;
fn before_layout(
&mut self,
@@ -811,18 +812,9 @@ mod element {
fn after_layout(
&mut self,
bounds: Bounds<Pixels>,
_state: &mut Self::BeforeLayout,
_before_layout: &mut Self::BeforeLayout,
cx: &mut ElementContext,
) -> PaneAxisLayout {
let dragged_handle = cx.with_element_state::<Rc<RefCell<Option<usize>>>, _>(
Some(self.basis.into()),
|state, _cx| {
let state = state
.unwrap()
.unwrap_or_else(|| Rc::new(RefCell::new(None)));
(state.clone(), Some(state))
},
);
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
let flexes = self.flexes.lock().clone();
let len = self.children.len();
debug_assert!(flexes.len() == len);
@@ -844,13 +836,9 @@ mod element {
let mut origin = bounds.origin;
let space_per_flex = bounds.size.along(self.axis) / total_flex;
let mut bounding_boxes = self.bounding_boxes.lock();
bounding_boxes.clear();
let mut focus_target_bounds = None;
let mut layout = PaneAxisLayout {
dragged_handle: dragged_handle.clone(),
children: Vec::new(),
};
let mut children = Vec::new();
for (ix, mut child) in mem::take(&mut self.children).into_iter().enumerate() {
let child_flex = active_pane_magnification
.map(|magnification| {
@@ -866,25 +854,76 @@ mod element {
.size
.apply_along(self.axis, |_| space_per_flex * child_flex)
.map(|d| d.round());
let child_bounds = Bounds::new(origin, child_size);
let child_bounds = Bounds {
origin,
size: child_size,
};
bounding_boxes.push(Some(child_bounds));
child.layout(origin, child_size.into(), cx);
let child_measurement = child.layout(child_size.into(), cx);
if let Some(child_focus_target_bounds) = child_measurement.focus_target_bounds {
focus_target_bounds = Some(Bounds::new(
child_bounds.origin + child_focus_target_bounds.origin,
child_focus_target_bounds.size,
));
}
origin = origin.apply_along(self.axis, |val| val + child_size.along(self.axis));
layout.children.push(PaneAxisChildLayout {
children.push(PaneAxisChildLayout {
bounds: child_bounds,
element: child,
handle: None,
})
}
for (ix, child_layout) in layout.children.iter_mut().enumerate() {
if active_pane_magnification.is_none() {
if ix < len - 1 {
(focus_target_bounds, children)
}
fn before_paint(
&mut self,
bounds: Bounds<Pixels>,
_before_layout: &mut Self::BeforeLayout,
children: &mut Self::AfterLayout,
cx: &mut ElementContext,
) -> PaneAxisLayout {
let dragged_handle = cx.with_element_state::<Rc<RefCell<Option<usize>>>, _>(
Some(self.basis.into()),
|state, _cx| {
let state = state
.unwrap()
.unwrap_or_else(|| Rc::new(RefCell::new(None)));
(state.clone(), Some(state))
},
);
let magnification_value = WorkspaceSettings::get(None, cx).active_pane_magnification;
let active_pane_magnification = if magnification_value == 1. {
None
} else {
Some(magnification_value)
};
let mut origin = bounds.origin;
let mut bounding_boxes = self.bounding_boxes.lock();
bounding_boxes.clear();
let mut layout = PaneAxisLayout {
dragged_handle: dragged_handle.clone(),
children: Vec::new(),
};
for child in children {
let child_bounds = Bounds {
origin,
size: child.bounds.size,
};
bounding_boxes.push(Some(child_bounds));
cx.with_absolute_element_offset(origin, |cx| child.element.before_paint(cx));
origin =
origin.apply_along(self.axis, |val| val + child_bounds.size.along(self.axis));
child.bounds = child_bounds;
}
if active_pane_magnification.is_none() {
let mut child_layouts = layout.children.iter_mut().peekable();
while let Some(child_layout) = child_layouts.next() {
if child_layouts.peek().is_some() {
child_layout.handle =
Some(Self::layout_handle(self.axis, child_layout.bounds, cx));
}
@@ -898,7 +937,8 @@ mod element {
&mut self,
bounds: gpui::Bounds<ui::prelude::Pixels>,
_: &mut Self::BeforeLayout,
layout: &mut Self::AfterLayout,
_: &mut Self::AfterLayout,
layout: &mut Self::BeforePaint,
cx: &mut ui::prelude::ElementContext,
) {
for child in &mut layout.children {

View File

@@ -4007,12 +4007,9 @@ impl Render for Workspace {
.border_color(colors.border)
.child({
let this = cx.view().clone();
canvas(
move |bounds, cx| this.update(cx, |this, _cx| this.bounds = bounds),
|_, _, _| {},
)
.absolute()
.size_full()
canvas(move |bounds, cx| this.update(cx, |this, _cx| this.bounds = bounds))
.absolute()
.size_full()
})
.when(self.zoomed.is_none(), |this| {
this.on_drag_move(cx.listener(
@@ -4973,6 +4970,7 @@ struct DisconnectedOverlay;
impl Element for DisconnectedOverlay {
type BeforeLayout = AnyElement;
type AfterLayout = ();
type BeforePaint = ();
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
let mut background = cx.theme().colors().elevated_surface_background;
@@ -4996,20 +4994,31 @@ impl Element for DisconnectedOverlay {
}
fn after_layout(
&mut self,
_bounds: Bounds<Pixels>,
overlay: &mut Self::BeforeLayout,
cx: &mut ElementContext,
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
(overlay.after_layout(cx), ())
}
fn before_paint(
&mut self,
bounds: Bounds<Pixels>,
overlay: &mut Self::BeforeLayout,
_after_layout: &mut Self::AfterLayout,
cx: &mut ElementContext,
) {
cx.insert_hitbox(bounds, true);
overlay.after_layout(cx);
overlay.before_paint(cx);
}
fn paint(
&mut self,
_: Bounds<Pixels>,
overlay: &mut Self::BeforeLayout,
_: &mut Self::AfterLayout,
_after_layout: &mut Self::AfterLayout,
_: &mut Self::BeforePaint,
cx: &mut ElementContext,
) {
overlay.paint(cx)