Merge branch 'main' into zed2-breadcrumbs
This commit is contained in:
commit
13f4cc563c
58 changed files with 2606 additions and 3167 deletions
|
@ -2,8 +2,8 @@ use crate::{
|
|||
div, Action, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext,
|
||||
BackgroundExecutor, Context, Div, Entity, EventEmitter, ForegroundExecutor, InputEvent,
|
||||
KeyDownEvent, Keystroke, Model, ModelContext, Render, Result, Task, TestDispatcher,
|
||||
TestPlatform, TestWindow, View, ViewContext, VisualContext, WindowContext, WindowHandle,
|
||||
WindowOptions,
|
||||
TestPlatform, TestWindow, TestWindowHandlers, View, ViewContext, VisualContext, WindowContext,
|
||||
WindowHandle, WindowOptions,
|
||||
};
|
||||
use anyhow::{anyhow, bail};
|
||||
use futures::{Stream, StreamExt};
|
||||
|
@ -502,6 +502,19 @@ impl<'a> VisualTestContext<'a> {
|
|||
self.cx.dispatch_action(self.window, action)
|
||||
}
|
||||
|
||||
pub fn window_title(&mut self) -> Option<String> {
|
||||
self.cx
|
||||
.update_window(self.window, |_, cx| {
|
||||
cx.window
|
||||
.platform_window
|
||||
.as_test()
|
||||
.unwrap()
|
||||
.window_title
|
||||
.clone()
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn simulate_keystrokes(&mut self, keystrokes: &str) {
|
||||
self.cx.simulate_keystrokes(self.window, keystrokes)
|
||||
}
|
||||
|
@ -509,6 +522,39 @@ impl<'a> VisualTestContext<'a> {
|
|||
pub fn simulate_input(&mut self, input: &str) {
|
||||
self.cx.simulate_input(self.window, input)
|
||||
}
|
||||
|
||||
pub fn simulate_activation(&mut self) {
|
||||
self.simulate_window_events(&mut |handlers| {
|
||||
handlers
|
||||
.active_status_change
|
||||
.iter_mut()
|
||||
.for_each(|f| f(true));
|
||||
})
|
||||
}
|
||||
|
||||
pub fn simulate_deactivation(&mut self) {
|
||||
self.simulate_window_events(&mut |handlers| {
|
||||
handlers
|
||||
.active_status_change
|
||||
.iter_mut()
|
||||
.for_each(|f| f(false));
|
||||
})
|
||||
}
|
||||
|
||||
fn simulate_window_events(&mut self, f: &mut dyn FnMut(&mut TestWindowHandlers)) {
|
||||
let handlers = self
|
||||
.cx
|
||||
.update_window(self.window, |_, cx| {
|
||||
cx.window
|
||||
.platform_window
|
||||
.as_test()
|
||||
.unwrap()
|
||||
.handlers
|
||||
.clone()
|
||||
})
|
||||
.unwrap();
|
||||
f(&mut *handlers.lock());
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Context for VisualTestContext<'a> {
|
||||
|
|
|
@ -166,7 +166,6 @@ impl TextState {
|
|||
runs: Option<Vec<TextRun>>,
|
||||
cx: &mut WindowContext,
|
||||
) -> LayoutId {
|
||||
let text_system = cx.text_system().clone();
|
||||
let text_style = cx.text_style();
|
||||
let font_size = text_style.font_size.to_pixels(cx.rem_size());
|
||||
let line_height = text_style
|
||||
|
@ -174,18 +173,16 @@ impl TextState {
|
|||
.to_pixels(font_size.into(), cx.rem_size());
|
||||
let text = SharedString::from(text);
|
||||
|
||||
let rem_size = cx.rem_size();
|
||||
|
||||
let runs = if let Some(runs) = runs {
|
||||
runs
|
||||
} else {
|
||||
vec![text_style.to_run(text.len())]
|
||||
};
|
||||
|
||||
let layout_id = cx.request_measured_layout(Default::default(), rem_size, {
|
||||
let layout_id = cx.request_measured_layout(Default::default(), {
|
||||
let element_state = self.clone();
|
||||
|
||||
move |known_dimensions, available_space| {
|
||||
move |known_dimensions, available_space, cx| {
|
||||
let wrap_width = if text_style.white_space == WhiteSpace::Normal {
|
||||
known_dimensions.width.or(match available_space.width {
|
||||
crate::AvailableSpace::Definite(x) => Some(x),
|
||||
|
@ -203,7 +200,8 @@ impl TextState {
|
|||
}
|
||||
}
|
||||
|
||||
let Some(lines) = text_system
|
||||
let Some(lines) = cx
|
||||
.text_system()
|
||||
.shape_text(
|
||||
&text, font_size, &runs, wrap_width, // Wrap if we know the width.
|
||||
)
|
||||
|
|
|
@ -109,7 +109,6 @@ impl Element for UniformList {
|
|||
cx: &mut WindowContext,
|
||||
) -> (LayoutId, Self::State) {
|
||||
let max_items = self.item_count;
|
||||
let rem_size = cx.rem_size();
|
||||
let item_size = state
|
||||
.as_ref()
|
||||
.map(|s| s.item_size)
|
||||
|
@ -120,9 +119,7 @@ impl Element for UniformList {
|
|||
.layout(state.map(|s| s.interactive), cx, |style, cx| {
|
||||
cx.request_measured_layout(
|
||||
style,
|
||||
rem_size,
|
||||
move |known_dimensions: Size<Option<Pixels>>,
|
||||
available_space: Size<AvailableSpace>| {
|
||||
move |known_dimensions, available_space, _cx| {
|
||||
let desired_height = item_size.height * max_items;
|
||||
let width =
|
||||
known_dimensions
|
||||
|
|
|
@ -655,6 +655,20 @@ pub struct Corners<T: Clone + Default + Debug> {
|
|||
pub bottom_left: T,
|
||||
}
|
||||
|
||||
impl<T> Corners<T>
|
||||
where
|
||||
T: Clone + Default + Debug,
|
||||
{
|
||||
pub fn all(value: T) -> Self {
|
||||
Self {
|
||||
top_left: value.clone(),
|
||||
top_right: value.clone(),
|
||||
bottom_right: value.clone(),
|
||||
bottom_left: value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Corners<AbsoluteLength> {
|
||||
pub fn to_pixels(&self, size: Size<Pixels>, rem_size: Pixels) -> Corners<Pixels> {
|
||||
let max = size.width.max(size.height) / 2.;
|
||||
|
|
|
@ -158,6 +158,11 @@ pub(crate) trait PlatformWindow {
|
|||
fn draw(&self, scene: Scene);
|
||||
|
||||
fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas>;
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
fn as_test(&self) -> Option<&TestWindow> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PlatformDispatcher: Send + Sync {
|
||||
|
|
|
@ -189,13 +189,9 @@ impl Platform for TestPlatform {
|
|||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_become_active(&self, _callback: Box<dyn FnMut()>) {
|
||||
unimplemented!()
|
||||
}
|
||||
fn on_become_active(&self, _callback: Box<dyn FnMut()>) {}
|
||||
|
||||
fn on_resign_active(&self, _callback: Box<dyn FnMut()>) {
|
||||
unimplemented!()
|
||||
}
|
||||
fn on_resign_active(&self, _callback: Box<dyn FnMut()>) {}
|
||||
|
||||
fn on_quit(&self, _callback: Box<dyn FnMut()>) {}
|
||||
|
||||
|
|
|
@ -11,19 +11,20 @@ use std::{
|
|||
};
|
||||
|
||||
#[derive(Default)]
|
||||
struct Handlers {
|
||||
active_status_change: Vec<Box<dyn FnMut(bool)>>,
|
||||
input: Vec<Box<dyn FnMut(crate::InputEvent) -> bool>>,
|
||||
moved: Vec<Box<dyn FnMut()>>,
|
||||
resize: Vec<Box<dyn FnMut(Size<Pixels>, f32)>>,
|
||||
pub(crate) struct TestWindowHandlers {
|
||||
pub(crate) active_status_change: Vec<Box<dyn FnMut(bool)>>,
|
||||
pub(crate) input: Vec<Box<dyn FnMut(crate::InputEvent) -> bool>>,
|
||||
pub(crate) moved: Vec<Box<dyn FnMut()>>,
|
||||
pub(crate) resize: Vec<Box<dyn FnMut(Size<Pixels>, f32)>>,
|
||||
}
|
||||
|
||||
pub struct TestWindow {
|
||||
bounds: WindowBounds,
|
||||
current_scene: Mutex<Option<Scene>>,
|
||||
display: Rc<dyn PlatformDisplay>,
|
||||
pub(crate) window_title: Option<String>,
|
||||
pub(crate) input_handler: Option<Arc<Mutex<Box<dyn PlatformInputHandler>>>>,
|
||||
handlers: Mutex<Handlers>,
|
||||
pub(crate) handlers: Arc<Mutex<TestWindowHandlers>>,
|
||||
platform: Weak<TestPlatform>,
|
||||
sprite_atlas: Arc<dyn PlatformAtlas>,
|
||||
}
|
||||
|
@ -42,6 +43,7 @@ impl TestWindow {
|
|||
input_handler: None,
|
||||
sprite_atlas: Arc::new(TestAtlas::new()),
|
||||
handlers: Default::default(),
|
||||
window_title: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -100,8 +102,8 @@ impl PlatformWindow for TestWindow {
|
|||
todo!()
|
||||
}
|
||||
|
||||
fn set_title(&mut self, _title: &str) {
|
||||
todo!()
|
||||
fn set_title(&mut self, title: &str) {
|
||||
self.window_title = Some(title.to_owned());
|
||||
}
|
||||
|
||||
fn set_edited(&mut self, _edited: bool) {
|
||||
|
@ -167,6 +169,10 @@ impl PlatformWindow for TestWindow {
|
|||
fn sprite_atlas(&self) -> sync::Arc<dyn crate::PlatformAtlas> {
|
||||
self.sprite_atlas.clone()
|
||||
}
|
||||
|
||||
fn as_test(&self) -> Option<&TestWindow> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TestAtlasState {
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
use super::{AbsoluteLength, Bounds, DefiniteLength, Edges, Length, Pixels, Point, Size, Style};
|
||||
use crate::{
|
||||
AbsoluteLength, Bounds, DefiniteLength, Edges, Length, Pixels, Point, Size, Style,
|
||||
WindowContext,
|
||||
};
|
||||
use collections::{HashMap, HashSet};
|
||||
use smallvec::SmallVec;
|
||||
use std::fmt::Debug;
|
||||
|
@ -9,13 +12,21 @@ use taffy::{
|
|||
Taffy,
|
||||
};
|
||||
|
||||
type Measureable = dyn Fn(Size<Option<Pixels>>, Size<AvailableSpace>) -> Size<Pixels> + Send + Sync;
|
||||
|
||||
pub struct TaffyLayoutEngine {
|
||||
taffy: Taffy<Box<Measureable>>,
|
||||
taffy: Taffy,
|
||||
children_to_parents: HashMap<LayoutId, LayoutId>,
|
||||
absolute_layout_bounds: HashMap<LayoutId, Bounds<Pixels>>,
|
||||
computed_layouts: HashSet<LayoutId>,
|
||||
nodes_to_measure: HashMap<
|
||||
LayoutId,
|
||||
Box<
|
||||
dyn FnMut(
|
||||
Size<Option<Pixels>>,
|
||||
Size<AvailableSpace>,
|
||||
&mut WindowContext,
|
||||
) -> Size<Pixels>,
|
||||
>,
|
||||
>,
|
||||
}
|
||||
|
||||
static EXPECT_MESSAGE: &'static str =
|
||||
|
@ -28,6 +39,7 @@ impl TaffyLayoutEngine {
|
|||
children_to_parents: HashMap::default(),
|
||||
absolute_layout_bounds: HashMap::default(),
|
||||
computed_layouts: HashSet::default(),
|
||||
nodes_to_measure: HashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,6 +48,7 @@ impl TaffyLayoutEngine {
|
|||
self.children_to_parents.clear();
|
||||
self.absolute_layout_bounds.clear();
|
||||
self.computed_layouts.clear();
|
||||
self.nodes_to_measure.clear();
|
||||
}
|
||||
|
||||
pub fn request_layout(
|
||||
|
@ -65,18 +78,18 @@ impl TaffyLayoutEngine {
|
|||
&mut self,
|
||||
style: Style,
|
||||
rem_size: Pixels,
|
||||
measure: impl Fn(Size<Option<Pixels>>, Size<AvailableSpace>) -> Size<Pixels>
|
||||
+ Send
|
||||
+ Sync
|
||||
measure: impl FnMut(Size<Option<Pixels>>, Size<AvailableSpace>, &mut WindowContext) -> Size<Pixels>
|
||||
+ 'static,
|
||||
) -> LayoutId {
|
||||
let style = style.to_taffy(rem_size);
|
||||
|
||||
let measurable = Box::new(measure);
|
||||
self.taffy
|
||||
.new_leaf_with_context(style, measurable)
|
||||
let layout_id = self
|
||||
.taffy
|
||||
.new_leaf_with_context(style, ())
|
||||
.expect(EXPECT_MESSAGE)
|
||||
.into()
|
||||
.into();
|
||||
self.nodes_to_measure.insert(layout_id, Box::new(measure));
|
||||
layout_id
|
||||
}
|
||||
|
||||
// Used to understand performance
|
||||
|
@ -126,7 +139,12 @@ impl TaffyLayoutEngine {
|
|||
Ok(edges)
|
||||
}
|
||||
|
||||
pub fn compute_layout(&mut self, id: LayoutId, available_space: Size<AvailableSpace>) {
|
||||
pub fn compute_layout(
|
||||
&mut self,
|
||||
id: LayoutId,
|
||||
available_space: Size<AvailableSpace>,
|
||||
cx: &mut WindowContext,
|
||||
) {
|
||||
// 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)?);
|
||||
|
@ -159,8 +177,8 @@ impl TaffyLayoutEngine {
|
|||
.compute_layout_with_measure(
|
||||
id.into(),
|
||||
available_space.into(),
|
||||
|known_dimensions, available_space, _node_id, context| {
|
||||
let Some(measure) = context else {
|
||||
|known_dimensions, available_space, node_id, _context| {
|
||||
let Some(measure) = self.nodes_to_measure.get_mut(&node_id.into()) else {
|
||||
return taffy::geometry::Size::default();
|
||||
};
|
||||
|
||||
|
@ -169,10 +187,11 @@ impl TaffyLayoutEngine {
|
|||
height: known_dimensions.height.map(Pixels),
|
||||
};
|
||||
|
||||
measure(known_dimensions, available_space.into()).into()
|
||||
measure(known_dimensions, available_space.into(), cx).into()
|
||||
},
|
||||
)
|
||||
.expect(EXPECT_MESSAGE);
|
||||
|
||||
// println!("compute_layout took {:?}", started_at.elapsed());
|
||||
}
|
||||
|
||||
|
|
|
@ -209,9 +209,7 @@ impl AnyView {
|
|||
) {
|
||||
cx.with_absolute_element_offset(origin, |cx| {
|
||||
let (layout_id, rendered_element) = (self.layout)(self, cx);
|
||||
cx.window
|
||||
.layout_engine
|
||||
.compute_layout(layout_id, available_space);
|
||||
cx.compute_layout(layout_id, available_space);
|
||||
(self.paint)(self, rendered_element, cx);
|
||||
})
|
||||
}
|
||||
|
@ -240,6 +238,10 @@ impl Element for AnyView {
|
|||
}
|
||||
|
||||
fn paint(self, _: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
|
||||
debug_assert!(
|
||||
state.is_some(),
|
||||
"state is None. Did you include an AnyView twice in the tree?"
|
||||
);
|
||||
(self.paint)(&self, state.take().unwrap(), cx)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -209,7 +209,7 @@ pub struct Window {
|
|||
sprite_atlas: Arc<dyn PlatformAtlas>,
|
||||
rem_size: Pixels,
|
||||
viewport_size: Size<Pixels>,
|
||||
pub(crate) layout_engine: TaffyLayoutEngine,
|
||||
layout_engine: Option<TaffyLayoutEngine>,
|
||||
pub(crate) root_view: Option<AnyView>,
|
||||
pub(crate) element_id_stack: GlobalElementId,
|
||||
pub(crate) previous_frame: Frame,
|
||||
|
@ -327,7 +327,7 @@ impl Window {
|
|||
sprite_atlas,
|
||||
rem_size: px(16.),
|
||||
viewport_size: content_size,
|
||||
layout_engine: TaffyLayoutEngine::new(),
|
||||
layout_engine: Some(TaffyLayoutEngine::new()),
|
||||
root_view: None,
|
||||
element_id_stack: GlobalElementId::default(),
|
||||
previous_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())),
|
||||
|
@ -606,9 +606,11 @@ impl<'a> WindowContext<'a> {
|
|||
self.app.layout_id_buffer.extend(children.into_iter());
|
||||
let rem_size = self.rem_size();
|
||||
|
||||
self.window
|
||||
.layout_engine
|
||||
.request_layout(style, rem_size, &self.app.layout_id_buffer)
|
||||
self.window.layout_engine.as_mut().unwrap().request_layout(
|
||||
style,
|
||||
rem_size,
|
||||
&self.app.layout_id_buffer,
|
||||
)
|
||||
}
|
||||
|
||||
/// Add a node to the layout tree for the current frame. Instead of taking a `Style` and children,
|
||||
|
@ -618,22 +620,25 @@ impl<'a> WindowContext<'a> {
|
|||
/// The given closure is invoked at layout time with the known dimensions and available space and
|
||||
/// returns a `Size`.
|
||||
pub fn request_measured_layout<
|
||||
F: Fn(Size<Option<Pixels>>, Size<AvailableSpace>) -> Size<Pixels> + Send + Sync + 'static,
|
||||
F: FnMut(Size<Option<Pixels>>, Size<AvailableSpace>, &mut WindowContext) -> Size<Pixels>
|
||||
+ 'static,
|
||||
>(
|
||||
&mut self,
|
||||
style: Style,
|
||||
rem_size: Pixels,
|
||||
measure: F,
|
||||
) -> LayoutId {
|
||||
let rem_size = self.rem_size();
|
||||
self.window
|
||||
.layout_engine
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.request_measured_layout(style, rem_size, measure)
|
||||
}
|
||||
|
||||
pub fn compute_layout(&mut self, layout_id: LayoutId, available_space: Size<AvailableSpace>) {
|
||||
self.window
|
||||
.layout_engine
|
||||
.compute_layout(layout_id, available_space)
|
||||
let mut layout_engine = self.window.layout_engine.take().unwrap();
|
||||
layout_engine.compute_layout(layout_id, available_space, self);
|
||||
self.window.layout_engine = Some(layout_engine);
|
||||
}
|
||||
|
||||
/// Obtain the bounds computed for the given LayoutId relative to the window. This method should not
|
||||
|
@ -643,6 +648,8 @@ impl<'a> WindowContext<'a> {
|
|||
let mut bounds = self
|
||||
.window
|
||||
.layout_engine
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.layout_bounds(layout_id)
|
||||
.map(Into::into);
|
||||
bounds.origin += self.element_offset();
|
||||
|
@ -678,6 +685,10 @@ impl<'a> WindowContext<'a> {
|
|||
self.window.platform_window.zoom();
|
||||
}
|
||||
|
||||
pub fn set_window_title(&mut self, title: &str) {
|
||||
self.window.platform_window.set_title(title);
|
||||
}
|
||||
|
||||
pub fn display(&self) -> Option<Rc<dyn PlatformDisplay>> {
|
||||
self.platform
|
||||
.displays()
|
||||
|
@ -1189,7 +1200,7 @@ impl<'a> WindowContext<'a> {
|
|||
self.text_system().start_frame();
|
||||
|
||||
let window = &mut *self.window;
|
||||
window.layout_engine.clear();
|
||||
window.layout_engine.as_mut().unwrap().clear();
|
||||
|
||||
mem::swap(&mut window.previous_frame, &mut window.current_frame);
|
||||
let frame = &mut window.current_frame;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue