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 view = cx.view().clone();
let scroll_handle = self.saved_conversations_scroll_handle.clone(); let scroll_handle = self.saved_conversations_scroll_handle.clone();
let conversation_count = self.saved_conversations.len(); let conversation_count = self.saved_conversations.len();
canvas( todo!("replace canvas")
move |bounds, cx| { // canvas(
let mut saved_conversations = uniform_list( // move |_, cx| {
view, // let saved_conversations = uniform_list(
"saved_conversations", // view.clone(),
conversation_count, // "saved_conversations",
|this, range, cx| { // conversation_count,
range // |this, range, cx| {
.map(|ix| this.render_saved_conversation(ix, cx)) // range
.collect() // .map(|ix| this.render_saved_conversation(ix, cx))
}, // .collect()
) // },
.track_scroll(scroll_handle) // )
.into_any_element(); // .track_scroll(scroll_handle.clone())
saved_conversations.layout( // .into_any_element();
bounds.origin, // saved_conversations.layout(absolute_offset, available_space, cx)
bounds.size.map(AvailableSpace::Definite), // // compute layout for saved conversations
cx, // saved_conversations
); // },
saved_conversations // move |bounds, saved_conversations, cx| {
}, // saved_conversations.layout(
|_bounds, mut saved_conversations, cx| saved_conversations.paint(cx), // bounds.origin,
) // bounds.size.map(AvailableSpace::Definite),
.size_full() // cx,
.into_any_element() // );
// 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() { } else if let Some(editor) = self.active_conversation_editor() {
let editor = editor.clone(); let editor = editor.clone();
let conversation = editor.read(cx).conversation.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 thickness = px(1.);
let color = cx.theme().colors().text; let color = cx.theme().colors().text;
canvas( canvas(move |bounds, cx| {
|_, _| {}, let start_x = (bounds.left() + bounds.right() - thickness) / 2.;
move |bounds, _, cx| { let start_y = (bounds.top() + bounds.bottom() - thickness) / 2.;
let start_x = (bounds.left() + bounds.right() - thickness) / 2.; let right = bounds.right();
let start_y = (bounds.top() + bounds.bottom() - thickness) / 2.; let top = bounds.top();
let right = bounds.right();
let top = bounds.top();
cx.paint_quad(fill( cx.paint_quad(fill(
Bounds::from_corners( Bounds::from_corners(
point(start_x, top), point(start_x, top),
point( point(
start_x + thickness, start_x + thickness,
if is_last { if is_last {
start_y start_y
} else { } else {
bounds.bottom() + if overdraw { px(1.) } else { px(0.) } bounds.bottom() + if overdraw { px(1.) } else { px(0.) }
}, },
),
), ),
color, ),
)); color,
cx.paint_quad(fill( ));
Bounds::from_corners(point(start_x, start_y), point(right, start_y + thickness)), cx.paint_quad(fill(
color, Bounds::from_corners(point(start_x, start_y), point(right, start_y + thickness)),
)); color,
}, ));
) })
.w(width) .w(width)
.h(line_height) .h(line_height)
} }

View File

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

View File

