Wire up List::after_layout
Note that we're still not making use of the new focus target bounds returned by the child elements.
This commit is contained in:
@@ -282,9 +282,12 @@ enum ElementDrawPhase<BeforeLayout, AfterLayout, 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>>,
|
||||
}
|
||||
|
||||
@@ -604,7 +607,7 @@ impl Element for Empty {
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
_cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
(None, ())
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ use crate::{
|
||||
|
||||
/// The state that the anchored element element uses to track its children.
|
||||
pub struct AnchoredState {
|
||||
layout_id: LayoutId,
|
||||
child_layout_ids: SmallVec<[LayoutId; 4]>,
|
||||
}
|
||||
|
||||
@@ -89,19 +88,13 @@ impl Element for Anchored {
|
||||
|
||||
let layout_id = cx.request_layout(&anchored_style, child_layout_ids.iter().copied());
|
||||
|
||||
(
|
||||
layout_id,
|
||||
AnchoredState {
|
||||
layout_id,
|
||||
child_layout_ids,
|
||||
},
|
||||
)
|
||||
(layout_id, AnchoredState { child_layout_ids })
|
||||
}
|
||||
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
let mut focus_target_bounds = None;
|
||||
|
||||
@@ -40,9 +40,9 @@ impl Element for Canvas {
|
||||
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
_cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
(None, ())
|
||||
}
|
||||
|
||||
@@ -1361,6 +1361,7 @@ impl Interactivity {
|
||||
)
|
||||
}
|
||||
|
||||
/// todo!()
|
||||
pub fn after_layout<R>(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
@@ -1411,7 +1412,6 @@ impl Interactivity {
|
||||
)
|
||||
}
|
||||
|
||||
// self.content_size = content_size;
|
||||
/// Commit the bounds of this element according to this interactivity state's configured styles.
|
||||
pub fn before_paint<R>(
|
||||
&mut self,
|
||||
|
||||
@@ -260,7 +260,7 @@ impl Element for Img {
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
self.interactivity
|
||||
|
||||
@@ -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.
|
||||
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.layout(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.layout(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.layout(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,6 +560,7 @@ pub struct ListOffset {
|
||||
|
||||
impl Element for List {
|
||||
type BeforeLayout = ();
|
||||
type AfterLayout = Option<ListLayout>;
|
||||
type BeforePaint = ListBeforePaintState;
|
||||
|
||||
fn before_layout(
|
||||
@@ -589,20 +627,18 @@ impl Element for List {
|
||||
(layout_id, ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_: &mut Self::BeforeLayout,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> ListBeforePaintState {
|
||||
) -> (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,41 +650,70 @@ 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.layout(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);
|
||||
ListBeforePaintState {
|
||||
hitbox,
|
||||
layout: layout_response,
|
||||
}
|
||||
ListBeforePaintState { hitbox, layout }
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
bounds: Bounds<crate::Pixels>,
|
||||
_: &mut Self::BeforeLayout,
|
||||
_: &mut Self::AfterLayout,
|
||||
before_paint: &mut Self::BeforePaint,
|
||||
cx: &mut crate::ElementContext,
|
||||
) {
|
||||
cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
|
||||
for item in &mut before_paint.layout.item_elements {
|
||||
item.paint(cx);
|
||||
for item in &mut before_paint.layout.items {
|
||||
item.element.paint(cx);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ impl Element for Svg {
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
self.interactivity
|
||||
|
||||
@@ -29,9 +29,9 @@ impl Element for &'static str {
|
||||
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
(None, ())
|
||||
}
|
||||
@@ -88,7 +88,7 @@ impl Element for SharedString {
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
_cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
(None, ())
|
||||
}
|
||||
@@ -186,7 +186,7 @@ impl Element for StyledText {
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
_cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
(None, ())
|
||||
}
|
||||
|
||||
@@ -224,8 +224,8 @@ impl Element for UniformList {
|
||||
);
|
||||
for mut item in items {
|
||||
let measurement = item.layout(available_space, cx);
|
||||
if let Some(child_focus_target_bounds) = measurement.focus_target_bounds {
|
||||
focus_target_bounds = Some(child_focus_target_bounds);
|
||||
if measurement.focus_target_bounds.is_some() {
|
||||
focus_target_bounds = measurement.focus_target_bounds;
|
||||
}
|
||||
state.items.push(item);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user