Render code actions indicator

Co-Authored-By: Nathan <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra 2023-11-09 18:43:26 +01:00
parent cfee1401ed
commit 5d15886675
8 changed files with 225 additions and 126 deletions

View file

@ -63,6 +63,19 @@ trait ElementObject<V> {
fn initialize(&mut self, view_state: &mut V, cx: &mut ViewContext<V>);
fn layout(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) -> LayoutId;
fn paint(&mut self, view_state: &mut V, cx: &mut ViewContext<V>);
fn measure(
&mut self,
available_space: Size<AvailableSpace>,
view_state: &mut V,
cx: &mut ViewContext<V>,
) -> Size<Pixels>;
fn draw(
&mut self,
origin: Point<Pixels>,
available_space: Size<AvailableSpace>,
view_state: &mut V,
cx: &mut ViewContext<V>,
);
}
struct RenderedElement<V: 'static, E: Element<V>> {
@ -81,6 +94,11 @@ enum ElementRenderPhase<V> {
layout_id: LayoutId,
frame_state: Option<V>,
},
LayoutComputed {
layout_id: LayoutId,
available_space: Size<AvailableSpace>,
frame_state: Option<V>,
},
Painted,
}
@ -137,7 +155,9 @@ where
}
}
ElementRenderPhase::Start => panic!("must call initialize before layout"),
ElementRenderPhase::LayoutRequested { .. } | ElementRenderPhase::Painted => {
ElementRenderPhase::LayoutRequested { .. }
| ElementRenderPhase::LayoutComputed { .. }
| ElementRenderPhase::Painted => {
panic!("element rendered twice")
}
};
@ -154,6 +174,11 @@ where
ElementRenderPhase::LayoutRequested {
layout_id,
mut frame_state,
}
| ElementRenderPhase::LayoutComputed {
layout_id,
mut frame_state,
..
} => {
let bounds = cx.layout_bounds(layout_id);
if let Some(id) = self.element.id() {
@ -173,6 +198,62 @@ where
_ => panic!("must call layout before paint"),
};
}
fn measure(
&mut self,
available_space: Size<AvailableSpace>,
view_state: &mut V,
cx: &mut ViewContext<V>,
) -> Size<Pixels> {
if matches!(&self.phase, ElementRenderPhase::Start) {
self.initialize(view_state, cx);
}
if matches!(&self.phase, ElementRenderPhase::Initialized { .. }) {
self.layout(view_state, cx);
}
let layout_id = match &mut self.phase {
ElementRenderPhase::LayoutRequested {
layout_id,
frame_state,
} => {
cx.compute_layout(*layout_id, available_space);
let layout_id = *layout_id;
self.phase = ElementRenderPhase::LayoutComputed {
layout_id,
available_space,
frame_state: frame_state.take(),
};
layout_id
}
ElementRenderPhase::LayoutComputed {
layout_id,
available_space: prev_available_space,
..
} => {
if available_space != *prev_available_space {
cx.compute_layout(*layout_id, available_space);
*prev_available_space = available_space;
}
*layout_id
}
_ => panic!("cannot measure after painting"),
};
cx.layout_bounds(layout_id).size
}
fn draw(
&mut self,
origin: Point<Pixels>,
available_space: Size<AvailableSpace>,
view_state: &mut V,
cx: &mut ViewContext<V>,
) {
self.measure(available_space, view_state, cx);
cx.with_element_offset(Some(origin), |cx| self.paint(view_state, cx))
}
}
pub struct AnyElement<V>(Box<dyn ElementObject<V>>);
@ -206,10 +287,7 @@ impl<V> AnyElement<V> {
view_state: &mut V,
cx: &mut ViewContext<V>,
) -> Size<Pixels> {
self.initialize(view_state, cx);
let layout_id = self.layout(view_state, cx);
cx.compute_layout(layout_id, available_space);
cx.layout_bounds(layout_id).size
self.0.measure(available_space, view_state, cx)
}
/// Initializes this element and performs layout in the available space, then paints it at the given origin.
@ -220,10 +298,7 @@ impl<V> AnyElement<V> {
view_state: &mut V,
cx: &mut ViewContext<V>,
) {
self.initialize(view_state, cx);
let layout_id = self.layout(view_state, cx);
cx.compute_layout(layout_id, available_space);
cx.with_element_offset(Some(origin), |cx| self.paint(view_state, cx))
self.0.draw(origin, available_space, view_state, cx)
}
}

View file

@ -1,5 +1,6 @@
use super::{AbsoluteLength, Bounds, DefiniteLength, Edges, Length, Pixels, Point, Size, Style};
use collections::HashMap;
use collections::{HashMap, HashSet};
use smallvec::SmallVec;
use std::fmt::Debug;
use taffy::{
geometry::{Point as TaffyPoint, Rect as TaffyRect, Size as TaffySize},
@ -12,6 +13,7 @@ pub struct TaffyLayoutEngine {
taffy: Taffy,
children_to_parents: HashMap<LayoutId, LayoutId>,
absolute_layout_bounds: HashMap<LayoutId, Bounds<Pixels>>,
computed_layouts: HashSet<LayoutId>,
}
static EXPECT_MESSAGE: &'static str =
@ -23,9 +25,17 @@ impl TaffyLayoutEngine {
taffy: Taffy::new(),
children_to_parents: HashMap::default(),
absolute_layout_bounds: HashMap::default(),
computed_layouts: HashSet::default(),
}
}
pub fn clear(&mut self) {
self.taffy.clear();
self.children_to_parents.clear();
self.absolute_layout_bounds.clear();
self.computed_layouts.clear();
}
pub fn request_layout(
&mut self,
style: &Style,
@ -115,6 +125,7 @@ impl TaffyLayoutEngine {
}
pub fn compute_layout(&mut self, id: LayoutId, available_space: Size<AvailableSpace>) {
// Leaving this here until we have a better instrumentation approach.
// println!("Laying out {} children", self.count_all_children(id)?);
// println!("Max layout depth: {}", self.max_depth(0, id)?);
@ -124,6 +135,22 @@ impl TaffyLayoutEngine {
// println!("N{} --> N{}", u64::from(a), u64::from(b));
// }
// println!("");
//
if !self.computed_layouts.insert(id) {
let mut stack = SmallVec::<[LayoutId; 64]>::new();
stack.push(id);
while let Some(id) = stack.pop() {
self.absolute_layout_bounds.remove(&id);
stack.extend(
self.taffy
.children(id.into())
.expect(EXPECT_MESSAGE)
.into_iter()
.map(Into::into),
);
}
}
// let started_at = std::time::Instant::now();
self.taffy
@ -397,7 +424,7 @@ where
}
}
#[derive(Copy, Clone, Default, Debug)]
#[derive(Copy, Clone, Default, Debug, Eq, PartialEq)]
pub enum AvailableSpace {
/// The amount of space available is the specified number of pixels
Definite(Pixels),

View file

@ -1060,6 +1060,8 @@ impl<'a> WindowContext<'a> {
self.text_system().start_frame();
let window = &mut *self.window;
window.layout_engine.clear();
mem::swap(&mut window.previous_frame, &mut window.current_frame);
let frame = &mut window.current_frame;
frame.element_states.clear();