@@ -476,8 +476,8 @@ pub struct Editor {
+ Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>, + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
>, >,
>, >,
last_bounds: Option<Bounds<Pixels>>, last_layout_bounds: Option<Bounds<Pixels>>,
expect_bounds_change: Option<Bounds<Pixels>>, expect_layout_bounds_change: Option<Bounds<Pixels>>,
} }
#[derive(Clone)] #[derive(Clone)]
@@ -1492,8 +1492,8 @@ impl Editor {
inlay_hint_cache: InlayHintCache::new(inlay_hint_settings), inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
gutter_hovered: false, gutter_hovered: false,
pixel_position_of_newest_cursor: None, pixel_position_of_newest_cursor: None,
last_bounds: None, last_layout_bounds: None,
expect_bounds_change: None, expect_layout_bounds_change: None,
gutter_width: Default::default(), gutter_width: Default::default(),
style: None, style: None,
show_cursor_names: false, 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) let icon_size = buttons(&diagnostic, cx.block_id)
.into_any_element() .into_any_element()
.measure(AvailableSpace::min_size(), cx); .layout(AvailableSpace::min_size(), cx)
.size;
h_flex() h_flex()
.id(cx.block_id) .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>) { 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 display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let mut scroll_position = self.scroll_manager.scroll_position(&display_map); let mut scroll_position = self.scroll_manager.scroll_position(&display_map);
let original_y = scroll_position.y; 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. { if scroll_position.y != 0. {
scroll_position.y += (bounds.top() - last_bounds.top()) / line_height; scroll_position.y += (bounds.top() - last_bounds.top()) / line_height;
if scroll_position.y < 0. { if scroll_position.y < 0. {

View File

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

View File

@@ -734,7 +734,8 @@ impl VisualTestContext {
self.update(|cx| { self.update(|cx| {
cx.with_element_context(|cx| { cx.with_element_context(|cx| {
let mut element = f(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); element.paint(cx);
}); });

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use crate::{AnyElement, Element, ElementId, IntoElement}; use crate::{AnyElement, Bounds, Element, ElementId, IntoElement, Pixels};
pub use easing::*; pub use easing::*;
@@ -86,8 +86,8 @@ struct AnimationState {
impl<E: IntoElement + 'static> Element for AnimationElement<E> { impl<E: IntoElement + 'static> Element for AnimationElement<E> {
type BeforeLayout = AnyElement; type BeforeLayout = AnyElement;
type AfterLayout = (); type AfterLayout = ();
type BeforePaint = ();
fn before_layout( fn before_layout(
&mut self, &mut self,
@@ -136,18 +136,29 @@ impl<E: IntoElement + 'static> Element for AnimationElement<E> {
fn after_layout( fn after_layout(
&mut self, &mut self,
_bounds: crate::Bounds<crate::Pixels>, _bounds: Bounds<Pixels>,
element: &mut Self::BeforeLayout, element: &mut Self::BeforeLayout,
cx: &mut crate::ElementContext, cx: &mut crate::ElementContext,
) -> Self::AfterLayout { ) -> (Option<Bounds<Pixels>>, ()) {
element.after_layout(cx); (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( fn paint(
&mut self, &mut self,
_bounds: crate::Bounds<crate::Pixels>, _bounds: Bounds<Pixels>,
element: &mut Self::BeforeLayout, element: &mut Self::BeforeLayout,
_: &mut Self::AfterLayout, _after_layout: &mut Self::AfterLayout,
_before_paint: &mut Self::BeforePaint,
cx: &mut crate::ElementContext, cx: &mut crate::ElementContext,
) { ) {
element.paint(cx); 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. /// Construct a canvas element with the given paint callback.
/// Useful for adding short term custom drawing to a view. /// Useful for adding short term custom drawing to a view.
pub fn canvas<T>( pub fn canvas(paint: impl 'static + FnOnce(Bounds<Pixels>, &mut ElementContext)) -> Canvas {
after_layout: impl 'static + FnOnce(Bounds<Pixels>, &mut ElementContext) -> T,
paint: impl 'static + FnOnce(Bounds<Pixels>, T, &mut ElementContext),
) -> Canvas<T> {
Canvas { Canvas {
after_layout: Some(Box::new(after_layout)),
paint: Some(Box::new(paint)), paint: Some(Box::new(paint)),
style: StyleRefinement::default(), 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 /// A canvas element, meant for accessing the low level paint API without defining a whole
/// custom element /// custom element
pub struct Canvas<T> { pub struct Canvas {
after_layout: Option<Box<dyn FnOnce(Bounds<Pixels>, &mut ElementContext) -> T>>, paint: Option<Box<dyn FnOnce(Bounds<Pixels>, &mut ElementContext)>>,
paint: Option<Box<dyn FnOnce(Bounds<Pixels>, T, &mut ElementContext)>>,
style: StyleRefinement, style: StyleRefinement,
} }
impl<T: 'static> IntoElement for Canvas<T> { impl IntoElement for Canvas {
type Element = Self; type Element = Self;
fn into_element(self) -> Self::Element { 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 BeforeLayout = Style;
type AfterLayout = Option<T>; type AfterLayout = ();
type BeforePaint = ();
fn before_layout(&mut self, cx: &mut ElementContext) -> (crate::LayoutId, Self::BeforeLayout) { fn before_layout(&mut self, cx: &mut ElementContext) -> (crate::LayoutId, Self::BeforeLayout) {
let mut style = Style::default(); let mut style = Style::default();
@@ -44,28 +40,35 @@ impl<T: 'static> Element for Canvas<T> {
fn after_layout( fn after_layout(
&mut self, &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, _before_layout: &mut Style,
cx: &mut ElementContext, _after_layout: &mut Self::AfterLayout,
) -> Option<T> { _cx: &mut ElementContext,
Some(self.after_layout.take().unwrap()(bounds, cx)) ) {
} }
fn paint( fn paint(
&mut self, &mut self,
bounds: Bounds<Pixels>, bounds: Bounds<Pixels>,
style: &mut Style, style: &mut Style,
after_layout: &mut Self::AfterLayout, _after_layout: &mut Self::AfterLayout,
_before_paint: &mut Self::BeforePaint,
cx: &mut ElementContext, cx: &mut ElementContext,
) { ) {
let after_layout = after_layout.take().unwrap(); style.paint(bounds, cx, |cx| (self.paint.take().unwrap())(bounds, cx));
style.paint(bounds, cx, |cx| {
(self.paint.take().unwrap())(bounds, after_layout, cx)
});
} }
} }
impl<T> Styled for Canvas<T> { impl Styled for Canvas {
fn style(&mut self) -> &mut crate::StyleRefinement { fn style(&mut self) -> &mut crate::StyleRefinement {
&mut self.style &mut self.style
} }

View File

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

View File

@@ -1121,7 +1121,8 @@ impl ParentElement for Div {
impl Element for Div { impl Element for Div {
type BeforeLayout = DivFrameState; type BeforeLayout = DivFrameState;
type AfterLayout = Option<Hitbox>; type AfterLayout = ();
type BeforePaint = Option<Hitbox>;
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) { fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
let mut child_layout_ids = SmallVec::new(); let mut child_layout_ids = SmallVec::new();
@@ -1143,7 +1144,7 @@ impl Element for Div {
bounds: Bounds<Pixels>, bounds: Bounds<Pixels>,
before_layout: &mut Self::BeforeLayout, before_layout: &mut Self::BeforeLayout,
cx: &mut ElementContext, cx: &mut ElementContext,
) -> Option<Hitbox> { ) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
let mut child_min = point(Pixels::MAX, Pixels::MAX); let mut child_min = point(Pixels::MAX, Pixels::MAX);
let mut child_max = Point::default(); let mut child_max = Point::default();
let content_size = if before_layout.child_layout_ids.is_empty() { let content_size = if before_layout.child_layout_ids.is_empty() {
@@ -1177,25 +1178,49 @@ impl Element for Div {
(child_max - child_min).into() (child_max - child_min).into()
}; };
self.interactivity.after_layout( let focus_target_bounds = self.interactivity.after_layout(
bounds, bounds,
content_size, content_size,
cx, cx,
|_style, scroll_offset, hitbox, cx| { |_, scroll_offset, mut focus_target_bounds, cx| {
cx.with_element_offset(scroll_offset, |cx| { cx.with_element_offset(scroll_offset, |cx| {
for child in &mut self.children { 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 hitbox
}, })
)
} }
fn paint( fn paint(
&mut self, &mut self,
bounds: Bounds<Pixels>, bounds: Bounds<Pixels>,
_before_layout: &mut Self::BeforeLayout, _before_layout: &mut Self::BeforeLayout,
_after_layout: &mut Self::AfterLayout,
hitbox: &mut Option<Hitbox>, hitbox: &mut Option<Hitbox>,
cx: &mut ElementContext, 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>( pub fn after_layout<R>(
&mut self, &mut self,
bounds: Bounds<Pixels>, bounds: Bounds<Pixels>,
content_size: Size<Pixels>, content_size: Size<Pixels>,
cx: &mut ElementContext, 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 { ) -> R {
self.content_size = content_size; self.content_size = content_size;
cx.with_element_state::<InteractiveElementState, _>( 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_text_style(style.text_style().cloned(), |cx| {
cx.with_content_mask(style.overflow_mask(bounds, cx.rem_size()), |cx| { cx.with_content_mask(style.overflow_mask(bounds, cx.rem_size()), |cx| {
let hitbox = if self.should_insert_hitbox(&style) { let hitbox = if self.should_insert_hitbox(&style) {
@@ -1375,7 +1434,11 @@ impl Interactivity {
None 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); let result = f(&style, scroll_offset, hitbox, cx);
(result, element_state) (result, element_state)
}) })
@@ -2263,6 +2326,7 @@ where
{ {
type BeforeLayout = E::BeforeLayout; type BeforeLayout = E::BeforeLayout;
type AfterLayout = E::AfterLayout; type AfterLayout = E::AfterLayout;
type BeforePaint = E::BeforePaint;
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) { fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
self.element.before_layout(cx) self.element.before_layout(cx)
@@ -2271,10 +2335,21 @@ where
fn after_layout( fn after_layout(
&mut self, &mut self,
bounds: Bounds<Pixels>, bounds: Bounds<Pixels>,
state: &mut Self::BeforeLayout, before_layout: &mut Self::BeforeLayout,
cx: &mut ElementContext, cx: &mut ElementContext,
) -> E::AfterLayout { ) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
self.element.after_layout(bounds, state, cx) 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( fn paint(
@@ -2282,9 +2357,11 @@ where
bounds: Bounds<Pixels>, bounds: Bounds<Pixels>,
before_layout: &mut Self::BeforeLayout, before_layout: &mut Self::BeforeLayout,
after_layout: &mut Self::AfterLayout, after_layout: &mut Self::AfterLayout,
before_paint: &mut Self::BeforePaint,
cx: &mut ElementContext, 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 BeforeLayout = E::BeforeLayout;
type AfterLayout = E::AfterLayout; type AfterLayout = E::AfterLayout;
type BeforePaint = E::BeforePaint;
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) { fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
self.element.before_layout(cx) self.element.before_layout(cx)
@@ -2354,10 +2432,21 @@ where
fn after_layout( fn after_layout(
&mut self, &mut self,
bounds: Bounds<Pixels>, bounds: Bounds<Pixels>,
state: &mut Self::BeforeLayout, before_layout: &mut Self::BeforeLayout,
cx: &mut ElementContext, cx: &mut ElementContext,
) -> E::AfterLayout { ) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
self.element.after_layout(bounds, state, cx) 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( fn paint(
@@ -2365,9 +2454,11 @@ where
bounds: Bounds<Pixels>, bounds: Bounds<Pixels>,
before_layout: &mut Self::BeforeLayout, before_layout: &mut Self::BeforeLayout,
after_layout: &mut Self::AfterLayout, after_layout: &mut Self::AfterLayout,
before_paint: &mut Self::BeforePaint,
cx: &mut ElementContext, 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 { impl Element for Img {
type BeforeLayout = (); type BeforeLayout = ();
type AfterLayout = Option<Hitbox>; type AfterLayout = ();
type BeforePaint = Option<Hitbox>;
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) { fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
let layout_id = self.interactivity.before_layout(cx, |mut style, cx| { let layout_id = self.interactivity.before_layout(cx, |mut style, cx| {
@@ -261,16 +262,29 @@ impl Element for Img {
bounds: Bounds<Pixels>, bounds: Bounds<Pixels>,
_before_layout: &mut Self::BeforeLayout, _before_layout: &mut Self::BeforeLayout,
cx: &mut ElementContext, 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> { ) -> Option<Hitbox> {
self.interactivity self.interactivity
.after_layout(bounds, bounds.size, cx, |_, _, hitbox, _| hitbox) .before_paint(bounds, cx, |_, _, hitbox, _| hitbox)
} }
fn paint( fn paint(
&mut self, &mut self,
bounds: Bounds<Pixels>, bounds: Bounds<Pixels>,
_: &mut Self::BeforeLayout, _before_layout: &mut Self::BeforeLayout,
hitbox: &mut Self::AfterLayout, _after_layout: &mut Self::AfterLayout,
hitbox: &mut Self::BeforePaint,
cx: &mut ElementContext, cx: &mut ElementContext,
) { ) {
let source = self.source.clone(); let source = self.source.clone();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -121,7 +121,7 @@ pub(crate) struct DeferredDraw {
text_style_stack: Vec<TextStyleRefinement>, text_style_stack: Vec<TextStyleRefinement>,
element: Option<AnyElement>, element: Option<AnyElement>,
absolute_offset: Point<Pixels>, absolute_offset: Point<Pixels>,
layout_range: Range<AfterLayoutIndex>, before_paint_range: Range<BeforePaintIndex>,
paint_range: Range<PaintIndex>, paint_range: Range<PaintIndex>,
} }
@@ -146,6 +146,12 @@ pub(crate) struct Frame {
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub(crate) struct AfterLayoutIndex { pub(crate) struct AfterLayoutIndex {
accessed_element_states_index: usize,
line_layout_index: LineLayoutIndex,
}
#[derive(Clone, Default)]
pub(crate) struct BeforePaintIndex {
hitboxes_index: usize, hitboxes_index: usize,
tooltips_index: usize, tooltips_index: usize,
deferred_draws_index: usize, deferred_draws_index: usize,
@@ -399,7 +405,8 @@ impl<'a> ElementContext<'a> {
// Layout all root elements. // Layout all root elements.
let mut root_element = self.window.root_view.as_ref().unwrap().clone().into_any(); 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 = let mut sorted_deferred_draws =
(0..self.window.next_frame.deferred_draws.len()).collect::<SmallVec<[_; 8]>>(); (0..self.window.next_frame.deferred_draws.len()).collect::<SmallVec<[_; 8]>>();
@@ -411,13 +418,15 @@ impl<'a> ElementContext<'a> {
let mut tooltip_element = None; let mut tooltip_element = None;
if let Some(prompt) = self.window.prompt.take() { if let Some(prompt) = self.window.prompt.take() {
let mut element = prompt.view.any_view().into_any(); 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); prompt_element = Some(element);
self.window.prompt = Some(prompt); self.window.prompt = Some(prompt);
} else if let Some(active_drag) = self.app.active_drag.take() { } else if let Some(active_drag) = self.app.active_drag.take() {
let mut element = active_drag.view.clone().into_any(); let mut element = active_drag.view.clone().into_any();
element.layout(AvailableSpace::min_size(), self);
let offset = self.mouse_position() - active_drag.cursor_offset; 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); active_drag_element = Some(element);
self.app.active_drag = Some(active_drag); self.app.active_drag = Some(active_drag);
} else { } else {
@@ -446,7 +455,7 @@ impl<'a> ElementContext<'a> {
let tooltip_request = tooltip_request.unwrap(); let tooltip_request = tooltip_request.unwrap();
let mut element = tooltip_request.tooltip.view.clone().into_any(); let mut element = tooltip_request.tooltip.view.clone().into_any();
let mouse_position = tooltip_request.tooltip.mouse_position; 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 mut tooltip_bounds = Bounds::new(mouse_position + point(px(1.), px(1.)), tooltip_size);
let window_bounds = Bounds { 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 { self.window.tooltip_bounds = Some(TooltipBounds {
id: tooltip_request.id, id: tooltip_request.id,
@@ -500,16 +509,16 @@ impl<'a> ElementContext<'a> {
.dispatch_tree .dispatch_tree
.set_active_node(deferred_draw.parent_node); .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() { if let Some(element) = deferred_draw.element.as_mut() {
self.with_absolute_element_offset(deferred_draw.absolute_offset, |cx| { self.with_absolute_element_offset(deferred_draw.absolute_offset, |cx| {
element.after_layout(cx) element.before_paint(cx)
}); });
} else { } 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(); let layout_end = self.before_paint_index();
deferred_draw.layout_range = layout_start..layout_end; deferred_draw.before_paint_range = layout_start..layout_end;
} }
assert_eq!( assert_eq!(
self.window.next_frame.deferred_draws.len(), self.window.next_frame.deferred_draws.len(),
@@ -548,6 +557,26 @@ impl<'a> ElementContext<'a> {
pub(crate) fn after_layout_index(&self) -> AfterLayoutIndex { pub(crate) fn after_layout_index(&self) -> AfterLayoutIndex {
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(), hitboxes_index: self.window.next_frame.hitboxes.len(),
tooltips_index: self.window.next_frame.tooltip_requests.len(), tooltips_index: self.window.next_frame.tooltip_requests.len(),
deferred_draws_index: self.window.next_frame.deferred_draws.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; let window = &mut self.window;
window.next_frame.hitboxes.extend( window.next_frame.hitboxes.extend(
window.rendered_frame.hitboxes[range.start.hitboxes_index..range.end.hitboxes_index] window.rendered_frame.hitboxes[range.start.hitboxes_index..range.end.hitboxes_index]
@@ -595,7 +624,7 @@ impl<'a> ElementContext<'a> {
priority: deferred_draw.priority, priority: deferred_draw.priority,
element: None, element: None,
absolute_offset: deferred_draw.absolute_offset, 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(), paint_range: deferred_draw.paint_range.clone(),
}), }),
); );
@@ -974,7 +1003,7 @@ impl<'a> ElementContext<'a> {
assert_eq!( assert_eq!(
window.draw_phase, window.draw_phase,
DrawPhase::Layout, 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(); let parent_node = window.next_frame.dispatch_tree.active_node_id().unwrap();
window.next_frame.deferred_draws.push(DeferredDraw { window.next_frame.deferred_draws.push(DeferredDraw {
@@ -984,7 +1013,7 @@ impl<'a> ElementContext<'a> {
priority, priority,
element: Some(element), element: Some(element),
absolute_offset, absolute_offset,
layout_range: AfterLayoutIndex::default()..AfterLayoutIndex::default(), before_paint_range: BeforePaintIndex::default()..BeforePaintIndex::default(),
paint_range: PaintIndex::default()..PaintIndex::default(), paint_range: PaintIndex::default()..PaintIndex::default(),
}); });
} }
@@ -1397,7 +1426,7 @@ impl<'a> ElementContext<'a> {
bounds 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 /// the returned [Hitbox] during `paint` or in an event handler
/// to determine whether the inserted hitbox was the topmost. /// to determine whether the inserted hitbox was the topmost.
pub fn insert_hitbox(&mut self, bounds: Bounds<Pixels>, opaque: bool) -> Hitbox { 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 { impl Render for ImageView {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement { 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 square_size = 32.0;
let start_y = bounds.origin.y.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_2()
.border_color(cx.theme().styles.colors.border) .border_color(cx.theme().styles.colors.border)
.size_full() .size_full()

View File

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

View File

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

View File

@@ -169,7 +169,8 @@ pub struct PopoverMenuFrameState {
impl<M: ManagedView> Element for PopoverMenu<M> { impl<M: ManagedView> Element for PopoverMenu<M> {
type BeforeLayout = PopoverMenuFrameState; 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) { fn before_layout(&mut self, cx: &mut ElementContext) -> (gpui::LayoutId, Self::BeforeLayout) {
self.with_element_state(cx, |this, element_state, cx| { self.with_element_state(cx, |this, element_state, cx| {
@@ -219,14 +220,40 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
_bounds: Bounds<Pixels>, _bounds: Bounds<Pixels>,
before_layout: &mut Self::BeforeLayout, before_layout: &mut Self::BeforeLayout,
cx: &mut ElementContext, cx: &mut ElementContext,
) -> Option<HitboxId> { ) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
self.with_element_state(cx, |_this, element_state, cx| { 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) = 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() { 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| { before_layout.child_layout_id.map(|layout_id| {
@@ -241,6 +268,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
&mut self, &mut self,
_: Bounds<gpui::Pixels>, _: Bounds<gpui::Pixels>,
before_layout: &mut Self::BeforeLayout, before_layout: &mut Self::BeforeLayout,
_after_layout: &mut Self::AfterLayout,
child_hitbox: &mut Option<HitboxId>, child_hitbox: &mut Option<HitboxId>,
cx: &mut ElementContext, cx: &mut ElementContext,
) { ) {

View File

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

View File

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

View File

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