Compare commits
16 Commits
inline-ass
...
before-pai
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2205430814 | ||
|
|
fa410ec150 | ||
|
|
504ea1aaea | ||
|
|
d75f1e64b8 | ||
|
|
1358758226 | ||
|
|
9de4dc12dd | ||
|
|
7ae13c9753 | ||
|
|
d4bae0475f | ||
|
|
72a4e525d7 | ||
|
|
15f4e93102 | ||
|
|
dbfb588e3e | ||
|
|
8ead209099 | ||
|
|
eb14932950 | ||
|
|
d3756cdab3 | ||
|
|
d4c54f3479 | ||
|
|
c024526db2 |
@@ -1094,31 +1094,37 @@ impl AssistantPanel {
|
||||
let view = cx.view().clone();
|
||||
let scroll_handle = self.saved_conversations_scroll_handle.clone();
|
||||
let conversation_count = self.saved_conversations.len();
|
||||
canvas(
|
||||
move |bounds, cx| {
|
||||
let mut saved_conversations = uniform_list(
|
||||
view,
|
||||
"saved_conversations",
|
||||
conversation_count,
|
||||
|this, range, cx| {
|
||||
range
|
||||
.map(|ix| this.render_saved_conversation(ix, cx))
|
||||
.collect()
|
||||
},
|
||||
)
|
||||
.track_scroll(scroll_handle)
|
||||
.into_any_element();
|
||||
saved_conversations.layout(
|
||||
bounds.origin,
|
||||
bounds.size.map(AvailableSpace::Definite),
|
||||
cx,
|
||||
);
|
||||
saved_conversations
|
||||
},
|
||||
|_bounds, mut saved_conversations, cx| saved_conversations.paint(cx),
|
||||
)
|
||||
.size_full()
|
||||
.into_any_element()
|
||||
todo!("replace canvas")
|
||||
// canvas(
|
||||
// move |_, cx| {
|
||||
// let saved_conversations = uniform_list(
|
||||
// view.clone(),
|
||||
// "saved_conversations",
|
||||
// conversation_count,
|
||||
// |this, range, cx| {
|
||||
// range
|
||||
// .map(|ix| this.render_saved_conversation(ix, cx))
|
||||
// .collect()
|
||||
// },
|
||||
// )
|
||||
// .track_scroll(scroll_handle.clone())
|
||||
// .into_any_element();
|
||||
// saved_conversations.layout(absolute_offset, available_space, cx)
|
||||
// // compute layout for saved conversations
|
||||
// saved_conversations
|
||||
// },
|
||||
// move |bounds, saved_conversations, cx| {
|
||||
// saved_conversations.layout(
|
||||
// bounds.origin,
|
||||
// bounds.size.map(AvailableSpace::Definite),
|
||||
// cx,
|
||||
// );
|
||||
// saved_conversations
|
||||
// },
|
||||
// |_bounds, mut saved_conversations, cx| saved_conversations.paint(cx),
|
||||
// )
|
||||
// .size_full()
|
||||
// .into_any_element()
|
||||
} else if let Some(editor) = self.active_conversation_editor() {
|
||||
let editor = editor.clone();
|
||||
let conversation = editor.read(cx).conversation.clone();
|
||||
|
||||
@@ -2834,34 +2834,31 @@ fn render_tree_branch(is_last: bool, overdraw: bool, cx: &mut WindowContext) ->
|
||||
let thickness = px(1.);
|
||||
let color = cx.theme().colors().text;
|
||||
|
||||
canvas(
|
||||
|_, _| {},
|
||||
move |bounds, _, cx| {
|
||||
let start_x = (bounds.left() + bounds.right() - thickness) / 2.;
|
||||
let start_y = (bounds.top() + bounds.bottom() - thickness) / 2.;
|
||||
let right = bounds.right();
|
||||
let top = bounds.top();
|
||||
canvas(move |bounds, cx| {
|
||||
let start_x = (bounds.left() + bounds.right() - thickness) / 2.;
|
||||
let start_y = (bounds.top() + bounds.bottom() - thickness) / 2.;
|
||||
let right = bounds.right();
|
||||
let top = bounds.top();
|
||||
|
||||
cx.paint_quad(fill(
|
||||
Bounds::from_corners(
|
||||
point(start_x, top),
|
||||
point(
|
||||
start_x + thickness,
|
||||
if is_last {
|
||||
start_y
|
||||
} else {
|
||||
bounds.bottom() + if overdraw { px(1.) } else { px(0.) }
|
||||
},
|
||||
),
|
||||
cx.paint_quad(fill(
|
||||
Bounds::from_corners(
|
||||
point(start_x, top),
|
||||
point(
|
||||
start_x + thickness,
|
||||
if is_last {
|
||||
start_y
|
||||
} else {
|
||||
bounds.bottom() + if overdraw { px(1.) } else { px(0.) }
|
||||
},
|
||||
),
|
||||
color,
|
||||
));
|
||||
cx.paint_quad(fill(
|
||||
Bounds::from_corners(point(start_x, start_y), point(right, start_y + thickness)),
|
||||
color,
|
||||
));
|
||||
},
|
||||
)
|
||||
),
|
||||
color,
|
||||
));
|
||||
cx.paint_quad(fill(
|
||||
Bounds::from_corners(point(start_x, start_y), point(right, start_y + thickness)),
|
||||
color,
|
||||
));
|
||||
})
|
||||
.w(width)
|
||||
.h(line_height)
|
||||
}
|
||||
|
||||
@@ -318,26 +318,23 @@ impl Render for CollabTitlebarItem {
|
||||
}
|
||||
|
||||
fn render_color_ribbon(color: Hsla) -> impl Element {
|
||||
canvas(
|
||||
move |_, _| {},
|
||||
move |bounds, _, cx| {
|
||||
let height = bounds.size.height;
|
||||
let horizontal_offset = height;
|
||||
let vertical_offset = px(height.0 / 2.0);
|
||||
let mut path = Path::new(bounds.lower_left());
|
||||
path.curve_to(
|
||||
bounds.origin + point(horizontal_offset, vertical_offset),
|
||||
bounds.origin + point(px(0.0), vertical_offset),
|
||||
);
|
||||
path.line_to(bounds.upper_right() + point(-horizontal_offset, vertical_offset));
|
||||
path.curve_to(
|
||||
bounds.lower_right(),
|
||||
bounds.upper_right() + point(px(0.0), vertical_offset),
|
||||
);
|
||||
path.line_to(bounds.lower_left());
|
||||
cx.paint_path(path, color);
|
||||
},
|
||||
)
|
||||
canvas(move |bounds, cx| {
|
||||
let height = bounds.size.height;
|
||||
let horizontal_offset = height;
|
||||
let vertical_offset = px(height.0 / 2.0);
|
||||
let mut path = Path::new(bounds.lower_left());
|
||||
path.curve_to(
|
||||
bounds.origin + point(horizontal_offset, vertical_offset),
|
||||
bounds.origin + point(px(0.0), vertical_offset),
|
||||
);
|
||||
path.line_to(bounds.upper_right() + point(-horizontal_offset, vertical_offset));
|
||||
path.curve_to(
|
||||
bounds.lower_right(),
|
||||
bounds.upper_right() + point(px(0.0), vertical_offset),
|
||||
);
|
||||
path.line_to(bounds.lower_left());
|
||||
cx.paint_path(path, color);
|
||||
})
|
||||
.h_1()
|
||||
.w_full()
|
||||
}
|
||||
|
||||
@@ -476,8 +476,8 @@ pub struct Editor {
|
||||
+ Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
|
||||
>,
|
||||
>,
|
||||
last_bounds: Option<Bounds<Pixels>>,
|
||||
expect_bounds_change: Option<Bounds<Pixels>>,
|
||||
last_layout_bounds: Option<Bounds<Pixels>>,
|
||||
expect_layout_bounds_change: Option<Bounds<Pixels>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -1492,8 +1492,8 @@ impl Editor {
|
||||
inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
|
||||
gutter_hovered: false,
|
||||
pixel_position_of_newest_cursor: None,
|
||||
last_bounds: None,
|
||||
expect_bounds_change: None,
|
||||
last_layout_bounds: None,
|
||||
expect_layout_bounds_change: None,
|
||||
gutter_width: Default::default(),
|
||||
style: None,
|
||||
show_cursor_names: false,
|
||||
@@ -10778,7 +10778,8 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, _is_valid: bool) -> Ren
|
||||
|
||||
let icon_size = buttons(&diagnostic, cx.block_id)
|
||||
.into_any_element()
|
||||
.measure(AvailableSpace::min_size(), cx);
|
||||
.layout(AvailableSpace::min_size(), cx)
|
||||
.size;
|
||||
|
||||
h_flex()
|
||||
.id(cx.block_id)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1173,7 +1173,7 @@ impl SearchableItem for Editor {
|
||||
}
|
||||
|
||||
fn search_bar_visibility_changed(&mut self, _visible: bool, _cx: &mut ViewContext<Self>) {
|
||||
self.expect_bounds_change = self.last_bounds;
|
||||
self.expect_layout_bounds_change = self.last_layout_bounds;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ impl Editor {
|
||||
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||
let mut scroll_position = self.scroll_manager.scroll_position(&display_map);
|
||||
let original_y = scroll_position.y;
|
||||
if let Some(last_bounds) = self.expect_bounds_change.take() {
|
||||
if let Some(last_bounds) = self.expect_layout_bounds_change.take() {
|
||||
if scroll_position.y != 0. {
|
||||
scroll_position.y += (bounds.top() - last_bounds.top()) / line_height;
|
||||
if scroll_position.y < 0. {
|
||||
|
||||
@@ -936,24 +936,25 @@ impl Render for ExtensionsPage {
|
||||
let view = cx.view().clone();
|
||||
let scroll_handle = self.list.clone();
|
||||
this.child(
|
||||
canvas(
|
||||
move |bounds, cx| {
|
||||
let mut list = uniform_list::<_, ExtensionCard, _>(
|
||||
view,
|
||||
"entries",
|
||||
count,
|
||||
Self::render_extensions,
|
||||
)
|
||||
.size_full()
|
||||
.pb_4()
|
||||
.track_scroll(scroll_handle)
|
||||
.into_any_element();
|
||||
list.layout(bounds.origin, bounds.size.into(), cx);
|
||||
list
|
||||
},
|
||||
|_bounds, mut list, cx| list.paint(cx),
|
||||
)
|
||||
.size_full(),
|
||||
// canvas(
|
||||
// move |bounds, cx| {
|
||||
// let mut list = uniform_list::<_, ExtensionCard, _>(
|
||||
// view,
|
||||
// "entries",
|
||||
// count,
|
||||
// Self::render_extensions,
|
||||
// )
|
||||
// .size_full()
|
||||
// .pb_4()
|
||||
// .track_scroll(scroll_handle)
|
||||
// .into_any_element();
|
||||
// list.layout(bounds.origin, bounds.size.into(), cx);
|
||||
// list
|
||||
// },
|
||||
// |_bounds, mut list, cx| list.paint(cx),
|
||||
// )
|
||||
// .size_full(),
|
||||
todo!("replace canvas"),
|
||||
)
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -734,7 +734,8 @@ impl VisualTestContext {
|
||||
self.update(|cx| {
|
||||
cx.with_element_context(|cx| {
|
||||
let mut element = f(cx);
|
||||
element.layout(origin, space, cx);
|
||||
element.layout(space, cx);
|
||||
cx.with_element_offset(origin, |cx| element.before_paint(cx));
|
||||
element.paint(cx);
|
||||
});
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
use crate::{
|
||||
util::FluentBuilder, ArenaBox, AvailableSpace, Bounds, DispatchNodeId, ElementContext,
|
||||
ElementId, LayoutId, Pixels, Point, Size, ViewContext, WindowContext, ELEMENT_ARENA,
|
||||
ElementId, LayoutId, Pixels, Size, ViewContext, WindowContext, ELEMENT_ARENA,
|
||||
};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
pub(crate) use smallvec::SmallVec;
|
||||
@@ -45,33 +45,46 @@ use std::{any::Any, fmt::Debug, mem, ops::DerefMut};
|
||||
/// for more details.
|
||||
pub trait Element: 'static + IntoElement {
|
||||
/// The type of state returned from [`Element::before_layout`]. A mutable reference to this state is subsequently
|
||||
/// provided to [`Element::after_layout`] and [`Element::paint`].
|
||||
/// provided to [`Element::after_layout`], [`Element::before_paint`] and [`Element::paint`].
|
||||
type BeforeLayout: 'static;
|
||||
|
||||
/// The type of state returned from [`Element::after_layout`]. A mutable reference to this state is subsequently
|
||||
/// provided to [`Element::paint`].
|
||||
/// provided to [`Element::before_paint`] and [`Element::paint`].
|
||||
type AfterLayout: 'static;
|
||||
|
||||
/// The type of state returned from [`Element::before_paint`]. A mutable reference to this state is subsequently
|
||||
/// provided to [`Element::paint`].
|
||||
type BeforePaint: 'static;
|
||||
|
||||
/// Before an element can be painted, we need to know where it's going to be and how big it is.
|
||||
/// Use this method to request a layout from Taffy and initialize the element's state.
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout);
|
||||
|
||||
/// After laying out an element, we need to commit its bounds to the current frame for hitbox
|
||||
/// purposes. The state argument is the same state that was returned from [`Element::before_layout()`].
|
||||
/// todo!()
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> Self::AfterLayout;
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout);
|
||||
|
||||
/// Before painting an element, we need to commit its bounds to the current frame for hitbox
|
||||
/// purposes.
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> Self::BeforePaint;
|
||||
|
||||
/// Once layout has been completed, this method will be called to paint the element to the screen.
|
||||
/// The state argument is the same state that was returned from [`Element::before_layout()`].
|
||||
fn paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
after_layout: &mut Self::AfterLayout,
|
||||
before_paint: &mut Self::BeforePaint,
|
||||
cx: &mut ElementContext,
|
||||
);
|
||||
|
||||
@@ -163,6 +176,7 @@ impl<C: RenderOnce> Component<C> {
|
||||
impl<C: RenderOnce> Element for Component<C> {
|
||||
type BeforeLayout = AnyElement;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = ();
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
let mut element = self
|
||||
@@ -176,12 +190,23 @@ impl<C: RenderOnce> Element for Component<C> {
|
||||
}
|
||||
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
element: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
let bounds = element.after_layout(cx);
|
||||
(bounds, ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
_: Bounds<Pixels>,
|
||||
element: &mut AnyElement,
|
||||
_: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
element.after_layout(cx);
|
||||
element.before_paint(cx);
|
||||
}
|
||||
|
||||
fn paint(
|
||||
@@ -189,6 +214,7 @@ impl<C: RenderOnce> Element for Component<C> {
|
||||
_: Bounds<Pixels>,
|
||||
element: &mut Self::BeforeLayout,
|
||||
_: &mut Self::AfterLayout,
|
||||
_: &mut Self::BeforePaint,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
element.paint(cx)
|
||||
@@ -212,46 +238,59 @@ trait ElementObject {
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> LayoutId;
|
||||
|
||||
fn after_layout(&mut self, cx: &mut ElementContext);
|
||||
fn after_layout(&mut self, cx: &mut ElementContext) -> Option<Bounds<Pixels>>;
|
||||
|
||||
fn before_paint(&mut self, cx: &mut ElementContext);
|
||||
|
||||
fn paint(&mut self, cx: &mut ElementContext);
|
||||
|
||||
fn measure(
|
||||
fn layout(
|
||||
&mut self,
|
||||
available_space: Size<AvailableSpace>,
|
||||
cx: &mut ElementContext,
|
||||
) -> Size<Pixels>;
|
||||
) -> ElementMeasurement;
|
||||
}
|
||||
|
||||
/// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window.
|
||||
pub struct Drawable<E: Element> {
|
||||
/// The drawn element.
|
||||
pub element: E,
|
||||
phase: ElementDrawPhase<E::BeforeLayout, E::AfterLayout>,
|
||||
phase: ElementDrawPhase<E::BeforeLayout, E::AfterLayout, E::BeforePaint>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
enum ElementDrawPhase<BeforeLayout, AfterLayout> {
|
||||
enum ElementDrawPhase<BeforeLayout, AfterLayout, BeforePaint> {
|
||||
#[default]
|
||||
Start,
|
||||
BeforeLayout {
|
||||
layout_id: LayoutId,
|
||||
before_layout: BeforeLayout,
|
||||
},
|
||||
LayoutComputed {
|
||||
layout_id: LayoutId,
|
||||
available_space: Size<AvailableSpace>,
|
||||
before_layout: BeforeLayout,
|
||||
},
|
||||
AfterLayout {
|
||||
layout_id: LayoutId,
|
||||
available_space: Option<Size<AvailableSpace>>,
|
||||
before_layout: BeforeLayout,
|
||||
after_layout: AfterLayout,
|
||||
},
|
||||
BeforePaint {
|
||||
node_id: DispatchNodeId,
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: BeforeLayout,
|
||||
after_layout: AfterLayout,
|
||||
before_paint: BeforePaint,
|
||||
},
|
||||
Painted,
|
||||
}
|
||||
|
||||
/// todo!()
|
||||
#[derive(Default)]
|
||||
pub struct ElementMeasurement {
|
||||
/// The size of the element.
|
||||
pub size: Size<Pixels>,
|
||||
/// The bounds for the focus target inside of the measured element.
|
||||
pub focus_target_bounds: Option<Bounds<Pixels>>,
|
||||
}
|
||||
|
||||
/// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window.
|
||||
impl<E: Element> Drawable<E> {
|
||||
fn new(element: E) -> Self {
|
||||
@@ -275,92 +314,133 @@ impl<E: Element> Drawable<E> {
|
||||
}
|
||||
}
|
||||
|
||||
fn after_layout(&mut self, cx: &mut ElementContext) {
|
||||
fn after_layout(&mut self, cx: &mut ElementContext) -> Option<Bounds<Pixels>> {
|
||||
match mem::take(&mut self.phase) {
|
||||
ElementDrawPhase::BeforeLayout {
|
||||
layout_id,
|
||||
mut before_layout,
|
||||
}
|
||||
| ElementDrawPhase::LayoutComputed {
|
||||
layout_id,
|
||||
mut before_layout,
|
||||
..
|
||||
} => {
|
||||
let bounds = cx.layout_bounds(layout_id);
|
||||
let node_id = cx.window.next_frame.dispatch_tree.push_node();
|
||||
let after_layout = self.element.after_layout(bounds, &mut before_layout, cx);
|
||||
let (focus_target_bounds, after_layout) =
|
||||
self.element.after_layout(bounds, &mut before_layout, cx);
|
||||
self.phase = ElementDrawPhase::AfterLayout {
|
||||
node_id,
|
||||
bounds,
|
||||
layout_id,
|
||||
available_space: None,
|
||||
before_layout,
|
||||
after_layout,
|
||||
};
|
||||
cx.window.next_frame.dispatch_tree.pop_node();
|
||||
|
||||
focus_target_bounds
|
||||
}
|
||||
_ => panic!("must call before_layout before after_layout"),
|
||||
}
|
||||
}
|
||||
|
||||
fn paint(&mut self, cx: &mut ElementContext) -> E::BeforeLayout {
|
||||
fn before_paint(&mut self, cx: &mut ElementContext) {
|
||||
match mem::take(&mut self.phase) {
|
||||
ElementDrawPhase::AfterLayout {
|
||||
node_id,
|
||||
bounds,
|
||||
layout_id,
|
||||
mut before_layout,
|
||||
mut after_layout,
|
||||
..
|
||||
} => {
|
||||
cx.window.next_frame.dispatch_tree.set_active_node(node_id);
|
||||
self.element
|
||||
.paint(bounds, &mut before_layout, &mut after_layout, cx);
|
||||
self.phase = ElementDrawPhase::Painted;
|
||||
before_layout
|
||||
let bounds = cx.layout_bounds(layout_id);
|
||||
let node_id = cx.window.next_frame.dispatch_tree.push_node();
|
||||
let before_paint =
|
||||
self.element
|
||||
.before_paint(bounds, &mut before_layout, &mut after_layout, cx);
|
||||
self.phase = ElementDrawPhase::BeforePaint {
|
||||
node_id,
|
||||
bounds,
|
||||
before_layout,
|
||||
after_layout,
|
||||
before_paint,
|
||||
};
|
||||
cx.window.next_frame.dispatch_tree.pop_node();
|
||||
}
|
||||
_ => panic!("must call after_layout before paint"),
|
||||
_ => panic!("must call after_layout before before_paint"),
|
||||
}
|
||||
}
|
||||
|
||||
fn measure(
|
||||
fn paint(&mut self, cx: &mut ElementContext) -> E::BeforeLayout {
|
||||
match mem::take(&mut self.phase) {
|
||||
ElementDrawPhase::BeforePaint {
|
||||
node_id,
|
||||
bounds,
|
||||
mut before_layout,
|
||||
mut after_layout,
|
||||
mut before_paint,
|
||||
..
|
||||
} => {
|
||||
cx.window.next_frame.dispatch_tree.set_active_node(node_id);
|
||||
self.element.paint(
|
||||
bounds,
|
||||
&mut before_layout,
|
||||
&mut after_layout,
|
||||
&mut before_paint,
|
||||
cx,
|
||||
);
|
||||
self.phase = ElementDrawPhase::Painted;
|
||||
before_layout
|
||||
}
|
||||
_ => panic!("must call before_paint before paint"),
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
available_space: Size<AvailableSpace>,
|
||||
cx: &mut ElementContext,
|
||||
) -> Size<Pixels> {
|
||||
) -> ElementMeasurement {
|
||||
if matches!(&self.phase, ElementDrawPhase::Start) {
|
||||
self.before_layout(cx);
|
||||
}
|
||||
|
||||
let layout_id = match mem::take(&mut self.phase) {
|
||||
match mem::take(&mut self.phase) {
|
||||
ElementDrawPhase::BeforeLayout {
|
||||
layout_id,
|
||||
before_layout,
|
||||
mut before_layout,
|
||||
} => {
|
||||
cx.compute_layout(layout_id, available_space);
|
||||
self.phase = ElementDrawPhase::LayoutComputed {
|
||||
let bounds = cx.layout_bounds(layout_id);
|
||||
let (focus_target_bounds, after_layout) =
|
||||
self.element.after_layout(bounds, &mut before_layout, cx);
|
||||
self.phase = ElementDrawPhase::AfterLayout {
|
||||
layout_id,
|
||||
available_space,
|
||||
available_space: Some(available_space),
|
||||
before_layout,
|
||||
after_layout,
|
||||
};
|
||||
layout_id
|
||||
ElementMeasurement {
|
||||
size: bounds.size,
|
||||
focus_target_bounds,
|
||||
}
|
||||
}
|
||||
ElementDrawPhase::LayoutComputed {
|
||||
ElementDrawPhase::AfterLayout {
|
||||
layout_id,
|
||||
available_space: prev_available_space,
|
||||
before_layout,
|
||||
mut before_layout,
|
||||
..
|
||||
} => {
|
||||
if available_space != prev_available_space {
|
||||
if Some(available_space) != prev_available_space {
|
||||
cx.compute_layout(layout_id, available_space);
|
||||
}
|
||||
self.phase = ElementDrawPhase::LayoutComputed {
|
||||
let bounds = cx.layout_bounds(layout_id);
|
||||
let (focus_target_bounds, after_layout) =
|
||||
self.element.after_layout(bounds, &mut before_layout, cx);
|
||||
self.phase = ElementDrawPhase::AfterLayout {
|
||||
layout_id,
|
||||
available_space,
|
||||
available_space: Some(available_space),
|
||||
before_layout,
|
||||
after_layout,
|
||||
};
|
||||
layout_id
|
||||
ElementMeasurement {
|
||||
size: bounds.size,
|
||||
focus_target_bounds,
|
||||
}
|
||||
}
|
||||
_ => panic!("cannot measure after painting"),
|
||||
};
|
||||
|
||||
cx.layout_bounds(layout_id).size
|
||||
_ => panic!("cannot layout after painting"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -377,20 +457,24 @@ where
|
||||
Drawable::before_layout(self, cx)
|
||||
}
|
||||
|
||||
fn after_layout(&mut self, cx: &mut ElementContext) {
|
||||
Drawable::after_layout(self, cx);
|
||||
fn after_layout(&mut self, cx: &mut ElementContext) -> Option<Bounds<Pixels>> {
|
||||
Drawable::after_layout(self, cx)
|
||||
}
|
||||
|
||||
fn before_paint(&mut self, cx: &mut ElementContext) {
|
||||
Drawable::before_paint(self, cx);
|
||||
}
|
||||
|
||||
fn paint(&mut self, cx: &mut ElementContext) {
|
||||
Drawable::paint(self, cx);
|
||||
}
|
||||
|
||||
fn measure(
|
||||
fn layout(
|
||||
&mut self,
|
||||
available_space: Size<AvailableSpace>,
|
||||
cx: &mut ElementContext,
|
||||
) -> Size<Pixels> {
|
||||
Drawable::measure(self, available_space, cx)
|
||||
) -> ElementMeasurement {
|
||||
Drawable::layout(self, available_space, cx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,41 +504,35 @@ impl AnyElement {
|
||||
self.0.before_layout(cx)
|
||||
}
|
||||
|
||||
/// Commits the element bounds of this [AnyElement] for hitbox purposes.
|
||||
pub fn after_layout(&mut self, cx: &mut ElementContext) {
|
||||
/// todo!()
|
||||
pub fn after_layout(&mut self, cx: &mut ElementContext) -> Option<Bounds<Pixels>> {
|
||||
self.0.after_layout(cx)
|
||||
}
|
||||
|
||||
/// Commits the element bounds of this [AnyElement] for hitbox purposes.
|
||||
pub fn before_paint(&mut self, cx: &mut ElementContext) {
|
||||
self.0.before_paint(cx)
|
||||
}
|
||||
|
||||
/// Paints the element stored in this `AnyElement`.
|
||||
pub fn paint(&mut self, cx: &mut ElementContext) {
|
||||
self.0.paint(cx)
|
||||
}
|
||||
|
||||
/// Initializes this element and performs layout within the given available space to determine its size.
|
||||
pub fn measure(
|
||||
&mut self,
|
||||
available_space: Size<AvailableSpace>,
|
||||
cx: &mut ElementContext,
|
||||
) -> Size<Pixels> {
|
||||
self.0.measure(available_space, cx)
|
||||
}
|
||||
|
||||
/// Initializes this element, performs layout if needed and commits its bounds for hitbox purposes.
|
||||
pub fn layout(
|
||||
&mut self,
|
||||
absolute_offset: Point<Pixels>,
|
||||
available_space: Size<AvailableSpace>,
|
||||
cx: &mut ElementContext,
|
||||
) -> Size<Pixels> {
|
||||
let size = self.measure(available_space, cx);
|
||||
cx.with_absolute_element_offset(absolute_offset, |cx| self.after_layout(cx));
|
||||
size
|
||||
) -> ElementMeasurement {
|
||||
self.0.layout(available_space, cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl Element for AnyElement {
|
||||
type BeforeLayout = ();
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = ();
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
let layout_id = self.before_layout(cx);
|
||||
@@ -466,8 +544,19 @@ impl Element for AnyElement {
|
||||
_: Bounds<Pixels>,
|
||||
_: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
let bounds = self.after_layout(cx);
|
||||
(bounds, ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
_: Bounds<Pixels>,
|
||||
_: &mut Self::BeforeLayout,
|
||||
_: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
self.after_layout(cx)
|
||||
self.before_paint(cx)
|
||||
}
|
||||
|
||||
fn paint(
|
||||
@@ -475,6 +564,7 @@ impl Element for AnyElement {
|
||||
_: Bounds<Pixels>,
|
||||
_: &mut Self::BeforeLayout,
|
||||
_: &mut Self::AfterLayout,
|
||||
_: &mut Self::BeforePaint,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
self.paint(cx)
|
||||
@@ -507,6 +597,7 @@ impl IntoElement for Empty {
|
||||
impl Element for Empty {
|
||||
type BeforeLayout = ();
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = ();
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
(cx.request_layout(&crate::Style::default(), None), ())
|
||||
@@ -515,7 +606,17 @@ impl Element for Empty {
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_state: &mut Self::BeforeLayout,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
(None, ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
_cx: &mut ElementContext,
|
||||
) {
|
||||
}
|
||||
@@ -525,6 +626,7 @@ impl Element for Empty {
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
_before_paint: &mut Self::BeforePaint,
|
||||
_cx: &mut ElementContext,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ impl ParentElement for Anchored {
|
||||
impl Element for Anchored {
|
||||
type BeforeLayout = AnchoredState;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = ();
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (crate::LayoutId, Self::BeforeLayout) {
|
||||
let child_layout_ids = self
|
||||
@@ -91,9 +92,27 @@ impl Element for Anchored {
|
||||
}
|
||||
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
let mut focus_target_bounds = None;
|
||||
for child in &mut self.children {
|
||||
if let Some(child_focus_target_bounds) = child.after_layout(cx) {
|
||||
if focus_target_bounds.is_none() {
|
||||
focus_target_bounds = Some(child_focus_target_bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
(focus_target_bounds, ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
if before_layout.child_layout_ids.is_empty() {
|
||||
@@ -167,7 +186,7 @@ impl Element for Anchored {
|
||||
|
||||
cx.with_element_offset(offset, |cx| {
|
||||
for child in &mut self.children {
|
||||
child.after_layout(cx);
|
||||
child.before_paint(cx);
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -177,6 +196,7 @@ impl Element for Anchored {
|
||||
_bounds: crate::Bounds<crate::Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
_before_paint: &mut Self::BeforePaint,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
for child in &mut self.children {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use crate::{AnyElement, Element, ElementId, IntoElement};
|
||||
use crate::{AnyElement, Bounds, Element, ElementId, IntoElement, Pixels};
|
||||
|
||||
pub use easing::*;
|
||||
|
||||
@@ -86,8 +86,8 @@ struct AnimationState {
|
||||
|
||||
impl<E: IntoElement + 'static> Element for AnimationElement<E> {
|
||||
type BeforeLayout = AnyElement;
|
||||
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = ();
|
||||
|
||||
fn before_layout(
|
||||
&mut self,
|
||||
@@ -136,18 +136,29 @@ impl<E: IntoElement + 'static> Element for AnimationElement<E> {
|
||||
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
_bounds: crate::Bounds<crate::Pixels>,
|
||||
_bounds: Bounds<Pixels>,
|
||||
element: &mut Self::BeforeLayout,
|
||||
cx: &mut crate::ElementContext,
|
||||
) -> Self::AfterLayout {
|
||||
element.after_layout(cx);
|
||||
) -> (Option<Bounds<Pixels>>, ()) {
|
||||
(element.after_layout(cx), ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
element: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut crate::ElementContext,
|
||||
) -> Self::BeforePaint {
|
||||
element.before_paint(cx);
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
_bounds: crate::Bounds<crate::Pixels>,
|
||||
_bounds: Bounds<Pixels>,
|
||||
element: &mut Self::BeforeLayout,
|
||||
_: &mut Self::AfterLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
_before_paint: &mut Self::BeforePaint,
|
||||
cx: &mut crate::ElementContext,
|
||||
) {
|
||||
element.paint(cx);
|
||||
|
||||
@@ -4,12 +4,8 @@ use crate::{Bounds, Element, ElementContext, IntoElement, Pixels, Style, StyleRe
|
||||
|
||||
/// Construct a canvas element with the given paint callback.
|
||||
/// Useful for adding short term custom drawing to a view.
|
||||
pub fn canvas<T>(
|
||||
after_layout: impl 'static + FnOnce(Bounds<Pixels>, &mut ElementContext) -> T,
|
||||
paint: impl 'static + FnOnce(Bounds<Pixels>, T, &mut ElementContext),
|
||||
) -> Canvas<T> {
|
||||
pub fn canvas(paint: impl 'static + FnOnce(Bounds<Pixels>, &mut ElementContext)) -> Canvas {
|
||||
Canvas {
|
||||
after_layout: Some(Box::new(after_layout)),
|
||||
paint: Some(Box::new(paint)),
|
||||
style: StyleRefinement::default(),
|
||||
}
|
||||
@@ -17,13 +13,12 @@ pub fn canvas<T>(
|
||||
|
||||
/// A canvas element, meant for accessing the low level paint API without defining a whole
|
||||
/// custom element
|
||||
pub struct Canvas<T> {
|
||||
after_layout: Option<Box<dyn FnOnce(Bounds<Pixels>, &mut ElementContext) -> T>>,
|
||||
paint: Option<Box<dyn FnOnce(Bounds<Pixels>, T, &mut ElementContext)>>,
|
||||
pub struct Canvas {
|
||||
paint: Option<Box<dyn FnOnce(Bounds<Pixels>, &mut ElementContext)>>,
|
||||
style: StyleRefinement,
|
||||
}
|
||||
|
||||
impl<T: 'static> IntoElement for Canvas<T> {
|
||||
impl IntoElement for Canvas {
|
||||
type Element = Self;
|
||||
|
||||
fn into_element(self) -> Self::Element {
|
||||
@@ -31,9 +26,10 @@ impl<T: 'static> IntoElement for Canvas<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> Element for Canvas<T> {
|
||||
impl Element for Canvas {
|
||||
type BeforeLayout = Style;
|
||||
type AfterLayout = Option<T>;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = ();
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (crate::LayoutId, Self::BeforeLayout) {
|
||||
let mut style = Style::default();
|
||||
@@ -44,28 +40,35 @@ impl<T: 'static> Element for Canvas<T> {
|
||||
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
(None, ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Style,
|
||||
cx: &mut ElementContext,
|
||||
) -> Option<T> {
|
||||
Some(self.after_layout.take().unwrap()(bounds, cx))
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
_cx: &mut ElementContext,
|
||||
) {
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
style: &mut Style,
|
||||
after_layout: &mut Self::AfterLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
_before_paint: &mut Self::BeforePaint,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
let after_layout = after_layout.take().unwrap();
|
||||
style.paint(bounds, cx, |cx| {
|
||||
(self.paint.take().unwrap())(bounds, after_layout, cx)
|
||||
});
|
||||
style.paint(bounds, cx, |cx| (self.paint.take().unwrap())(bounds, cx));
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Styled for Canvas<T> {
|
||||
impl Styled for Canvas {
|
||||
fn style(&mut self) -> &mut crate::StyleRefinement {
|
||||
&mut self.style
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ impl Deferred {
|
||||
impl Element for Deferred {
|
||||
type BeforeLayout = ();
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = ();
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, ()) {
|
||||
let layout_id = self.child.as_mut().unwrap().before_layout(cx);
|
||||
@@ -39,6 +40,17 @@ impl Element for Deferred {
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
let bounds = self.child.as_mut().unwrap().after_layout(cx);
|
||||
(bounds, ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
let child = self.child.take().unwrap();
|
||||
let element_offset = cx.element_offset();
|
||||
@@ -50,6 +62,7 @@ impl Element for Deferred {
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
_before_paint: &mut Self::BeforePaint,
|
||||
_cx: &mut ElementContext,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -1121,7 +1121,8 @@ impl ParentElement for Div {
|
||||
|
||||
impl Element for Div {
|
||||
type BeforeLayout = DivFrameState;
|
||||
type AfterLayout = Option<Hitbox>;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = Option<Hitbox>;
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
let mut child_layout_ids = SmallVec::new();
|
||||
@@ -1143,7 +1144,7 @@ impl Element for Div {
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> Option<Hitbox> {
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
let mut child_min = point(Pixels::MAX, Pixels::MAX);
|
||||
let mut child_max = Point::default();
|
||||
let content_size = if before_layout.child_layout_ids.is_empty() {
|
||||
@@ -1177,25 +1178,49 @@ impl Element for Div {
|
||||
(child_max - child_min).into()
|
||||
};
|
||||
|
||||
self.interactivity.after_layout(
|
||||
let focus_target_bounds = self.interactivity.after_layout(
|
||||
bounds,
|
||||
content_size,
|
||||
cx,
|
||||
|_style, scroll_offset, hitbox, cx| {
|
||||
|_, scroll_offset, mut focus_target_bounds, cx| {
|
||||
cx.with_element_offset(scroll_offset, |cx| {
|
||||
for child in &mut self.children {
|
||||
child.after_layout(cx);
|
||||
if let Some(child_focus_bounds) = child.after_layout(cx) {
|
||||
focus_target_bounds = Some(child_focus_bounds);
|
||||
}
|
||||
}
|
||||
|
||||
focus_target_bounds
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
(focus_target_bounds, ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> Option<Hitbox> {
|
||||
self.interactivity
|
||||
.before_paint(bounds, cx, |_style, scroll_offset, hitbox, cx| {
|
||||
cx.with_element_offset(scroll_offset, |cx| {
|
||||
for child in &mut self.children {
|
||||
child.before_paint(cx);
|
||||
}
|
||||
});
|
||||
hitbox
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
hitbox: &mut Option<Hitbox>,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
@@ -1336,13 +1361,13 @@ impl Interactivity {
|
||||
)
|
||||
}
|
||||
|
||||
/// Commit the bounds of this element according to this interactivity state's configured styles.
|
||||
/// todo!()
|
||||
pub fn after_layout<R>(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
content_size: Size<Pixels>,
|
||||
cx: &mut ElementContext,
|
||||
f: impl FnOnce(&Style, Point<Pixels>, Option<Hitbox>, &mut ElementContext) -> R,
|
||||
f: impl FnOnce(&Style, Point<Pixels>, Option<Bounds<Pixels>>, &mut ElementContext) -> R,
|
||||
) -> R {
|
||||
self.content_size = content_size;
|
||||
cx.with_element_state::<InteractiveElementState, _>(
|
||||
@@ -1367,6 +1392,40 @@ impl Interactivity {
|
||||
}
|
||||
}
|
||||
|
||||
cx.with_text_style(style.text_style().cloned(), |cx| {
|
||||
cx.with_content_mask(style.overflow_mask(bounds, cx.rem_size()), |cx| {
|
||||
let scroll_offset = self.clamp_scroll_position(bounds, &style, cx);
|
||||
let focus_target_bounds =
|
||||
self.tracked_focus_handle.as_ref().and_then(|focus_handle| {
|
||||
if focus_handle.is_focused(cx) {
|
||||
Some(bounds)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
let result = f(&style, scroll_offset, focus_target_bounds, cx);
|
||||
(result, element_state)
|
||||
})
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Commit the bounds of this element according to this interactivity state's configured styles.
|
||||
pub fn before_paint<R>(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
cx: &mut ElementContext,
|
||||
f: impl FnOnce(&Style, Point<Pixels>, Option<Hitbox>, &mut ElementContext) -> R,
|
||||
) -> R {
|
||||
cx.with_element_state::<InteractiveElementState, _>(
|
||||
self.element_id.clone(),
|
||||
|element_state, cx| {
|
||||
let mut element_state =
|
||||
element_state.map(|element_state| element_state.unwrap_or_default());
|
||||
let style = self.compute_style_internal(None, element_state.as_mut(), cx);
|
||||
|
||||
cx.with_text_style(style.text_style().cloned(), |cx| {
|
||||
cx.with_content_mask(style.overflow_mask(bounds, cx.rem_size()), |cx| {
|
||||
let hitbox = if self.should_insert_hitbox(&style) {
|
||||
@@ -1375,7 +1434,11 @@ impl Interactivity {
|
||||
None
|
||||
};
|
||||
|
||||
let scroll_offset = self.clamp_scroll_position(bounds, &style, cx);
|
||||
let scroll_offset = self
|
||||
.scroll_offset
|
||||
.as_ref()
|
||||
.map(|scroll_offset| *scroll_offset.borrow())
|
||||
.unwrap_or_default();
|
||||
let result = f(&style, scroll_offset, hitbox, cx);
|
||||
(result, element_state)
|
||||
})
|
||||
@@ -2263,6 +2326,7 @@ where
|
||||
{
|
||||
type BeforeLayout = E::BeforeLayout;
|
||||
type AfterLayout = E::AfterLayout;
|
||||
type BeforePaint = E::BeforePaint;
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
self.element.before_layout(cx)
|
||||
@@ -2271,10 +2335,21 @@ where
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
state: &mut Self::BeforeLayout,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> E::AfterLayout {
|
||||
self.element.after_layout(bounds, state, cx)
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
self.element.after_layout(bounds, before_layout, cx)
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> E::BeforePaint {
|
||||
self.element
|
||||
.before_paint(bounds, before_layout, after_layout, cx)
|
||||
}
|
||||
|
||||
fn paint(
|
||||
@@ -2282,9 +2357,11 @@ where
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
after_layout: &mut Self::AfterLayout,
|
||||
before_paint: &mut Self::BeforePaint,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
self.element.paint(bounds, before_layout, after_layout, cx)
|
||||
self.element
|
||||
.paint(bounds, before_layout, after_layout, before_paint, cx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2346,6 +2423,7 @@ where
|
||||
{
|
||||
type BeforeLayout = E::BeforeLayout;
|
||||
type AfterLayout = E::AfterLayout;
|
||||
type BeforePaint = E::BeforePaint;
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
self.element.before_layout(cx)
|
||||
@@ -2354,10 +2432,21 @@ where
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
state: &mut Self::BeforeLayout,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> E::AfterLayout {
|
||||
self.element.after_layout(bounds, state, cx)
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
self.element.after_layout(bounds, before_layout, cx)
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> E::BeforePaint {
|
||||
self.element
|
||||
.before_paint(bounds, before_layout, after_layout, cx)
|
||||
}
|
||||
|
||||
fn paint(
|
||||
@@ -2365,9 +2454,11 @@ where
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
after_layout: &mut Self::AfterLayout,
|
||||
before_paint: &mut Self::BeforePaint,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
self.element.paint(bounds, before_layout, after_layout, cx);
|
||||
self.element
|
||||
.paint(bounds, before_layout, after_layout, before_paint, cx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -230,7 +230,8 @@ impl Img {
|
||||
|
||||
impl Element for Img {
|
||||
type BeforeLayout = ();
|
||||
type AfterLayout = Option<Hitbox>;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = Option<Hitbox>;
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
let layout_id = self.interactivity.before_layout(cx, |mut style, cx| {
|
||||
@@ -261,16 +262,29 @@ impl Element for Img {
|
||||
bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
self.interactivity
|
||||
.after_layout(bounds, bounds.size, cx, |_, _, _, _| {});
|
||||
(None, ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> Option<Hitbox> {
|
||||
self.interactivity
|
||||
.after_layout(bounds, bounds.size, cx, |_, _, hitbox, _| hitbox)
|
||||
.before_paint(bounds, cx, |_, _, hitbox, _| hitbox)
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_: &mut Self::BeforeLayout,
|
||||
hitbox: &mut Self::AfterLayout,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
hitbox: &mut Self::BeforePaint,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
let source = self.source.clone();
|
||||
|
||||
@@ -89,17 +89,28 @@ pub enum ListSizingBehavior {
|
||||
Auto,
|
||||
}
|
||||
|
||||
struct LayoutItemsResponse {
|
||||
/// Layout information computed by the [List] element during `after_layout`.
|
||||
pub struct ListLayout {
|
||||
max_item_width: Pixels,
|
||||
scroll_top: ListOffset,
|
||||
available_item_space: Size<AvailableSpace>,
|
||||
item_elements: VecDeque<AnyElement>,
|
||||
items: VecDeque<MeasuredItem>,
|
||||
focus_target: Option<FocusTarget>,
|
||||
}
|
||||
|
||||
/// Frame state used by the [List] element after layout.
|
||||
pub struct ListAfterLayoutState {
|
||||
struct MeasuredItem {
|
||||
element: AnyElement,
|
||||
size: Size<Pixels>,
|
||||
}
|
||||
|
||||
struct FocusTarget {
|
||||
item_index: usize,
|
||||
bounds_in_item: Bounds<Pixels>,
|
||||
}
|
||||
|
||||
/// Frame state used by the [List] element during paint.
|
||||
pub struct ListBeforePaintState {
|
||||
hitbox: Hitbox,
|
||||
layout: LayoutItemsResponse,
|
||||
layout: ListLayout,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -376,13 +387,14 @@ impl StateInner {
|
||||
available_height: Pixels,
|
||||
padding: &Edges<Pixels>,
|
||||
cx: &mut ElementContext,
|
||||
) -> LayoutItemsResponse {
|
||||
) -> ListLayout {
|
||||
let old_items = self.items.clone();
|
||||
let mut measured_items = VecDeque::new();
|
||||
let mut item_elements = VecDeque::new();
|
||||
let mut items = VecDeque::new();
|
||||
let mut rendered_height = padding.top;
|
||||
let mut max_item_width = px(0.);
|
||||
let mut scroll_top = self.logical_scroll_top();
|
||||
let mut focus_target = None;
|
||||
|
||||
let available_item_space = size(
|
||||
available_width.map_or(AvailableSpace::MinContent, |width| {
|
||||
@@ -411,10 +423,20 @@ impl StateInner {
|
||||
// If we're within the visible area or the height wasn't cached, render and measure the item's element
|
||||
if visible_height < available_height || size.is_none() {
|
||||
let mut element = (self.render_item)(scroll_top.item_ix + ix, cx);
|
||||
let element_size = element.measure(available_item_space, cx);
|
||||
size = Some(element_size);
|
||||
let element_measurement = element.layout(available_item_space, cx);
|
||||
size = Some(element_measurement.size);
|
||||
if visible_height < available_height {
|
||||
item_elements.push_back(element);
|
||||
if let Some(focus_target_bounds) = element_measurement.focus_target_bounds {
|
||||
focus_target = Some(FocusTarget {
|
||||
item_index: items.len(),
|
||||
bounds_in_item: focus_target_bounds,
|
||||
});
|
||||
}
|
||||
|
||||
items.push_back(MeasuredItem {
|
||||
element,
|
||||
size: element_measurement.size,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -435,11 +457,26 @@ impl StateInner {
|
||||
cursor.prev(&());
|
||||
if cursor.item().is_some() {
|
||||
let mut element = (self.render_item)(cursor.start().0, cx);
|
||||
let element_size = element.measure(available_item_space, cx);
|
||||
let element_measurement = element.layout(available_item_space, cx);
|
||||
|
||||
rendered_height += element_size.height;
|
||||
measured_items.push_front(ListItem::Rendered { size: element_size });
|
||||
item_elements.push_front(element)
|
||||
rendered_height += element_measurement.size.height;
|
||||
measured_items.push_front(ListItem::Rendered {
|
||||
size: element_measurement.size,
|
||||
});
|
||||
|
||||
items.push_front(MeasuredItem {
|
||||
element,
|
||||
size: element_measurement.size,
|
||||
});
|
||||
|
||||
if let Some(focus_target_bounds) = element_measurement.focus_target_bounds {
|
||||
focus_target = Some(FocusTarget {
|
||||
item_index: 0,
|
||||
bounds_in_item: focus_target_bounds,
|
||||
});
|
||||
} else if let Some(focus_target) = focus_target.as_mut() {
|
||||
focus_target.item_index += 1;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -474,7 +511,7 @@ impl StateInner {
|
||||
*size
|
||||
} else {
|
||||
let mut element = (self.render_item)(cursor.start().0, cx);
|
||||
element.measure(available_item_space, cx)
|
||||
element.layout(available_item_space, cx).size
|
||||
};
|
||||
|
||||
leading_overdraw += size.height;
|
||||
@@ -493,11 +530,11 @@ impl StateInner {
|
||||
|
||||
self.items = new_items;
|
||||
|
||||
LayoutItemsResponse {
|
||||
ListLayout {
|
||||
max_item_width,
|
||||
scroll_top,
|
||||
available_item_space,
|
||||
item_elements,
|
||||
items,
|
||||
focus_target,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -523,7 +560,8 @@ pub struct ListOffset {
|
||||
|
||||
impl Element for List {
|
||||
type BeforeLayout = ();
|
||||
type AfterLayout = ListAfterLayoutState;
|
||||
type AfterLayout = Option<ListLayout>;
|
||||
type BeforePaint = ListBeforePaintState;
|
||||
|
||||
fn before_layout(
|
||||
&mut self,
|
||||
@@ -592,17 +630,15 @@ impl Element for List {
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_: &mut Self::BeforeLayout,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> ListAfterLayoutState {
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
let state = &mut *self.state.0.borrow_mut();
|
||||
state.reset = false;
|
||||
|
||||
let mut style = Style::default();
|
||||
style.refine(&self.style);
|
||||
|
||||
let hitbox = cx.insert_hitbox(bounds, false);
|
||||
|
||||
// If the width of the list has changed, invalidate all cached item heights
|
||||
if state.last_layout_bounds.map_or(true, |last_bounds| {
|
||||
last_bounds.size.width != bounds.size.width
|
||||
@@ -614,48 +650,77 @@ impl Element for List {
|
||||
}
|
||||
|
||||
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
|
||||
let mut layout_response =
|
||||
state.layout_items(Some(bounds.size.width), bounds.size.height, &padding, cx);
|
||||
let layout = state.layout_items(Some(bounds.size.width), bounds.size.height, &padding, cx);
|
||||
let focus_target_bounds = layout.focus_target.as_ref().map(|focus_target| {
|
||||
let mut item_origin =
|
||||
bounds.origin + Point::new(px(0.), padding.top - layout.scroll_top.offset_in_item);
|
||||
item_origin.y -= layout.scroll_top.offset_in_item;
|
||||
for item in layout.items.iter().take(focus_target.item_index) {
|
||||
item_origin.y += item.size.height;
|
||||
}
|
||||
Bounds::new(
|
||||
item_origin + focus_target.bounds_in_item.origin,
|
||||
focus_target.bounds_in_item.size,
|
||||
)
|
||||
});
|
||||
(focus_target_bounds, Some(layout))
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_: &mut Self::BeforeLayout,
|
||||
layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> ListBeforePaintState {
|
||||
let mut layout = layout.take().unwrap();
|
||||
let state = &mut *self.state.0.borrow_mut();
|
||||
state.reset = false;
|
||||
|
||||
let mut style = Style::default();
|
||||
style.refine(&self.style);
|
||||
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
|
||||
|
||||
let hitbox = cx.insert_hitbox(bounds, false);
|
||||
|
||||
// Only paint the visible items, if there is actually any space for them (taking padding into account)
|
||||
if bounds.size.height > padding.top + padding.bottom {
|
||||
// Paint the visible items
|
||||
cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
|
||||
let mut item_origin = bounds.origin + Point::new(px(0.), padding.top);
|
||||
item_origin.y -= layout_response.scroll_top.offset_in_item;
|
||||
for mut item_element in &mut layout_response.item_elements {
|
||||
let item_size = item_element.measure(layout_response.available_item_space, cx);
|
||||
item_element.layout(item_origin, layout_response.available_item_space, cx);
|
||||
item_origin.y += item_size.height;
|
||||
item_origin.y -= layout.scroll_top.offset_in_item;
|
||||
for item in &mut layout.items {
|
||||
cx.with_absolute_element_offset(item_origin, |cx| {
|
||||
item.element.before_paint(cx)
|
||||
});
|
||||
item_origin.y += item.size.height;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
state.last_layout_bounds = Some(bounds);
|
||||
state.last_padding = Some(padding);
|
||||
ListAfterLayoutState {
|
||||
hitbox,
|
||||
layout: layout_response,
|
||||
}
|
||||
ListBeforePaintState { hitbox, layout }
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
bounds: Bounds<crate::Pixels>,
|
||||
_: &mut Self::BeforeLayout,
|
||||
after_layout: &mut Self::AfterLayout,
|
||||
_: &mut Self::AfterLayout,
|
||||
before_paint: &mut Self::BeforePaint,
|
||||
cx: &mut crate::ElementContext,
|
||||
) {
|
||||
cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
|
||||
for item in &mut after_layout.layout.item_elements {
|
||||
item.paint(cx);
|
||||
for item in &mut before_paint.layout.items {
|
||||
item.element.paint(cx);
|
||||
}
|
||||
});
|
||||
|
||||
let list_state = self.state.clone();
|
||||
let height = bounds.size.height;
|
||||
let scroll_top = after_layout.layout.scroll_top;
|
||||
let hitbox_id = after_layout.hitbox.id;
|
||||
let scroll_top = before_paint.layout.scroll_top;
|
||||
let hitbox_id = before_paint.hitbox.id;
|
||||
cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
|
||||
if phase == DispatchPhase::Bubble && hitbox_id.is_hovered(cx) {
|
||||
list_state.0.borrow_mut().scroll(
|
||||
|
||||
@@ -38,7 +38,8 @@ impl Svg {
|
||||
|
||||
impl Element for Svg {
|
||||
type BeforeLayout = ();
|
||||
type AfterLayout = Option<Hitbox>;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = Option<Hitbox>;
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
let layout_id = self
|
||||
@@ -52,15 +53,28 @@ impl Element for Svg {
|
||||
bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
self.interactivity
|
||||
.after_layout(bounds, bounds.size, cx, |_, _, _, _| {});
|
||||
(None, ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> Option<Hitbox> {
|
||||
self.interactivity
|
||||
.after_layout(bounds, bounds.size, cx, |_, _, hitbox, _| hitbox)
|
||||
.before_paint(bounds, cx, |_, _, hitbox, _| hitbox)
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
hitbox: &mut Option<Hitbox>,
|
||||
cx: &mut ElementContext,
|
||||
) where
|
||||
|
||||
@@ -19,6 +19,7 @@ use util::ResultExt;
|
||||
impl Element for &'static str {
|
||||
type BeforeLayout = TextState;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = ();
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
let mut state = TextState::default();
|
||||
@@ -27,9 +28,19 @@ impl Element for &'static str {
|
||||
}
|
||||
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
(None, ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_text_state: &mut Self::BeforeLayout,
|
||||
_: &mut Self::AfterLayout,
|
||||
_cx: &mut ElementContext,
|
||||
) {
|
||||
}
|
||||
@@ -38,6 +49,7 @@ impl Element for &'static str {
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
text_state: &mut TextState,
|
||||
_: &mut Self::AfterLayout,
|
||||
_: &mut (),
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
@@ -64,6 +76,7 @@ impl IntoElement for String {
|
||||
impl Element for SharedString {
|
||||
type BeforeLayout = TextState;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = ();
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
let mut state = TextState::default();
|
||||
@@ -72,9 +85,19 @@ impl Element for SharedString {
|
||||
}
|
||||
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
(None, ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_text_state: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
_cx: &mut ElementContext,
|
||||
) {
|
||||
}
|
||||
@@ -83,7 +106,8 @@ impl Element for SharedString {
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
text_state: &mut Self::BeforeLayout,
|
||||
_: &mut Self::AfterLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
_: &mut Self::BeforePaint,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
let text_str: &str = self.as_ref();
|
||||
@@ -150,6 +174,7 @@ impl StyledText {
|
||||
impl Element for StyledText {
|
||||
type BeforeLayout = TextState;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = ();
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
let mut state = TextState::default();
|
||||
@@ -158,9 +183,19 @@ impl Element for StyledText {
|
||||
}
|
||||
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
(None, ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_state: &mut Self::BeforeLayout,
|
||||
_: &mut Self::AfterLayout,
|
||||
_cx: &mut ElementContext,
|
||||
) {
|
||||
}
|
||||
@@ -170,6 +205,7 @@ impl Element for StyledText {
|
||||
bounds: Bounds<Pixels>,
|
||||
text_state: &mut Self::BeforeLayout,
|
||||
_: &mut Self::AfterLayout,
|
||||
_: &mut (),
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
text_state.paint(bounds, &self.text, cx)
|
||||
@@ -403,16 +439,27 @@ impl InteractiveText {
|
||||
|
||||
impl Element for InteractiveText {
|
||||
type BeforeLayout = TextState;
|
||||
type AfterLayout = Hitbox;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = Hitbox;
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
self.text.before_layout(cx)
|
||||
}
|
||||
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
(None, ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
state: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> Hitbox {
|
||||
cx.with_element_state::<InteractiveTextState, _>(
|
||||
@@ -430,7 +477,7 @@ impl Element for InteractiveText {
|
||||
}
|
||||
}
|
||||
|
||||
self.text.after_layout(bounds, state, cx);
|
||||
self.text.before_paint(bounds, state, &mut (), cx);
|
||||
let hitbox = cx.insert_hitbox(bounds, false);
|
||||
(hitbox, interactive_state)
|
||||
},
|
||||
@@ -441,6 +488,7 @@ impl Element for InteractiveText {
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
text_state: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
hitbox: &mut Hitbox,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
@@ -577,7 +625,7 @@ impl Element for InteractiveText {
|
||||
});
|
||||
}
|
||||
|
||||
self.text.paint(bounds, text_state, &mut (), cx);
|
||||
self.text.paint(bounds, text_state, &mut (), &mut (), cx);
|
||||
|
||||
((), Some(interactive_state))
|
||||
},
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
|
||||
use crate::{
|
||||
point, px, size, AnyElement, AvailableSpace, Bounds, ContentMask, Element, ElementContext,
|
||||
ElementId, Hitbox, InteractiveElement, Interactivity, IntoElement, LayoutId, Pixels, Render,
|
||||
ScrollHandle, Size, StyleRefinement, Styled, View, ViewContext, WindowContext,
|
||||
ElementId, ElementMeasurement, Hitbox, InteractiveElement, Interactivity, IntoElement,
|
||||
LayoutId, Pixels, Render, ScrollHandle, Size, StyleRefinement, Styled, View, ViewContext,
|
||||
WindowContext,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
|
||||
@@ -70,8 +71,9 @@ pub struct UniformList {
|
||||
|
||||
/// Frame state used by the [UniformList].
|
||||
pub struct UniformListFrameState {
|
||||
item_size: Size<Pixels>,
|
||||
item_height: Pixels,
|
||||
items: SmallVec<[AnyElement; 32]>,
|
||||
visible_range: Range<usize>,
|
||||
}
|
||||
|
||||
/// A handle for controlling the scroll position of a uniform list.
|
||||
@@ -105,19 +107,22 @@ impl Styled for UniformList {
|
||||
|
||||
impl Element for UniformList {
|
||||
type BeforeLayout = UniformListFrameState;
|
||||
type AfterLayout = Option<Hitbox>;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = Option<Hitbox>;
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
let max_items = self.item_count;
|
||||
let item_size = self.measure_item(None, cx);
|
||||
let item_measurement = self.measure_item(None, cx);
|
||||
let layout_id = self.interactivity.before_layout(cx, |style, cx| {
|
||||
cx.request_measured_layout(style, move |known_dimensions, available_space, _cx| {
|
||||
let desired_height = item_size.height * max_items;
|
||||
let desired_height = item_measurement.size.height * max_items;
|
||||
let width = known_dimensions
|
||||
.width
|
||||
.unwrap_or(match available_space.width {
|
||||
AvailableSpace::Definite(x) => x,
|
||||
AvailableSpace::MinContent | AvailableSpace::MaxContent => item_size.width,
|
||||
AvailableSpace::MinContent | AvailableSpace::MaxContent => {
|
||||
item_measurement.size.width
|
||||
}
|
||||
});
|
||||
|
||||
let height = match available_space.height {
|
||||
@@ -131,8 +136,9 @@ impl Element for UniformList {
|
||||
(
|
||||
layout_id,
|
||||
UniformListFrameState {
|
||||
item_size,
|
||||
item_height: Pixels::default(),
|
||||
items: SmallVec::new(),
|
||||
visible_range: 0..0,
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -140,28 +146,26 @@ impl Element for UniformList {
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
state: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> Option<Hitbox> {
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
let style = self.interactivity.compute_style(None, cx);
|
||||
let border = style.border_widths.to_pixels(cx.rem_size());
|
||||
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
|
||||
|
||||
let padded_bounds = Bounds::from_corners(
|
||||
bounds.origin + point(border.left + padding.left, border.top + padding.top),
|
||||
bounds.lower_right()
|
||||
- point(border.right + padding.right, border.bottom + padding.bottom),
|
||||
let padded_size = size(
|
||||
bounds.size.width - border.left - padding.left - border.right - padding.right,
|
||||
bounds.size.height - border.top - padding.top - border.bottom - padding.bottom,
|
||||
);
|
||||
|
||||
let item_height = self.measure_item(Some(padded_size.width), cx).size.height;
|
||||
let content_size = Size {
|
||||
width: padded_bounds.size.width,
|
||||
height: before_layout.item_size.height * self.item_count + padding.top + padding.bottom,
|
||||
width: padded_size.width,
|
||||
height: item_height * self.item_count + padding.top + padding.bottom,
|
||||
};
|
||||
|
||||
let shared_scroll_offset = self.interactivity.scroll_offset.clone().unwrap();
|
||||
|
||||
let item_height = self.measure_item(Some(padded_bounds.size.width), cx).height;
|
||||
let shared_scroll_to_item = self
|
||||
// todo!("add support for scrolling to the focused element?");
|
||||
let deferred_scroll_to_item = self
|
||||
.scroll_handle
|
||||
.as_mut()
|
||||
.and_then(|handle| handle.deferred_scroll_to_item.take());
|
||||
@@ -170,73 +174,103 @@ impl Element for UniformList {
|
||||
bounds,
|
||||
content_size,
|
||||
cx,
|
||||
|style, mut scroll_offset, hitbox, cx| {
|
||||
|style, mut scroll_offset, mut focus_target_bounds, cx| {
|
||||
if self.item_count == 0 {
|
||||
return (focus_target_bounds, ());
|
||||
}
|
||||
|
||||
let border = style.border_widths.to_pixels(cx.rem_size());
|
||||
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
|
||||
|
||||
let padded_size = size(
|
||||
bounds.size.width - border.left - padding.left - border.right - padding.right,
|
||||
bounds.size.height - border.top - padding.top - border.bottom - padding.bottom,
|
||||
);
|
||||
|
||||
let content_height = item_height * self.item_count + padding.top + padding.bottom;
|
||||
let min_scroll_offset = padded_size.height - content_height;
|
||||
let is_scrolled = scroll_offset.y != px(0.);
|
||||
|
||||
if is_scrolled && scroll_offset.y < min_scroll_offset {
|
||||
shared_scroll_offset.borrow_mut().y = min_scroll_offset;
|
||||
scroll_offset.y = min_scroll_offset;
|
||||
}
|
||||
|
||||
if let Some(ix) = deferred_scroll_to_item {
|
||||
let list_height = padded_size.height;
|
||||
let mut updated_scroll_offset = shared_scroll_offset.borrow_mut();
|
||||
let item_top = item_height * ix + padding.top;
|
||||
let item_bottom = item_top + item_height;
|
||||
let scroll_top = -updated_scroll_offset.y;
|
||||
if item_top < scroll_top + padding.top {
|
||||
updated_scroll_offset.y = -(item_top) + padding.top;
|
||||
} else if item_bottom > scroll_top + list_height - padding.bottom {
|
||||
updated_scroll_offset.y = -(item_bottom - list_height) - padding.bottom;
|
||||
}
|
||||
scroll_offset = *updated_scroll_offset;
|
||||
}
|
||||
|
||||
let first_visible_element_ix =
|
||||
(-(scroll_offset.y + padding.top) / item_height).floor() as usize;
|
||||
let last_visible_element_ix =
|
||||
((-scroll_offset.y + padded_size.height) / item_height).ceil() as usize;
|
||||
let visible_range =
|
||||
first_visible_element_ix..cmp::min(last_visible_element_ix, self.item_count);
|
||||
|
||||
let mut items = (self.render_items)(visible_range.clone(), cx);
|
||||
let available_space = size(
|
||||
AvailableSpace::Definite(padded_size.width),
|
||||
AvailableSpace::Definite(item_height),
|
||||
);
|
||||
for mut item in items {
|
||||
let measurement = item.layout(available_space, cx);
|
||||
if measurement.focus_target_bounds.is_some() {
|
||||
focus_target_bounds = measurement.focus_target_bounds;
|
||||
}
|
||||
state.items.push(item);
|
||||
}
|
||||
state.item_height = item_height;
|
||||
state.visible_range = visible_range;
|
||||
|
||||
(focus_target_bounds, ())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
state: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> Option<Hitbox> {
|
||||
self.interactivity
|
||||
.before_paint(bounds, cx, |style, scroll_offset, hitbox, cx| {
|
||||
let border = style.border_widths.to_pixels(cx.rem_size());
|
||||
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
|
||||
let padded_bounds = Bounds::from_corners(
|
||||
bounds.origin + point(border.left + padding.left, border.top),
|
||||
bounds.lower_right() - point(border.right + padding.right, border.bottom),
|
||||
);
|
||||
|
||||
if self.item_count > 0 {
|
||||
let content_height =
|
||||
item_height * self.item_count + padding.top + padding.bottom;
|
||||
let min_scroll_offset = padded_bounds.size.height - content_height;
|
||||
let is_scrolled = scroll_offset.y != px(0.);
|
||||
|
||||
if is_scrolled && scroll_offset.y < min_scroll_offset {
|
||||
shared_scroll_offset.borrow_mut().y = min_scroll_offset;
|
||||
scroll_offset.y = min_scroll_offset;
|
||||
let content_mask = ContentMask { bounds };
|
||||
cx.with_content_mask(Some(content_mask), |cx| {
|
||||
for (item, ix) in state.items.iter_mut().zip(state.visible_range.clone()) {
|
||||
let item_y = state.item_height * ix + scroll_offset.y + padding.top;
|
||||
let item_origin = padded_bounds.origin + point(px(0.), item_y);
|
||||
cx.with_absolute_element_offset(item_origin, |cx| item.before_paint(cx));
|
||||
}
|
||||
|
||||
if let Some(ix) = shared_scroll_to_item {
|
||||
let list_height = padded_bounds.size.height;
|
||||
let mut updated_scroll_offset = shared_scroll_offset.borrow_mut();
|
||||
let item_top = item_height * ix + padding.top;
|
||||
let item_bottom = item_top + item_height;
|
||||
let scroll_top = -updated_scroll_offset.y;
|
||||
if item_top < scroll_top + padding.top {
|
||||
updated_scroll_offset.y = -(item_top) + padding.top;
|
||||
} else if item_bottom > scroll_top + list_height - padding.bottom {
|
||||
updated_scroll_offset.y = -(item_bottom - list_height) - padding.bottom;
|
||||
}
|
||||
scroll_offset = *updated_scroll_offset;
|
||||
}
|
||||
|
||||
let first_visible_element_ix =
|
||||
(-(scroll_offset.y + padding.top) / item_height).floor() as usize;
|
||||
let last_visible_element_ix = ((-scroll_offset.y + padded_bounds.size.height)
|
||||
/ item_height)
|
||||
.ceil() as usize;
|
||||
let visible_range = first_visible_element_ix
|
||||
..cmp::min(last_visible_element_ix, self.item_count);
|
||||
|
||||
let mut items = (self.render_items)(visible_range.clone(), cx);
|
||||
let content_mask = ContentMask { bounds };
|
||||
cx.with_content_mask(Some(content_mask), |cx| {
|
||||
for (mut item, ix) in items.into_iter().zip(visible_range) {
|
||||
let item_origin = padded_bounds.origin
|
||||
+ point(px(0.), item_height * ix + scroll_offset.y + padding.top);
|
||||
let available_space = size(
|
||||
AvailableSpace::Definite(padded_bounds.size.width),
|
||||
AvailableSpace::Definite(item_height),
|
||||
);
|
||||
item.layout(item_origin, available_space, cx);
|
||||
before_layout.items.push(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
hitbox
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
bounds: Bounds<crate::Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
hitbox: &mut Option<Hitbox>,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
@@ -264,9 +298,13 @@ impl UniformList {
|
||||
self
|
||||
}
|
||||
|
||||
fn measure_item(&self, list_width: Option<Pixels>, cx: &mut ElementContext) -> Size<Pixels> {
|
||||
fn measure_item(
|
||||
&self,
|
||||
list_width: Option<Pixels>,
|
||||
cx: &mut ElementContext,
|
||||
) -> ElementMeasurement {
|
||||
if self.item_count == 0 {
|
||||
return Size::default();
|
||||
return ElementMeasurement::default();
|
||||
}
|
||||
|
||||
let item_ix = cmp::min(self.item_to_measure_index, self.item_count - 1);
|
||||
@@ -278,7 +316,7 @@ impl UniformList {
|
||||
}),
|
||||
AvailableSpace::MinContent,
|
||||
);
|
||||
item_to_measure.measure(available_space, cx)
|
||||
item_to_measure.layout(available_space, cx)
|
||||
}
|
||||
|
||||
/// Track and render scroll state of this list with reference to the given scroll handle.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::{
|
||||
seal::Sealed, AfterLayoutIndex, AnyElement, AnyModel, AnyWeakModel, AppContext, Bounds,
|
||||
ContentMask, Element, ElementContext, ElementId, Entity, EntityId, Flatten, FocusHandle,
|
||||
FocusableView, IntoElement, LayoutId, Model, PaintIndex, Pixels, Render, Style,
|
||||
StyleRefinement, TextStyle, ViewContext, VisualContext, WeakModel,
|
||||
seal::Sealed, AfterLayoutIndex, AnyElement, AnyModel, AnyWeakModel, AppContext,
|
||||
BeforePaintIndex, Bounds, ContentMask, Element, ElementContext, ElementId, Entity, EntityId,
|
||||
Flatten, FocusHandle, FocusableView, IntoElement, LayoutId, Model, PaintIndex, Pixels, Render,
|
||||
Style, StyleRefinement, TextStyle, ViewContext, VisualContext, WeakModel,
|
||||
};
|
||||
use anyhow::{Context, Result};
|
||||
use refineable::Refineable;
|
||||
@@ -24,13 +24,16 @@ impl<V> Sealed for View<V> {}
|
||||
|
||||
struct AnyViewState {
|
||||
after_layout_range: Range<AfterLayoutIndex>,
|
||||
before_paint_range: Range<BeforePaintIndex>,
|
||||
paint_range: Range<PaintIndex>,
|
||||
focus_target_bounds: Option<Bounds<Pixels>>,
|
||||
cache_key: ViewCacheKey,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ViewCacheKey {
|
||||
bounds: Bounds<Pixels>,
|
||||
after_layout_bounds: Bounds<Pixels>,
|
||||
before_paint_bounds: Bounds<Pixels>,
|
||||
content_mask: ContentMask<Pixels>,
|
||||
text_style: TextStyle,
|
||||
}
|
||||
@@ -92,6 +95,7 @@ impl<V: 'static> View<V> {
|
||||
impl<V: Render> Element for View<V> {
|
||||
type BeforeLayout = AnyElement;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = ();
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
|
||||
@@ -102,14 +106,24 @@ impl<V: Render> Element for View<V> {
|
||||
}
|
||||
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
element: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
(element.after_layout(cx), ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
_: Bounds<Pixels>,
|
||||
element: &mut Self::BeforeLayout,
|
||||
_: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
cx.set_view_id(self.entity_id());
|
||||
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
|
||||
element.after_layout(cx)
|
||||
element.before_paint(cx)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -118,6 +132,7 @@ impl<V: Render> Element for View<V> {
|
||||
_: Bounds<Pixels>,
|
||||
element: &mut Self::BeforeLayout,
|
||||
_: &mut Self::AfterLayout,
|
||||
_: &mut Self::BeforePaint,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
|
||||
@@ -277,7 +292,8 @@ impl<V: Render> From<View<V>> for AnyView {
|
||||
|
||||
impl Element for AnyView {
|
||||
type BeforeLayout = Option<AnyElement>;
|
||||
type AfterLayout = Option<AnyElement>;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = ();
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
if let Some(style) = self.cached_style.as_ref() {
|
||||
@@ -299,20 +315,16 @@ impl Element for AnyView {
|
||||
bounds: Bounds<Pixels>,
|
||||
element: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> Option<AnyElement> {
|
||||
cx.set_view_id(self.entity_id());
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
if self.cached_style.is_some() {
|
||||
cx.with_element_state::<AnyViewState, _>(
|
||||
let focus_target_bounds = cx.with_element_state::<AnyViewState, _>(
|
||||
Some(ElementId::View(self.entity_id())),
|
||||
|element_state, cx| {
|
||||
let mut element_state = element_state.unwrap();
|
||||
|
||||
let content_mask = cx.content_mask();
|
||||
let text_style = cx.text_style();
|
||||
|
||||
if let Some(mut element_state) = element_state {
|
||||
if element_state.cache_key.bounds == bounds
|
||||
&& element_state.cache_key.content_mask == content_mask
|
||||
if element_state.cache_key.after_layout_bounds == bounds
|
||||
&& element_state.cache_key.text_style == text_style
|
||||
&& !cx.window.dirty_views.contains(&self.entity_id())
|
||||
&& !cx.window.refreshing
|
||||
@@ -321,34 +333,106 @@ impl Element for AnyView {
|
||||
cx.reuse_after_layout(element_state.after_layout_range.clone());
|
||||
let after_layout_end = cx.after_layout_index();
|
||||
element_state.after_layout_range = after_layout_start..after_layout_end;
|
||||
return (None, Some(element_state));
|
||||
return (element_state.focus_target_bounds, Some(element_state));
|
||||
}
|
||||
}
|
||||
|
||||
let after_layout_start = cx.after_layout_index();
|
||||
let mut element = (self.render)(self, cx);
|
||||
element.layout(bounds.origin, bounds.size.into(), cx);
|
||||
let mut rendered_element = (self.render)(self, cx);
|
||||
let element_measurement = rendered_element.layout(bounds.size.into(), cx);
|
||||
*element = Some(rendered_element);
|
||||
let focus_target_bounds =
|
||||
element_measurement
|
||||
.focus_target_bounds
|
||||
.map(|focus_target_bounds| {
|
||||
Bounds::new(
|
||||
bounds.origin + focus_target_bounds.origin,
|
||||
focus_target_bounds.size,
|
||||
)
|
||||
});
|
||||
let after_layout_end = cx.after_layout_index();
|
||||
|
||||
(
|
||||
Some(element),
|
||||
Some(AnyViewState {
|
||||
after_layout_range: after_layout_start..after_layout_end,
|
||||
paint_range: PaintIndex::default()..PaintIndex::default(),
|
||||
cache_key: ViewCacheKey {
|
||||
bounds,
|
||||
content_mask,
|
||||
text_style,
|
||||
},
|
||||
}),
|
||||
)
|
||||
let view_state = AnyViewState {
|
||||
after_layout_range: after_layout_start..after_layout_end,
|
||||
before_paint_range: BeforePaintIndex::default()
|
||||
..BeforePaintIndex::default(),
|
||||
paint_range: PaintIndex::default()..PaintIndex::default(),
|
||||
focus_target_bounds,
|
||||
cache_key: ViewCacheKey {
|
||||
after_layout_bounds: bounds,
|
||||
text_style,
|
||||
..Default::default()
|
||||
},
|
||||
};
|
||||
|
||||
(focus_target_bounds, Some(view_state))
|
||||
},
|
||||
);
|
||||
(focus_target_bounds, ())
|
||||
} else {
|
||||
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
|
||||
let bounds = element.as_mut().unwrap().after_layout(cx);
|
||||
(bounds, ())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
element: &mut Self::BeforeLayout,
|
||||
_: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
cx.set_view_id(self.entity_id());
|
||||
if self.cached_style.is_some() {
|
||||
cx.with_element_state::<AnyViewState, _>(
|
||||
Some(ElementId::View(self.entity_id())),
|
||||
|element_state, cx| {
|
||||
let mut element_state = element_state.unwrap().unwrap();
|
||||
let content_mask = cx.content_mask();
|
||||
|
||||
if let Some(element) = element {
|
||||
let before_paint_start = cx.before_paint_index();
|
||||
cx.with_absolute_element_offset(bounds.origin, |cx| {
|
||||
element.before_paint(cx)
|
||||
});
|
||||
let before_paint_end = cx.before_paint_index();
|
||||
element_state.before_paint_range = before_paint_start..before_paint_end;
|
||||
element_state.cache_key.before_paint_bounds = bounds;
|
||||
element_state.cache_key.content_mask = content_mask;
|
||||
} else if element_state.cache_key.before_paint_bounds == bounds
|
||||
&& element_state.cache_key.content_mask == content_mask
|
||||
{
|
||||
let before_paint_start = cx.before_paint_index();
|
||||
cx.reuse_before_paint(element_state.before_paint_range.clone());
|
||||
let before_paint_end = cx.before_paint_index();
|
||||
element_state.before_paint_range = before_paint_start..before_paint_end;
|
||||
} else {
|
||||
let mut rendered_element = (self.render)(self, cx);
|
||||
let after_layout_start = cx.after_layout_index();
|
||||
rendered_element.layout(bounds.size.into(), cx);
|
||||
let after_layout_end = cx.after_layout_index();
|
||||
|
||||
let before_paint_start = cx.before_paint_index();
|
||||
cx.with_absolute_element_offset(bounds.origin, |cx| {
|
||||
rendered_element.before_paint(cx)
|
||||
});
|
||||
let before_paint_end = cx.before_paint_index();
|
||||
element_state.after_layout_range = after_layout_start..after_layout_end;
|
||||
element_state.before_paint_range = before_paint_start..before_paint_end;
|
||||
element_state.cache_key.before_paint_bounds = bounds;
|
||||
element_state.cache_key.content_mask = content_mask;
|
||||
|
||||
*element = Some(rendered_element);
|
||||
}
|
||||
|
||||
((), Some(element_state))
|
||||
},
|
||||
)
|
||||
} else {
|
||||
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
|
||||
let mut element = element.take().unwrap();
|
||||
element.after_layout(cx);
|
||||
Some(element)
|
||||
element.as_mut().unwrap().before_paint(cx)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -356,8 +440,9 @@ impl Element for AnyView {
|
||||
fn paint(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_: &mut Self::BeforeLayout,
|
||||
element: &mut Self::AfterLayout,
|
||||
element: &mut Self::BeforeLayout,
|
||||
_: &mut Self::AfterLayout,
|
||||
_: &mut Self::BeforePaint,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
if self.cached_style.is_some() {
|
||||
|
||||
@@ -121,7 +121,7 @@ pub(crate) struct DeferredDraw {
|
||||
text_style_stack: Vec<TextStyleRefinement>,
|
||||
element: Option<AnyElement>,
|
||||
absolute_offset: Point<Pixels>,
|
||||
layout_range: Range<AfterLayoutIndex>,
|
||||
before_paint_range: Range<BeforePaintIndex>,
|
||||
paint_range: Range<PaintIndex>,
|
||||
}
|
||||
|
||||
@@ -146,6 +146,12 @@ pub(crate) struct Frame {
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub(crate) struct AfterLayoutIndex {
|
||||
accessed_element_states_index: usize,
|
||||
line_layout_index: LineLayoutIndex,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub(crate) struct BeforePaintIndex {
|
||||
hitboxes_index: usize,
|
||||
tooltips_index: usize,
|
||||
deferred_draws_index: usize,
|
||||
@@ -399,7 +405,8 @@ impl<'a> ElementContext<'a> {
|
||||
|
||||
// Layout all root elements.
|
||||
let mut root_element = self.window.root_view.as_ref().unwrap().clone().into_any();
|
||||
root_element.layout(Point::default(), self.window.viewport_size.into(), self);
|
||||
root_element.layout(self.window.viewport_size.into(), self);
|
||||
root_element.before_paint(self);
|
||||
|
||||
let mut sorted_deferred_draws =
|
||||
(0..self.window.next_frame.deferred_draws.len()).collect::<SmallVec<[_; 8]>>();
|
||||
@@ -411,13 +418,15 @@ impl<'a> ElementContext<'a> {
|
||||
let mut tooltip_element = None;
|
||||
if let Some(prompt) = self.window.prompt.take() {
|
||||
let mut element = prompt.view.any_view().into_any();
|
||||
element.layout(Point::default(), self.window.viewport_size.into(), self);
|
||||
element.layout(self.window.viewport_size.into(), self);
|
||||
element.before_paint(self);
|
||||
prompt_element = Some(element);
|
||||
self.window.prompt = Some(prompt);
|
||||
} else if let Some(active_drag) = self.app.active_drag.take() {
|
||||
let mut element = active_drag.view.clone().into_any();
|
||||
element.layout(AvailableSpace::min_size(), self);
|
||||
let offset = self.mouse_position() - active_drag.cursor_offset;
|
||||
element.layout(offset, AvailableSpace::min_size(), self);
|
||||
self.with_element_offset(offset, |cx| element.before_paint(cx));
|
||||
active_drag_element = Some(element);
|
||||
self.app.active_drag = Some(active_drag);
|
||||
} else {
|
||||
@@ -446,7 +455,7 @@ impl<'a> ElementContext<'a> {
|
||||
let tooltip_request = tooltip_request.unwrap();
|
||||
let mut element = tooltip_request.tooltip.view.clone().into_any();
|
||||
let mouse_position = tooltip_request.tooltip.mouse_position;
|
||||
let tooltip_size = element.measure(AvailableSpace::min_size(), self);
|
||||
let tooltip_size = element.layout(AvailableSpace::min_size(), self).size;
|
||||
|
||||
let mut tooltip_bounds = Bounds::new(mouse_position + point(px(1.), px(1.)), tooltip_size);
|
||||
let window_bounds = Bounds {
|
||||
@@ -478,7 +487,7 @@ impl<'a> ElementContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
self.with_absolute_element_offset(tooltip_bounds.origin, |cx| element.after_layout(cx));
|
||||
self.with_absolute_element_offset(tooltip_bounds.origin, |cx| element.before_paint(cx));
|
||||
|
||||
self.window.tooltip_bounds = Some(TooltipBounds {
|
||||
id: tooltip_request.id,
|
||||
@@ -500,16 +509,16 @@ impl<'a> ElementContext<'a> {
|
||||
.dispatch_tree
|
||||
.set_active_node(deferred_draw.parent_node);
|
||||
|
||||
let layout_start = self.after_layout_index();
|
||||
let layout_start = self.before_paint_index();
|
||||
if let Some(element) = deferred_draw.element.as_mut() {
|
||||
self.with_absolute_element_offset(deferred_draw.absolute_offset, |cx| {
|
||||
element.after_layout(cx)
|
||||
element.before_paint(cx)
|
||||
});
|
||||
} else {
|
||||
self.reuse_after_layout(deferred_draw.layout_range.clone());
|
||||
self.reuse_before_paint(deferred_draw.before_paint_range.clone());
|
||||
}
|
||||
let layout_end = self.after_layout_index();
|
||||
deferred_draw.layout_range = layout_start..layout_end;
|
||||
let layout_end = self.before_paint_index();
|
||||
deferred_draw.before_paint_range = layout_start..layout_end;
|
||||
}
|
||||
assert_eq!(
|
||||
self.window.next_frame.deferred_draws.len(),
|
||||
@@ -548,6 +557,26 @@ impl<'a> ElementContext<'a> {
|
||||
|
||||
pub(crate) fn after_layout_index(&self) -> AfterLayoutIndex {
|
||||
AfterLayoutIndex {
|
||||
accessed_element_states_index: self.window.next_frame.accessed_element_states.len(),
|
||||
line_layout_index: self.window.text_system.layout_index(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn reuse_after_layout(&mut self, range: Range<AfterLayoutIndex>) {
|
||||
let window = &mut self.window;
|
||||
window.next_frame.accessed_element_states.extend(
|
||||
window.rendered_frame.accessed_element_states[range.start.accessed_element_states_index
|
||||
..range.end.accessed_element_states_index]
|
||||
.iter()
|
||||
.cloned(),
|
||||
);
|
||||
window
|
||||
.text_system
|
||||
.reuse_layouts(range.start.line_layout_index..range.end.line_layout_index);
|
||||
}
|
||||
|
||||
pub(crate) fn before_paint_index(&self) -> BeforePaintIndex {
|
||||
BeforePaintIndex {
|
||||
hitboxes_index: self.window.next_frame.hitboxes.len(),
|
||||
tooltips_index: self.window.next_frame.tooltip_requests.len(),
|
||||
deferred_draws_index: self.window.next_frame.deferred_draws.len(),
|
||||
@@ -557,7 +586,7 @@ impl<'a> ElementContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn reuse_after_layout(&mut self, range: Range<AfterLayoutIndex>) {
|
||||
pub(crate) fn reuse_before_paint(&mut self, range: Range<BeforePaintIndex>) {
|
||||
let window = &mut self.window;
|
||||
window.next_frame.hitboxes.extend(
|
||||
window.rendered_frame.hitboxes[range.start.hitboxes_index..range.end.hitboxes_index]
|
||||
@@ -595,7 +624,7 @@ impl<'a> ElementContext<'a> {
|
||||
priority: deferred_draw.priority,
|
||||
element: None,
|
||||
absolute_offset: deferred_draw.absolute_offset,
|
||||
layout_range: deferred_draw.layout_range.clone(),
|
||||
before_paint_range: deferred_draw.before_paint_range.clone(),
|
||||
paint_range: deferred_draw.paint_range.clone(),
|
||||
}),
|
||||
);
|
||||
@@ -974,7 +1003,7 @@ impl<'a> ElementContext<'a> {
|
||||
assert_eq!(
|
||||
window.draw_phase,
|
||||
DrawPhase::Layout,
|
||||
"defer_draw can only be called during before_layout or after_layout"
|
||||
"defer_draw can only be called during before_layout or before_paint"
|
||||
);
|
||||
let parent_node = window.next_frame.dispatch_tree.active_node_id().unwrap();
|
||||
window.next_frame.deferred_draws.push(DeferredDraw {
|
||||
@@ -984,7 +1013,7 @@ impl<'a> ElementContext<'a> {
|
||||
priority,
|
||||
element: Some(element),
|
||||
absolute_offset,
|
||||
layout_range: AfterLayoutIndex::default()..AfterLayoutIndex::default(),
|
||||
before_paint_range: BeforePaintIndex::default()..BeforePaintIndex::default(),
|
||||
paint_range: PaintIndex::default()..PaintIndex::default(),
|
||||
});
|
||||
}
|
||||
@@ -1397,7 +1426,7 @@ impl<'a> ElementContext<'a> {
|
||||
bounds
|
||||
}
|
||||
|
||||
/// This method should be called during `after_layout`. You can use
|
||||
/// This method should be called during `before_paint`. You can use
|
||||
/// the returned [Hitbox] during `paint` or in an event handler
|
||||
/// to determine whether the inserted hitbox was the topmost.
|
||||
pub fn insert_hitbox(&mut self, bounds: Bounds<Pixels>, opaque: bool) -> Hitbox {
|
||||
|
||||
@@ -155,7 +155,7 @@ impl FocusableView for ImageView {
|
||||
|
||||
impl Render for ImageView {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
let checkered_background = |bounds: Bounds<Pixels>, _, cx: &mut ElementContext| {
|
||||
let checkered_background = |bounds: Bounds<Pixels>, cx: &mut ElementContext| {
|
||||
let square_size = 32.0;
|
||||
|
||||
let start_y = bounds.origin.y.0;
|
||||
@@ -190,7 +190,7 @@ impl Render for ImageView {
|
||||
}
|
||||
};
|
||||
|
||||
let checkered_background = canvas(|_, _| (), checkered_background)
|
||||
let checkered_background = canvas(checkered_background)
|
||||
.border_2()
|
||||
.border_color(cx.theme().styles.colors.border)
|
||||
.size_full()
|
||||
|
||||
@@ -363,14 +363,15 @@ impl Render for SyntaxTreeView {
|
||||
.text_bg(cx.theme().colors().background).into_any_element();
|
||||
|
||||
rendered = rendered.child(
|
||||
canvas(
|
||||
move |bounds, cx| {
|
||||
list.layout(bounds.origin, bounds.size.into(), cx);
|
||||
list
|
||||
},
|
||||
|_, mut list, cx| list.paint(cx),
|
||||
)
|
||||
.size_full(),
|
||||
// canvas(
|
||||
// move |bounds, cx| {
|
||||
// list.layout(bounds.origin, bounds.size.into(), cx);
|
||||
// list
|
||||
// },
|
||||
// |_, mut list, cx| list.paint(cx),
|
||||
// )
|
||||
// .size_full(),
|
||||
todo!("replace canvas"),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -542,7 +542,7 @@ impl TerminalElement {
|
||||
|
||||
impl Element for TerminalElement {
|
||||
type BeforeLayout = ();
|
||||
type AfterLayout = LayoutState;
|
||||
type BeforePaint = LayoutState;
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
self.interactivity.occlude_mouse();
|
||||
@@ -556,14 +556,14 @@ impl Element for TerminalElement {
|
||||
(layout_id, ())
|
||||
}
|
||||
|
||||
fn after_layout(
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> Self::AfterLayout {
|
||||
) -> Self::BeforePaint {
|
||||
self.interactivity
|
||||
.after_layout(bounds, bounds.size, cx, |_, _, hitbox, cx| {
|
||||
.before_paint(bounds, bounds.size, cx, |_, _, hitbox, cx| {
|
||||
let hitbox = hitbox.unwrap();
|
||||
let settings = ThemeSettings::get_global(cx).clone();
|
||||
|
||||
@@ -776,7 +776,7 @@ impl Element for TerminalElement {
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_: &mut Self::BeforeLayout,
|
||||
layout: &mut Self::AfterLayout,
|
||||
layout: &mut Self::BeforePaint,
|
||||
cx: &mut ElementContext<'_>,
|
||||
) {
|
||||
cx.paint_quad(fill(bounds, layout.background_color));
|
||||
|
||||
@@ -169,7 +169,8 @@ pub struct PopoverMenuFrameState {
|
||||
|
||||
impl<M: ManagedView> Element for PopoverMenu<M> {
|
||||
type BeforeLayout = PopoverMenuFrameState;
|
||||
type AfterLayout = Option<HitboxId>;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = Option<HitboxId>;
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (gpui::LayoutId, Self::BeforeLayout) {
|
||||
self.with_element_state(cx, |this, element_state, cx| {
|
||||
@@ -219,14 +220,40 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
|
||||
_bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> Option<HitboxId> {
|
||||
self.with_element_state(cx, |_this, element_state, cx| {
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
cx.with_element_id(Some(self.id.clone()), |cx| {
|
||||
let mut focus_target_bounds = None;
|
||||
|
||||
if let Some(child) = before_layout.child_element.as_mut() {
|
||||
child.after_layout(cx);
|
||||
if let Some(child_focus_target_bounds) = child.after_layout(cx) {
|
||||
focus_target_bounds = Some(child_focus_target_bounds);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(menu) = before_layout.menu_element.as_mut() {
|
||||
menu.after_layout(cx);
|
||||
if let Some(menu_focus_target_bounds) = menu.after_layout(cx) {
|
||||
focus_target_bounds = Some(menu_focus_target_bounds);
|
||||
}
|
||||
}
|
||||
|
||||
(focus_target_bounds, ())
|
||||
})
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> Option<HitboxId> {
|
||||
self.with_element_state(cx, |_this, element_state, cx| {
|
||||
if let Some(child) = before_layout.child_element.as_mut() {
|
||||
child.before_paint(cx);
|
||||
}
|
||||
|
||||
if let Some(menu) = before_layout.menu_element.as_mut() {
|
||||
menu.before_paint(cx);
|
||||
}
|
||||
|
||||
before_layout.child_layout_id.map(|layout_id| {
|
||||
@@ -241,6 +268,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
|
||||
&mut self,
|
||||
_: Bounds<gpui::Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
child_hitbox: &mut Option<HitboxId>,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
|
||||
@@ -97,7 +97,8 @@ pub struct MenuHandleFrameState {
|
||||
|
||||
impl<M: ManagedView> Element for RightClickMenu<M> {
|
||||
type BeforeLayout = MenuHandleFrameState;
|
||||
type AfterLayout = Hitbox;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = Hitbox;
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (gpui::LayoutId, Self::BeforeLayout) {
|
||||
self.with_element_state(cx, |this, element_state, cx| {
|
||||
@@ -144,20 +145,46 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
|
||||
}
|
||||
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
cx.with_element_id(Some(self.id.clone()), |cx| {
|
||||
let mut focus_target_bounds = None;
|
||||
|
||||
if let Some(child) = before_layout.child_element.as_mut() {
|
||||
if let Some(child_focus_target_bounds) = child.after_layout(cx) {
|
||||
focus_target_bounds = Some(child_focus_target_bounds);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(menu) = before_layout.menu_element.as_mut() {
|
||||
if let Some(menu_focus_target_bounds) = menu.after_layout(cx) {
|
||||
focus_target_bounds = Some(menu_focus_target_bounds);
|
||||
}
|
||||
}
|
||||
|
||||
(focus_target_bounds, ())
|
||||
})
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> Hitbox {
|
||||
cx.with_element_id(Some(self.id.clone()), |cx| {
|
||||
let hitbox = cx.insert_hitbox(bounds, false);
|
||||
|
||||
if let Some(child) = before_layout.child_element.as_mut() {
|
||||
child.after_layout(cx);
|
||||
child.before_paint(cx);
|
||||
}
|
||||
|
||||
if let Some(menu) = before_layout.menu_element.as_mut() {
|
||||
menu.after_layout(cx);
|
||||
menu.before_paint(cx);
|
||||
}
|
||||
|
||||
hitbox
|
||||
@@ -168,7 +195,8 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
|
||||
&mut self,
|
||||
_bounds: Bounds<gpui::Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
hitbox: &mut Self::AfterLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
hitbox: &mut Self::BeforePaint,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
self.with_element_state(cx, |this, element_state, cx| {
|
||||
|
||||
@@ -649,7 +649,7 @@ mod element {
|
||||
children: Vec<PaneAxisChildLayout>,
|
||||
}
|
||||
|
||||
struct PaneAxisChildLayout {
|
||||
pub struct PaneAxisChildLayout {
|
||||
bounds: Bounds<Pixels>,
|
||||
element: AnyElement,
|
||||
handle: Option<PaneAxisHandleLayout>,
|
||||
@@ -793,7 +793,8 @@ mod element {
|
||||
|
||||
impl Element for PaneAxisElement {
|
||||
type BeforeLayout = ();
|
||||
type AfterLayout = PaneAxisLayout;
|
||||
type AfterLayout = Vec<PaneAxisChildLayout>;
|
||||
type BeforePaint = PaneAxisLayout;
|
||||
|
||||
fn before_layout(
|
||||
&mut self,
|
||||
@@ -811,18 +812,9 @@ mod element {
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_state: &mut Self::BeforeLayout,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> PaneAxisLayout {
|
||||
let dragged_handle = cx.with_element_state::<Rc<RefCell<Option<usize>>>, _>(
|
||||
Some(self.basis.into()),
|
||||
|state, _cx| {
|
||||
let state = state
|
||||
.unwrap()
|
||||
.unwrap_or_else(|| Rc::new(RefCell::new(None)));
|
||||
(state.clone(), Some(state))
|
||||
},
|
||||
);
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
let flexes = self.flexes.lock().clone();
|
||||
let len = self.children.len();
|
||||
debug_assert!(flexes.len() == len);
|
||||
@@ -844,13 +836,9 @@ mod element {
|
||||
let mut origin = bounds.origin;
|
||||
let space_per_flex = bounds.size.along(self.axis) / total_flex;
|
||||
|
||||
let mut bounding_boxes = self.bounding_boxes.lock();
|
||||
bounding_boxes.clear();
|
||||
let mut focus_target_bounds = None;
|
||||
|
||||
let mut layout = PaneAxisLayout {
|
||||
dragged_handle: dragged_handle.clone(),
|
||||
children: Vec::new(),
|
||||
};
|
||||
let mut children = Vec::new();
|
||||
for (ix, mut child) in mem::take(&mut self.children).into_iter().enumerate() {
|
||||
let child_flex = active_pane_magnification
|
||||
.map(|magnification| {
|
||||
@@ -866,25 +854,76 @@ mod element {
|
||||
.size
|
||||
.apply_along(self.axis, |_| space_per_flex * child_flex)
|
||||
.map(|d| d.round());
|
||||
let child_bounds = Bounds::new(origin, child_size);
|
||||
|
||||
let child_bounds = Bounds {
|
||||
origin,
|
||||
size: child_size,
|
||||
};
|
||||
bounding_boxes.push(Some(child_bounds));
|
||||
child.layout(origin, child_size.into(), cx);
|
||||
let child_measurement = child.layout(child_size.into(), cx);
|
||||
if let Some(child_focus_target_bounds) = child_measurement.focus_target_bounds {
|
||||
focus_target_bounds = Some(Bounds::new(
|
||||
child_bounds.origin + child_focus_target_bounds.origin,
|
||||
child_focus_target_bounds.size,
|
||||
));
|
||||
}
|
||||
|
||||
origin = origin.apply_along(self.axis, |val| val + child_size.along(self.axis));
|
||||
layout.children.push(PaneAxisChildLayout {
|
||||
children.push(PaneAxisChildLayout {
|
||||
bounds: child_bounds,
|
||||
element: child,
|
||||
handle: None,
|
||||
})
|
||||
}
|
||||
|
||||
for (ix, child_layout) in layout.children.iter_mut().enumerate() {
|
||||
if active_pane_magnification.is_none() {
|
||||
if ix < len - 1 {
|
||||
(focus_target_bounds, children)
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
children: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> PaneAxisLayout {
|
||||
let dragged_handle = cx.with_element_state::<Rc<RefCell<Option<usize>>>, _>(
|
||||
Some(self.basis.into()),
|
||||
|state, _cx| {
|
||||
let state = state
|
||||
.unwrap()
|
||||
.unwrap_or_else(|| Rc::new(RefCell::new(None)));
|
||||
(state.clone(), Some(state))
|
||||
},
|
||||
);
|
||||
|
||||
let magnification_value = WorkspaceSettings::get(None, cx).active_pane_magnification;
|
||||
let active_pane_magnification = if magnification_value == 1. {
|
||||
None
|
||||
} else {
|
||||
Some(magnification_value)
|
||||
};
|
||||
|
||||
let mut origin = bounds.origin;
|
||||
|
||||
let mut bounding_boxes = self.bounding_boxes.lock();
|
||||
bounding_boxes.clear();
|
||||
|
||||
let mut layout = PaneAxisLayout {
|
||||
dragged_handle: dragged_handle.clone(),
|
||||
children: Vec::new(),
|
||||
};
|
||||
for child in children {
|
||||
let child_bounds = Bounds {
|
||||
origin,
|
||||
size: child.bounds.size,
|
||||
};
|
||||
bounding_boxes.push(Some(child_bounds));
|
||||
cx.with_absolute_element_offset(origin, |cx| child.element.before_paint(cx));
|
||||
origin =
|
||||
origin.apply_along(self.axis, |val| val + child_bounds.size.along(self.axis));
|
||||
child.bounds = child_bounds;
|
||||
}
|
||||
|
||||
if active_pane_magnification.is_none() {
|
||||
let mut child_layouts = layout.children.iter_mut().peekable();
|
||||
while let Some(child_layout) = child_layouts.next() {
|
||||
if child_layouts.peek().is_some() {
|
||||
child_layout.handle =
|
||||
Some(Self::layout_handle(self.axis, child_layout.bounds, cx));
|
||||
}
|
||||
@@ -898,7 +937,8 @@ mod element {
|
||||
&mut self,
|
||||
bounds: gpui::Bounds<ui::prelude::Pixels>,
|
||||
_: &mut Self::BeforeLayout,
|
||||
layout: &mut Self::AfterLayout,
|
||||
_: &mut Self::AfterLayout,
|
||||
layout: &mut Self::BeforePaint,
|
||||
cx: &mut ui::prelude::ElementContext,
|
||||
) {
|
||||
for child in &mut layout.children {
|
||||
|
||||
@@ -4007,12 +4007,9 @@ impl Render for Workspace {
|
||||
.border_color(colors.border)
|
||||
.child({
|
||||
let this = cx.view().clone();
|
||||
canvas(
|
||||
move |bounds, cx| this.update(cx, |this, _cx| this.bounds = bounds),
|
||||
|_, _, _| {},
|
||||
)
|
||||
.absolute()
|
||||
.size_full()
|
||||
canvas(move |bounds, cx| this.update(cx, |this, _cx| this.bounds = bounds))
|
||||
.absolute()
|
||||
.size_full()
|
||||
})
|
||||
.when(self.zoomed.is_none(), |this| {
|
||||
this.on_drag_move(cx.listener(
|
||||
@@ -4973,6 +4970,7 @@ struct DisconnectedOverlay;
|
||||
impl Element for DisconnectedOverlay {
|
||||
type BeforeLayout = AnyElement;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = ();
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
let mut background = cx.theme().colors().elevated_surface_background;
|
||||
@@ -4996,20 +4994,31 @@ impl Element for DisconnectedOverlay {
|
||||
}
|
||||
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
overlay: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
(overlay.after_layout(cx), ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
overlay: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
cx.insert_hitbox(bounds, true);
|
||||
overlay.after_layout(cx);
|
||||
overlay.before_paint(cx);
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
_: Bounds<Pixels>,
|
||||
overlay: &mut Self::BeforeLayout,
|
||||
_: &mut Self::AfterLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
_: &mut Self::BeforePaint,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
overlay.paint(cx)
|
||||
|
||||
Reference in New Issue
Block a user