Compare commits

...

1 Commits

Author SHA1 Message Date
Nathan Sobo
2e72b162a4 WIP: Start introducing dry vs wet paint concept 2024-02-09 16:45:43 -07:00
12 changed files with 147 additions and 53 deletions

View File

@@ -60,8 +60,19 @@ pub trait Element: 'static + IntoElement {
) -> (LayoutId, Self::State);
/// Once layout has been completed, this method will be called to paint the element to the screen.
/// If `wet` is false, this is a dry run, which is used to determine which geometry will be closest
/// to the user in the painted scene for purposes of hover state calculations. Avoid unnecessary
/// computation in the dry phase, but be sure to push opaque layers at a Z-index that matches what
/// you'll draw in the final scene.
///
/// The state argument is the same state that was returned from [`Element::request_layout()`].
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext);
fn paint(
&mut self,
bounds: Bounds<Pixels>,
wet: bool,
state: &mut Self::State,
cx: &mut ElementContext,
);
/// Convert this element into a dynamically-typed [`AnyElement`].
fn into_any(self) -> AnyElement {
@@ -108,10 +119,9 @@ pub trait IntoElement: Sized {
phase: ElementDrawPhase::Start,
};
let frame_state =
DrawableElement::draw(element, origin, available_space.map(Into::into), cx);
element.draw(origin, available_space.map(Into::into), cx);
if let Some(mut frame_state) = frame_state {
if let Some(mut frame_state) = element.into_frame_state() {
f(&mut frame_state, cx)
} else {
cx.with_element_state(element_id.unwrap(), |element_state, cx| {
@@ -203,8 +213,14 @@ impl<C: RenderOnce> Element for Component<C> {
(layout_id, element)
}
fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut ElementContext) {
element.paint(cx)
fn paint(
&mut self,
_: Bounds<Pixels>,
wet: bool,
element: &mut Self::State,
cx: &mut ElementContext,
) {
element.paint(wet, cx)
}
}
@@ -229,7 +245,7 @@ trait ElementObject {
fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId;
fn paint(&mut self, cx: &mut ElementContext);
fn paint(&mut self, wet: bool, cx: &mut ElementContext);
fn measure(
&mut self,
@@ -301,7 +317,7 @@ impl<E: Element> DrawableElement<E> {
layout_id
}
fn paint(mut self, cx: &mut ElementContext) -> Option<E::State> {
fn paint(&mut self, wet: bool, cx: &mut ElementContext) {
match self.phase {
ElementDrawPhase::LayoutRequested {
layout_id,
@@ -315,11 +331,8 @@ impl<E: Element> DrawableElement<E> {
let bounds = cx.layout_bounds(layout_id);
if let Some(mut frame_state) = frame_state {
self.element
.take()
.unwrap()
.paint(bounds, &mut frame_state, cx);
Some(frame_state)
let mut element = self.element.as_mut().unwrap();
element.paint(bounds, wet, &mut frame_state, cx);
} else {
let element_id = self
.element
@@ -329,13 +342,10 @@ impl<E: Element> DrawableElement<E> {
.expect("if we don't have frame state, we should have element state");
cx.with_element_state(element_id, |element_state, cx| {
let mut element_state = element_state.unwrap();
self.element
.take()
.unwrap()
.paint(bounds, &mut element_state, cx);
let mut element = self.element.as_mut().unwrap();
element.paint(bounds, wet, &mut element_state, cx);
((), element_state)
});
None
}
}
@@ -343,6 +353,14 @@ impl<E: Element> DrawableElement<E> {
}
}
fn into_frame_state(self) -> Option<E::State> {
match self.phase {
ElementDrawPhase::Start => None,
ElementDrawPhase::LayoutRequested { frame_state, .. }
| ElementDrawPhase::LayoutComputed { frame_state, .. } => frame_state,
}
}
fn measure(
&mut self,
available_space: Size<AvailableSpace>,
@@ -384,13 +402,16 @@ impl<E: Element> DrawableElement<E> {
}
fn draw(
mut self,
&mut self,
origin: Point<Pixels>,
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
) -> Option<E::State> {
) {
self.measure(available_space, cx);
cx.with_absolute_element_offset(origin, |cx| self.paint(cx))
cx.with_absolute_element_offset(origin, |cx| {
self.paint(false, cx);
self.paint(true, cx);
})
}
}
@@ -407,8 +428,8 @@ where
DrawableElement::request_layout(self.as_mut().unwrap(), cx)
}
fn paint(&mut self, cx: &mut ElementContext) {
DrawableElement::paint(self.take().unwrap(), cx);
fn paint(&mut self, wet: bool, cx: &mut ElementContext) {
DrawableElement::paint(self.as_mut().unwrap(), wet, cx);
}
fn measure(
@@ -425,7 +446,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);
}
}
@@ -451,8 +472,8 @@ impl AnyElement {
}
/// Paints the element stored in this `AnyElement`.
pub fn paint(&mut self, cx: &mut ElementContext) {
self.0.paint(cx)
pub fn paint(&mut self, wet: bool, cx: &mut ElementContext) {
self.0.paint(wet, cx)
}
/// Initializes this element and performs layout within the given available space to determine its size.
@@ -492,8 +513,14 @@ impl Element for AnyElement {
(layout_id, ())
}
fn paint(&mut self, _: Bounds<Pixels>, _: &mut Self::State, cx: &mut ElementContext) {
self.paint(cx)
fn paint(
&mut self,
_: Bounds<Pixels>,
wet: bool,
_: &mut Self::State,
cx: &mut ElementContext,
) {
self.paint(wet, cx)
}
}
@@ -542,6 +569,7 @@ impl Element for () {
fn paint(
&mut self,
_bounds: Bounds<Pixels>,
_wet: bool,
_state: &mut Self::State,
_cx: &mut ElementContext,
) {

View File

@@ -4,7 +4,9 @@ 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 + FnOnce(&Bounds<Pixels>, bool, &mut ElementContext),
) -> Canvas {
Canvas {
paint_callback: Some(Box::new(callback)),
style: StyleRefinement::default(),
@@ -14,7 +16,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 FnOnce(&Bounds<Pixels>, bool, &mut ElementContext)>>,
style: StyleRefinement,
}
@@ -44,9 +46,15 @@ impl Element for Canvas {
(layout_id, style)
}
fn paint(&mut self, bounds: Bounds<Pixels>, style: &mut Style, cx: &mut ElementContext) {
fn paint(
&mut self,
bounds: Bounds<Pixels>,
wet: bool,
style: &mut Style,
cx: &mut ElementContext,
) {
style.paint(bounds, cx, |cx| {
(self.paint_callback.take().unwrap())(&bounds, cx)
(self.paint_callback.take().unwrap())(&bounds, wet, cx)
});
}
}

View File

@@ -1084,6 +1084,7 @@ impl Element for Div {
fn paint(
&mut self,
bounds: Bounds<Pixels>,
wet: bool,
element_state: &mut Self::State,
cx: &mut ElementContext,
) {
@@ -1128,7 +1129,7 @@ impl Element for Div {
|_style, scroll_offset, cx| {
cx.with_element_offset(scroll_offset, |cx| {
for child in &mut self.children {
child.paint(cx);
child.paint(wet, cx);
}
})
},
@@ -2136,8 +2137,14 @@ where
self.element.request_layout(state, cx)
}
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext) {
self.element.paint(bounds, state, cx)
fn paint(
&mut self,
bounds: Bounds<Pixels>,
wet: bool,
state: &mut Self::State,
cx: &mut ElementContext,
) {
self.element.paint(bounds, wet, state, cx)
}
}
@@ -2211,8 +2218,14 @@ where
self.element.request_layout(state, cx)
}
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext) {
self.element.paint(bounds, state, cx)
fn paint(
&mut self,
bounds: Bounds<Pixels>,
wet: bool,
state: &mut Self::State,
cx: &mut ElementContext,
) {
self.element.paint(bounds, wet, state, cx)
}
}

View File

@@ -102,6 +102,7 @@ impl Element for Img {
fn paint(
&mut self,
bounds: Bounds<Pixels>,
wet: bool,
element_state: &mut Self::State,
cx: &mut ElementContext,
) {

View File

@@ -372,6 +372,7 @@ impl Element for List {
fn paint(
&mut self,
bounds: Bounds<crate::Pixels>,
wet: bool,
_state: &mut Self::State,
cx: &mut crate::ElementContext,
) {

View File

@@ -96,6 +96,7 @@ impl Element for Overlay {
fn paint(
&mut self,
bounds: crate::Bounds<crate::Pixels>,
wet: bool,
element_state: &mut Self::State,
cx: &mut ElementContext,
) {
@@ -170,7 +171,7 @@ impl Element for Overlay {
cx.with_absolute_element_offset(offset, |cx| {
cx.break_content_mask(|cx| {
for child in &mut self.children {
child.paint(cx);
child.paint(wet, cx);
}
})
})

View File

@@ -42,6 +42,7 @@ impl Element for Svg {
fn paint(
&mut self,
bounds: Bounds<Pixels>,
wet: bool,
element_state: &mut Self::State,
cx: &mut ElementContext,
) where

View File

@@ -29,7 +29,13 @@ impl Element for &'static str {
(layout_id, state)
}
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut ElementContext) {
fn paint(
&mut self,
bounds: Bounds<Pixels>,
wet: bool,
state: &mut TextState,
cx: &mut ElementContext,
) {
state.paint(bounds, self, cx)
}
}
@@ -71,7 +77,13 @@ impl Element for SharedString {
(layout_id, state)
}
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut ElementContext) {
fn paint(
&mut self,
bounds: Bounds<Pixels>,
wet: bool,
state: &mut TextState,
cx: &mut ElementContext,
) {
let text_str: &str = self.as_ref();
state.paint(bounds, text_str, cx)
}
@@ -150,7 +162,13 @@ impl Element for StyledText {
(layout_id, state)
}
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext) {
fn paint(
&mut self,
bounds: Bounds<Pixels>,
wet: bool,
state: &mut Self::State,
cx: &mut ElementContext,
) {
state.paint(bounds, &self.text, cx)
}
}
@@ -419,7 +437,13 @@ impl Element for InteractiveText {
}
}
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext) {
fn paint(
&mut self,
bounds: Bounds<Pixels>,
wet: bool,
state: &mut Self::State,
cx: &mut ElementContext,
) {
if let Some(click_listener) = self.click_listener.take() {
let mouse_position = cx.mouse_position();
if let Some(ix) = state.text_state.index_for_position(bounds, mouse_position) {
@@ -547,7 +571,7 @@ impl Element for InteractiveText {
}
}
self.text.paint(bounds, &mut state.text_state, cx)
self.text.paint(bounds, wet, &mut state.text_state, cx)
}
}

View File

@@ -157,6 +157,7 @@ impl Element for UniformList {
fn paint(
&mut self,
bounds: Bounds<crate::Pixels>,
wet: bool,
element_state: &mut Self::State,
cx: &mut ElementContext,
) {

View File

@@ -104,8 +104,16 @@ 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));
fn paint(
&mut self,
_: Bounds<Pixels>,
wet: bool,
element: &mut Self::State,
cx: &mut ElementContext,
) {
cx.paint_view(self.entity_id(), |cx| {
element.as_mut().unwrap().paint(wet, cx)
});
}
}
@@ -247,6 +255,7 @@ impl AnyView {
self.model.entity_id()
}
/// Draws this view at the given origin, with its sizing based on the `available_space`.
pub(crate) fn draw(
&self,
origin: Point<Pixels>,
@@ -257,7 +266,8 @@ impl AnyView {
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.paint(false, cx);
rendered_element.paint(true, cx);
});
})
}
@@ -301,10 +311,16 @@ impl Element for AnyView {
})
}
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext) {
fn paint(
&mut self,
bounds: Bounds<Pixels>,
wet: bool,
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(wet, cx);
return;
}
@@ -321,7 +337,7 @@ impl Element for AnyView {
}
}
if let Some(mut element) = state.element.take() {
if let Some(mut element) = state.element.as_mut() {
element.paint(cx);
} else {
let mut element = (self.request_layout)(self, cx).1;

View File

@@ -191,7 +191,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
element_state: &mut Self::State,
cx: &mut ElementContext,
) {
if let Some(mut child) = element_state.child_element.take() {
if let Some(mut child) = element_state.child_element.as_mut() {
child.paint(cx);
}
@@ -199,7 +199,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
element_state.child_bounds = Some(cx.layout_bounds(child_layout_id));
}
if let Some(mut menu) = element_state.menu_element.take() {
if let Some(mut menu) = element_state.menu_element.as_mut() {
menu.paint(cx);
if let Some(child_bounds) = element_state.child_bounds {

View File

@@ -118,11 +118,11 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
element_state: &mut Self::State,
cx: &mut ElementContext,
) {
if let Some(mut child) = element_state.child_element.take() {
if let Some(mut child) = element_state.child_element.as_mut() {
child.paint(cx);
}
if let Some(mut menu) = element_state.menu_element.take() {
if let Some(mut menu) = element_state.menu_element.as_mut() {
menu.paint(cx);
return;
}