Introduce autoscroll support for elements (#10889)
This pull request introduces the new `ElementContext::request_autoscroll(bounds)` and `ElementContext::take_autoscroll()` methods in GPUI. These new APIs enable container elements such as `List` to change their scroll position if one of their children requested an autoscroll. We plan to use this in the revamped assistant. As a drive-by, we also: - Renamed `Element::before_layout` to `Element::request_layout` - Renamed `Element::after_layout` to `Element::prepaint` - Introduced a new `List::splice_focusable` method to splice focusable elements into the list, which enables rendering offscreen elements that are focused. Release Notes: - N/A --------- Co-authored-by: Nathan <nathan@zed.dev>
This commit is contained in:
parent
efcd31c254
commit
bcbf2f2fd3
31 changed files with 780 additions and 513 deletions
|
@ -1108,7 +1108,7 @@ impl AssistantPanel {
|
||||||
)
|
)
|
||||||
.track_scroll(scroll_handle)
|
.track_scroll(scroll_handle)
|
||||||
.into_any_element();
|
.into_any_element();
|
||||||
saved_conversations.layout(
|
saved_conversations.prepaint_as_root(
|
||||||
bounds.origin,
|
bounds.origin,
|
||||||
bounds.size.map(AvailableSpace::Definite),
|
bounds.size.map(AvailableSpace::Definite),
|
||||||
cx,
|
cx,
|
||||||
|
|
|
@ -10801,7 +10801,7 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, _is_valid: bool) -> Ren
|
||||||
|
|
||||||
let icon_size = buttons(&diagnostic, cx.block_id)
|
let icon_size = buttons(&diagnostic, cx.block_id)
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
.measure(AvailableSpace::min_size(), cx);
|
.layout_as_root(AvailableSpace::min_size(), cx);
|
||||||
|
|
||||||
h_flex()
|
h_flex()
|
||||||
.id(cx.block_id)
|
.id(cx.block_id)
|
||||||
|
|
|
@ -864,7 +864,7 @@ impl EditorElement {
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.into_any();
|
.into_any();
|
||||||
hover_element.layout(fold_bounds.origin, fold_bounds.size.into(), cx);
|
hover_element.prepaint_as_root(fold_bounds.origin, fold_bounds.size.into(), cx);
|
||||||
Some(FoldLayout {
|
Some(FoldLayout {
|
||||||
display_range,
|
display_range,
|
||||||
hover_element,
|
hover_element,
|
||||||
|
@ -882,12 +882,15 @@ impl EditorElement {
|
||||||
line_layouts: &[LineWithInvisibles],
|
line_layouts: &[LineWithInvisibles],
|
||||||
text_hitbox: &Hitbox,
|
text_hitbox: &Hitbox,
|
||||||
content_origin: gpui::Point<Pixels>,
|
content_origin: gpui::Point<Pixels>,
|
||||||
|
scroll_position: gpui::Point<f32>,
|
||||||
scroll_pixel_position: gpui::Point<Pixels>,
|
scroll_pixel_position: gpui::Point<Pixels>,
|
||||||
line_height: Pixels,
|
line_height: Pixels,
|
||||||
em_width: Pixels,
|
em_width: Pixels,
|
||||||
|
autoscroll_containing_element: bool,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) -> Vec<CursorLayout> {
|
) -> Vec<CursorLayout> {
|
||||||
self.editor.update(cx, |editor, cx| {
|
let mut autoscroll_bounds = None;
|
||||||
|
let cursor_layouts = self.editor.update(cx, |editor, cx| {
|
||||||
let mut cursors = Vec::new();
|
let mut cursors = Vec::new();
|
||||||
for (player_color, selections) in selections {
|
for (player_color, selections) in selections {
|
||||||
for selection in selections {
|
for selection in selections {
|
||||||
|
@ -932,7 +935,7 @@ impl EditorElement {
|
||||||
cursor_row_layout.font_size,
|
cursor_row_layout.font_size,
|
||||||
&[TextRun {
|
&[TextRun {
|
||||||
len,
|
len,
|
||||||
font: font,
|
font,
|
||||||
color: self.style.background,
|
color: self.style.background,
|
||||||
background_color: None,
|
background_color: None,
|
||||||
strikethrough: None,
|
strikethrough: None,
|
||||||
|
@ -953,7 +956,27 @@ impl EditorElement {
|
||||||
editor.pixel_position_of_newest_cursor = Some(point(
|
editor.pixel_position_of_newest_cursor = Some(point(
|
||||||
text_hitbox.origin.x + x + block_width / 2.,
|
text_hitbox.origin.x + x + block_width / 2.,
|
||||||
text_hitbox.origin.y + y + line_height / 2.,
|
text_hitbox.origin.y + y + line_height / 2.,
|
||||||
))
|
));
|
||||||
|
|
||||||
|
if autoscroll_containing_element {
|
||||||
|
let top = text_hitbox.origin.y
|
||||||
|
+ (cursor_position.row() as f32 - scroll_position.y - 3.).max(0.)
|
||||||
|
* line_height;
|
||||||
|
let left = text_hitbox.origin.x
|
||||||
|
+ (cursor_position.column() as f32 - scroll_position.x - 3.)
|
||||||
|
.max(0.)
|
||||||
|
* em_width;
|
||||||
|
|
||||||
|
let bottom = text_hitbox.origin.y
|
||||||
|
+ (cursor_position.row() as f32 - scroll_position.y + 4.)
|
||||||
|
* line_height;
|
||||||
|
let right = text_hitbox.origin.x
|
||||||
|
+ (cursor_position.column() as f32 - scroll_position.x + 4.)
|
||||||
|
* em_width;
|
||||||
|
|
||||||
|
autoscroll_bounds =
|
||||||
|
Some(Bounds::from_corners(point(left, top), point(right, bottom)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut cursor = CursorLayout {
|
let mut cursor = CursorLayout {
|
||||||
|
@ -975,7 +998,13 @@ impl EditorElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cursors
|
cursors
|
||||||
})
|
});
|
||||||
|
|
||||||
|
if let Some(bounds) = autoscroll_bounds {
|
||||||
|
cx.request_autoscroll(bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor_layouts
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout_scrollbar(
|
fn layout_scrollbar(
|
||||||
|
@ -1073,7 +1102,7 @@ impl EditorElement {
|
||||||
AvailableSpace::MinContent,
|
AvailableSpace::MinContent,
|
||||||
AvailableSpace::Definite(line_height * 0.55),
|
AvailableSpace::Definite(line_height * 0.55),
|
||||||
);
|
);
|
||||||
let fold_indicator_size = fold_indicator.measure(available_space, cx);
|
let fold_indicator_size = fold_indicator.layout_as_root(available_space, cx);
|
||||||
|
|
||||||
let position = point(
|
let position = point(
|
||||||
gutter_dimensions.width - gutter_dimensions.right_padding,
|
gutter_dimensions.width - gutter_dimensions.right_padding,
|
||||||
|
@ -1086,7 +1115,7 @@ impl EditorElement {
|
||||||
(line_height - fold_indicator_size.height) / 2.,
|
(line_height - fold_indicator_size.height) / 2.,
|
||||||
);
|
);
|
||||||
let origin = gutter_hitbox.origin + position + centering_offset;
|
let origin = gutter_hitbox.origin + position + centering_offset;
|
||||||
fold_indicator.layout(origin, available_space, cx);
|
fold_indicator.prepaint_as_root(origin, available_space, cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1177,7 +1206,7 @@ impl EditorElement {
|
||||||
let absolute_offset = point(start_x, start_y);
|
let absolute_offset = point(start_x, start_y);
|
||||||
let available_space = size(AvailableSpace::MinContent, AvailableSpace::MinContent);
|
let available_space = size(AvailableSpace::MinContent, AvailableSpace::MinContent);
|
||||||
|
|
||||||
element.layout(absolute_offset, available_space, cx);
|
element.prepaint_as_root(absolute_offset, available_space, cx);
|
||||||
|
|
||||||
Some(element)
|
Some(element)
|
||||||
}
|
}
|
||||||
|
@ -1233,7 +1262,11 @@ impl EditorElement {
|
||||||
let start_y = ix as f32 * line_height - (scroll_top % line_height);
|
let start_y = ix as f32 * line_height - (scroll_top % line_height);
|
||||||
let absolute_offset = gutter_hitbox.origin + point(start_x, start_y);
|
let absolute_offset = gutter_hitbox.origin + point(start_x, start_y);
|
||||||
|
|
||||||
element.layout(absolute_offset, size(width, AvailableSpace::MinContent), cx);
|
element.prepaint_as_root(
|
||||||
|
absolute_offset,
|
||||||
|
size(width, AvailableSpace::MinContent),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
|
||||||
Some(element)
|
Some(element)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1269,7 +1302,7 @@ impl EditorElement {
|
||||||
AvailableSpace::MinContent,
|
AvailableSpace::MinContent,
|
||||||
AvailableSpace::Definite(line_height),
|
AvailableSpace::Definite(line_height),
|
||||||
);
|
);
|
||||||
let indicator_size = button.measure(available_space, cx);
|
let indicator_size = button.layout_as_root(available_space, cx);
|
||||||
|
|
||||||
let blame_width = gutter_dimensions
|
let blame_width = gutter_dimensions
|
||||||
.git_blame_entries_width
|
.git_blame_entries_width
|
||||||
|
@ -1284,7 +1317,7 @@ impl EditorElement {
|
||||||
let mut y = newest_selection_head.row() as f32 * line_height - scroll_pixel_position.y;
|
let mut y = newest_selection_head.row() as f32 * line_height - scroll_pixel_position.y;
|
||||||
y += (line_height - indicator_size.height) / 2.;
|
y += (line_height - indicator_size.height) / 2.;
|
||||||
|
|
||||||
button.layout(gutter_hitbox.origin + point(x, y), available_space, cx);
|
button.prepaint_as_root(gutter_hitbox.origin + point(x, y), available_space, cx);
|
||||||
Some(button)
|
Some(button)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1773,7 +1806,7 @@ impl EditorElement {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let size = element.measure(available_space, cx);
|
let size = element.layout_as_root(available_space, cx);
|
||||||
(element, size)
|
(element, size)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1843,7 +1876,9 @@ impl EditorElement {
|
||||||
if !matches!(block.style, BlockStyle::Sticky) {
|
if !matches!(block.style, BlockStyle::Sticky) {
|
||||||
origin += point(-scroll_pixel_position.x, Pixels::ZERO);
|
origin += point(-scroll_pixel_position.x, Pixels::ZERO);
|
||||||
}
|
}
|
||||||
block.element.layout(origin, block.available_space, cx);
|
block
|
||||||
|
.element
|
||||||
|
.prepaint_as_root(origin, block.available_space, cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1875,7 +1910,7 @@ impl EditorElement {
|
||||||
};
|
};
|
||||||
|
|
||||||
let available_space = size(AvailableSpace::MinContent, AvailableSpace::MinContent);
|
let available_space = size(AvailableSpace::MinContent, AvailableSpace::MinContent);
|
||||||
let context_menu_size = context_menu.measure(available_space, cx);
|
let context_menu_size = context_menu.layout_as_root(available_space, cx);
|
||||||
|
|
||||||
let cursor_row_layout = &line_layouts[(position.row() - start_row) as usize].line;
|
let cursor_row_layout = &line_layouts[(position.row() - start_row) as usize].line;
|
||||||
let x = cursor_row_layout.x_for_index(position.column() as usize) - scroll_pixel_position.x;
|
let x = cursor_row_layout.x_for_index(position.column() as usize) - scroll_pixel_position.x;
|
||||||
|
@ -1910,7 +1945,7 @@ impl EditorElement {
|
||||||
.with_priority(1)
|
.with_priority(1)
|
||||||
.into_any();
|
.into_any();
|
||||||
|
|
||||||
element.layout(gpui::Point::default(), AvailableSpace::min_size(), cx);
|
element.prepaint_as_root(gpui::Point::default(), AvailableSpace::min_size(), cx);
|
||||||
Some(element)
|
Some(element)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1972,7 +2007,7 @@ impl EditorElement {
|
||||||
let mut overall_height = Pixels::ZERO;
|
let mut overall_height = Pixels::ZERO;
|
||||||
let mut measured_hover_popovers = Vec::new();
|
let mut measured_hover_popovers = Vec::new();
|
||||||
for mut hover_popover in hover_popovers {
|
for mut hover_popover in hover_popovers {
|
||||||
let size = hover_popover.measure(available_space, cx);
|
let size = hover_popover.layout_as_root(available_space, cx);
|
||||||
let horizontal_offset =
|
let horizontal_offset =
|
||||||
(text_hitbox.upper_right().x - (hovered_point.x + size.width)).min(Pixels::ZERO);
|
(text_hitbox.upper_right().x - (hovered_point.x + size.width)).min(Pixels::ZERO);
|
||||||
|
|
||||||
|
@ -1992,7 +2027,7 @@ impl EditorElement {
|
||||||
.occlude()
|
.occlude()
|
||||||
.on_mouse_move(|_, cx| cx.stop_propagation())
|
.on_mouse_move(|_, cx| cx.stop_propagation())
|
||||||
.into_any_element();
|
.into_any_element();
|
||||||
occlusion.measure(size(width, HOVER_POPOVER_GAP).into(), cx);
|
occlusion.layout_as_root(size(width, HOVER_POPOVER_GAP).into(), cx);
|
||||||
cx.defer_draw(occlusion, origin, 2);
|
cx.defer_draw(occlusion, origin, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3327,10 +3362,10 @@ enum Invisible {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for EditorElement {
|
impl Element for EditorElement {
|
||||||
type BeforeLayout = ();
|
type RequestLayoutState = ();
|
||||||
type AfterLayout = EditorLayout;
|
type PrepaintState = EditorLayout;
|
||||||
|
|
||||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (gpui::LayoutId, ()) {
|
fn request_layout(&mut self, cx: &mut ElementContext) -> (gpui::LayoutId, ()) {
|
||||||
self.editor.update(cx, |editor, cx| {
|
self.editor.update(cx, |editor, cx| {
|
||||||
editor.set_style(self.style.clone(), cx);
|
editor.set_style(self.style.clone(), cx);
|
||||||
|
|
||||||
|
@ -3377,12 +3412,12 @@ impl Element for EditorElement {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_layout(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
_: &mut Self::BeforeLayout,
|
_: &mut Self::RequestLayoutState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) -> Self::AfterLayout {
|
) -> Self::PrepaintState {
|
||||||
let text_style = TextStyleRefinement {
|
let text_style = TextStyleRefinement {
|
||||||
font_size: Some(self.style.text.font_size),
|
font_size: Some(self.style.text.font_size),
|
||||||
line_height: Some(self.style.text.line_height),
|
line_height: Some(self.style.text.line_height),
|
||||||
|
@ -3466,11 +3501,12 @@ impl Element for EditorElement {
|
||||||
let content_origin =
|
let content_origin =
|
||||||
text_hitbox.origin + point(gutter_dimensions.margin, Pixels::ZERO);
|
text_hitbox.origin + point(gutter_dimensions.margin, Pixels::ZERO);
|
||||||
|
|
||||||
let autoscroll_horizontally = self.editor.update(cx, |editor, cx| {
|
let mut autoscroll_requested = false;
|
||||||
let autoscroll_horizontally =
|
let mut autoscroll_horizontally = false;
|
||||||
editor.autoscroll_vertically(bounds, line_height, cx);
|
self.editor.update(cx, |editor, cx| {
|
||||||
|
autoscroll_requested = editor.autoscroll_requested();
|
||||||
|
autoscroll_horizontally = editor.autoscroll_vertically(bounds, line_height, cx);
|
||||||
snapshot = editor.snapshot(cx);
|
snapshot = editor.snapshot(cx);
|
||||||
autoscroll_horizontally
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut scroll_position = snapshot.scroll_position();
|
let mut scroll_position = snapshot.scroll_position();
|
||||||
|
@ -3643,9 +3679,11 @@ impl Element for EditorElement {
|
||||||
&line_layouts,
|
&line_layouts,
|
||||||
&text_hitbox,
|
&text_hitbox,
|
||||||
content_origin,
|
content_origin,
|
||||||
|
scroll_position,
|
||||||
scroll_pixel_position,
|
scroll_pixel_position,
|
||||||
line_height,
|
line_height,
|
||||||
em_width,
|
em_width,
|
||||||
|
autoscroll_requested,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -3806,8 +3844,8 @@ impl Element for EditorElement {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<gpui::Pixels>,
|
bounds: Bounds<gpui::Pixels>,
|
||||||
_: &mut Self::BeforeLayout,
|
_: &mut Self::RequestLayoutState,
|
||||||
layout: &mut Self::AfterLayout,
|
layout: &mut Self::PrepaintState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
let focus_handle = self.editor.focus_handle(cx);
|
let focus_handle = self.editor.focus_handle(cx);
|
||||||
|
@ -4187,7 +4225,7 @@ impl CursorLayout {
|
||||||
.child(cursor_name.string.clone())
|
.child(cursor_name.string.clone())
|
||||||
.into_any_element();
|
.into_any_element();
|
||||||
|
|
||||||
name_element.layout(
|
name_element.prepaint_as_root(
|
||||||
name_origin,
|
name_origin,
|
||||||
size(AvailableSpace::MinContent, AvailableSpace::MinContent),
|
size(AvailableSpace::MinContent, AvailableSpace::MinContent),
|
||||||
cx,
|
cx,
|
||||||
|
@ -4467,7 +4505,7 @@ mod tests {
|
||||||
let state = cx
|
let state = cx
|
||||||
.update_window(window.into(), |_view, cx| {
|
.update_window(window.into(), |_view, cx| {
|
||||||
cx.with_element_context(|cx| {
|
cx.with_element_context(|cx| {
|
||||||
element.after_layout(
|
element.prepaint(
|
||||||
Bounds {
|
Bounds {
|
||||||
origin: point(px(500.), px(500.)),
|
origin: point(px(500.), px(500.)),
|
||||||
size: size(px(500.), px(500.)),
|
size: size(px(500.), px(500.)),
|
||||||
|
@ -4562,7 +4600,7 @@ mod tests {
|
||||||
let state = cx
|
let state = cx
|
||||||
.update_window(window.into(), |_view, cx| {
|
.update_window(window.into(), |_view, cx| {
|
||||||
cx.with_element_context(|cx| {
|
cx.with_element_context(|cx| {
|
||||||
element.after_layout(
|
element.prepaint(
|
||||||
Bounds {
|
Bounds {
|
||||||
origin: point(px(500.), px(500.)),
|
origin: point(px(500.), px(500.)),
|
||||||
size: size(px(500.), px(500.)),
|
size: size(px(500.), px(500.)),
|
||||||
|
@ -4627,7 +4665,7 @@ mod tests {
|
||||||
let state = cx
|
let state = cx
|
||||||
.update_window(window.into(), |_view, cx| {
|
.update_window(window.into(), |_view, cx| {
|
||||||
cx.with_element_context(|cx| {
|
cx.with_element_context(|cx| {
|
||||||
element.after_layout(
|
element.prepaint(
|
||||||
Bounds {
|
Bounds {
|
||||||
origin: point(px(500.), px(500.)),
|
origin: point(px(500.), px(500.)),
|
||||||
size: size(px(500.), px(500.)),
|
size: size(px(500.), px(500.)),
|
||||||
|
@ -4823,7 +4861,7 @@ mod tests {
|
||||||
let layout_state = cx
|
let layout_state = cx
|
||||||
.update_window(window.into(), |_, cx| {
|
.update_window(window.into(), |_, cx| {
|
||||||
cx.with_element_context(|cx| {
|
cx.with_element_context(|cx| {
|
||||||
element.after_layout(
|
element.prepaint(
|
||||||
Bounds {
|
Bounds {
|
||||||
origin: point(px(500.), px(500.)),
|
origin: point(px(500.), px(500.)),
|
||||||
size: size(px(500.), px(500.)),
|
size: size(px(500.), px(500.)),
|
||||||
|
|
|
@ -275,7 +275,7 @@ impl ScrollManager {
|
||||||
self.show_scrollbars
|
self.show_scrollbars
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_autoscroll_request(&self) -> bool {
|
pub fn autoscroll_requested(&self) -> bool {
|
||||||
self.autoscroll_request.is_some()
|
self.autoscroll_request.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,10 @@ impl AutoscrollStrategy {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Editor {
|
impl Editor {
|
||||||
|
pub fn autoscroll_requested(&self) -> bool {
|
||||||
|
self.scroll_manager.autoscroll_requested()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn autoscroll_vertically(
|
pub fn autoscroll_vertically(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
|
|
|
@ -948,7 +948,7 @@ impl Render for ExtensionsPage {
|
||||||
.pb_4()
|
.pb_4()
|
||||||
.track_scroll(scroll_handle)
|
.track_scroll(scroll_handle)
|
||||||
.into_any_element();
|
.into_any_element();
|
||||||
list.layout(bounds.origin, bounds.size.into(), cx);
|
list.prepaint_as_root(bounds.origin, bounds.size.into(), cx);
|
||||||
list
|
list
|
||||||
},
|
},
|
||||||
|_bounds, mut list, cx| list.paint(cx),
|
|_bounds, mut list, cx| list.paint(cx),
|
||||||
|
|
|
@ -734,7 +734,8 @@ impl VisualTestContext {
|
||||||
self.update(|cx| {
|
self.update(|cx| {
|
||||||
cx.with_element_context(|cx| {
|
cx.with_element_context(|cx| {
|
||||||
let mut element = f(cx);
|
let mut element = f(cx);
|
||||||
element.layout(origin, space, cx);
|
element.layout_as_root(space, cx);
|
||||||
|
cx.with_absolute_element_offset(origin, |cx| element.prepaint(cx));
|
||||||
element.paint(cx);
|
element.paint(cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -44,34 +44,34 @@ use std::{any::Any, fmt::Debug, mem, ops::DerefMut};
|
||||||
/// You can create custom elements by implementing this trait, see the module-level documentation
|
/// You can create custom elements by implementing this trait, see the module-level documentation
|
||||||
/// for more details.
|
/// for more details.
|
||||||
pub trait Element: 'static + IntoElement {
|
pub trait Element: 'static + IntoElement {
|
||||||
/// The type of state returned from [`Element::before_layout`]. A mutable reference to this state is subsequently
|
/// The type of state returned from [`Element::request_layout`]. A mutable reference to this state is subsequently
|
||||||
/// provided to [`Element::after_layout`] and [`Element::paint`].
|
/// provided to [`Element::prepaint`] and [`Element::paint`].
|
||||||
type BeforeLayout: 'static;
|
type RequestLayoutState: 'static;
|
||||||
|
|
||||||
/// The type of state returned from [`Element::after_layout`]. A mutable reference to this state is subsequently
|
/// The type of state returned from [`Element::prepaint`]. A mutable reference to this state is subsequently
|
||||||
/// provided to [`Element::paint`].
|
/// provided to [`Element::paint`].
|
||||||
type AfterLayout: 'static;
|
type PrepaintState: 'static;
|
||||||
|
|
||||||
/// Before an element can be painted, we need to know where it's going to be and how big it is.
|
/// Before an element can be painted, we need to know where it's going to be and how big it is.
|
||||||
/// Use this method to request a layout from Taffy and initialize the element's state.
|
/// Use this method to request a layout from Taffy and initialize the element's state.
|
||||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout);
|
fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState);
|
||||||
|
|
||||||
/// After laying out an element, we need to commit its bounds to the current frame for hitbox
|
/// 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()`].
|
/// purposes. The state argument is the same state that was returned from [`Element::request_layout()`].
|
||||||
fn after_layout(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
before_layout: &mut Self::BeforeLayout,
|
request_layout: &mut Self::RequestLayoutState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) -> Self::AfterLayout;
|
) -> Self::PrepaintState;
|
||||||
|
|
||||||
/// Once layout has been completed, this method will be called to paint the element to the screen.
|
/// Once layout has been completed, this method will be called to paint the element to the screen.
|
||||||
/// The state argument is the same state that was returned from [`Element::before_layout()`].
|
/// The state argument is the same state that was returned from [`Element::request_layout()`].
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
before_layout: &mut Self::BeforeLayout,
|
request_layout: &mut Self::RequestLayoutState,
|
||||||
after_layout: &mut Self::AfterLayout,
|
prepaint: &mut Self::PrepaintState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -161,34 +161,29 @@ impl<C: RenderOnce> Component<C> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: RenderOnce> Element for Component<C> {
|
impl<C: RenderOnce> Element for Component<C> {
|
||||||
type BeforeLayout = AnyElement;
|
type RequestLayoutState = AnyElement;
|
||||||
type AfterLayout = ();
|
type PrepaintState = ();
|
||||||
|
|
||||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
let mut element = self
|
let mut element = self
|
||||||
.0
|
.0
|
||||||
.take()
|
.take()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.render(cx.deref_mut())
|
.render(cx.deref_mut())
|
||||||
.into_any_element();
|
.into_any_element();
|
||||||
let layout_id = element.before_layout(cx);
|
let layout_id = element.request_layout(cx);
|
||||||
(layout_id, element)
|
(layout_id, element)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_layout(
|
fn prepaint(&mut self, _: Bounds<Pixels>, element: &mut AnyElement, cx: &mut ElementContext) {
|
||||||
&mut self,
|
element.prepaint(cx);
|
||||||
_: Bounds<Pixels>,
|
|
||||||
element: &mut AnyElement,
|
|
||||||
cx: &mut ElementContext,
|
|
||||||
) {
|
|
||||||
element.after_layout(cx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: Bounds<Pixels>,
|
_: Bounds<Pixels>,
|
||||||
element: &mut Self::BeforeLayout,
|
element: &mut Self::RequestLayoutState,
|
||||||
_: &mut Self::AfterLayout,
|
_: &mut Self::PrepaintState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
element.paint(cx)
|
element.paint(cx)
|
||||||
|
@ -210,13 +205,13 @@ pub(crate) struct GlobalElementId(SmallVec<[ElementId; 32]>);
|
||||||
trait ElementObject {
|
trait ElementObject {
|
||||||
fn inner_element(&mut self) -> &mut dyn Any;
|
fn inner_element(&mut self) -> &mut dyn Any;
|
||||||
|
|
||||||
fn before_layout(&mut self, cx: &mut ElementContext) -> LayoutId;
|
fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId;
|
||||||
|
|
||||||
fn after_layout(&mut self, cx: &mut ElementContext);
|
fn prepaint(&mut self, cx: &mut ElementContext);
|
||||||
|
|
||||||
fn paint(&mut self, cx: &mut ElementContext);
|
fn paint(&mut self, cx: &mut ElementContext);
|
||||||
|
|
||||||
fn measure(
|
fn layout_as_root(
|
||||||
&mut self,
|
&mut self,
|
||||||
available_space: Size<AvailableSpace>,
|
available_space: Size<AvailableSpace>,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
|
@ -227,27 +222,27 @@ trait ElementObject {
|
||||||
pub struct Drawable<E: Element> {
|
pub struct Drawable<E: Element> {
|
||||||
/// The drawn element.
|
/// The drawn element.
|
||||||
pub element: E,
|
pub element: E,
|
||||||
phase: ElementDrawPhase<E::BeforeLayout, E::AfterLayout>,
|
phase: ElementDrawPhase<E::RequestLayoutState, E::PrepaintState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
enum ElementDrawPhase<BeforeLayout, AfterLayout> {
|
enum ElementDrawPhase<RequestLayoutState, PrepaintState> {
|
||||||
#[default]
|
#[default]
|
||||||
Start,
|
Start,
|
||||||
BeforeLayout {
|
RequestLayoutState {
|
||||||
layout_id: LayoutId,
|
layout_id: LayoutId,
|
||||||
before_layout: BeforeLayout,
|
request_layout: RequestLayoutState,
|
||||||
},
|
},
|
||||||
LayoutComputed {
|
LayoutComputed {
|
||||||
layout_id: LayoutId,
|
layout_id: LayoutId,
|
||||||
available_space: Size<AvailableSpace>,
|
available_space: Size<AvailableSpace>,
|
||||||
before_layout: BeforeLayout,
|
request_layout: RequestLayoutState,
|
||||||
},
|
},
|
||||||
AfterLayout {
|
PrepaintState {
|
||||||
node_id: DispatchNodeId,
|
node_id: DispatchNodeId,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
before_layout: BeforeLayout,
|
request_layout: RequestLayoutState,
|
||||||
after_layout: AfterLayout,
|
prepaint: PrepaintState,
|
||||||
},
|
},
|
||||||
Painted,
|
Painted,
|
||||||
}
|
}
|
||||||
|
@ -261,91 +256,91 @@ impl<E: Element> Drawable<E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn before_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
|
fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
|
||||||
match mem::take(&mut self.phase) {
|
match mem::take(&mut self.phase) {
|
||||||
ElementDrawPhase::Start => {
|
ElementDrawPhase::Start => {
|
||||||
let (layout_id, before_layout) = self.element.before_layout(cx);
|
let (layout_id, request_layout) = self.element.request_layout(cx);
|
||||||
self.phase = ElementDrawPhase::BeforeLayout {
|
self.phase = ElementDrawPhase::RequestLayoutState {
|
||||||
layout_id,
|
layout_id,
|
||||||
before_layout,
|
request_layout,
|
||||||
};
|
};
|
||||||
layout_id
|
layout_id
|
||||||
}
|
}
|
||||||
_ => panic!("must call before_layout only once"),
|
_ => panic!("must call request_layout only once"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_layout(&mut self, cx: &mut ElementContext) {
|
fn prepaint(&mut self, cx: &mut ElementContext) {
|
||||||
match mem::take(&mut self.phase) {
|
match mem::take(&mut self.phase) {
|
||||||
ElementDrawPhase::BeforeLayout {
|
ElementDrawPhase::RequestLayoutState {
|
||||||
layout_id,
|
layout_id,
|
||||||
mut before_layout,
|
mut request_layout,
|
||||||
}
|
}
|
||||||
| ElementDrawPhase::LayoutComputed {
|
| ElementDrawPhase::LayoutComputed {
|
||||||
layout_id,
|
layout_id,
|
||||||
mut before_layout,
|
mut request_layout,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let bounds = cx.layout_bounds(layout_id);
|
let bounds = cx.layout_bounds(layout_id);
|
||||||
let node_id = cx.window.next_frame.dispatch_tree.push_node();
|
let node_id = cx.window.next_frame.dispatch_tree.push_node();
|
||||||
let after_layout = self.element.after_layout(bounds, &mut before_layout, cx);
|
let prepaint = self.element.prepaint(bounds, &mut request_layout, cx);
|
||||||
self.phase = ElementDrawPhase::AfterLayout {
|
self.phase = ElementDrawPhase::PrepaintState {
|
||||||
node_id,
|
node_id,
|
||||||
bounds,
|
bounds,
|
||||||
before_layout,
|
request_layout,
|
||||||
after_layout,
|
prepaint,
|
||||||
};
|
};
|
||||||
cx.window.next_frame.dispatch_tree.pop_node();
|
cx.window.next_frame.dispatch_tree.pop_node();
|
||||||
}
|
}
|
||||||
_ => panic!("must call before_layout before after_layout"),
|
_ => panic!("must call request_layout before prepaint"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(&mut self, cx: &mut ElementContext) -> E::BeforeLayout {
|
fn paint(&mut self, cx: &mut ElementContext) -> E::RequestLayoutState {
|
||||||
match mem::take(&mut self.phase) {
|
match mem::take(&mut self.phase) {
|
||||||
ElementDrawPhase::AfterLayout {
|
ElementDrawPhase::PrepaintState {
|
||||||
node_id,
|
node_id,
|
||||||
bounds,
|
bounds,
|
||||||
mut before_layout,
|
mut request_layout,
|
||||||
mut after_layout,
|
mut prepaint,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
cx.window.next_frame.dispatch_tree.set_active_node(node_id);
|
cx.window.next_frame.dispatch_tree.set_active_node(node_id);
|
||||||
self.element
|
self.element
|
||||||
.paint(bounds, &mut before_layout, &mut after_layout, cx);
|
.paint(bounds, &mut request_layout, &mut prepaint, cx);
|
||||||
self.phase = ElementDrawPhase::Painted;
|
self.phase = ElementDrawPhase::Painted;
|
||||||
before_layout
|
request_layout
|
||||||
}
|
}
|
||||||
_ => panic!("must call after_layout before paint"),
|
_ => panic!("must call prepaint before paint"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn measure(
|
fn layout_as_root(
|
||||||
&mut self,
|
&mut self,
|
||||||
available_space: Size<AvailableSpace>,
|
available_space: Size<AvailableSpace>,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) -> Size<Pixels> {
|
) -> Size<Pixels> {
|
||||||
if matches!(&self.phase, ElementDrawPhase::Start) {
|
if matches!(&self.phase, ElementDrawPhase::Start) {
|
||||||
self.before_layout(cx);
|
self.request_layout(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
let layout_id = match mem::take(&mut self.phase) {
|
let layout_id = match mem::take(&mut self.phase) {
|
||||||
ElementDrawPhase::BeforeLayout {
|
ElementDrawPhase::RequestLayoutState {
|
||||||
layout_id,
|
layout_id,
|
||||||
before_layout,
|
request_layout,
|
||||||
} => {
|
} => {
|
||||||
cx.compute_layout(layout_id, available_space);
|
cx.compute_layout(layout_id, available_space);
|
||||||
self.phase = ElementDrawPhase::LayoutComputed {
|
self.phase = ElementDrawPhase::LayoutComputed {
|
||||||
layout_id,
|
layout_id,
|
||||||
available_space,
|
available_space,
|
||||||
before_layout,
|
request_layout,
|
||||||
};
|
};
|
||||||
layout_id
|
layout_id
|
||||||
}
|
}
|
||||||
ElementDrawPhase::LayoutComputed {
|
ElementDrawPhase::LayoutComputed {
|
||||||
layout_id,
|
layout_id,
|
||||||
available_space: prev_available_space,
|
available_space: prev_available_space,
|
||||||
before_layout,
|
request_layout,
|
||||||
} => {
|
} => {
|
||||||
if available_space != prev_available_space {
|
if available_space != prev_available_space {
|
||||||
cx.compute_layout(layout_id, available_space);
|
cx.compute_layout(layout_id, available_space);
|
||||||
|
@ -353,7 +348,7 @@ impl<E: Element> Drawable<E> {
|
||||||
self.phase = ElementDrawPhase::LayoutComputed {
|
self.phase = ElementDrawPhase::LayoutComputed {
|
||||||
layout_id,
|
layout_id,
|
||||||
available_space,
|
available_space,
|
||||||
before_layout,
|
request_layout,
|
||||||
};
|
};
|
||||||
layout_id
|
layout_id
|
||||||
}
|
}
|
||||||
|
@ -367,30 +362,30 @@ impl<E: Element> Drawable<E> {
|
||||||
impl<E> ElementObject for Drawable<E>
|
impl<E> ElementObject for Drawable<E>
|
||||||
where
|
where
|
||||||
E: Element,
|
E: Element,
|
||||||
E::BeforeLayout: 'static,
|
E::RequestLayoutState: 'static,
|
||||||
{
|
{
|
||||||
fn inner_element(&mut self) -> &mut dyn Any {
|
fn inner_element(&mut self) -> &mut dyn Any {
|
||||||
&mut self.element
|
&mut self.element
|
||||||
}
|
}
|
||||||
|
|
||||||
fn before_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
|
fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
|
||||||
Drawable::before_layout(self, cx)
|
Drawable::request_layout(self, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_layout(&mut self, cx: &mut ElementContext) {
|
fn prepaint(&mut self, cx: &mut ElementContext) {
|
||||||
Drawable::after_layout(self, cx);
|
Drawable::prepaint(self, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(&mut self, cx: &mut ElementContext) {
|
fn paint(&mut self, cx: &mut ElementContext) {
|
||||||
Drawable::paint(self, cx);
|
Drawable::paint(self, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn measure(
|
fn layout_as_root(
|
||||||
&mut self,
|
&mut self,
|
||||||
available_space: Size<AvailableSpace>,
|
available_space: Size<AvailableSpace>,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) -> Size<Pixels> {
|
) -> Size<Pixels> {
|
||||||
Drawable::measure(self, available_space, cx)
|
Drawable::layout_as_root(self, available_space, cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,7 +396,7 @@ impl AnyElement {
|
||||||
pub(crate) fn new<E>(element: E) -> Self
|
pub(crate) fn new<E>(element: E) -> Self
|
||||||
where
|
where
|
||||||
E: 'static + Element,
|
E: 'static + Element,
|
||||||
E::BeforeLayout: Any,
|
E::RequestLayoutState: Any,
|
||||||
{
|
{
|
||||||
let element = ELEMENT_ARENA
|
let element = ELEMENT_ARENA
|
||||||
.with_borrow_mut(|arena| arena.alloc(|| Drawable::new(element)))
|
.with_borrow_mut(|arena| arena.alloc(|| Drawable::new(element)))
|
||||||
|
@ -416,13 +411,14 @@ impl AnyElement {
|
||||||
|
|
||||||
/// Request the layout ID of the element stored in this `AnyElement`.
|
/// Request the layout ID of the element stored in this `AnyElement`.
|
||||||
/// Used for laying out child elements in a parent element.
|
/// Used for laying out child elements in a parent element.
|
||||||
pub fn before_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
|
pub fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
|
||||||
self.0.before_layout(cx)
|
self.0.request_layout(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Commits the element bounds of this [AnyElement] for hitbox purposes.
|
/// Prepares the element to be painted by storing its bounds, giving it a chance to draw hitboxes and
|
||||||
pub fn after_layout(&mut self, cx: &mut ElementContext) {
|
/// request autoscroll before the final paint pass is confirmed.
|
||||||
self.0.after_layout(cx)
|
pub fn prepaint(&mut self, cx: &mut ElementContext) {
|
||||||
|
self.0.prepaint(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Paints the element stored in this `AnyElement`.
|
/// Paints the element stored in this `AnyElement`.
|
||||||
|
@ -430,51 +426,55 @@ impl AnyElement {
|
||||||
self.0.paint(cx)
|
self.0.paint(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initializes this element and performs layout within the given available space to determine its size.
|
/// Performs layout for this element within the given available space and returns its size.
|
||||||
pub fn measure(
|
pub fn layout_as_root(
|
||||||
&mut self,
|
&mut self,
|
||||||
available_space: Size<AvailableSpace>,
|
available_space: Size<AvailableSpace>,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) -> Size<Pixels> {
|
) -> Size<Pixels> {
|
||||||
self.0.measure(available_space, cx)
|
self.0.layout_as_root(available_space, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initializes this element, performs layout if needed and commits its bounds for hitbox purposes.
|
/// Prepaints this element at the given absolute origin.
|
||||||
pub fn layout(
|
pub fn prepaint_at(&mut self, origin: Point<Pixels>, cx: &mut ElementContext) {
|
||||||
|
cx.with_absolute_element_offset(origin, |cx| self.0.prepaint(cx));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs layout on this element in the available space, then prepaints it at the given absolute origin.
|
||||||
|
pub fn prepaint_as_root(
|
||||||
&mut self,
|
&mut self,
|
||||||
absolute_offset: Point<Pixels>,
|
origin: Point<Pixels>,
|
||||||
available_space: Size<AvailableSpace>,
|
available_space: Size<AvailableSpace>,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) -> Size<Pixels> {
|
) {
|
||||||
let size = self.measure(available_space, cx);
|
self.layout_as_root(available_space, cx);
|
||||||
cx.with_absolute_element_offset(absolute_offset, |cx| self.after_layout(cx));
|
cx.with_absolute_element_offset(origin, |cx| self.0.prepaint(cx));
|
||||||
size
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for AnyElement {
|
impl Element for AnyElement {
|
||||||
type BeforeLayout = ();
|
type RequestLayoutState = ();
|
||||||
type AfterLayout = ();
|
type PrepaintState = ();
|
||||||
|
|
||||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
let layout_id = self.before_layout(cx);
|
let layout_id = self.request_layout(cx);
|
||||||
(layout_id, ())
|
(layout_id, ())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_layout(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: Bounds<Pixels>,
|
_: Bounds<Pixels>,
|
||||||
_: &mut Self::BeforeLayout,
|
_: &mut Self::RequestLayoutState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
self.after_layout(cx)
|
self.prepaint(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: Bounds<Pixels>,
|
_: Bounds<Pixels>,
|
||||||
_: &mut Self::BeforeLayout,
|
_: &mut Self::RequestLayoutState,
|
||||||
_: &mut Self::AfterLayout,
|
_: &mut Self::PrepaintState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
self.paint(cx)
|
self.paint(cx)
|
||||||
|
@ -505,17 +505,17 @@ impl IntoElement for Empty {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for Empty {
|
impl Element for Empty {
|
||||||
type BeforeLayout = ();
|
type RequestLayoutState = ();
|
||||||
type AfterLayout = ();
|
type PrepaintState = ();
|
||||||
|
|
||||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
(cx.request_layout(&crate::Style::default(), None), ())
|
(cx.request_layout(&crate::Style::default(), None), ())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_layout(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_bounds: Bounds<Pixels>,
|
_bounds: Bounds<Pixels>,
|
||||||
_state: &mut Self::BeforeLayout,
|
_state: &mut Self::RequestLayoutState,
|
||||||
_cx: &mut ElementContext,
|
_cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
@ -523,8 +523,8 @@ impl Element for Empty {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_bounds: Bounds<Pixels>,
|
_bounds: Bounds<Pixels>,
|
||||||
_before_layout: &mut Self::BeforeLayout,
|
_request_layout: &mut Self::RequestLayoutState,
|
||||||
_after_layout: &mut Self::AfterLayout,
|
_prepaint: &mut Self::PrepaintState,
|
||||||
_cx: &mut ElementContext,
|
_cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,14 +69,17 @@ impl ParentElement for Anchored {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for Anchored {
|
impl Element for Anchored {
|
||||||
type BeforeLayout = AnchoredState;
|
type RequestLayoutState = AnchoredState;
|
||||||
type AfterLayout = ();
|
type PrepaintState = ();
|
||||||
|
|
||||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (crate::LayoutId, Self::BeforeLayout) {
|
fn request_layout(
|
||||||
|
&mut self,
|
||||||
|
cx: &mut ElementContext,
|
||||||
|
) -> (crate::LayoutId, Self::RequestLayoutState) {
|
||||||
let child_layout_ids = self
|
let child_layout_ids = self
|
||||||
.children
|
.children
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.map(|child| child.before_layout(cx))
|
.map(|child| child.request_layout(cx))
|
||||||
.collect::<SmallVec<_>>();
|
.collect::<SmallVec<_>>();
|
||||||
|
|
||||||
let anchored_style = Style {
|
let anchored_style = Style {
|
||||||
|
@ -90,19 +93,19 @@ impl Element for Anchored {
|
||||||
(layout_id, AnchoredState { child_layout_ids })
|
(layout_id, AnchoredState { child_layout_ids })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_layout(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
before_layout: &mut Self::BeforeLayout,
|
request_layout: &mut Self::RequestLayoutState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
if before_layout.child_layout_ids.is_empty() {
|
if request_layout.child_layout_ids.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut child_min = point(Pixels::MAX, Pixels::MAX);
|
let mut child_min = point(Pixels::MAX, Pixels::MAX);
|
||||||
let mut child_max = Point::default();
|
let mut child_max = Point::default();
|
||||||
for child_layout_id in &before_layout.child_layout_ids {
|
for child_layout_id in &request_layout.child_layout_ids {
|
||||||
let child_bounds = cx.layout_bounds(*child_layout_id);
|
let child_bounds = cx.layout_bounds(*child_layout_id);
|
||||||
child_min = child_min.min(&child_bounds.origin);
|
child_min = child_min.min(&child_bounds.origin);
|
||||||
child_max = child_max.max(&child_bounds.lower_right());
|
child_max = child_max.max(&child_bounds.lower_right());
|
||||||
|
@ -167,7 +170,7 @@ impl Element for Anchored {
|
||||||
|
|
||||||
cx.with_element_offset(offset, |cx| {
|
cx.with_element_offset(offset, |cx| {
|
||||||
for child in &mut self.children {
|
for child in &mut self.children {
|
||||||
child.after_layout(cx);
|
child.prepaint(cx);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -175,8 +178,8 @@ impl Element for Anchored {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_bounds: crate::Bounds<crate::Pixels>,
|
_bounds: crate::Bounds<crate::Pixels>,
|
||||||
_before_layout: &mut Self::BeforeLayout,
|
_request_layout: &mut Self::RequestLayoutState,
|
||||||
_after_layout: &mut Self::AfterLayout,
|
_prepaint: &mut Self::PrepaintState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
for child in &mut self.children {
|
for child in &mut self.children {
|
||||||
|
|
|
@ -85,14 +85,14 @@ struct AnimationState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: IntoElement + 'static> Element for AnimationElement<E> {
|
impl<E: IntoElement + 'static> Element for AnimationElement<E> {
|
||||||
type BeforeLayout = AnyElement;
|
type RequestLayoutState = AnyElement;
|
||||||
|
|
||||||
type AfterLayout = ();
|
type PrepaintState = ();
|
||||||
|
|
||||||
fn before_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
cx: &mut crate::ElementContext,
|
cx: &mut crate::ElementContext,
|
||||||
) -> (crate::LayoutId, Self::BeforeLayout) {
|
) -> (crate::LayoutId, Self::RequestLayoutState) {
|
||||||
cx.with_element_state(Some(self.id.clone()), |state, cx| {
|
cx.with_element_state(Some(self.id.clone()), |state, cx| {
|
||||||
let state = state.unwrap().unwrap_or_else(|| AnimationState {
|
let state = state.unwrap().unwrap_or_else(|| AnimationState {
|
||||||
start: Instant::now(),
|
start: Instant::now(),
|
||||||
|
@ -130,24 +130,24 @@ impl<E: IntoElement + 'static> Element for AnimationElement<E> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
((element.before_layout(cx), element), Some(state))
|
((element.request_layout(cx), element), Some(state))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_layout(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_bounds: crate::Bounds<crate::Pixels>,
|
_bounds: crate::Bounds<crate::Pixels>,
|
||||||
element: &mut Self::BeforeLayout,
|
element: &mut Self::RequestLayoutState,
|
||||||
cx: &mut crate::ElementContext,
|
cx: &mut crate::ElementContext,
|
||||||
) -> Self::AfterLayout {
|
) -> Self::PrepaintState {
|
||||||
element.after_layout(cx);
|
element.prepaint(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_bounds: crate::Bounds<crate::Pixels>,
|
_bounds: crate::Bounds<crate::Pixels>,
|
||||||
element: &mut Self::BeforeLayout,
|
element: &mut Self::RequestLayoutState,
|
||||||
_: &mut Self::AfterLayout,
|
_: &mut Self::PrepaintState,
|
||||||
cx: &mut crate::ElementContext,
|
cx: &mut crate::ElementContext,
|
||||||
) {
|
) {
|
||||||
element.paint(cx);
|
element.paint(cx);
|
||||||
|
|
|
@ -5,11 +5,11 @@ use crate::{Bounds, Element, ElementContext, IntoElement, Pixels, Style, StyleRe
|
||||||
/// Construct a canvas element with the given paint callback.
|
/// Construct a canvas element with the given paint callback.
|
||||||
/// Useful for adding short term custom drawing to a view.
|
/// Useful for adding short term custom drawing to a view.
|
||||||
pub fn canvas<T>(
|
pub fn canvas<T>(
|
||||||
after_layout: impl 'static + FnOnce(Bounds<Pixels>, &mut ElementContext) -> T,
|
prepaint: impl 'static + FnOnce(Bounds<Pixels>, &mut ElementContext) -> T,
|
||||||
paint: impl 'static + FnOnce(Bounds<Pixels>, T, &mut ElementContext),
|
paint: impl 'static + FnOnce(Bounds<Pixels>, T, &mut ElementContext),
|
||||||
) -> Canvas<T> {
|
) -> Canvas<T> {
|
||||||
Canvas {
|
Canvas {
|
||||||
after_layout: Some(Box::new(after_layout)),
|
prepaint: Some(Box::new(prepaint)),
|
||||||
paint: Some(Box::new(paint)),
|
paint: Some(Box::new(paint)),
|
||||||
style: StyleRefinement::default(),
|
style: StyleRefinement::default(),
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ pub fn canvas<T>(
|
||||||
/// A canvas element, meant for accessing the low level paint API without defining a whole
|
/// A canvas element, meant for accessing the low level paint API without defining a whole
|
||||||
/// custom element
|
/// custom element
|
||||||
pub struct Canvas<T> {
|
pub struct Canvas<T> {
|
||||||
after_layout: Option<Box<dyn FnOnce(Bounds<Pixels>, &mut ElementContext) -> T>>,
|
prepaint: Option<Box<dyn FnOnce(Bounds<Pixels>, &mut ElementContext) -> T>>,
|
||||||
paint: Option<Box<dyn FnOnce(Bounds<Pixels>, T, &mut ElementContext)>>,
|
paint: Option<Box<dyn FnOnce(Bounds<Pixels>, T, &mut ElementContext)>>,
|
||||||
style: StyleRefinement,
|
style: StyleRefinement,
|
||||||
}
|
}
|
||||||
|
@ -32,35 +32,38 @@ impl<T: 'static> IntoElement for Canvas<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: 'static> Element for Canvas<T> {
|
impl<T: 'static> Element for Canvas<T> {
|
||||||
type BeforeLayout = Style;
|
type RequestLayoutState = Style;
|
||||||
type AfterLayout = Option<T>;
|
type PrepaintState = Option<T>;
|
||||||
|
|
||||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (crate::LayoutId, Self::BeforeLayout) {
|
fn request_layout(
|
||||||
|
&mut self,
|
||||||
|
cx: &mut ElementContext,
|
||||||
|
) -> (crate::LayoutId, Self::RequestLayoutState) {
|
||||||
let mut style = Style::default();
|
let mut style = Style::default();
|
||||||
style.refine(&self.style);
|
style.refine(&self.style);
|
||||||
let layout_id = cx.request_layout(&style, []);
|
let layout_id = cx.request_layout(&style, []);
|
||||||
(layout_id, style)
|
(layout_id, style)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_layout(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
_before_layout: &mut Style,
|
_request_layout: &mut Style,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) -> Option<T> {
|
) -> Option<T> {
|
||||||
Some(self.after_layout.take().unwrap()(bounds, cx))
|
Some(self.prepaint.take().unwrap()(bounds, cx))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
style: &mut Style,
|
style: &mut Style,
|
||||||
after_layout: &mut Self::AfterLayout,
|
prepaint: &mut Self::PrepaintState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
let after_layout = after_layout.take().unwrap();
|
let prepaint = prepaint.take().unwrap();
|
||||||
style.paint(bounds, cx, |cx| {
|
style.paint(bounds, cx, |cx| {
|
||||||
(self.paint.take().unwrap())(bounds, after_layout, cx)
|
(self.paint.take().unwrap())(bounds, prepaint, cx)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,18 +26,18 @@ impl Deferred {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for Deferred {
|
impl Element for Deferred {
|
||||||
type BeforeLayout = ();
|
type RequestLayoutState = ();
|
||||||
type AfterLayout = ();
|
type PrepaintState = ();
|
||||||
|
|
||||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, ()) {
|
fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, ()) {
|
||||||
let layout_id = self.child.as_mut().unwrap().before_layout(cx);
|
let layout_id = self.child.as_mut().unwrap().request_layout(cx);
|
||||||
(layout_id, ())
|
(layout_id, ())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_layout(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_bounds: Bounds<Pixels>,
|
_bounds: Bounds<Pixels>,
|
||||||
_before_layout: &mut Self::BeforeLayout,
|
_request_layout: &mut Self::RequestLayoutState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
let child = self.child.take().unwrap();
|
let child = self.child.take().unwrap();
|
||||||
|
@ -48,8 +48,8 @@ impl Element for Deferred {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_bounds: Bounds<Pixels>,
|
_bounds: Bounds<Pixels>,
|
||||||
_before_layout: &mut Self::BeforeLayout,
|
_request_layout: &mut Self::RequestLayoutState,
|
||||||
_after_layout: &mut Self::AfterLayout,
|
_prepaint: &mut Self::PrepaintState,
|
||||||
_cx: &mut ElementContext,
|
_cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -1120,17 +1120,17 @@ impl ParentElement for Div {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for Div {
|
impl Element for Div {
|
||||||
type BeforeLayout = DivFrameState;
|
type RequestLayoutState = DivFrameState;
|
||||||
type AfterLayout = Option<Hitbox>;
|
type PrepaintState = Option<Hitbox>;
|
||||||
|
|
||||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
let mut child_layout_ids = SmallVec::new();
|
let mut child_layout_ids = SmallVec::new();
|
||||||
let layout_id = self.interactivity.before_layout(cx, |style, cx| {
|
let layout_id = self.interactivity.request_layout(cx, |style, cx| {
|
||||||
cx.with_text_style(style.text_style().cloned(), |cx| {
|
cx.with_text_style(style.text_style().cloned(), |cx| {
|
||||||
child_layout_ids = self
|
child_layout_ids = self
|
||||||
.children
|
.children
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.map(|child| child.before_layout(cx))
|
.map(|child| child.request_layout(cx))
|
||||||
.collect::<SmallVec<_>>();
|
.collect::<SmallVec<_>>();
|
||||||
cx.request_layout(&style, child_layout_ids.iter().copied())
|
cx.request_layout(&style, child_layout_ids.iter().copied())
|
||||||
})
|
})
|
||||||
|
@ -1138,23 +1138,23 @@ impl Element for Div {
|
||||||
(layout_id, DivFrameState { child_layout_ids })
|
(layout_id, DivFrameState { child_layout_ids })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_layout(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
before_layout: &mut Self::BeforeLayout,
|
request_layout: &mut Self::RequestLayoutState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) -> Option<Hitbox> {
|
) -> Option<Hitbox> {
|
||||||
let mut child_min = point(Pixels::MAX, Pixels::MAX);
|
let mut child_min = point(Pixels::MAX, Pixels::MAX);
|
||||||
let mut child_max = Point::default();
|
let mut child_max = Point::default();
|
||||||
let content_size = if before_layout.child_layout_ids.is_empty() {
|
let content_size = if request_layout.child_layout_ids.is_empty() {
|
||||||
bounds.size
|
bounds.size
|
||||||
} else if let Some(scroll_handle) = self.interactivity.tracked_scroll_handle.as_ref() {
|
} else if let Some(scroll_handle) = self.interactivity.tracked_scroll_handle.as_ref() {
|
||||||
let mut state = scroll_handle.0.borrow_mut();
|
let mut state = scroll_handle.0.borrow_mut();
|
||||||
state.child_bounds = Vec::with_capacity(before_layout.child_layout_ids.len());
|
state.child_bounds = Vec::with_capacity(request_layout.child_layout_ids.len());
|
||||||
state.bounds = bounds;
|
state.bounds = bounds;
|
||||||
let requested = state.requested_scroll_top.take();
|
let requested = state.requested_scroll_top.take();
|
||||||
|
|
||||||
for (ix, child_layout_id) in before_layout.child_layout_ids.iter().enumerate() {
|
for (ix, child_layout_id) in request_layout.child_layout_ids.iter().enumerate() {
|
||||||
let child_bounds = cx.layout_bounds(*child_layout_id);
|
let child_bounds = cx.layout_bounds(*child_layout_id);
|
||||||
child_min = child_min.min(&child_bounds.origin);
|
child_min = child_min.min(&child_bounds.origin);
|
||||||
child_max = child_max.max(&child_bounds.lower_right());
|
child_max = child_max.max(&child_bounds.lower_right());
|
||||||
|
@ -1169,7 +1169,7 @@ impl Element for Div {
|
||||||
}
|
}
|
||||||
(child_max - child_min).into()
|
(child_max - child_min).into()
|
||||||
} else {
|
} else {
|
||||||
for child_layout_id in &before_layout.child_layout_ids {
|
for child_layout_id in &request_layout.child_layout_ids {
|
||||||
let child_bounds = cx.layout_bounds(*child_layout_id);
|
let child_bounds = cx.layout_bounds(*child_layout_id);
|
||||||
child_min = child_min.min(&child_bounds.origin);
|
child_min = child_min.min(&child_bounds.origin);
|
||||||
child_max = child_max.max(&child_bounds.lower_right());
|
child_max = child_max.max(&child_bounds.lower_right());
|
||||||
|
@ -1177,14 +1177,14 @@ impl Element for Div {
|
||||||
(child_max - child_min).into()
|
(child_max - child_min).into()
|
||||||
};
|
};
|
||||||
|
|
||||||
self.interactivity.after_layout(
|
self.interactivity.prepaint(
|
||||||
bounds,
|
bounds,
|
||||||
content_size,
|
content_size,
|
||||||
cx,
|
cx,
|
||||||
|_style, scroll_offset, hitbox, cx| {
|
|_style, scroll_offset, hitbox, cx| {
|
||||||
cx.with_element_offset(scroll_offset, |cx| {
|
cx.with_element_offset(scroll_offset, |cx| {
|
||||||
for child in &mut self.children {
|
for child in &mut self.children {
|
||||||
child.after_layout(cx);
|
child.prepaint(cx);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
hitbox
|
hitbox
|
||||||
|
@ -1195,7 +1195,7 @@ impl Element for Div {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
_before_layout: &mut Self::BeforeLayout,
|
_request_layout: &mut Self::RequestLayoutState,
|
||||||
hitbox: &mut Option<Hitbox>,
|
hitbox: &mut Option<Hitbox>,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
|
@ -1274,7 +1274,7 @@ pub struct Interactivity {
|
||||||
|
|
||||||
impl Interactivity {
|
impl Interactivity {
|
||||||
/// Layout this element according to this interactivity state's configured styles
|
/// Layout this element according to this interactivity state's configured styles
|
||||||
pub fn before_layout(
|
pub fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
f: impl FnOnce(Style, &mut ElementContext) -> LayoutId,
|
f: impl FnOnce(Style, &mut ElementContext) -> LayoutId,
|
||||||
|
@ -1337,7 +1337,7 @@ impl Interactivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Commit the bounds of this element according to this interactivity state's configured styles.
|
/// Commit the bounds of this element according to this interactivity state's configured styles.
|
||||||
pub fn after_layout<R>(
|
pub fn prepaint<R>(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
content_size: Size<Pixels>,
|
content_size: Size<Pixels>,
|
||||||
|
@ -2261,30 +2261,30 @@ impl<E> Element for Focusable<E>
|
||||||
where
|
where
|
||||||
E: Element,
|
E: Element,
|
||||||
{
|
{
|
||||||
type BeforeLayout = E::BeforeLayout;
|
type RequestLayoutState = E::RequestLayoutState;
|
||||||
type AfterLayout = E::AfterLayout;
|
type PrepaintState = E::PrepaintState;
|
||||||
|
|
||||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
self.element.before_layout(cx)
|
self.element.request_layout(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_layout(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
state: &mut Self::BeforeLayout,
|
state: &mut Self::RequestLayoutState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) -> E::AfterLayout {
|
) -> E::PrepaintState {
|
||||||
self.element.after_layout(bounds, state, cx)
|
self.element.prepaint(bounds, state, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
before_layout: &mut Self::BeforeLayout,
|
request_layout: &mut Self::RequestLayoutState,
|
||||||
after_layout: &mut Self::AfterLayout,
|
prepaint: &mut Self::PrepaintState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
self.element.paint(bounds, before_layout, after_layout, cx)
|
self.element.paint(bounds, request_layout, prepaint, cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2344,30 +2344,30 @@ impl<E> Element for Stateful<E>
|
||||||
where
|
where
|
||||||
E: Element,
|
E: Element,
|
||||||
{
|
{
|
||||||
type BeforeLayout = E::BeforeLayout;
|
type RequestLayoutState = E::RequestLayoutState;
|
||||||
type AfterLayout = E::AfterLayout;
|
type PrepaintState = E::PrepaintState;
|
||||||
|
|
||||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
self.element.before_layout(cx)
|
self.element.request_layout(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_layout(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
state: &mut Self::BeforeLayout,
|
state: &mut Self::RequestLayoutState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) -> E::AfterLayout {
|
) -> E::PrepaintState {
|
||||||
self.element.after_layout(bounds, state, cx)
|
self.element.prepaint(bounds, state, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
before_layout: &mut Self::BeforeLayout,
|
request_layout: &mut Self::RequestLayoutState,
|
||||||
after_layout: &mut Self::AfterLayout,
|
prepaint: &mut Self::PrepaintState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
self.element.paint(bounds, before_layout, after_layout, cx);
|
self.element.paint(bounds, request_layout, prepaint, cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -229,11 +229,11 @@ impl Img {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for Img {
|
impl Element for Img {
|
||||||
type BeforeLayout = ();
|
type RequestLayoutState = ();
|
||||||
type AfterLayout = Option<Hitbox>;
|
type PrepaintState = Option<Hitbox>;
|
||||||
|
|
||||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
let layout_id = self.interactivity.before_layout(cx, |mut style, cx| {
|
let layout_id = self.interactivity.request_layout(cx, |mut style, cx| {
|
||||||
if let Some(data) = self.source.data(cx) {
|
if let Some(data) = self.source.data(cx) {
|
||||||
let image_size = data.size();
|
let image_size = data.size();
|
||||||
match (style.size.width, style.size.height) {
|
match (style.size.width, style.size.height) {
|
||||||
|
@ -256,21 +256,21 @@ impl Element for Img {
|
||||||
(layout_id, ())
|
(layout_id, ())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_layout(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
_before_layout: &mut Self::BeforeLayout,
|
_request_layout: &mut Self::RequestLayoutState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) -> Option<Hitbox> {
|
) -> Option<Hitbox> {
|
||||||
self.interactivity
|
self.interactivity
|
||||||
.after_layout(bounds, bounds.size, cx, |_, _, hitbox, _| hitbox)
|
.prepaint(bounds, bounds.size, cx, |_, _, hitbox, _| hitbox)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
_: &mut Self::BeforeLayout,
|
_: &mut Self::RequestLayoutState,
|
||||||
hitbox: &mut Self::AfterLayout,
|
hitbox: &mut Self::PrepaintState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
let source = self.source.clone();
|
let source = self.source.clone();
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
point, px, size, AnyElement, AvailableSpace, Bounds, ContentMask, DispatchPhase, Edges,
|
point, px, size, AnyElement, AvailableSpace, Bounds, ContentMask, DispatchPhase, Edges,
|
||||||
Element, ElementContext, Hitbox, IntoElement, Pixels, Point, ScrollWheelEvent, Size, Style,
|
Element, ElementContext, FocusHandle, Hitbox, IntoElement, Pixels, Point, ScrollWheelEvent,
|
||||||
StyleRefinement, Styled, WindowContext,
|
Size, Style, StyleRefinement, Styled, WindowContext,
|
||||||
};
|
};
|
||||||
use collections::VecDeque;
|
use collections::VecDeque;
|
||||||
use refineable::Refineable as _;
|
use refineable::Refineable as _;
|
||||||
|
@ -92,20 +92,58 @@ pub enum ListSizingBehavior {
|
||||||
struct LayoutItemsResponse {
|
struct LayoutItemsResponse {
|
||||||
max_item_width: Pixels,
|
max_item_width: Pixels,
|
||||||
scroll_top: ListOffset,
|
scroll_top: ListOffset,
|
||||||
available_item_space: Size<AvailableSpace>,
|
item_layouts: VecDeque<ItemLayout>,
|
||||||
item_elements: VecDeque<AnyElement>,
|
}
|
||||||
|
|
||||||
|
struct ItemLayout {
|
||||||
|
index: usize,
|
||||||
|
element: AnyElement,
|
||||||
|
size: Size<Pixels>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Frame state used by the [List] element after layout.
|
/// Frame state used by the [List] element after layout.
|
||||||
pub struct ListAfterLayoutState {
|
pub struct ListPrepaintState {
|
||||||
hitbox: Hitbox,
|
hitbox: Hitbox,
|
||||||
layout: LayoutItemsResponse,
|
layout: LayoutItemsResponse,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
enum ListItem {
|
enum ListItem {
|
||||||
Unrendered,
|
Unmeasured {
|
||||||
Rendered { size: Size<Pixels> },
|
focus_handle: Option<FocusHandle>,
|
||||||
|
},
|
||||||
|
Measured {
|
||||||
|
size: Size<Pixels>,
|
||||||
|
focus_handle: Option<FocusHandle>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ListItem {
|
||||||
|
fn size(&self) -> Option<Size<Pixels>> {
|
||||||
|
if let ListItem::Measured { size, .. } = self {
|
||||||
|
Some(*size)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn focus_handle(&self) -> Option<FocusHandle> {
|
||||||
|
match self {
|
||||||
|
ListItem::Unmeasured { focus_handle } | ListItem::Measured { focus_handle, .. } => {
|
||||||
|
focus_handle.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains_focused(&self, cx: &WindowContext) -> bool {
|
||||||
|
match self {
|
||||||
|
ListItem::Unmeasured { focus_handle } | ListItem::Measured { focus_handle, .. } => {
|
||||||
|
focus_handle
|
||||||
|
.as_ref()
|
||||||
|
.is_some_and(|handle| handle.contains_focused(cx))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq)]
|
#[derive(Clone, Debug, Default, PartialEq)]
|
||||||
|
@ -114,6 +152,7 @@ struct ListItemSummary {
|
||||||
rendered_count: usize,
|
rendered_count: usize,
|
||||||
unrendered_count: usize,
|
unrendered_count: usize,
|
||||||
height: Pixels,
|
height: Pixels,
|
||||||
|
has_focus_handles: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
@ -131,45 +170,45 @@ struct Height(Pixels);
|
||||||
impl ListState {
|
impl ListState {
|
||||||
/// Construct a new list state, for storage on a view.
|
/// Construct a new list state, for storage on a view.
|
||||||
///
|
///
|
||||||
/// the overdraw parameter controls how much extra space is rendered
|
/// The overdraw parameter controls how much extra space is rendered
|
||||||
/// above and below the visible area. This can help ensure that the list
|
/// above and below the visible area. Elements within this area will
|
||||||
/// doesn't flicker or pop in when scrolling.
|
/// be measured even though they are not visible. This can help ensure
|
||||||
pub fn new<F>(
|
/// that the list doesn't flicker or pop in when scrolling.
|
||||||
element_count: usize,
|
pub fn new<R>(
|
||||||
orientation: ListAlignment,
|
item_count: usize,
|
||||||
|
alignment: ListAlignment,
|
||||||
overdraw: Pixels,
|
overdraw: Pixels,
|
||||||
render_item: F,
|
render_item: R,
|
||||||
) -> Self
|
) -> Self
|
||||||
where
|
where
|
||||||
F: 'static + FnMut(usize, &mut WindowContext) -> AnyElement,
|
R: 'static + FnMut(usize, &mut WindowContext) -> AnyElement,
|
||||||
{
|
{
|
||||||
let mut items = SumTree::new();
|
let this = Self(Rc::new(RefCell::new(StateInner {
|
||||||
items.extend((0..element_count).map(|_| ListItem::Unrendered), &());
|
|
||||||
Self(Rc::new(RefCell::new(StateInner {
|
|
||||||
last_layout_bounds: None,
|
last_layout_bounds: None,
|
||||||
last_padding: None,
|
last_padding: None,
|
||||||
render_item: Box::new(render_item),
|
render_item: Box::new(render_item),
|
||||||
items,
|
items: SumTree::new(),
|
||||||
logical_scroll_top: None,
|
logical_scroll_top: None,
|
||||||
alignment: orientation,
|
alignment,
|
||||||
overdraw,
|
overdraw,
|
||||||
scroll_handler: None,
|
scroll_handler: None,
|
||||||
reset: false,
|
reset: false,
|
||||||
})))
|
})));
|
||||||
|
this.splice(0..0, item_count);
|
||||||
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reset this instantiation of the list state.
|
/// Reset this instantiation of the list state.
|
||||||
///
|
///
|
||||||
/// Note that this will cause scroll events to be dropped until the next paint.
|
/// Note that this will cause scroll events to be dropped until the next paint.
|
||||||
pub fn reset(&self, element_count: usize) {
|
pub fn reset(&self, element_count: usize) {
|
||||||
|
{
|
||||||
let state = &mut *self.0.borrow_mut();
|
let state = &mut *self.0.borrow_mut();
|
||||||
state.reset = true;
|
state.reset = true;
|
||||||
|
|
||||||
state.logical_scroll_top = None;
|
state.logical_scroll_top = None;
|
||||||
state.items = SumTree::new();
|
}
|
||||||
state
|
|
||||||
.items
|
self.splice(0..element_count, element_count);
|
||||||
.extend((0..element_count).map(|_| ListItem::Unrendered), &());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The number of items in this list.
|
/// The number of items in this list.
|
||||||
|
@ -177,11 +216,39 @@ impl ListState {
|
||||||
self.0.borrow().items.summary().count
|
self.0.borrow().items.summary().count
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register with the list state that the items in `old_range` have been replaced
|
/// Inform the list state that the items in `old_range` have been replaced
|
||||||
/// by `count` new items that must be recalculated.
|
/// by `count` new items that must be recalculated.
|
||||||
pub fn splice(&self, old_range: Range<usize>, count: usize) {
|
pub fn splice(&self, old_range: Range<usize>, count: usize) {
|
||||||
|
self.splice_focusable(old_range, (0..count).map(|_| None))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register with the list state that the items in `old_range` have been replaced
|
||||||
|
/// by new items. As opposed to [`splice`], this method allows an iterator of optional focus handles
|
||||||
|
/// to be supplied to properly integrate with items in the list that can be focused. If a focused item
|
||||||
|
/// is scrolled out of view, the list will continue to render it to allow keyboard interaction.
|
||||||
|
pub fn splice_focusable(
|
||||||
|
&self,
|
||||||
|
old_range: Range<usize>,
|
||||||
|
focus_handles: impl IntoIterator<Item = Option<FocusHandle>>,
|
||||||
|
) {
|
||||||
let state = &mut *self.0.borrow_mut();
|
let state = &mut *self.0.borrow_mut();
|
||||||
|
|
||||||
|
let mut old_items = state.items.cursor::<Count>();
|
||||||
|
let mut new_items = old_items.slice(&Count(old_range.start), Bias::Right, &());
|
||||||
|
old_items.seek_forward(&Count(old_range.end), Bias::Right, &());
|
||||||
|
|
||||||
|
let mut spliced_count = 0;
|
||||||
|
new_items.extend(
|
||||||
|
focus_handles.into_iter().map(|focus_handle| {
|
||||||
|
spliced_count += 1;
|
||||||
|
ListItem::Unmeasured { focus_handle }
|
||||||
|
}),
|
||||||
|
&(),
|
||||||
|
);
|
||||||
|
new_items.append(old_items.suffix(&()), &());
|
||||||
|
drop(old_items);
|
||||||
|
state.items = new_items;
|
||||||
|
|
||||||
if let Some(ListOffset {
|
if let Some(ListOffset {
|
||||||
item_ix,
|
item_ix,
|
||||||
offset_in_item,
|
offset_in_item,
|
||||||
|
@ -191,18 +258,9 @@ impl ListState {
|
||||||
*item_ix = old_range.start;
|
*item_ix = old_range.start;
|
||||||
*offset_in_item = px(0.);
|
*offset_in_item = px(0.);
|
||||||
} else if old_range.end <= *item_ix {
|
} else if old_range.end <= *item_ix {
|
||||||
*item_ix = *item_ix - (old_range.end - old_range.start) + count;
|
*item_ix = *item_ix - (old_range.end - old_range.start) + spliced_count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut old_heights = state.items.cursor::<Count>();
|
|
||||||
let mut new_heights = old_heights.slice(&Count(old_range.start), Bias::Right, &());
|
|
||||||
old_heights.seek_forward(&Count(old_range.end), Bias::Right, &());
|
|
||||||
|
|
||||||
new_heights.extend((0..count).map(|_| ListItem::Unrendered), &());
|
|
||||||
new_heights.append(old_heights.suffix(&()), &());
|
|
||||||
drop(old_heights);
|
|
||||||
state.items = new_heights;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a handler that will be called when the list is scrolled.
|
/// Set a handler that will be called when the list is scrolled.
|
||||||
|
@ -279,7 +337,7 @@ impl ListState {
|
||||||
let scroll_top = cursor.start().1 .0 + scroll_top.offset_in_item;
|
let scroll_top = cursor.start().1 .0 + scroll_top.offset_in_item;
|
||||||
|
|
||||||
cursor.seek_forward(&Count(ix), Bias::Right, &());
|
cursor.seek_forward(&Count(ix), Bias::Right, &());
|
||||||
if let Some(&ListItem::Rendered { size }) = cursor.item() {
|
if let Some(&ListItem::Measured { size, .. }) = cursor.item() {
|
||||||
let &(Count(count), Height(top)) = cursor.start();
|
let &(Count(count), Height(top)) = cursor.start();
|
||||||
if count == ix {
|
if count == ix {
|
||||||
let top = bounds.top() + top - scroll_top;
|
let top = bounds.top() + top - scroll_top;
|
||||||
|
@ -379,10 +437,11 @@ impl StateInner {
|
||||||
) -> LayoutItemsResponse {
|
) -> LayoutItemsResponse {
|
||||||
let old_items = self.items.clone();
|
let old_items = self.items.clone();
|
||||||
let mut measured_items = VecDeque::new();
|
let mut measured_items = VecDeque::new();
|
||||||
let mut item_elements = VecDeque::new();
|
let mut item_layouts = VecDeque::new();
|
||||||
let mut rendered_height = padding.top;
|
let mut rendered_height = padding.top;
|
||||||
let mut max_item_width = px(0.);
|
let mut max_item_width = px(0.);
|
||||||
let mut scroll_top = self.logical_scroll_top();
|
let mut scroll_top = self.logical_scroll_top();
|
||||||
|
let mut rendered_focused_item = false;
|
||||||
|
|
||||||
let available_item_space = size(
|
let available_item_space = size(
|
||||||
available_width.map_or(AvailableSpace::MinContent, |width| {
|
available_width.map_or(AvailableSpace::MinContent, |width| {
|
||||||
|
@ -401,27 +460,34 @@ impl StateInner {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the previously cached height if available
|
// Use the previously cached height and focus handle if available
|
||||||
let mut size = if let ListItem::Rendered { size } = item {
|
let mut size = item.size();
|
||||||
Some(*size)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
// If we're within the visible area or the height wasn't cached, render and measure the item's element
|
// If we're within the visible area or the height wasn't cached, render and measure the item's element
|
||||||
if visible_height < available_height || size.is_none() {
|
if visible_height < available_height || size.is_none() {
|
||||||
let mut element = (self.render_item)(scroll_top.item_ix + ix, cx);
|
let item_index = scroll_top.item_ix + ix;
|
||||||
let element_size = element.measure(available_item_space, cx);
|
let mut element = (self.render_item)(item_index, cx);
|
||||||
|
let element_size = element.layout_as_root(available_item_space, cx);
|
||||||
size = Some(element_size);
|
size = Some(element_size);
|
||||||
if visible_height < available_height {
|
if visible_height < available_height {
|
||||||
item_elements.push_back(element);
|
item_layouts.push_back(ItemLayout {
|
||||||
|
index: item_index,
|
||||||
|
element,
|
||||||
|
size: element_size,
|
||||||
|
});
|
||||||
|
if item.contains_focused(cx) {
|
||||||
|
rendered_focused_item = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let size = size.unwrap();
|
let size = size.unwrap();
|
||||||
rendered_height += size.height;
|
rendered_height += size.height;
|
||||||
max_item_width = max_item_width.max(size.width);
|
max_item_width = max_item_width.max(size.width);
|
||||||
measured_items.push_back(ListItem::Rendered { size });
|
measured_items.push_back(ListItem::Measured {
|
||||||
|
size,
|
||||||
|
focus_handle: item.focus_handle(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
rendered_height += padding.bottom;
|
rendered_height += padding.bottom;
|
||||||
|
|
||||||
|
@ -433,13 +499,24 @@ impl StateInner {
|
||||||
if rendered_height - scroll_top.offset_in_item < available_height {
|
if rendered_height - scroll_top.offset_in_item < available_height {
|
||||||
while rendered_height < available_height {
|
while rendered_height < available_height {
|
||||||
cursor.prev(&());
|
cursor.prev(&());
|
||||||
if cursor.item().is_some() {
|
if let Some(item) = cursor.item() {
|
||||||
let mut element = (self.render_item)(cursor.start().0, cx);
|
let item_index = cursor.start().0;
|
||||||
let element_size = element.measure(available_item_space, cx);
|
let mut element = (self.render_item)(item_index, cx);
|
||||||
|
let element_size = element.layout_as_root(available_item_space, cx);
|
||||||
|
let focus_handle = item.focus_handle();
|
||||||
rendered_height += element_size.height;
|
rendered_height += element_size.height;
|
||||||
measured_items.push_front(ListItem::Rendered { size: element_size });
|
measured_items.push_front(ListItem::Measured {
|
||||||
item_elements.push_front(element)
|
size: element_size,
|
||||||
|
focus_handle,
|
||||||
|
});
|
||||||
|
item_layouts.push_front(ItemLayout {
|
||||||
|
index: item_index,
|
||||||
|
element,
|
||||||
|
size: element_size,
|
||||||
|
});
|
||||||
|
if item.contains_focused(cx) {
|
||||||
|
rendered_focused_item = true;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -470,15 +547,18 @@ impl StateInner {
|
||||||
while leading_overdraw < self.overdraw {
|
while leading_overdraw < self.overdraw {
|
||||||
cursor.prev(&());
|
cursor.prev(&());
|
||||||
if let Some(item) = cursor.item() {
|
if let Some(item) = cursor.item() {
|
||||||
let size = if let ListItem::Rendered { size } = item {
|
let size = if let ListItem::Measured { size, .. } = item {
|
||||||
*size
|
*size
|
||||||
} else {
|
} else {
|
||||||
let mut element = (self.render_item)(cursor.start().0, cx);
|
let mut element = (self.render_item)(cursor.start().0, cx);
|
||||||
element.measure(available_item_space, cx)
|
element.layout_as_root(available_item_space, cx)
|
||||||
};
|
};
|
||||||
|
|
||||||
leading_overdraw += size.height;
|
leading_overdraw += size.height;
|
||||||
measured_items.push_front(ListItem::Rendered { size });
|
measured_items.push_front(ListItem::Measured {
|
||||||
|
size,
|
||||||
|
focus_handle: item.focus_handle(),
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -490,23 +570,83 @@ impl StateInner {
|
||||||
new_items.extend(measured_items, &());
|
new_items.extend(measured_items, &());
|
||||||
cursor.seek(&Count(measured_range.end), Bias::Right, &());
|
cursor.seek(&Count(measured_range.end), Bias::Right, &());
|
||||||
new_items.append(cursor.suffix(&()), &());
|
new_items.append(cursor.suffix(&()), &());
|
||||||
|
|
||||||
self.items = new_items;
|
self.items = new_items;
|
||||||
|
|
||||||
|
// If none of the visible items are focused, check if an off-screen item is focused
|
||||||
|
// and include it to be rendered after the visible items so keyboard interaction continues
|
||||||
|
// to work for it.
|
||||||
|
if !rendered_focused_item {
|
||||||
|
let mut cursor = self
|
||||||
|
.items
|
||||||
|
.filter::<_, Count>(|summary| summary.has_focus_handles);
|
||||||
|
cursor.next(&());
|
||||||
|
while let Some(item) = cursor.item() {
|
||||||
|
if item.contains_focused(cx) {
|
||||||
|
let item_index = cursor.start().0;
|
||||||
|
let mut element = (self.render_item)(cursor.start().0, cx);
|
||||||
|
let size = element.layout_as_root(available_item_space, cx);
|
||||||
|
item_layouts.push_back(ItemLayout {
|
||||||
|
index: item_index,
|
||||||
|
element,
|
||||||
|
size,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cursor.next(&());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LayoutItemsResponse {
|
LayoutItemsResponse {
|
||||||
max_item_width,
|
max_item_width,
|
||||||
scroll_top,
|
scroll_top,
|
||||||
available_item_space,
|
item_layouts,
|
||||||
item_elements,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn prepaint_items(
|
||||||
|
&mut self,
|
||||||
|
bounds: Bounds<Pixels>,
|
||||||
|
padding: Edges<Pixels>,
|
||||||
|
cx: &mut ElementContext,
|
||||||
|
) -> Result<LayoutItemsResponse, ListOffset> {
|
||||||
|
cx.transact(|cx| {
|
||||||
|
let mut layout_response =
|
||||||
|
self.layout_items(Some(bounds.size.width), bounds.size.height, &padding, cx);
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
let mut item_origin = bounds.origin + Point::new(px(0.), padding.top);
|
||||||
|
item_origin.y -= layout_response.scroll_top.offset_in_item;
|
||||||
|
for item in &mut layout_response.item_layouts {
|
||||||
|
cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
|
||||||
|
item.element.prepaint_at(item_origin, cx);
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(autoscroll_bounds) = cx.take_autoscroll() {
|
||||||
|
if bounds.intersect(&autoscroll_bounds) != autoscroll_bounds {
|
||||||
|
return Err(ListOffset {
|
||||||
|
item_ix: item.index,
|
||||||
|
offset_in_item: autoscroll_bounds.origin.y - item_origin.y,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
item_origin.y += item.size.height;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
layout_response.item_layouts.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(layout_response)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for ListItem {
|
impl std::fmt::Debug for ListItem {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Unrendered => write!(f, "Unrendered"),
|
Self::Unmeasured { .. } => write!(f, "Unrendered"),
|
||||||
Self::Rendered { size, .. } => f.debug_struct("Rendered").field("size", size).finish(),
|
Self::Measured { size, .. } => f.debug_struct("Rendered").field("size", size).finish(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -522,13 +662,13 @@ pub struct ListOffset {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for List {
|
impl Element for List {
|
||||||
type BeforeLayout = ();
|
type RequestLayoutState = ();
|
||||||
type AfterLayout = ListAfterLayoutState;
|
type PrepaintState = ListPrepaintState;
|
||||||
|
|
||||||
fn before_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
cx: &mut crate::ElementContext,
|
cx: &mut crate::ElementContext,
|
||||||
) -> (crate::LayoutId, Self::BeforeLayout) {
|
) -> (crate::LayoutId, Self::RequestLayoutState) {
|
||||||
let layout_id = match self.sizing_behavior {
|
let layout_id = match self.sizing_behavior {
|
||||||
ListSizingBehavior::Infer => {
|
ListSizingBehavior::Infer => {
|
||||||
let mut style = Style::default();
|
let mut style = Style::default();
|
||||||
|
@ -589,12 +729,12 @@ impl Element for List {
|
||||||
(layout_id, ())
|
(layout_id, ())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_layout(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
_: &mut Self::BeforeLayout,
|
_: &mut Self::RequestLayoutState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) -> ListAfterLayoutState {
|
) -> ListPrepaintState {
|
||||||
let state = &mut *self.state.0.borrow_mut();
|
let state = &mut *self.state.0.borrow_mut();
|
||||||
state.reset = false;
|
state.reset = false;
|
||||||
|
|
||||||
|
@ -607,55 +747,47 @@ impl Element for List {
|
||||||
if state.last_layout_bounds.map_or(true, |last_bounds| {
|
if state.last_layout_bounds.map_or(true, |last_bounds| {
|
||||||
last_bounds.size.width != bounds.size.width
|
last_bounds.size.width != bounds.size.width
|
||||||
}) {
|
}) {
|
||||||
state.items = SumTree::from_iter(
|
let new_items = SumTree::from_iter(
|
||||||
(0..state.items.summary().count).map(|_| ListItem::Unrendered),
|
state.items.iter().map(|item| ListItem::Unmeasured {
|
||||||
|
focus_handle: item.focus_handle(),
|
||||||
|
}),
|
||||||
&(),
|
&(),
|
||||||
)
|
);
|
||||||
|
|
||||||
|
state.items = new_items;
|
||||||
}
|
}
|
||||||
|
|
||||||
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
|
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
|
||||||
let mut layout_response =
|
let layout = match state.prepaint_items(bounds, padding, cx) {
|
||||||
state.layout_items(Some(bounds.size.width), bounds.size.height, &padding, cx);
|
Ok(layout) => layout,
|
||||||
|
Err(autoscroll_request) => {
|
||||||
// Only paint the visible items, if there is actually any space for them (taking padding into account)
|
state.logical_scroll_top = Some(autoscroll_request);
|
||||||
if bounds.size.height > padding.top + padding.bottom {
|
state.prepaint_items(bounds, padding, cx).unwrap()
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
state.last_layout_bounds = Some(bounds);
|
state.last_layout_bounds = Some(bounds);
|
||||||
state.last_padding = Some(padding);
|
state.last_padding = Some(padding);
|
||||||
ListAfterLayoutState {
|
ListPrepaintState { hitbox, layout }
|
||||||
hitbox,
|
|
||||||
layout: layout_response,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<crate::Pixels>,
|
bounds: Bounds<crate::Pixels>,
|
||||||
_: &mut Self::BeforeLayout,
|
_: &mut Self::RequestLayoutState,
|
||||||
after_layout: &mut Self::AfterLayout,
|
prepaint: &mut Self::PrepaintState,
|
||||||
cx: &mut crate::ElementContext,
|
cx: &mut crate::ElementContext,
|
||||||
) {
|
) {
|
||||||
cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
|
cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
|
||||||
for item in &mut after_layout.layout.item_elements {
|
for item in &mut prepaint.layout.item_layouts {
|
||||||
item.paint(cx);
|
item.element.paint(cx);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let list_state = self.state.clone();
|
let list_state = self.state.clone();
|
||||||
let height = bounds.size.height;
|
let height = bounds.size.height;
|
||||||
let scroll_top = after_layout.layout.scroll_top;
|
let scroll_top = prepaint.layout.scroll_top;
|
||||||
let hitbox_id = after_layout.hitbox.id;
|
let hitbox_id = prepaint.hitbox.id;
|
||||||
cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
|
cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
|
||||||
if phase == DispatchPhase::Bubble && hitbox_id.is_hovered(cx) {
|
if phase == DispatchPhase::Bubble && hitbox_id.is_hovered(cx) {
|
||||||
list_state.0.borrow_mut().scroll(
|
list_state.0.borrow_mut().scroll(
|
||||||
|
@ -688,17 +820,21 @@ impl sum_tree::Item for ListItem {
|
||||||
|
|
||||||
fn summary(&self) -> Self::Summary {
|
fn summary(&self) -> Self::Summary {
|
||||||
match self {
|
match self {
|
||||||
ListItem::Unrendered => ListItemSummary {
|
ListItem::Unmeasured { focus_handle } => ListItemSummary {
|
||||||
count: 1,
|
count: 1,
|
||||||
rendered_count: 0,
|
rendered_count: 0,
|
||||||
unrendered_count: 1,
|
unrendered_count: 1,
|
||||||
height: px(0.),
|
height: px(0.),
|
||||||
|
has_focus_handles: focus_handle.is_some(),
|
||||||
},
|
},
|
||||||
ListItem::Rendered { size } => ListItemSummary {
|
ListItem::Measured {
|
||||||
|
size, focus_handle, ..
|
||||||
|
} => ListItemSummary {
|
||||||
count: 1,
|
count: 1,
|
||||||
rendered_count: 1,
|
rendered_count: 1,
|
||||||
unrendered_count: 0,
|
unrendered_count: 0,
|
||||||
height: size.height,
|
height: size.height,
|
||||||
|
has_focus_handles: focus_handle.is_some(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -712,6 +848,7 @@ impl sum_tree::Summary for ListItemSummary {
|
||||||
self.rendered_count += summary.rendered_count;
|
self.rendered_count += summary.rendered_count;
|
||||||
self.unrendered_count += summary.unrendered_count;
|
self.unrendered_count += summary.unrendered_count;
|
||||||
self.height += summary.height;
|
self.height += summary.height;
|
||||||
|
self.has_focus_handles |= summary.has_focus_handles;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,30 +37,30 @@ impl Svg {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for Svg {
|
impl Element for Svg {
|
||||||
type BeforeLayout = ();
|
type RequestLayoutState = ();
|
||||||
type AfterLayout = Option<Hitbox>;
|
type PrepaintState = Option<Hitbox>;
|
||||||
|
|
||||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
let layout_id = self
|
let layout_id = self
|
||||||
.interactivity
|
.interactivity
|
||||||
.before_layout(cx, |style, cx| cx.request_layout(&style, None));
|
.request_layout(cx, |style, cx| cx.request_layout(&style, None));
|
||||||
(layout_id, ())
|
(layout_id, ())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_layout(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
_before_layout: &mut Self::BeforeLayout,
|
_request_layout: &mut Self::RequestLayoutState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) -> Option<Hitbox> {
|
) -> Option<Hitbox> {
|
||||||
self.interactivity
|
self.interactivity
|
||||||
.after_layout(bounds, bounds.size, cx, |_, _, hitbox, _| hitbox)
|
.prepaint(bounds, bounds.size, cx, |_, _, hitbox, _| hitbox)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
_before_layout: &mut Self::BeforeLayout,
|
_request_layout: &mut Self::RequestLayoutState,
|
||||||
hitbox: &mut Option<Hitbox>,
|
hitbox: &mut Option<Hitbox>,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) where
|
) where
|
||||||
|
|
|
@ -17,19 +17,19 @@ use std::{
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
impl Element for &'static str {
|
impl Element for &'static str {
|
||||||
type BeforeLayout = TextState;
|
type RequestLayoutState = TextState;
|
||||||
type AfterLayout = ();
|
type PrepaintState = ();
|
||||||
|
|
||||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
let mut state = TextState::default();
|
let mut state = TextState::default();
|
||||||
let layout_id = state.layout(SharedString::from(*self), None, cx);
|
let layout_id = state.layout(SharedString::from(*self), None, cx);
|
||||||
(layout_id, state)
|
(layout_id, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_layout(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_bounds: Bounds<Pixels>,
|
_bounds: Bounds<Pixels>,
|
||||||
_text_state: &mut Self::BeforeLayout,
|
_text_state: &mut Self::RequestLayoutState,
|
||||||
_cx: &mut ElementContext,
|
_cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
@ -62,19 +62,19 @@ impl IntoElement for String {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for SharedString {
|
impl Element for SharedString {
|
||||||
type BeforeLayout = TextState;
|
type RequestLayoutState = TextState;
|
||||||
type AfterLayout = ();
|
type PrepaintState = ();
|
||||||
|
|
||||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
let mut state = TextState::default();
|
let mut state = TextState::default();
|
||||||
let layout_id = state.layout(self.clone(), None, cx);
|
let layout_id = state.layout(self.clone(), None, cx);
|
||||||
(layout_id, state)
|
(layout_id, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_layout(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_bounds: Bounds<Pixels>,
|
_bounds: Bounds<Pixels>,
|
||||||
_text_state: &mut Self::BeforeLayout,
|
_text_state: &mut Self::RequestLayoutState,
|
||||||
_cx: &mut ElementContext,
|
_cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
@ -82,8 +82,8 @@ impl Element for SharedString {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
text_state: &mut Self::BeforeLayout,
|
text_state: &mut Self::RequestLayoutState,
|
||||||
_: &mut Self::AfterLayout,
|
_: &mut Self::PrepaintState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
let text_str: &str = self.as_ref();
|
let text_str: &str = self.as_ref();
|
||||||
|
@ -148,19 +148,19 @@ impl StyledText {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for StyledText {
|
impl Element for StyledText {
|
||||||
type BeforeLayout = TextState;
|
type RequestLayoutState = TextState;
|
||||||
type AfterLayout = ();
|
type PrepaintState = ();
|
||||||
|
|
||||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
let mut state = TextState::default();
|
let mut state = TextState::default();
|
||||||
let layout_id = state.layout(self.text.clone(), self.runs.take(), cx);
|
let layout_id = state.layout(self.text.clone(), self.runs.take(), cx);
|
||||||
(layout_id, state)
|
(layout_id, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_layout(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_bounds: Bounds<Pixels>,
|
_bounds: Bounds<Pixels>,
|
||||||
_state: &mut Self::BeforeLayout,
|
_state: &mut Self::RequestLayoutState,
|
||||||
_cx: &mut ElementContext,
|
_cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
@ -168,8 +168,8 @@ impl Element for StyledText {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
text_state: &mut Self::BeforeLayout,
|
text_state: &mut Self::RequestLayoutState,
|
||||||
_: &mut Self::AfterLayout,
|
_: &mut Self::PrepaintState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
text_state.paint(bounds, &self.text, cx)
|
text_state.paint(bounds, &self.text, cx)
|
||||||
|
@ -402,17 +402,17 @@ impl InteractiveText {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for InteractiveText {
|
impl Element for InteractiveText {
|
||||||
type BeforeLayout = TextState;
|
type RequestLayoutState = TextState;
|
||||||
type AfterLayout = Hitbox;
|
type PrepaintState = Hitbox;
|
||||||
|
|
||||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
self.text.before_layout(cx)
|
self.text.request_layout(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_layout(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
state: &mut Self::BeforeLayout,
|
state: &mut Self::RequestLayoutState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) -> Hitbox {
|
) -> Hitbox {
|
||||||
cx.with_element_state::<InteractiveTextState, _>(
|
cx.with_element_state::<InteractiveTextState, _>(
|
||||||
|
@ -430,7 +430,7 @@ impl Element for InteractiveText {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.text.after_layout(bounds, state, cx);
|
self.text.prepaint(bounds, state, cx);
|
||||||
let hitbox = cx.insert_hitbox(bounds, false);
|
let hitbox = cx.insert_hitbox(bounds, false);
|
||||||
(hitbox, interactive_state)
|
(hitbox, interactive_state)
|
||||||
},
|
},
|
||||||
|
@ -440,7 +440,7 @@ impl Element for InteractiveText {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
text_state: &mut Self::BeforeLayout,
|
text_state: &mut Self::RequestLayoutState,
|
||||||
hitbox: &mut Hitbox,
|
hitbox: &mut Hitbox,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -104,13 +104,13 @@ impl Styled for UniformList {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for UniformList {
|
impl Element for UniformList {
|
||||||
type BeforeLayout = UniformListFrameState;
|
type RequestLayoutState = UniformListFrameState;
|
||||||
type AfterLayout = Option<Hitbox>;
|
type PrepaintState = Option<Hitbox>;
|
||||||
|
|
||||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
let max_items = self.item_count;
|
let max_items = self.item_count;
|
||||||
let item_size = self.measure_item(None, cx);
|
let item_size = self.measure_item(None, cx);
|
||||||
let layout_id = self.interactivity.before_layout(cx, |style, cx| {
|
let layout_id = self.interactivity.request_layout(cx, |style, cx| {
|
||||||
cx.request_measured_layout(style, move |known_dimensions, available_space, _cx| {
|
cx.request_measured_layout(style, move |known_dimensions, available_space, _cx| {
|
||||||
let desired_height = item_size.height * max_items;
|
let desired_height = item_size.height * max_items;
|
||||||
let width = known_dimensions
|
let width = known_dimensions
|
||||||
|
@ -137,10 +137,10 @@ impl Element for UniformList {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_layout(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
before_layout: &mut Self::BeforeLayout,
|
frame_state: &mut Self::RequestLayoutState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) -> Option<Hitbox> {
|
) -> Option<Hitbox> {
|
||||||
let style = self.interactivity.compute_style(None, cx);
|
let style = self.interactivity.compute_style(None, cx);
|
||||||
|
@ -155,7 +155,7 @@ impl Element for UniformList {
|
||||||
|
|
||||||
let content_size = Size {
|
let content_size = Size {
|
||||||
width: padded_bounds.size.width,
|
width: padded_bounds.size.width,
|
||||||
height: before_layout.item_size.height * self.item_count + padding.top + padding.bottom,
|
height: frame_state.item_size.height * self.item_count + padding.top + padding.bottom,
|
||||||
};
|
};
|
||||||
|
|
||||||
let shared_scroll_offset = self.interactivity.scroll_offset.clone().unwrap();
|
let shared_scroll_offset = self.interactivity.scroll_offset.clone().unwrap();
|
||||||
|
@ -166,7 +166,7 @@ impl Element for UniformList {
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.and_then(|handle| handle.deferred_scroll_to_item.take());
|
.and_then(|handle| handle.deferred_scroll_to_item.take());
|
||||||
|
|
||||||
self.interactivity.after_layout(
|
self.interactivity.prepaint(
|
||||||
bounds,
|
bounds,
|
||||||
content_size,
|
content_size,
|
||||||
cx,
|
cx,
|
||||||
|
@ -222,8 +222,9 @@ impl Element for UniformList {
|
||||||
AvailableSpace::Definite(padded_bounds.size.width),
|
AvailableSpace::Definite(padded_bounds.size.width),
|
||||||
AvailableSpace::Definite(item_height),
|
AvailableSpace::Definite(item_height),
|
||||||
);
|
);
|
||||||
item.layout(item_origin, available_space, cx);
|
item.layout_as_root(available_space, cx);
|
||||||
before_layout.items.push(item);
|
item.prepaint_at(item_origin, cx);
|
||||||
|
frame_state.items.push(item);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -236,13 +237,13 @@ impl Element for UniformList {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<crate::Pixels>,
|
bounds: Bounds<crate::Pixels>,
|
||||||
before_layout: &mut Self::BeforeLayout,
|
request_layout: &mut Self::RequestLayoutState,
|
||||||
hitbox: &mut Option<Hitbox>,
|
hitbox: &mut Option<Hitbox>,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
self.interactivity
|
self.interactivity
|
||||||
.paint(bounds, hitbox.as_ref(), cx, |_, cx| {
|
.paint(bounds, hitbox.as_ref(), cx, |_, cx| {
|
||||||
for item in &mut before_layout.items {
|
for item in &mut request_layout.items {
|
||||||
item.paint(cx);
|
item.paint(cx);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -278,7 +279,7 @@ impl UniformList {
|
||||||
}),
|
}),
|
||||||
AvailableSpace::MinContent,
|
AvailableSpace::MinContent,
|
||||||
);
|
);
|
||||||
item_to_measure.measure(available_space, cx)
|
item_to_measure.layout_as_root(available_space, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Track and render scroll state of this list with reference to the given scroll handle.
|
/// Track and render scroll state of this list with reference to the given scroll handle.
|
||||||
|
|
|
@ -283,6 +283,19 @@ impl DispatchTree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn truncate(&mut self, index: usize) {
|
||||||
|
for node in &self.nodes[index..] {
|
||||||
|
if let Some(focus_id) = node.focus_id {
|
||||||
|
self.focusable_node_ids.remove(&focus_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(view_id) = node.view_id {
|
||||||
|
self.view_node_ids.remove(&view_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.nodes.truncate(index);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn clear_pending_keystrokes(&mut self) {
|
pub fn clear_pending_keystrokes(&mut self) {
|
||||||
self.keystroke_matchers.clear();
|
self.keystroke_matchers.clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ impl TaffyLayoutEngine {
|
||||||
self.styles.clear();
|
self.styles.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn before_layout(
|
pub fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
style: &Style,
|
style: &Style,
|
||||||
rem_size: Pixels,
|
rem_size: Pixels,
|
||||||
|
|
|
@ -311,6 +311,10 @@ impl WindowTextSystem {
|
||||||
self.line_layout_cache.reuse_layouts(index)
|
self.line_layout_cache.reuse_layouts(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn truncate_layouts(&self, index: LineLayoutIndex) {
|
||||||
|
self.line_layout_cache.truncate_layouts(index)
|
||||||
|
}
|
||||||
|
|
||||||
/// Shape the given line, at the given font_size, for painting to the screen.
|
/// Shape the given line, at the given font_size, for painting to the screen.
|
||||||
/// Subsets of the line can be styled independently with the `runs` parameter.
|
/// Subsets of the line can be styled independently with the `runs` parameter.
|
||||||
///
|
///
|
||||||
|
|
|
@ -347,6 +347,14 @@ impl LineLayoutCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn truncate_layouts(&self, index: LineLayoutIndex) {
|
||||||
|
let mut current_frame = &mut *self.current_frame.write();
|
||||||
|
current_frame.used_lines.truncate(index.lines_index);
|
||||||
|
current_frame
|
||||||
|
.used_wrapped_lines
|
||||||
|
.truncate(index.wrapped_lines_index);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn finish_frame(&self) {
|
pub fn finish_frame(&self) {
|
||||||
let mut prev_frame = self.previous_frame.lock();
|
let mut prev_frame = self.previous_frame.lock();
|
||||||
let mut curr_frame = self.current_frame.write();
|
let mut curr_frame = self.current_frame.write();
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
seal::Sealed, AfterLayoutIndex, AnyElement, AnyModel, AnyWeakModel, AppContext, Bounds,
|
seal::Sealed, AnyElement, AnyModel, AnyWeakModel, AppContext, Bounds, ContentMask, Element,
|
||||||
ContentMask, Element, ElementContext, ElementId, Entity, EntityId, Flatten, FocusHandle,
|
ElementContext, ElementId, Entity, EntityId, Flatten, FocusHandle, FocusableView, IntoElement,
|
||||||
FocusableView, IntoElement, LayoutId, Model, PaintIndex, Pixels, Render, Style,
|
LayoutId, Model, PaintIndex, Pixels, PrepaintStateIndex, Render, Style, StyleRefinement,
|
||||||
StyleRefinement, TextStyle, ViewContext, VisualContext, WeakModel,
|
TextStyle, ViewContext, VisualContext, WeakModel,
|
||||||
};
|
};
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use refineable::Refineable;
|
use refineable::Refineable;
|
||||||
|
@ -23,7 +23,7 @@ pub struct View<V> {
|
||||||
impl<V> Sealed for View<V> {}
|
impl<V> Sealed for View<V> {}
|
||||||
|
|
||||||
struct AnyViewState {
|
struct AnyViewState {
|
||||||
after_layout_range: Range<AfterLayoutIndex>,
|
prepaint_range: Range<PrepaintStateIndex>,
|
||||||
paint_range: Range<PaintIndex>,
|
paint_range: Range<PaintIndex>,
|
||||||
cache_key: ViewCacheKey,
|
cache_key: ViewCacheKey,
|
||||||
}
|
}
|
||||||
|
@ -90,34 +90,34 @@ impl<V: 'static> View<V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: Render> Element for View<V> {
|
impl<V: Render> Element for View<V> {
|
||||||
type BeforeLayout = AnyElement;
|
type RequestLayoutState = AnyElement;
|
||||||
type AfterLayout = ();
|
type PrepaintState = ();
|
||||||
|
|
||||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
|
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
|
||||||
let mut element = self.update(cx, |view, cx| view.render(cx).into_any_element());
|
let mut element = self.update(cx, |view, cx| view.render(cx).into_any_element());
|
||||||
let layout_id = element.before_layout(cx);
|
let layout_id = element.request_layout(cx);
|
||||||
(layout_id, element)
|
(layout_id, element)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_layout(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: Bounds<Pixels>,
|
_: Bounds<Pixels>,
|
||||||
element: &mut Self::BeforeLayout,
|
element: &mut Self::RequestLayoutState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
cx.set_view_id(self.entity_id());
|
cx.set_view_id(self.entity_id());
|
||||||
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
|
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
|
||||||
element.after_layout(cx)
|
element.prepaint(cx)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: Bounds<Pixels>,
|
_: Bounds<Pixels>,
|
||||||
element: &mut Self::BeforeLayout,
|
element: &mut Self::RequestLayoutState,
|
||||||
_: &mut Self::AfterLayout,
|
_: &mut Self::PrepaintState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
|
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
|
||||||
|
@ -276,10 +276,10 @@ impl<V: Render> From<View<V>> for AnyView {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for AnyView {
|
impl Element for AnyView {
|
||||||
type BeforeLayout = Option<AnyElement>;
|
type RequestLayoutState = Option<AnyElement>;
|
||||||
type AfterLayout = Option<AnyElement>;
|
type PrepaintState = Option<AnyElement>;
|
||||||
|
|
||||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
if let Some(style) = self.cached_style.as_ref() {
|
if let Some(style) = self.cached_style.as_ref() {
|
||||||
let mut root_style = Style::default();
|
let mut root_style = Style::default();
|
||||||
root_style.refine(style);
|
root_style.refine(style);
|
||||||
|
@ -288,16 +288,16 @@ impl Element for AnyView {
|
||||||
} else {
|
} else {
|
||||||
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
|
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
|
||||||
let mut element = (self.render)(self, cx);
|
let mut element = (self.render)(self, cx);
|
||||||
let layout_id = element.before_layout(cx);
|
let layout_id = element.request_layout(cx);
|
||||||
(layout_id, Some(element))
|
(layout_id, Some(element))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_layout(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
element: &mut Self::BeforeLayout,
|
element: &mut Self::RequestLayoutState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) -> Option<AnyElement> {
|
) -> Option<AnyElement> {
|
||||||
cx.set_view_id(self.entity_id());
|
cx.set_view_id(self.entity_id());
|
||||||
|
@ -317,23 +317,24 @@ impl Element for AnyView {
|
||||||
&& !cx.window.dirty_views.contains(&self.entity_id())
|
&& !cx.window.dirty_views.contains(&self.entity_id())
|
||||||
&& !cx.window.refreshing
|
&& !cx.window.refreshing
|
||||||
{
|
{
|
||||||
let after_layout_start = cx.after_layout_index();
|
let prepaint_start = cx.prepaint_index();
|
||||||
cx.reuse_after_layout(element_state.after_layout_range.clone());
|
cx.reuse_prepaint(element_state.prepaint_range.clone());
|
||||||
let after_layout_end = cx.after_layout_index();
|
let prepaint_end = cx.prepaint_index();
|
||||||
element_state.after_layout_range = after_layout_start..after_layout_end;
|
element_state.prepaint_range = prepaint_start..prepaint_end;
|
||||||
return (None, Some(element_state));
|
return (None, Some(element_state));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let after_layout_start = cx.after_layout_index();
|
let prepaint_start = cx.prepaint_index();
|
||||||
let mut element = (self.render)(self, cx);
|
let mut element = (self.render)(self, cx);
|
||||||
element.layout(bounds.origin, bounds.size.into(), cx);
|
element.layout_as_root(bounds.size.into(), cx);
|
||||||
let after_layout_end = cx.after_layout_index();
|
element.prepaint_at(bounds.origin, cx);
|
||||||
|
let prepaint_end = cx.prepaint_index();
|
||||||
|
|
||||||
(
|
(
|
||||||
Some(element),
|
Some(element),
|
||||||
Some(AnyViewState {
|
Some(AnyViewState {
|
||||||
after_layout_range: after_layout_start..after_layout_end,
|
prepaint_range: prepaint_start..prepaint_end,
|
||||||
paint_range: PaintIndex::default()..PaintIndex::default(),
|
paint_range: PaintIndex::default()..PaintIndex::default(),
|
||||||
cache_key: ViewCacheKey {
|
cache_key: ViewCacheKey {
|
||||||
bounds,
|
bounds,
|
||||||
|
@ -347,7 +348,7 @@ impl Element for AnyView {
|
||||||
} else {
|
} else {
|
||||||
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
|
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
|
||||||
let mut element = element.take().unwrap();
|
let mut element = element.take().unwrap();
|
||||||
element.after_layout(cx);
|
element.prepaint(cx);
|
||||||
Some(element)
|
Some(element)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -356,8 +357,8 @@ impl Element for AnyView {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_bounds: Bounds<Pixels>,
|
_bounds: Bounds<Pixels>,
|
||||||
_: &mut Self::BeforeLayout,
|
_: &mut Self::RequestLayoutState,
|
||||||
element: &mut Self::AfterLayout,
|
element: &mut Self::PrepaintState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
if self.cached_style.is_some() {
|
if self.cached_style.is_some() {
|
||||||
|
|
|
@ -284,6 +284,9 @@ pub struct Window {
|
||||||
pub(crate) root_view: Option<AnyView>,
|
pub(crate) root_view: Option<AnyView>,
|
||||||
pub(crate) element_id_stack: GlobalElementId,
|
pub(crate) element_id_stack: GlobalElementId,
|
||||||
pub(crate) text_style_stack: Vec<TextStyleRefinement>,
|
pub(crate) text_style_stack: Vec<TextStyleRefinement>,
|
||||||
|
pub(crate) element_offset_stack: Vec<Point<Pixels>>,
|
||||||
|
pub(crate) content_mask_stack: Vec<ContentMask<Pixels>>,
|
||||||
|
pub(crate) requested_autoscroll: Option<Bounds<Pixels>>,
|
||||||
pub(crate) rendered_frame: Frame,
|
pub(crate) rendered_frame: Frame,
|
||||||
pub(crate) next_frame: Frame,
|
pub(crate) next_frame: Frame,
|
||||||
pub(crate) next_hitbox_id: HitboxId,
|
pub(crate) next_hitbox_id: HitboxId,
|
||||||
|
@ -549,6 +552,9 @@ impl Window {
|
||||||
root_view: None,
|
root_view: None,
|
||||||
element_id_stack: GlobalElementId::default(),
|
element_id_stack: GlobalElementId::default(),
|
||||||
text_style_stack: Vec::new(),
|
text_style_stack: Vec::new(),
|
||||||
|
element_offset_stack: Vec::new(),
|
||||||
|
content_mask_stack: Vec::new(),
|
||||||
|
requested_autoscroll: None,
|
||||||
rendered_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())),
|
rendered_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())),
|
||||||
next_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())),
|
next_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())),
|
||||||
next_frame_callbacks,
|
next_frame_callbacks,
|
||||||
|
@ -1023,6 +1029,7 @@ impl<'a> WindowContext<'a> {
|
||||||
#[profiling::function]
|
#[profiling::function]
|
||||||
pub fn draw(&mut self) {
|
pub fn draw(&mut self) {
|
||||||
self.window.dirty.set(false);
|
self.window.dirty.set(false);
|
||||||
|
self.window.requested_autoscroll = None;
|
||||||
|
|
||||||
// Restore the previously-used input handler.
|
// Restore the previously-used input handler.
|
||||||
if let Some(input_handler) = self.window.platform_window.take_input_handler() {
|
if let Some(input_handler) = self.window.platform_window.take_input_handler() {
|
||||||
|
|
|
@ -121,7 +121,7 @@ pub(crate) struct DeferredDraw {
|
||||||
text_style_stack: Vec<TextStyleRefinement>,
|
text_style_stack: Vec<TextStyleRefinement>,
|
||||||
element: Option<AnyElement>,
|
element: Option<AnyElement>,
|
||||||
absolute_offset: Point<Pixels>,
|
absolute_offset: Point<Pixels>,
|
||||||
layout_range: Range<AfterLayoutIndex>,
|
prepaint_range: Range<PrepaintStateIndex>,
|
||||||
paint_range: Range<PaintIndex>,
|
paint_range: Range<PaintIndex>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,8 +135,6 @@ pub(crate) struct Frame {
|
||||||
pub(crate) scene: Scene,
|
pub(crate) scene: Scene,
|
||||||
pub(crate) hitboxes: Vec<Hitbox>,
|
pub(crate) hitboxes: Vec<Hitbox>,
|
||||||
pub(crate) deferred_draws: Vec<DeferredDraw>,
|
pub(crate) deferred_draws: Vec<DeferredDraw>,
|
||||||
pub(crate) content_mask_stack: Vec<ContentMask<Pixels>>,
|
|
||||||
pub(crate) element_offset_stack: Vec<Point<Pixels>>,
|
|
||||||
pub(crate) input_handlers: Vec<Option<PlatformInputHandler>>,
|
pub(crate) input_handlers: Vec<Option<PlatformInputHandler>>,
|
||||||
pub(crate) tooltip_requests: Vec<Option<TooltipRequest>>,
|
pub(crate) tooltip_requests: Vec<Option<TooltipRequest>>,
|
||||||
pub(crate) cursor_styles: Vec<CursorStyleRequest>,
|
pub(crate) cursor_styles: Vec<CursorStyleRequest>,
|
||||||
|
@ -145,7 +143,7 @@ pub(crate) struct Frame {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub(crate) struct AfterLayoutIndex {
|
pub(crate) struct PrepaintStateIndex {
|
||||||
hitboxes_index: usize,
|
hitboxes_index: usize,
|
||||||
tooltips_index: usize,
|
tooltips_index: usize,
|
||||||
deferred_draws_index: usize,
|
deferred_draws_index: usize,
|
||||||
|
@ -176,8 +174,6 @@ impl Frame {
|
||||||
scene: Scene::default(),
|
scene: Scene::default(),
|
||||||
hitboxes: Vec::new(),
|
hitboxes: Vec::new(),
|
||||||
deferred_draws: Vec::new(),
|
deferred_draws: Vec::new(),
|
||||||
content_mask_stack: Vec::new(),
|
|
||||||
element_offset_stack: Vec::new(),
|
|
||||||
input_handlers: Vec::new(),
|
input_handlers: Vec::new(),
|
||||||
tooltip_requests: Vec::new(),
|
tooltip_requests: Vec::new(),
|
||||||
cursor_styles: Vec::new(),
|
cursor_styles: Vec::new(),
|
||||||
|
@ -399,29 +395,29 @@ impl<'a> ElementContext<'a> {
|
||||||
|
|
||||||
// Layout all root elements.
|
// Layout all root elements.
|
||||||
let mut root_element = self.window.root_view.as_ref().unwrap().clone().into_any();
|
let mut root_element = self.window.root_view.as_ref().unwrap().clone().into_any();
|
||||||
root_element.layout(Point::default(), self.window.viewport_size.into(), self);
|
root_element.prepaint_as_root(Point::default(), self.window.viewport_size.into(), self);
|
||||||
|
|
||||||
let mut sorted_deferred_draws =
|
let mut sorted_deferred_draws =
|
||||||
(0..self.window.next_frame.deferred_draws.len()).collect::<SmallVec<[_; 8]>>();
|
(0..self.window.next_frame.deferred_draws.len()).collect::<SmallVec<[_; 8]>>();
|
||||||
sorted_deferred_draws.sort_by_key(|ix| self.window.next_frame.deferred_draws[*ix].priority);
|
sorted_deferred_draws.sort_by_key(|ix| self.window.next_frame.deferred_draws[*ix].priority);
|
||||||
self.layout_deferred_draws(&sorted_deferred_draws);
|
self.prepaint_deferred_draws(&sorted_deferred_draws);
|
||||||
|
|
||||||
let mut prompt_element = None;
|
let mut prompt_element = None;
|
||||||
let mut active_drag_element = None;
|
let mut active_drag_element = None;
|
||||||
let mut tooltip_element = None;
|
let mut tooltip_element = None;
|
||||||
if let Some(prompt) = self.window.prompt.take() {
|
if let Some(prompt) = self.window.prompt.take() {
|
||||||
let mut element = prompt.view.any_view().into_any();
|
let mut element = prompt.view.any_view().into_any();
|
||||||
element.layout(Point::default(), self.window.viewport_size.into(), self);
|
element.prepaint_as_root(Point::default(), self.window.viewport_size.into(), self);
|
||||||
prompt_element = Some(element);
|
prompt_element = Some(element);
|
||||||
self.window.prompt = Some(prompt);
|
self.window.prompt = Some(prompt);
|
||||||
} else if let Some(active_drag) = self.app.active_drag.take() {
|
} else if let Some(active_drag) = self.app.active_drag.take() {
|
||||||
let mut element = active_drag.view.clone().into_any();
|
let mut element = active_drag.view.clone().into_any();
|
||||||
let offset = self.mouse_position() - active_drag.cursor_offset;
|
let offset = self.mouse_position() - active_drag.cursor_offset;
|
||||||
element.layout(offset, AvailableSpace::min_size(), self);
|
element.prepaint_as_root(offset, AvailableSpace::min_size(), self);
|
||||||
active_drag_element = Some(element);
|
active_drag_element = Some(element);
|
||||||
self.app.active_drag = Some(active_drag);
|
self.app.active_drag = Some(active_drag);
|
||||||
} else {
|
} else {
|
||||||
tooltip_element = self.layout_tooltip();
|
tooltip_element = self.prepaint_tooltip();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.window.mouse_hit_test = self.window.next_frame.hit_test(self.window.mouse_position);
|
self.window.mouse_hit_test = self.window.next_frame.hit_test(self.window.mouse_position);
|
||||||
|
@ -441,12 +437,12 @@ impl<'a> ElementContext<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout_tooltip(&mut self) -> Option<AnyElement> {
|
fn prepaint_tooltip(&mut self) -> Option<AnyElement> {
|
||||||
let tooltip_request = self.window.next_frame.tooltip_requests.last().cloned()?;
|
let tooltip_request = self.window.next_frame.tooltip_requests.last().cloned()?;
|
||||||
let tooltip_request = tooltip_request.unwrap();
|
let tooltip_request = tooltip_request.unwrap();
|
||||||
let mut element = tooltip_request.tooltip.view.clone().into_any();
|
let mut element = tooltip_request.tooltip.view.clone().into_any();
|
||||||
let mouse_position = tooltip_request.tooltip.mouse_position;
|
let mouse_position = tooltip_request.tooltip.mouse_position;
|
||||||
let tooltip_size = element.measure(AvailableSpace::min_size(), self);
|
let tooltip_size = element.layout_as_root(AvailableSpace::min_size(), self);
|
||||||
|
|
||||||
let mut tooltip_bounds = Bounds::new(mouse_position + point(px(1.), px(1.)), tooltip_size);
|
let mut tooltip_bounds = Bounds::new(mouse_position + point(px(1.), px(1.)), tooltip_size);
|
||||||
let window_bounds = Bounds {
|
let window_bounds = Bounds {
|
||||||
|
@ -478,7 +474,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.prepaint(cx));
|
||||||
|
|
||||||
self.window.tooltip_bounds = Some(TooltipBounds {
|
self.window.tooltip_bounds = Some(TooltipBounds {
|
||||||
id: tooltip_request.id,
|
id: tooltip_request.id,
|
||||||
|
@ -487,7 +483,7 @@ impl<'a> ElementContext<'a> {
|
||||||
Some(element)
|
Some(element)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout_deferred_draws(&mut self, deferred_draw_indices: &[usize]) {
|
fn prepaint_deferred_draws(&mut self, deferred_draw_indices: &[usize]) {
|
||||||
assert_eq!(self.window.element_id_stack.len(), 0);
|
assert_eq!(self.window.element_id_stack.len(), 0);
|
||||||
|
|
||||||
let mut deferred_draws = mem::take(&mut self.window.next_frame.deferred_draws);
|
let mut deferred_draws = mem::take(&mut self.window.next_frame.deferred_draws);
|
||||||
|
@ -500,16 +496,16 @@ impl<'a> ElementContext<'a> {
|
||||||
.dispatch_tree
|
.dispatch_tree
|
||||||
.set_active_node(deferred_draw.parent_node);
|
.set_active_node(deferred_draw.parent_node);
|
||||||
|
|
||||||
let layout_start = self.after_layout_index();
|
let prepaint_start = self.prepaint_index();
|
||||||
if let Some(element) = deferred_draw.element.as_mut() {
|
if let Some(element) = deferred_draw.element.as_mut() {
|
||||||
self.with_absolute_element_offset(deferred_draw.absolute_offset, |cx| {
|
self.with_absolute_element_offset(deferred_draw.absolute_offset, |cx| {
|
||||||
element.after_layout(cx)
|
element.prepaint(cx)
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
self.reuse_after_layout(deferred_draw.layout_range.clone());
|
self.reuse_prepaint(deferred_draw.prepaint_range.clone());
|
||||||
}
|
}
|
||||||
let layout_end = self.after_layout_index();
|
let prepaint_end = self.prepaint_index();
|
||||||
deferred_draw.layout_range = layout_start..layout_end;
|
deferred_draw.prepaint_range = prepaint_start..prepaint_end;
|
||||||
}
|
}
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
self.window.next_frame.deferred_draws.len(),
|
self.window.next_frame.deferred_draws.len(),
|
||||||
|
@ -546,8 +542,8 @@ impl<'a> ElementContext<'a> {
|
||||||
self.window.element_id_stack.clear();
|
self.window.element_id_stack.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn after_layout_index(&self) -> AfterLayoutIndex {
|
pub(crate) fn prepaint_index(&self) -> PrepaintStateIndex {
|
||||||
AfterLayoutIndex {
|
PrepaintStateIndex {
|
||||||
hitboxes_index: self.window.next_frame.hitboxes.len(),
|
hitboxes_index: self.window.next_frame.hitboxes.len(),
|
||||||
tooltips_index: self.window.next_frame.tooltip_requests.len(),
|
tooltips_index: self.window.next_frame.tooltip_requests.len(),
|
||||||
deferred_draws_index: self.window.next_frame.deferred_draws.len(),
|
deferred_draws_index: self.window.next_frame.deferred_draws.len(),
|
||||||
|
@ -557,7 +553,7 @@ impl<'a> ElementContext<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn reuse_after_layout(&mut self, range: Range<AfterLayoutIndex>) {
|
pub(crate) fn reuse_prepaint(&mut self, range: Range<PrepaintStateIndex>) {
|
||||||
let window = &mut self.window;
|
let window = &mut self.window;
|
||||||
window.next_frame.hitboxes.extend(
|
window.next_frame.hitboxes.extend(
|
||||||
window.rendered_frame.hitboxes[range.start.hitboxes_index..range.end.hitboxes_index]
|
window.rendered_frame.hitboxes[range.start.hitboxes_index..range.end.hitboxes_index]
|
||||||
|
@ -595,7 +591,7 @@ impl<'a> ElementContext<'a> {
|
||||||
priority: deferred_draw.priority,
|
priority: deferred_draw.priority,
|
||||||
element: None,
|
element: None,
|
||||||
absolute_offset: deferred_draw.absolute_offset,
|
absolute_offset: deferred_draw.absolute_offset,
|
||||||
layout_range: deferred_draw.layout_range.clone(),
|
prepaint_range: deferred_draw.prepaint_range.clone(),
|
||||||
paint_range: deferred_draw.paint_range.clone(),
|
paint_range: deferred_draw.paint_range.clone(),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -715,9 +711,9 @@ impl<'a> ElementContext<'a> {
|
||||||
) -> R {
|
) -> R {
|
||||||
if let Some(mask) = mask {
|
if let Some(mask) = mask {
|
||||||
let mask = mask.intersect(&self.content_mask());
|
let mask = mask.intersect(&self.content_mask());
|
||||||
self.window_mut().next_frame.content_mask_stack.push(mask);
|
self.window_mut().content_mask_stack.push(mask);
|
||||||
let result = f(self);
|
let result = f(self);
|
||||||
self.window_mut().next_frame.content_mask_stack.pop();
|
self.window_mut().content_mask_stack.pop();
|
||||||
result
|
result
|
||||||
} else {
|
} else {
|
||||||
f(self)
|
f(self)
|
||||||
|
@ -746,15 +742,61 @@ impl<'a> ElementContext<'a> {
|
||||||
offset: Point<Pixels>,
|
offset: Point<Pixels>,
|
||||||
f: impl FnOnce(&mut Self) -> R,
|
f: impl FnOnce(&mut Self) -> R,
|
||||||
) -> R {
|
) -> R {
|
||||||
self.window_mut()
|
self.window_mut().element_offset_stack.push(offset);
|
||||||
.next_frame
|
|
||||||
.element_offset_stack
|
|
||||||
.push(offset);
|
|
||||||
let result = f(self);
|
let result = f(self);
|
||||||
self.window_mut().next_frame.element_offset_stack.pop();
|
self.window_mut().element_offset_stack.pop();
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Perform prepaint on child elements in a "retryable" manner, so that any side effects
|
||||||
|
/// of prepaints can be discarded before prepainting again. This is used to support autoscroll
|
||||||
|
/// where we need to prepaint children to detect the autoscroll bounds, then adjust the
|
||||||
|
/// element offset and prepaint again. See [`List`] for an example.
|
||||||
|
pub fn transact<T, U>(&mut self, f: impl FnOnce(&mut Self) -> Result<T, U>) -> Result<T, U> {
|
||||||
|
let index = self.prepaint_index();
|
||||||
|
let result = f(self);
|
||||||
|
if result.is_err() {
|
||||||
|
self.window
|
||||||
|
.next_frame
|
||||||
|
.hitboxes
|
||||||
|
.truncate(index.hitboxes_index);
|
||||||
|
self.window
|
||||||
|
.next_frame
|
||||||
|
.tooltip_requests
|
||||||
|
.truncate(index.tooltips_index);
|
||||||
|
self.window
|
||||||
|
.next_frame
|
||||||
|
.deferred_draws
|
||||||
|
.truncate(index.deferred_draws_index);
|
||||||
|
self.window
|
||||||
|
.next_frame
|
||||||
|
.dispatch_tree
|
||||||
|
.truncate(index.dispatch_tree_index);
|
||||||
|
self.window
|
||||||
|
.next_frame
|
||||||
|
.accessed_element_states
|
||||||
|
.truncate(index.accessed_element_states_index);
|
||||||
|
self.window
|
||||||
|
.text_system
|
||||||
|
.truncate_layouts(index.line_layout_index);
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// When you call this method during [`prepaint`], containing elements will attempt to
|
||||||
|
/// scroll to cause the specified bounds to become visible. When they decide to autoscroll, they will call
|
||||||
|
/// [`prepaint`] again with a new set of bounds. See [`List`] for an example of an element
|
||||||
|
/// that supports this method being called on the elements it contains.
|
||||||
|
pub fn request_autoscroll(&mut self, bounds: Bounds<Pixels>) {
|
||||||
|
self.window.requested_autoscroll = Some(bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This method can be called from a containing element such as [`List`] to support the autoscroll behavior
|
||||||
|
/// described in [`request_autoscroll`].
|
||||||
|
pub fn take_autoscroll(&mut self) -> Option<Bounds<Pixels>> {
|
||||||
|
self.window.requested_autoscroll.take()
|
||||||
|
}
|
||||||
|
|
||||||
/// Remove an asset from GPUI's cache
|
/// Remove an asset from GPUI's cache
|
||||||
pub fn remove_cached_asset<A: Asset + 'static>(
|
pub fn remove_cached_asset<A: Asset + 'static>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -835,7 +877,6 @@ impl<'a> ElementContext<'a> {
|
||||||
/// Obtain the current element offset.
|
/// Obtain the current element offset.
|
||||||
pub fn element_offset(&self) -> Point<Pixels> {
|
pub fn element_offset(&self) -> Point<Pixels> {
|
||||||
self.window()
|
self.window()
|
||||||
.next_frame
|
|
||||||
.element_offset_stack
|
.element_offset_stack
|
||||||
.last()
|
.last()
|
||||||
.copied()
|
.copied()
|
||||||
|
@ -845,7 +886,6 @@ impl<'a> ElementContext<'a> {
|
||||||
/// Obtain the current content mask.
|
/// Obtain the current content mask.
|
||||||
pub fn content_mask(&self) -> ContentMask<Pixels> {
|
pub fn content_mask(&self) -> ContentMask<Pixels> {
|
||||||
self.window()
|
self.window()
|
||||||
.next_frame
|
|
||||||
.content_mask_stack
|
.content_mask_stack
|
||||||
.last()
|
.last()
|
||||||
.cloned()
|
.cloned()
|
||||||
|
@ -974,7 +1014,7 @@ impl<'a> ElementContext<'a> {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
window.draw_phase,
|
window.draw_phase,
|
||||||
DrawPhase::Layout,
|
DrawPhase::Layout,
|
||||||
"defer_draw can only be called during before_layout or after_layout"
|
"defer_draw can only be called during request_layout or prepaint"
|
||||||
);
|
);
|
||||||
let parent_node = window.next_frame.dispatch_tree.active_node_id().unwrap();
|
let parent_node = window.next_frame.dispatch_tree.active_node_id().unwrap();
|
||||||
window.next_frame.deferred_draws.push(DeferredDraw {
|
window.next_frame.deferred_draws.push(DeferredDraw {
|
||||||
|
@ -984,7 +1024,7 @@ impl<'a> ElementContext<'a> {
|
||||||
priority,
|
priority,
|
||||||
element: Some(element),
|
element: Some(element),
|
||||||
absolute_offset,
|
absolute_offset,
|
||||||
layout_range: AfterLayoutIndex::default()..AfterLayoutIndex::default(),
|
prepaint_range: PrepaintStateIndex::default()..PrepaintStateIndex::default(),
|
||||||
paint_range: PaintIndex::default()..PaintIndex::default(),
|
paint_range: PaintIndex::default()..PaintIndex::default(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1349,7 +1389,7 @@ impl<'a> ElementContext<'a> {
|
||||||
.layout_engine
|
.layout_engine
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.before_layout(style, rem_size, &self.cx.app.layout_id_buffer)
|
.request_layout(style, rem_size, &self.cx.app.layout_id_buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a node to the layout tree for the current frame. Instead of taking a `Style` and children,
|
/// Add a node to the layout tree for the current frame. Instead of taking a `Style` and children,
|
||||||
|
@ -1397,7 +1437,7 @@ impl<'a> ElementContext<'a> {
|
||||||
bounds
|
bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This method should be called during `after_layout`. You can use
|
/// This method should be called during `prepaint`. You can use
|
||||||
/// the returned [Hitbox] during `paint` or in an event handler
|
/// the returned [Hitbox] during `paint` or in an event handler
|
||||||
/// to determine whether the inserted hitbox was the topmost.
|
/// to determine whether the inserted hitbox was the topmost.
|
||||||
pub fn insert_hitbox(&mut self, bounds: Bounds<Pixels>, opaque: bool) -> Hitbox {
|
pub fn insert_hitbox(&mut self, bounds: Bounds<Pixels>, opaque: bool) -> Hitbox {
|
||||||
|
|
|
@ -365,7 +365,7 @@ impl Render for SyntaxTreeView {
|
||||||
rendered = rendered.child(
|
rendered = rendered.child(
|
||||||
canvas(
|
canvas(
|
||||||
move |bounds, cx| {
|
move |bounds, cx| {
|
||||||
list.layout(bounds.origin, bounds.size.into(), cx);
|
list.prepaint_as_root(bounds.origin, bounds.size.into(), cx);
|
||||||
list
|
list
|
||||||
},
|
},
|
||||||
|_, mut list, cx| list.paint(cx),
|
|_, mut list, cx| list.paint(cx),
|
||||||
|
|
|
@ -541,12 +541,12 @@ impl TerminalElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for TerminalElement {
|
impl Element for TerminalElement {
|
||||||
type BeforeLayout = ();
|
type RequestLayoutState = ();
|
||||||
type AfterLayout = LayoutState;
|
type PrepaintState = LayoutState;
|
||||||
|
|
||||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
self.interactivity.occlude_mouse();
|
self.interactivity.occlude_mouse();
|
||||||
let layout_id = self.interactivity.before_layout(cx, |mut style, cx| {
|
let layout_id = self.interactivity.request_layout(cx, |mut style, cx| {
|
||||||
style.size.width = relative(1.).into();
|
style.size.width = relative(1.).into();
|
||||||
style.size.height = relative(1.).into();
|
style.size.height = relative(1.).into();
|
||||||
let layout_id = cx.request_layout(&style, None);
|
let layout_id = cx.request_layout(&style, None);
|
||||||
|
@ -556,14 +556,14 @@ impl Element for TerminalElement {
|
||||||
(layout_id, ())
|
(layout_id, ())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_layout(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
_: &mut Self::BeforeLayout,
|
_: &mut Self::RequestLayoutState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) -> Self::AfterLayout {
|
) -> Self::PrepaintState {
|
||||||
self.interactivity
|
self.interactivity
|
||||||
.after_layout(bounds, bounds.size, cx, |_, _, hitbox, cx| {
|
.prepaint(bounds, bounds.size, cx, |_, _, hitbox, cx| {
|
||||||
let hitbox = hitbox.unwrap();
|
let hitbox = hitbox.unwrap();
|
||||||
let settings = ThemeSettings::get_global(cx).clone();
|
let settings = ThemeSettings::get_global(cx).clone();
|
||||||
|
|
||||||
|
@ -669,7 +669,7 @@ impl Element for TerminalElement {
|
||||||
.id("terminal-element")
|
.id("terminal-element")
|
||||||
.tooltip(move |cx| Tooltip::text(hovered_word.word.clone(), cx))
|
.tooltip(move |cx| Tooltip::text(hovered_word.word.clone(), cx))
|
||||||
.into_any_element();
|
.into_any_element();
|
||||||
element.layout(offset, bounds.size.into(), cx);
|
element.prepaint_as_root(offset, bounds.size.into(), cx);
|
||||||
element
|
element
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -775,8 +775,8 @@ impl Element for TerminalElement {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
_: &mut Self::BeforeLayout,
|
_: &mut Self::RequestLayoutState,
|
||||||
layout: &mut Self::AfterLayout,
|
layout: &mut Self::PrepaintState,
|
||||||
cx: &mut ElementContext<'_>,
|
cx: &mut ElementContext<'_>,
|
||||||
) {
|
) {
|
||||||
cx.paint_quad(fill(bounds, layout.background_color));
|
cx.paint_quad(fill(bounds, layout.background_color));
|
||||||
|
|
|
@ -168,10 +168,13 @@ pub struct PopoverMenuFrameState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M: ManagedView> Element for PopoverMenu<M> {
|
impl<M: ManagedView> Element for PopoverMenu<M> {
|
||||||
type BeforeLayout = PopoverMenuFrameState;
|
type RequestLayoutState = PopoverMenuFrameState;
|
||||||
type AfterLayout = Option<HitboxId>;
|
type PrepaintState = Option<HitboxId>;
|
||||||
|
|
||||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (gpui::LayoutId, Self::BeforeLayout) {
|
fn request_layout(
|
||||||
|
&mut self,
|
||||||
|
cx: &mut ElementContext,
|
||||||
|
) -> (gpui::LayoutId, Self::RequestLayoutState) {
|
||||||
self.with_element_state(cx, |this, element_state, cx| {
|
self.with_element_state(cx, |this, element_state, cx| {
|
||||||
let mut menu_layout_id = None;
|
let mut menu_layout_id = None;
|
||||||
|
|
||||||
|
@ -186,7 +189,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
|
||||||
.with_priority(1)
|
.with_priority(1)
|
||||||
.into_any();
|
.into_any();
|
||||||
|
|
||||||
menu_layout_id = Some(element.before_layout(cx));
|
menu_layout_id = Some(element.request_layout(cx));
|
||||||
element
|
element
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -196,7 +199,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
|
||||||
|
|
||||||
let child_layout_id = child_element
|
let child_layout_id = child_element
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.map(|child_element| child_element.before_layout(cx));
|
.map(|child_element| child_element.request_layout(cx));
|
||||||
|
|
||||||
let layout_id = cx.request_layout(
|
let layout_id = cx.request_layout(
|
||||||
&gpui::Style::default(),
|
&gpui::Style::default(),
|
||||||
|
@ -214,22 +217,22 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_layout(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_bounds: Bounds<Pixels>,
|
_bounds: Bounds<Pixels>,
|
||||||
before_layout: &mut Self::BeforeLayout,
|
request_layout: &mut Self::RequestLayoutState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) -> Option<HitboxId> {
|
) -> Option<HitboxId> {
|
||||||
self.with_element_state(cx, |_this, element_state, cx| {
|
self.with_element_state(cx, |_this, element_state, cx| {
|
||||||
if let Some(child) = before_layout.child_element.as_mut() {
|
if let Some(child) = request_layout.child_element.as_mut() {
|
||||||
child.after_layout(cx);
|
child.prepaint(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(menu) = before_layout.menu_element.as_mut() {
|
if let Some(menu) = request_layout.menu_element.as_mut() {
|
||||||
menu.after_layout(cx);
|
menu.prepaint(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
before_layout.child_layout_id.map(|layout_id| {
|
request_layout.child_layout_id.map(|layout_id| {
|
||||||
let bounds = cx.layout_bounds(layout_id);
|
let bounds = cx.layout_bounds(layout_id);
|
||||||
element_state.child_bounds = Some(bounds);
|
element_state.child_bounds = Some(bounds);
|
||||||
cx.insert_hitbox(bounds, false).id
|
cx.insert_hitbox(bounds, false).id
|
||||||
|
@ -240,16 +243,16 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: Bounds<gpui::Pixels>,
|
_: Bounds<gpui::Pixels>,
|
||||||
before_layout: &mut Self::BeforeLayout,
|
request_layout: &mut Self::RequestLayoutState,
|
||||||
child_hitbox: &mut Option<HitboxId>,
|
child_hitbox: &mut Option<HitboxId>,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
self.with_element_state(cx, |_this, _element_state, cx| {
|
self.with_element_state(cx, |_this, _element_state, cx| {
|
||||||
if let Some(mut child) = before_layout.child_element.take() {
|
if let Some(mut child) = request_layout.child_element.take() {
|
||||||
child.paint(cx);
|
child.paint(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(mut menu) = before_layout.menu_element.take() {
|
if let Some(mut menu) = request_layout.menu_element.take() {
|
||||||
menu.paint(cx);
|
menu.paint(cx);
|
||||||
|
|
||||||
if let Some(child_hitbox) = *child_hitbox {
|
if let Some(child_hitbox) = *child_hitbox {
|
||||||
|
|
|
@ -96,10 +96,13 @@ pub struct MenuHandleFrameState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M: ManagedView> Element for RightClickMenu<M> {
|
impl<M: ManagedView> Element for RightClickMenu<M> {
|
||||||
type BeforeLayout = MenuHandleFrameState;
|
type RequestLayoutState = MenuHandleFrameState;
|
||||||
type AfterLayout = Hitbox;
|
type PrepaintState = Hitbox;
|
||||||
|
|
||||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (gpui::LayoutId, Self::BeforeLayout) {
|
fn request_layout(
|
||||||
|
&mut self,
|
||||||
|
cx: &mut ElementContext,
|
||||||
|
) -> (gpui::LayoutId, Self::RequestLayoutState) {
|
||||||
self.with_element_state(cx, |this, element_state, cx| {
|
self.with_element_state(cx, |this, element_state, cx| {
|
||||||
let mut menu_layout_id = None;
|
let mut menu_layout_id = None;
|
||||||
|
|
||||||
|
@ -114,7 +117,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
|
||||||
.with_priority(1)
|
.with_priority(1)
|
||||||
.into_any();
|
.into_any();
|
||||||
|
|
||||||
menu_layout_id = Some(element.before_layout(cx));
|
menu_layout_id = Some(element.request_layout(cx));
|
||||||
element
|
element
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -125,7 +128,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
|
||||||
|
|
||||||
let child_layout_id = child_element
|
let child_layout_id = child_element
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.map(|child_element| child_element.before_layout(cx));
|
.map(|child_element| child_element.request_layout(cx));
|
||||||
|
|
||||||
let layout_id = cx.request_layout(
|
let layout_id = cx.request_layout(
|
||||||
&gpui::Style::default(),
|
&gpui::Style::default(),
|
||||||
|
@ -143,21 +146,21 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_layout(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
before_layout: &mut Self::BeforeLayout,
|
request_layout: &mut Self::RequestLayoutState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) -> Hitbox {
|
) -> Hitbox {
|
||||||
cx.with_element_id(Some(self.id.clone()), |cx| {
|
cx.with_element_id(Some(self.id.clone()), |cx| {
|
||||||
let hitbox = cx.insert_hitbox(bounds, false);
|
let hitbox = cx.insert_hitbox(bounds, false);
|
||||||
|
|
||||||
if let Some(child) = before_layout.child_element.as_mut() {
|
if let Some(child) = request_layout.child_element.as_mut() {
|
||||||
child.after_layout(cx);
|
child.prepaint(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(menu) = before_layout.menu_element.as_mut() {
|
if let Some(menu) = request_layout.menu_element.as_mut() {
|
||||||
menu.after_layout(cx);
|
menu.prepaint(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
hitbox
|
hitbox
|
||||||
|
@ -167,16 +170,16 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_bounds: Bounds<gpui::Pixels>,
|
_bounds: Bounds<gpui::Pixels>,
|
||||||
before_layout: &mut Self::BeforeLayout,
|
request_layout: &mut Self::RequestLayoutState,
|
||||||
hitbox: &mut Self::AfterLayout,
|
hitbox: &mut Self::PrepaintState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
self.with_element_state(cx, |this, element_state, cx| {
|
self.with_element_state(cx, |this, element_state, cx| {
|
||||||
if let Some(mut child) = before_layout.child_element.take() {
|
if let Some(mut child) = request_layout.child_element.take() {
|
||||||
child.paint(cx);
|
child.paint(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(mut menu) = before_layout.menu_element.take() {
|
if let Some(mut menu) = request_layout.menu_element.take() {
|
||||||
menu.paint(cx);
|
menu.paint(cx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -188,7 +191,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
|
||||||
let attach = this.attach;
|
let attach = this.attach;
|
||||||
let menu = element_state.menu.clone();
|
let menu = element_state.menu.clone();
|
||||||
let position = element_state.position.clone();
|
let position = element_state.position.clone();
|
||||||
let child_layout_id = before_layout.child_layout_id;
|
let child_layout_id = request_layout.child_layout_id;
|
||||||
let child_bounds = cx.layout_bounds(child_layout_id.unwrap());
|
let child_bounds = cx.layout_bounds(child_layout_id.unwrap());
|
||||||
|
|
||||||
let hitbox_id = hitbox.id;
|
let hitbox_id = hitbox.id;
|
||||||
|
|
|
@ -792,13 +792,13 @@ mod element {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for PaneAxisElement {
|
impl Element for PaneAxisElement {
|
||||||
type BeforeLayout = ();
|
type RequestLayoutState = ();
|
||||||
type AfterLayout = PaneAxisLayout;
|
type PrepaintState = PaneAxisLayout;
|
||||||
|
|
||||||
fn before_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
cx: &mut ui::prelude::ElementContext,
|
cx: &mut ui::prelude::ElementContext,
|
||||||
) -> (gpui::LayoutId, Self::BeforeLayout) {
|
) -> (gpui::LayoutId, Self::RequestLayoutState) {
|
||||||
let mut style = Style::default();
|
let mut style = Style::default();
|
||||||
style.flex_grow = 1.;
|
style.flex_grow = 1.;
|
||||||
style.flex_shrink = 1.;
|
style.flex_shrink = 1.;
|
||||||
|
@ -808,10 +808,10 @@ mod element {
|
||||||
(cx.request_layout(&style, None), ())
|
(cx.request_layout(&style, None), ())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_layout(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
_state: &mut Self::BeforeLayout,
|
_state: &mut Self::RequestLayoutState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) -> PaneAxisLayout {
|
) -> PaneAxisLayout {
|
||||||
let dragged_handle = cx.with_element_state::<Rc<RefCell<Option<usize>>>, _>(
|
let dragged_handle = cx.with_element_state::<Rc<RefCell<Option<usize>>>, _>(
|
||||||
|
@ -872,7 +872,8 @@ mod element {
|
||||||
size: child_size,
|
size: child_size,
|
||||||
};
|
};
|
||||||
bounding_boxes.push(Some(child_bounds));
|
bounding_boxes.push(Some(child_bounds));
|
||||||
child.layout(origin, child_size.into(), cx);
|
child.layout_as_root(child_size.into(), cx);
|
||||||
|
child.prepaint_at(origin, cx);
|
||||||
|
|
||||||
origin = origin.apply_along(self.axis, |val| val + child_size.along(self.axis));
|
origin = origin.apply_along(self.axis, |val| val + child_size.along(self.axis));
|
||||||
layout.children.push(PaneAxisChildLayout {
|
layout.children.push(PaneAxisChildLayout {
|
||||||
|
@ -897,8 +898,8 @@ mod element {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: gpui::Bounds<ui::prelude::Pixels>,
|
bounds: gpui::Bounds<ui::prelude::Pixels>,
|
||||||
_: &mut Self::BeforeLayout,
|
_: &mut Self::RequestLayoutState,
|
||||||
layout: &mut Self::AfterLayout,
|
layout: &mut Self::PrepaintState,
|
||||||
cx: &mut ui::prelude::ElementContext,
|
cx: &mut ui::prelude::ElementContext,
|
||||||
) {
|
) {
|
||||||
for child in &mut layout.children {
|
for child in &mut layout.children {
|
||||||
|
|
|
@ -4971,10 +4971,10 @@ fn parse_pixel_size_env_var(value: &str) -> Option<Size<DevicePixels>> {
|
||||||
struct DisconnectedOverlay;
|
struct DisconnectedOverlay;
|
||||||
|
|
||||||
impl Element for DisconnectedOverlay {
|
impl Element for DisconnectedOverlay {
|
||||||
type BeforeLayout = AnyElement;
|
type RequestLayoutState = AnyElement;
|
||||||
type AfterLayout = ();
|
type PrepaintState = ();
|
||||||
|
|
||||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
let mut background = cx.theme().colors().elevated_surface_background;
|
let mut background = cx.theme().colors().elevated_surface_background;
|
||||||
background.fade_out(0.2);
|
background.fade_out(0.2);
|
||||||
let mut overlay = div()
|
let mut overlay = div()
|
||||||
|
@ -4992,24 +4992,24 @@ impl Element for DisconnectedOverlay {
|
||||||
"Your connection to the remote project has been lost.",
|
"Your connection to the remote project has been lost.",
|
||||||
))
|
))
|
||||||
.into_any();
|
.into_any();
|
||||||
(overlay.before_layout(cx), overlay)
|
(overlay.request_layout(cx), overlay)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_layout(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
overlay: &mut Self::BeforeLayout,
|
overlay: &mut Self::RequestLayoutState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
cx.insert_hitbox(bounds, true);
|
cx.insert_hitbox(bounds, true);
|
||||||
overlay.after_layout(cx);
|
overlay.prepaint(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: Bounds<Pixels>,
|
_: Bounds<Pixels>,
|
||||||
overlay: &mut Self::BeforeLayout,
|
overlay: &mut Self::RequestLayoutState,
|
||||||
_: &mut Self::AfterLayout,
|
_: &mut Self::PrepaintState,
|
||||||
cx: &mut ElementContext,
|
cx: &mut ElementContext,
|
||||||
) {
|
) {
|
||||||
overlay.paint(cx)
|
overlay.paint(cx)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue