Split paint/layout to not layout a second time when doing actual paint
Co-Authored-By: Max Brunsfeld <max@zed.dev>
This commit is contained in:
@@ -1279,7 +1279,7 @@ impl Render for AssistantPanel {
|
||||
let conversation_count = self.saved_conversations.len();
|
||||
canvas(move |bounds, cx| {
|
||||
uniform_list(
|
||||
view,
|
||||
view.clone(),
|
||||
"saved_conversations",
|
||||
conversation_count,
|
||||
|this, range, cx| {
|
||||
@@ -1288,7 +1288,7 @@ impl Render for AssistantPanel {
|
||||
.collect()
|
||||
},
|
||||
)
|
||||
.track_scroll(scroll_handle)
|
||||
.track_scroll(scroll_handle.clone())
|
||||
.into_any_element()
|
||||
.draw(
|
||||
bounds.origin,
|
||||
|
||||
@@ -414,13 +414,13 @@ impl Render for ExtensionsPage {
|
||||
let item_count = self.extensions_entries.len();
|
||||
move |bounds, cx| {
|
||||
uniform_list::<_, Div, _>(
|
||||
view,
|
||||
view.clone(),
|
||||
"entries",
|
||||
item_count,
|
||||
Self::render_extensions,
|
||||
)
|
||||
.size_full()
|
||||
.track_scroll(scroll_handle)
|
||||
.track_scroll(scroll_handle.clone())
|
||||
.into_any_element()
|
||||
.draw(
|
||||
bounds.origin,
|
||||
|
||||
@@ -103,13 +103,13 @@ pub trait IntoElement: Sized {
|
||||
{
|
||||
let element = self.into_element();
|
||||
let element_id = element.element_id();
|
||||
let element = DrawableElement {
|
||||
let mut element = DrawableElement {
|
||||
element: Some(element),
|
||||
phase: ElementDrawPhase::Start,
|
||||
};
|
||||
|
||||
let frame_state =
|
||||
DrawableElement::draw(element, origin, available_space.map(Into::into), cx);
|
||||
DrawableElement::draw(&mut element, origin, available_space.map(Into::into), cx);
|
||||
|
||||
if let Some(mut frame_state) = frame_state {
|
||||
f(&mut frame_state, cx)
|
||||
@@ -301,8 +301,8 @@ impl<E: Element> DrawableElement<E> {
|
||||
layout_id
|
||||
}
|
||||
|
||||
fn paint(mut self, cx: &mut ElementContext) -> Option<E::State> {
|
||||
match self.phase {
|
||||
fn paint(&mut self, cx: &mut ElementContext) -> Option<&mut E::State> {
|
||||
match &mut self.phase {
|
||||
ElementDrawPhase::LayoutRequested {
|
||||
layout_id,
|
||||
frame_state,
|
||||
@@ -312,13 +312,13 @@ impl<E: Element> DrawableElement<E> {
|
||||
frame_state,
|
||||
..
|
||||
} => {
|
||||
let bounds = cx.layout_bounds(layout_id);
|
||||
let bounds = cx.layout_bounds(*layout_id);
|
||||
|
||||
if let Some(mut frame_state) = frame_state {
|
||||
if let Some(frame_state) = frame_state {
|
||||
self.element
|
||||
.take()
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.paint(bounds, &mut frame_state, cx);
|
||||
.paint(bounds, frame_state, cx);
|
||||
Some(frame_state)
|
||||
} else {
|
||||
let element_id = self
|
||||
@@ -330,7 +330,7 @@ impl<E: Element> DrawableElement<E> {
|
||||
cx.with_element_state(element_id, |element_state, cx| {
|
||||
let mut element_state = element_state.unwrap();
|
||||
self.element
|
||||
.take()
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.paint(bounds, &mut element_state, cx);
|
||||
((), element_state)
|
||||
@@ -384,11 +384,11 @@ impl<E: Element> DrawableElement<E> {
|
||||
}
|
||||
|
||||
fn draw(
|
||||
mut self,
|
||||
&mut self,
|
||||
origin: Point<Pixels>,
|
||||
available_space: Size<AvailableSpace>,
|
||||
cx: &mut ElementContext,
|
||||
) -> Option<E::State> {
|
||||
) -> Option<&mut E::State> {
|
||||
self.measure(available_space, cx);
|
||||
cx.with_absolute_element_offset(origin, |cx| self.paint(cx))
|
||||
}
|
||||
@@ -408,7 +408,7 @@ where
|
||||
}
|
||||
|
||||
fn paint(&mut self, cx: &mut ElementContext) {
|
||||
DrawableElement::paint(self.take().unwrap(), cx);
|
||||
DrawableElement::paint(self.as_mut().unwrap(), cx);
|
||||
}
|
||||
|
||||
fn measure(
|
||||
@@ -425,7 +425,7 @@ where
|
||||
available_space: Size<AvailableSpace>,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
DrawableElement::draw(self.take().unwrap(), origin, available_space, cx);
|
||||
DrawableElement::draw(self.as_mut().unwrap(), origin, available_space, cx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::{Bounds, Element, ElementContext, IntoElement, Pixels, Style, StyleRe
|
||||
|
||||
/// Construct a canvas element with the given paint callback.
|
||||
/// Useful for adding short term custom drawing to a view.
|
||||
pub fn canvas(callback: impl 'static + FnOnce(&Bounds<Pixels>, &mut ElementContext)) -> Canvas {
|
||||
pub fn canvas(callback: impl 'static + FnMut(&Bounds<Pixels>, &mut ElementContext)) -> Canvas {
|
||||
Canvas {
|
||||
paint_callback: Some(Box::new(callback)),
|
||||
style: StyleRefinement::default(),
|
||||
@@ -14,7 +14,7 @@ pub fn canvas(callback: impl 'static + FnOnce(&Bounds<Pixels>, &mut ElementConte
|
||||
/// A canvas element, meant for accessing the low level paint API without defining a whole
|
||||
/// custom element
|
||||
pub struct Canvas {
|
||||
paint_callback: Option<Box<dyn FnOnce(&Bounds<Pixels>, &mut ElementContext)>>,
|
||||
paint_callback: Option<Box<dyn FnMut(&Bounds<Pixels>, &mut ElementContext)>>,
|
||||
style: StyleRefinement,
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ impl Element for Canvas {
|
||||
|
||||
fn paint(&mut self, bounds: Bounds<Pixels>, style: &mut Style, cx: &mut ElementContext) {
|
||||
style.paint(bounds, cx, |cx| {
|
||||
(self.paint_callback.take().unwrap())(&bounds, cx)
|
||||
(self.paint_callback.as_mut().unwrap())(&bounds, cx)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ impl<V: Render> Element for View<V> {
|
||||
}
|
||||
|
||||
fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut ElementContext) {
|
||||
cx.paint_view(self.entity_id(), |cx| element.take().unwrap().paint(cx));
|
||||
cx.paint_view(self.entity_id(), |cx| element.as_mut().unwrap().paint(cx));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,22 +247,67 @@ impl AnyView {
|
||||
self.model.entity_id()
|
||||
}
|
||||
|
||||
pub(crate) fn draw(
|
||||
pub(crate) fn layout(
|
||||
&self,
|
||||
origin: Point<Pixels>,
|
||||
available_space: Size<AvailableSpace>,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
cx.paint_view(self.entity_id(), |cx| {
|
||||
) -> AnyElement {
|
||||
cx.in_layout(true);
|
||||
let element = cx.paint_view(self.entity_id(), |cx| {
|
||||
cx.with_absolute_element_offset(origin, |cx| {
|
||||
let (layout_id, mut rendered_element) = (self.request_layout)(self, cx);
|
||||
cx.compute_layout(layout_id, available_space);
|
||||
rendered_element.paint(cx)
|
||||
rendered_element
|
||||
})
|
||||
});
|
||||
cx.in_layout(false);
|
||||
element
|
||||
}
|
||||
|
||||
pub(crate) fn paint(
|
||||
&self,
|
||||
origin: Point<Pixels>,
|
||||
rendered_element: &mut AnyElement,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
cx.paint_view(self.entity_id(), |cx| {
|
||||
cx.with_absolute_element_offset(origin, |cx| {
|
||||
rendered_element.paint(cx);
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// pub(crate) fn draw(
|
||||
// &self,
|
||||
// origin: Point<Pixels>,
|
||||
// available_space: Size<AvailableSpace>,
|
||||
// cx: &mut ElementContext,
|
||||
// ) {
|
||||
// cx.paint_view(self.entity_id(), |cx| {
|
||||
// cx.with_absolute_element_offset(origin, |cx| {
|
||||
// cx.in_layout(true);
|
||||
// let (layout_id, mut rendered_element) = (self.request_layout)(self, cx);
|
||||
// assert_eq!(
|
||||
// *cx.window.next_frame.next_stacking_order_ids.last().unwrap(),
|
||||
// 0
|
||||
// );
|
||||
// // let prev_last_stacking_order_id = cx.window.next_frame.next_stacking_order_ids.last();
|
||||
// cx.compute_layout(layout_id, available_space);
|
||||
// cx.in_layout(false);
|
||||
// // assert_eq!(cx.window.next_frame.z_index_stack.len(), 0);
|
||||
// *cx.window
|
||||
// .next_frame
|
||||
// .next_stacking_order_ids
|
||||
// .last_mut()
|
||||
// .unwrap() = 0;
|
||||
// rendered_element.paint(cx)
|
||||
// });
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<V: Render> From<View<V>> for AnyView {
|
||||
fn from(value: View<V>) -> Self {
|
||||
AnyView {
|
||||
@@ -307,7 +352,7 @@ impl Element for AnyView {
|
||||
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext) {
|
||||
cx.paint_view(self.entity_id(), |cx| {
|
||||
if !self.cache {
|
||||
state.element.take().unwrap().paint(cx);
|
||||
state.element.as_mut().unwrap().paint(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -960,55 +960,51 @@ impl<'a> WindowContext<'a> {
|
||||
requested_handler.handler = input_handler;
|
||||
}
|
||||
|
||||
// TODO: Restructure this
|
||||
let root_view = self.window.root_view.take().unwrap();
|
||||
self.with_element_context(true, |cx| {
|
||||
let mut root_element = self.with_element_context(true, |cx| {
|
||||
cx.with_z_index(0, |cx| {
|
||||
cx.with_key_dispatch(Some(KeyContext::default()), None, |_, cx| {
|
||||
// // We need to use cx.cx here so we can utilize borrow splitting
|
||||
// for (action_type, action_listeners) in &cx.cx.app.global_action_listeners {
|
||||
// for action_listener in action_listeners.iter().cloned() {
|
||||
// cx.cx.window.next_frame.dispatch_tree.on_action(
|
||||
// *action_type,
|
||||
// Rc::new(
|
||||
// move |action: &dyn Any, phase, cx: &mut WindowContext<'_>| {
|
||||
// action_listener(action, phase, cx)
|
||||
// },
|
||||
// ),
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
|
||||
let available_space = cx.window.viewport_size.map(Into::into);
|
||||
root_view.draw(Point::default(), available_space, cx);
|
||||
let mut root_element = root_view.layout(Point::default(), available_space, cx);
|
||||
root_view.paint(Point::default(), &mut root_element, cx);
|
||||
root_element
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
if let Some(active_drag) = self.app.active_drag.take() {
|
||||
self.with_element_context(true, |cx| {
|
||||
cx.with_z_index(ACTIVE_DRAG_Z_INDEX, |cx| {
|
||||
let mut drag_element = None;
|
||||
let mut tooltip_element = None;
|
||||
|
||||
self.with_element_context(true, |cx| {
|
||||
cx.with_z_index(1, |cx| {
|
||||
if let Some(active_drag) = cx.app.active_drag.take() {
|
||||
let offset = cx.mouse_position() - active_drag.cursor_offset;
|
||||
let available_space =
|
||||
size(AvailableSpace::MinContent, AvailableSpace::MinContent);
|
||||
active_drag.view.draw(offset, available_space, cx);
|
||||
})
|
||||
});
|
||||
self.active_drag = Some(active_drag);
|
||||
} else if let Some(tooltip_request) = self.window.next_frame.tooltip_request.take() {
|
||||
self.with_element_context(true, |cx| {
|
||||
cx.with_z_index(1, |cx| {
|
||||
let mut element = active_drag.view.layout(offset, available_space, cx);
|
||||
active_drag.view.paint(offset, &mut element, cx);
|
||||
drag_element = Some(element);
|
||||
|
||||
cx.active_drag = Some(active_drag);
|
||||
} else if let Some(tooltip_request) = cx.window.next_frame.tooltip_request.take() {
|
||||
let available_space =
|
||||
size(AvailableSpace::MinContent, AvailableSpace::MinContent);
|
||||
tooltip_request.tooltip.view.draw(
|
||||
let mut element = tooltip_request.tooltip.view.layout(
|
||||
tooltip_request.tooltip.cursor_offset,
|
||||
available_space,
|
||||
cx,
|
||||
);
|
||||
})
|
||||
});
|
||||
self.window.next_frame.tooltip_request = Some(tooltip_request);
|
||||
}
|
||||
tooltip_request.tooltip.view.paint(
|
||||
tooltip_request.tooltip.cursor_offset,
|
||||
&mut element,
|
||||
cx,
|
||||
);
|
||||
tooltip_element = Some(element);
|
||||
|
||||
cx.window.next_frame.tooltip_request = Some(tooltip_request);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
assert_eq!(self.window.next_frame.z_index_stack.len(), 0);
|
||||
self.window.next_frame.next_stacking_order_ids = vec![0];
|
||||
@@ -1030,36 +1026,33 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
let available_space = cx.window.viewport_size.map(Into::into);
|
||||
root_view.draw(Point::default(), available_space, cx);
|
||||
// let available_space = cx.window.viewport_size.map(Into::into);
|
||||
// let mut root_element = root_view.layout(Point::default(), available_space, cx);
|
||||
root_view.paint(Point::default(), &mut root_element, cx);
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
if let Some(active_drag) = self.app.active_drag.take() {
|
||||
self.with_element_context(false, |cx| {
|
||||
cx.with_z_index(ACTIVE_DRAG_Z_INDEX, |cx| {
|
||||
let offset = cx.mouse_position() - active_drag.cursor_offset;
|
||||
let available_space =
|
||||
size(AvailableSpace::MinContent, AvailableSpace::MinContent);
|
||||
active_drag.view.draw(offset, available_space, cx);
|
||||
})
|
||||
});
|
||||
self.active_drag = Some(active_drag);
|
||||
} else if let Some(tooltip_request) = self.window.next_frame.tooltip_request.take() {
|
||||
self.with_element_context(false, |cx| {
|
||||
cx.with_z_index(1, |cx| {
|
||||
let available_space =
|
||||
size(AvailableSpace::MinContent, AvailableSpace::MinContent);
|
||||
tooltip_request.tooltip.view.draw(
|
||||
tooltip_request.tooltip.cursor_offset,
|
||||
available_space,
|
||||
cx,
|
||||
);
|
||||
})
|
||||
});
|
||||
self.window.next_frame.tooltip_request = Some(tooltip_request);
|
||||
}
|
||||
self.with_element_context(false, |cx| {
|
||||
cx.with_z_index(1, |cx| {
|
||||
if let Some(mut drag_element) = drag_element {
|
||||
if let Some(active_drag) = cx.app.active_drag.take() {
|
||||
let offset = cx.mouse_position() - active_drag.cursor_offset;
|
||||
active_drag.view.paint(offset, &mut drag_element, cx);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(mut tooltip_element) = tooltip_element {
|
||||
if let Some(tooltip_request) = cx.window.next_frame.tooltip_request.take() {
|
||||
tooltip_request.tooltip.view.paint(
|
||||
tooltip_request.tooltip.cursor_offset,
|
||||
&mut tooltip_element,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
self.window.dirty_views.clear();
|
||||
|
||||
|
||||
@@ -168,6 +168,7 @@ pub struct ElementContext<'a> {
|
||||
#[deref_mut]
|
||||
pub(crate) cx: WindowContext<'a>,
|
||||
pub(crate) pre_paint_pass: bool,
|
||||
pub(crate) in_layout: bool,
|
||||
}
|
||||
|
||||
impl<'a> WindowContext<'a> {
|
||||
@@ -182,6 +183,7 @@ impl<'a> WindowContext<'a> {
|
||||
f(&mut ElementContext {
|
||||
cx: WindowContext::new(self.app, self.window),
|
||||
pre_paint_pass,
|
||||
in_layout: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -318,6 +320,10 @@ impl<'a> VisualContext for ElementContext<'a> {
|
||||
}
|
||||
|
||||
impl<'a> ElementContext<'a> {
|
||||
pub(crate) fn in_layout(&mut self, in_layout: bool) {
|
||||
self.in_layout = in_layout;
|
||||
}
|
||||
|
||||
pub(crate) fn reuse_view(&mut self, next_stacking_order_id: u16) {
|
||||
let view_id = self.parent_view_id();
|
||||
let grafted_view_ids = self
|
||||
@@ -483,6 +489,10 @@ impl<'a> ElementContext<'a> {
|
||||
/// Called during painting to invoke the given closure in a new stacking context. The given
|
||||
/// z-index is interpreted relative to the previous call to `stack`.
|
||||
pub fn with_z_index<R>(&mut self, z_index: u16, f: impl FnOnce(&mut Self) -> R) -> R {
|
||||
// if self.in_layout {
|
||||
// panic!("Don't push z-index in layout");
|
||||
// }
|
||||
|
||||
let new_stacking_order_id = post_inc(
|
||||
self.window_mut()
|
||||
.next_frame
|
||||
@@ -1142,6 +1152,10 @@ impl<'a> ElementContext<'a> {
|
||||
return;
|
||||
}
|
||||
|
||||
// if self.in_layout {
|
||||
// panic!("Don't add an opaque layer during layout");
|
||||
// }
|
||||
|
||||
let stacking_order = self.window.next_frame.z_index_stack.clone();
|
||||
let view_id = self.parent_view_id();
|
||||
let depth_map = &mut self.window.next_frame.depth_map;
|
||||
|
||||
@@ -357,10 +357,12 @@ impl Render for SyntaxTreeView {
|
||||
.size_full()
|
||||
.track_scroll(self.list_scroll_handle.clone())
|
||||
.text_bg(cx.theme().colors().background);
|
||||
|
||||
let mut list_element = list.into_any_element();
|
||||
|
||||
rendered = rendered.child(
|
||||
canvas(move |bounds, cx| {
|
||||
list.into_any_element().draw(
|
||||
list_element.draw(
|
||||
bounds.origin,
|
||||
bounds.size.map(AvailableSpace::Definite),
|
||||
cx,
|
||||
|
||||
Reference in New Issue
Block a user