Overhaul the entire element system

Now the Element trait is designed to be wrapped in a Lifecycle enum that gets placed inside an ElementBox. This allows the framework to store data on behalf of the Element implementation, such as sizes, bounds, and also implementation-specific LayoutState and PaintState types. This makes it easier to reason about which data is available in each Element method.
This commit is contained in:
Nathan Sobo 2021-03-21 20:54:23 -06:00
parent 046fe3fff9
commit 119aa452b6
27 changed files with 931 additions and 642 deletions

7
Cargo.lock generated
View file

@ -757,6 +757,7 @@ dependencies = [
"pathfinder_geometry", "pathfinder_geometry",
"pin-project", "pin-project",
"rand 0.8.3", "rand 0.8.3",
"replace_with",
"smallvec", "smallvec",
"smol", "smol",
"tree-sitter", "tree-sitter",
@ -1259,6 +1260,12 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "replace_with"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3a8614ee435691de62bcffcf4a66d91b3594bf1428a5722e79103249a095690"
[[package]] [[package]]
name = "rust-argon2" name = "rust-argon2"
version = "0.8.3" version = "0.8.3"

View file

@ -14,6 +14,7 @@ pathfinder_color = "0.5"
pathfinder_geometry = "0.5" pathfinder_geometry = "0.5"
pin-project = "1.0.5" pin-project = "1.0.5"
rand = "0.8.3" rand = "0.8.3"
replace_with = "0.1.7"
smallvec = "1.6.1" smallvec = "1.6.1"
smol = "1.2" smol = "1.2"
tree-sitter = "0.17" tree-sitter = "0.17"

View file

@ -1,5 +1,5 @@
use crate::{ use crate::{
elements::Element, elements::ElementBox,
executor::{self, ForegroundTask}, executor::{self, ForegroundTask},
keymap::{self, Keystroke}, keymap::{self, Keystroke},
platform::{self, App as _, WindowOptions}, platform::{self, App as _, WindowOptions},
@ -30,7 +30,7 @@ pub trait Entity: 'static + Send + Sync {
pub trait View: Entity { pub trait View: Entity {
fn ui_name() -> &'static str; fn ui_name() -> &'static str;
fn render<'a>(&self, app: &AppContext) -> Box<dyn Element>; fn render<'a>(&self, app: &AppContext) -> ElementBox;
fn on_focus(&mut self, _ctx: &mut ViewContext<Self>) {} fn on_focus(&mut self, _ctx: &mut ViewContext<Self>) {}
fn on_blur(&mut self, _ctx: &mut ViewContext<Self>) {} fn on_blur(&mut self, _ctx: &mut ViewContext<Self>) {}
fn keymap_context(&self, _: &AppContext) -> keymap::Context { fn keymap_context(&self, _: &AppContext) -> keymap::Context {
@ -458,11 +458,11 @@ impl MutableAppContext {
self.ctx.focused_view_id(window_id) self.ctx.focused_view_id(window_id)
} }
pub fn render_view(&self, window_id: usize, view_id: usize) -> Result<Box<dyn Element>> { pub fn render_view(&self, window_id: usize, view_id: usize) -> Result<ElementBox> {
self.ctx.render_view(window_id, view_id) self.ctx.render_view(window_id, view_id)
} }
pub fn render_views(&self, window_id: usize) -> Result<HashMap<usize, Box<dyn Element>>> { pub fn render_views(&self, window_id: usize) -> Result<HashMap<usize, ElementBox>> {
self.ctx.render_views(window_id) self.ctx.render_views(window_id)
} }
@ -545,13 +545,6 @@ impl MutableAppContext {
responder_chain: Vec<usize>, responder_chain: Vec<usize>,
keystroke: &Keystroke, keystroke: &Keystroke,
) -> Result<bool> { ) -> Result<bool> {
log::info!(
"dispatch_keystroke {} {:?} {:?}",
window_id,
responder_chain,
keystroke
);
let mut context_chain = Vec::new(); let mut context_chain = Vec::new();
let mut context = keymap::Context::default(); let mut context = keymap::Context::default();
for view_id in &responder_chain { for view_id in &responder_chain {
@ -641,7 +634,6 @@ impl MutableAppContext {
let mut app = self.upgrade(); let mut app = self.upgrade();
let presenter = presenter.clone(); let presenter = presenter.clone();
window.on_event(Box::new(move |event| { window.on_event(Box::new(move |event| {
log::info!("event {:?}", event);
app.update(|ctx| { app.update(|ctx| {
if let Event::KeyDown { keystroke, .. } = &event { if let Event::KeyDown { keystroke, .. } = &event {
if ctx if ctx
@ -687,7 +679,6 @@ impl MutableAppContext {
} }
self.on_window_invalidated(window_id, move |invalidation, ctx| { self.on_window_invalidated(window_id, move |invalidation, ctx| {
log::info!("window invalidated");
let mut presenter = presenter.borrow_mut(); let mut presenter = presenter.borrow_mut();
presenter.invalidate(invalidation, ctx.downgrade()); presenter.invalidate(invalidation, ctx.downgrade());
let scene = presenter.build_scene(window.size(), window.scale_factor(), ctx); let scene = presenter.build_scene(window.size(), window.scale_factor(), ctx);
@ -1285,7 +1276,7 @@ impl AppContext {
.and_then(|window| window.focused_view) .and_then(|window| window.focused_view)
} }
pub fn render_view(&self, window_id: usize, view_id: usize) -> Result<Box<dyn Element>> { pub fn render_view(&self, window_id: usize, view_id: usize) -> Result<ElementBox> {
self.windows self.windows
.get(&window_id) .get(&window_id)
.and_then(|w| w.views.get(&view_id)) .and_then(|w| w.views.get(&view_id))
@ -1293,14 +1284,14 @@ impl AppContext {
.ok_or(anyhow!("view not found")) .ok_or(anyhow!("view not found"))
} }
pub fn render_views(&self, window_id: usize) -> Result<HashMap<usize, Box<dyn Element>>> { pub fn render_views(&self, window_id: usize) -> Result<HashMap<usize, ElementBox>> {
self.windows self.windows
.get(&window_id) .get(&window_id)
.map(|w| { .map(|w| {
w.views w.views
.iter() .iter()
.map(|(id, view)| (*id, view.render(self))) .map(|(id, view)| (*id, view.render(self)))
.collect::<HashMap<_, Box<dyn Element>>>() .collect::<HashMap<_, ElementBox>>()
}) })
.ok_or(anyhow!("window not found")) .ok_or(anyhow!("window not found"))
} }
@ -1388,7 +1379,7 @@ pub trait AnyView: Send + Sync {
fn as_any(&self) -> &dyn Any; fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any; fn as_any_mut(&mut self) -> &mut dyn Any;
fn ui_name(&self) -> &'static str; fn ui_name(&self) -> &'static str;
fn render<'a>(&self, app: &AppContext) -> Box<dyn Element>; fn render<'a>(&self, app: &AppContext) -> ElementBox;
fn on_focus(&mut self, app: &mut MutableAppContext, window_id: usize, view_id: usize); fn on_focus(&mut self, app: &mut MutableAppContext, window_id: usize, view_id: usize);
fn on_blur(&mut self, app: &mut MutableAppContext, window_id: usize, view_id: usize); fn on_blur(&mut self, app: &mut MutableAppContext, window_id: usize, view_id: usize);
fn keymap_context(&self, app: &AppContext) -> keymap::Context; fn keymap_context(&self, app: &AppContext) -> keymap::Context;
@ -1410,7 +1401,7 @@ where
T::ui_name() T::ui_name()
} }
fn render<'a>(&self, app: &AppContext) -> Box<dyn Element> { fn render<'a>(&self, app: &AppContext) -> ElementBox {
View::render(self, app) View::render(self, app)
} }
@ -2556,7 +2547,7 @@ mod tests {
} }
impl super::View for View { impl super::View for View {
fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> { fn render<'a>(&self, _: &AppContext) -> ElementBox {
Empty::new().boxed() Empty::new().boxed()
} }
@ -2628,7 +2619,7 @@ mod tests {
} }
impl super::View for View { impl super::View for View {
fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> { fn render<'a>(&self, _: &AppContext) -> ElementBox {
Empty::new().boxed() Empty::new().boxed()
} }
@ -2684,7 +2675,7 @@ mod tests {
} }
impl super::View for View { impl super::View for View {
fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> { fn render<'a>(&self, _: &AppContext) -> ElementBox {
Empty::new().boxed() Empty::new().boxed()
} }
@ -2738,7 +2729,7 @@ mod tests {
} }
impl super::View for View { impl super::View for View {
fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> { fn render<'a>(&self, _: &AppContext) -> ElementBox {
Empty::new().boxed() Empty::new().boxed()
} }
@ -2784,7 +2775,7 @@ mod tests {
} }
impl super::View for View { impl super::View for View {
fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> { fn render<'a>(&self, _: &AppContext) -> ElementBox {
Empty::new().boxed() Empty::new().boxed()
} }
@ -2835,7 +2826,7 @@ mod tests {
} }
impl super::View for View { impl super::View for View {
fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> { fn render<'a>(&self, _: &AppContext) -> ElementBox {
Empty::new().boxed() Empty::new().boxed()
} }
@ -2897,7 +2888,7 @@ mod tests {
} }
impl super::View for View { impl super::View for View {
fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> { fn render<'a>(&self, _: &AppContext) -> ElementBox {
Empty::new().boxed() Empty::new().boxed()
} }
@ -2939,7 +2930,7 @@ mod tests {
} }
impl super::View for View { impl super::View for View {
fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> { fn render<'a>(&self, _: &AppContext) -> ElementBox {
Empty::new().boxed() Empty::new().boxed()
} }
@ -2981,7 +2972,7 @@ mod tests {
} }
impl View for ViewA { impl View for ViewA {
fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> { fn render<'a>(&self, _: &AppContext) -> ElementBox {
Empty::new().boxed() Empty::new().boxed()
} }
@ -2999,7 +2990,7 @@ mod tests {
} }
impl View for ViewB { impl View for ViewB {
fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> { fn render<'a>(&self, _: &AppContext) -> ElementBox {
Empty::new().boxed() Empty::new().boxed()
} }
@ -3104,7 +3095,7 @@ mod tests {
} }
impl super::View for View { impl super::View for View {
fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> { fn render<'a>(&self, _: &AppContext) -> ElementBox {
Empty::new().boxed() Empty::new().boxed()
} }
@ -3174,7 +3165,7 @@ mod tests {
// } // }
// impl super::View for View { // impl super::View for View {
// fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> { // fn render<'a>(&self, _: &AppContext) -> ElementBox {
// Empty::new().boxed() // Empty::new().boxed()
// } // }
@ -3253,7 +3244,7 @@ mod tests {
} }
impl super::View for View { impl super::View for View {
fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> { fn render<'a>(&self, _: &AppContext) -> ElementBox {
Empty::new().boxed() Empty::new().boxed()
} }

View file

@ -1,21 +1,19 @@
use crate::{ use crate::{
AfterLayoutContext, AppContext, Element, Event, EventContext, LayoutContext, MutableAppContext, AfterLayoutContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
PaintContext, SizeConstraint, SizeConstraint,
}; };
use pathfinder_geometry::vector::{vec2f, Vector2F}; use pathfinder_geometry::vector::{vec2f, Vector2F};
pub struct Align { pub struct Align {
child: Box<dyn Element>, child: ElementBox,
alignment: Vector2F, alignment: Vector2F,
size: Option<Vector2F>,
} }
impl Align { impl Align {
pub fn new(child: Box<dyn Element>) -> Self { pub fn new(child: ElementBox) -> Self {
Self { Self {
child, child,
alignment: Vector2F::zero(), alignment: Vector2F::zero(),
size: None,
} }
} }
@ -26,43 +24,59 @@ impl Align {
} }
impl Element for Align { impl Element for Align {
type LayoutState = ();
type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
mut constraint: SizeConstraint, mut constraint: SizeConstraint,
ctx: &mut LayoutContext, ctx: &mut LayoutContext,
app: &AppContext, ) -> (Vector2F, Self::LayoutState) {
) -> Vector2F {
let mut size = constraint.max; let mut size = constraint.max;
constraint.min = Vector2F::zero(); constraint.min = Vector2F::zero();
let child_size = self.child.layout(constraint, ctx, app); let child_size = self.child.layout(constraint, ctx);
if size.x().is_infinite() { if size.x().is_infinite() {
size.set_x(child_size.x()); size.set_x(child_size.x());
} }
if size.y().is_infinite() { if size.y().is_infinite() {
size.set_y(child_size.y()); size.set_y(child_size.y());
} }
self.size = Some(size); (size, ())
size
} }
fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &mut MutableAppContext) { fn after_layout(
self.child.after_layout(ctx, app); &mut self,
_: Vector2F,
_: &mut Self::LayoutState,
ctx: &mut AfterLayoutContext,
) {
self.child.after_layout(ctx);
} }
fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) { fn paint(
let self_center = self.size.unwrap() / 2.0; &mut self,
let self_target = self_center + self_center * self.alignment; bounds: pathfinder_geometry::rect::RectF,
let child_center = self.child.size().unwrap() / 2.0; _: &mut Self::LayoutState,
ctx: &mut PaintContext,
) -> Self::PaintState {
let my_center = bounds.size() / 2.;
let my_target = my_center + my_center * self.alignment;
let child_center = self.child.size() / 2.;
let child_target = child_center + child_center * self.alignment; let child_target = child_center + child_center * self.alignment;
let origin = origin - (child_target - self_target);
self.child.paint(origin, ctx, app); self.child
.paint(bounds.origin() - (child_target - my_target), ctx);
} }
fn dispatch_event(&self, event: &Event, ctx: &mut EventContext, app: &AppContext) -> bool { fn dispatch_event(
self.child.dispatch_event(event, ctx, app) &mut self,
} event: &Event,
_: pathfinder_geometry::rect::RectF,
fn size(&self) -> Option<Vector2F> { _: &mut Self::LayoutState,
self.size _: &mut Self::PaintState,
ctx: &mut EventContext,
) -> bool {
self.child.dispatch_event(event, ctx)
} }
} }

View file

@ -1,16 +1,16 @@
use crate::{ use crate::{
AfterLayoutContext, AppContext, Element, Event, EventContext, LayoutContext, MutableAppContext, AfterLayoutContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
PaintContext, SizeConstraint, SizeConstraint,
}; };
use pathfinder_geometry::vector::Vector2F; use pathfinder_geometry::vector::Vector2F;
pub struct ConstrainedBox { pub struct ConstrainedBox {
child: Box<dyn Element>, child: ElementBox,
constraint: SizeConstraint, constraint: SizeConstraint,
} }
impl ConstrainedBox { impl ConstrainedBox {
pub fn new(child: Box<dyn Element>) -> Self { pub fn new(child: ElementBox) -> Self {
Self { Self {
child, child,
constraint: SizeConstraint { constraint: SizeConstraint {
@ -38,30 +38,46 @@ impl ConstrainedBox {
} }
impl Element for ConstrainedBox { impl Element for ConstrainedBox {
type LayoutState = ();
type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
mut constraint: SizeConstraint, mut constraint: SizeConstraint,
ctx: &mut LayoutContext, ctx: &mut LayoutContext,
app: &AppContext, ) -> (Vector2F, Self::LayoutState) {
) -> Vector2F {
constraint.min = constraint.min.max(self.constraint.min); constraint.min = constraint.min.max(self.constraint.min);
constraint.max = constraint.max.min(self.constraint.max); constraint.max = constraint.max.min(self.constraint.max);
self.child.layout(constraint, ctx, app) let size = self.child.layout(constraint, ctx);
(size, ())
} }
fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &mut MutableAppContext) { fn after_layout(
self.child.after_layout(ctx, app); &mut self,
_: Vector2F,
_: &mut Self::LayoutState,
ctx: &mut AfterLayoutContext,
) {
self.child.after_layout(ctx);
} }
fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) { fn paint(
self.child.paint(origin, ctx, app); &mut self,
bounds: pathfinder_geometry::rect::RectF,
_: &mut Self::LayoutState,
ctx: &mut PaintContext,
) -> Self::PaintState {
self.child.paint(bounds.origin(), ctx);
} }
fn dispatch_event(&self, event: &Event, ctx: &mut EventContext, app: &AppContext) -> bool { fn dispatch_event(
self.child.dispatch_event(event, ctx, app) &mut self,
} event: &Event,
_: pathfinder_geometry::rect::RectF,
fn size(&self) -> Option<Vector2F> { _: &mut Self::LayoutState,
self.child.size() _: &mut Self::PaintState,
ctx: &mut EventContext,
) -> bool {
self.child.dispatch_event(event, ctx)
} }
} }

View file

@ -4,8 +4,8 @@ use crate::{
color::ColorU, color::ColorU,
geometry::vector::{vec2f, Vector2F}, geometry::vector::{vec2f, Vector2F},
scene::{Border, Quad}, scene::{Border, Quad},
AfterLayoutContext, AppContext, Element, Event, EventContext, LayoutContext, MutableAppContext, AfterLayoutContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
PaintContext, SizeConstraint, SizeConstraint,
}; };
pub struct Container { pub struct Container {
@ -16,13 +16,11 @@ pub struct Container {
border: Border, border: Border,
corner_radius: f32, corner_radius: f32,
shadow: Option<Shadow>, shadow: Option<Shadow>,
child: Box<dyn Element>, child: ElementBox,
size: Option<Vector2F>,
origin: Option<Vector2F>,
} }
impl Container { impl Container {
pub fn new(child: Box<dyn Element>) -> Self { pub fn new(child: ElementBox) -> Self {
Self { Self {
margin: Margin::default(), margin: Margin::default(),
padding: Padding::default(), padding: Padding::default(),
@ -32,8 +30,6 @@ impl Container {
corner_radius: 0.0, corner_radius: 0.0,
shadow: None, shadow: None,
child, child,
size: None,
origin: None,
} }
} }
@ -122,43 +118,56 @@ impl Container {
} }
impl Element for Container { impl Element for Container {
type LayoutState = ();
type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
ctx: &mut LayoutContext, ctx: &mut LayoutContext,
app: &AppContext, ) -> (Vector2F, Self::LayoutState) {
) -> Vector2F {
let size_buffer = self.margin_size() + self.padding_size() + self.border_size(); let size_buffer = self.margin_size() + self.padding_size() + self.border_size();
let child_constraint = SizeConstraint { let child_constraint = SizeConstraint {
min: (constraint.min - size_buffer).max(Vector2F::zero()), min: (constraint.min - size_buffer).max(Vector2F::zero()),
max: (constraint.max - size_buffer).max(Vector2F::zero()), max: (constraint.max - size_buffer).max(Vector2F::zero()),
}; };
let child_size = self.child.layout(child_constraint, ctx, app); let child_size = self.child.layout(child_constraint, ctx);
let size = child_size + size_buffer; (child_size + size_buffer, ())
self.size = Some(size);
size
} }
fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &mut MutableAppContext) { fn after_layout(
self.child.after_layout(ctx, app); &mut self,
_: Vector2F,
_: &mut Self::LayoutState,
ctx: &mut AfterLayoutContext,
) {
self.child.after_layout(ctx);
} }
fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) { fn paint(
&mut self,
bounds: RectF,
_: &mut Self::LayoutState,
ctx: &mut PaintContext,
) -> Self::PaintState {
ctx.scene.push_quad(Quad { ctx.scene.push_quad(Quad {
bounds: RectF::new(origin, self.size.unwrap()), bounds,
background: self.background_color, background: self.background_color,
border: self.border, border: self.border,
corner_radius: self.corner_radius, corner_radius: self.corner_radius,
}); });
self.child.paint(origin, ctx, app); self.child.paint(bounds.origin(), ctx);
} }
fn dispatch_event(&self, event: &Event, ctx: &mut EventContext, app: &AppContext) -> bool { fn dispatch_event(
self.child.dispatch_event(event, ctx, app) &mut self,
} event: &Event,
_: RectF,
fn size(&self) -> Option<Vector2F> { _: &mut Self::LayoutState,
self.size _: &mut Self::PaintState,
ctx: &mut EventContext,
) -> bool {
self.child.dispatch_event(event, ctx)
} }
} }

View file

@ -1,45 +1,47 @@
use crate::{ use crate::{
AfterLayoutContext, AppContext, Element, Event, EventContext, LayoutContext, MutableAppContext, AfterLayoutContext, Element, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
PaintContext, SizeConstraint,
}; };
use pathfinder_geometry::vector::Vector2F; use pathfinder_geometry::{rect::RectF, vector::Vector2F};
pub struct Empty { pub struct Empty;
size: Option<Vector2F>,
origin: Option<Vector2F>,
}
impl Empty { impl Empty {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self
size: None,
origin: None,
}
} }
} }
impl Element for Empty { impl Element for Empty {
type LayoutState = ();
type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
_: &mut LayoutContext, _: &mut LayoutContext,
_: &AppContext, ) -> (Vector2F, Self::LayoutState) {
) -> Vector2F { (constraint.max, ())
self.size = Some(constraint.max);
constraint.max
} }
fn after_layout(&mut self, _: &mut AfterLayoutContext, _: &mut MutableAppContext) {} fn after_layout(&mut self, _: Vector2F, _: &mut Self::LayoutState, _: &mut AfterLayoutContext) {
fn paint(&mut self, origin: Vector2F, _: &mut PaintContext, _: &AppContext) {
self.origin = Some(origin);
} }
fn dispatch_event(&self, _: &Event, _: &mut EventContext, _: &AppContext) -> bool { fn paint(
&mut self,
_: RectF,
_: &mut Self::LayoutState,
_: &mut PaintContext,
) -> Self::PaintState {
}
fn dispatch_event(
&mut self,
_: &Event,
_: RectF,
_: &mut Self::LayoutState,
_: &mut Self::PaintState,
_: &mut EventContext,
) -> bool {
false false
} }
fn size(&self) -> Option<Vector2F> {
self.size
}
} }

View file

@ -1,69 +1,83 @@
use super::try_rect;
use crate::{ use crate::{
geometry::vector::Vector2F, AfterLayoutContext, AppContext, Element, Event, EventContext, geometry::vector::Vector2F, AfterLayoutContext, Element, ElementBox, Event, EventContext,
LayoutContext, MutableAppContext, PaintContext, SizeConstraint, LayoutContext, PaintContext, SizeConstraint,
}; };
use std::cell::RefCell;
pub struct EventHandler { pub struct EventHandler {
child: Box<dyn Element>, child: ElementBox,
mouse_down: Option<RefCell<Box<dyn FnMut(&mut EventContext, &AppContext) -> bool>>>, mouse_down: Option<Box<dyn FnMut(&mut EventContext) -> bool>>,
origin: Option<Vector2F>,
} }
impl EventHandler { impl EventHandler {
pub fn new(child: Box<dyn Element>) -> Self { pub fn new(child: ElementBox) -> Self {
Self { Self {
child, child,
mouse_down: None, mouse_down: None,
origin: None,
} }
} }
pub fn on_mouse_down<F>(mut self, callback: F) -> Self pub fn on_mouse_down<F>(mut self, callback: F) -> Self
where where
F: 'static + FnMut(&mut EventContext, &AppContext) -> bool, F: 'static + FnMut(&mut EventContext) -> bool,
{ {
self.mouse_down = Some(RefCell::new(Box::new(callback))); self.mouse_down = Some(Box::new(callback));
self self
} }
} }
impl Element for EventHandler { impl Element for EventHandler {
type LayoutState = ();
type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
ctx: &mut LayoutContext, ctx: &mut LayoutContext,
app: &AppContext, ) -> (Vector2F, Self::LayoutState) {
) -> Vector2F { let size = self.child.layout(constraint, ctx);
self.child.layout(constraint, ctx, app) (size, ())
} }
fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &mut MutableAppContext) { fn after_layout(
self.child.after_layout(ctx, app); &mut self,
_: Vector2F,
_: &mut Self::LayoutState,
ctx: &mut AfterLayoutContext,
) {
self.child.after_layout(ctx);
} }
fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) { fn paint(
self.origin = Some(origin); &mut self,
self.child.paint(origin, ctx, app); bounds: pathfinder_geometry::rect::RectF,
_: &mut Self::LayoutState,
ctx: &mut PaintContext,
) -> Self::PaintState {
self.child.paint(bounds.origin(), ctx);
} }
fn size(&self) -> Option<Vector2F> { fn dispatch_event(
self.child.size() &mut self,
} event: &Event,
bounds: pathfinder_geometry::rect::RectF,
fn dispatch_event(&self, event: &Event, ctx: &mut EventContext, app: &AppContext) -> bool { _: &mut Self::LayoutState,
match event { _: &mut Self::PaintState,
Event::LeftMouseDown { position, .. } => { ctx: &mut EventContext,
if let Some(callback) = self.mouse_down.as_ref() { ) -> bool {
let rect = try_rect(self.origin, self.size()).unwrap(); if self.child.dispatch_event(event, ctx) {
if rect.contains_point(*position) { true
return callback.borrow_mut()(ctx, app); } else {
match event {
Event::LeftMouseDown { position, .. } => {
if let Some(callback) = self.mouse_down.as_mut() {
if bounds.contains_point(*position) {
return callback(ctx);
}
} }
false
} }
false _ => false,
} }
_ => false,
} }
} }
} }

View file

@ -1,15 +1,14 @@
use std::any::Any;
use crate::{ use crate::{
AfterLayoutContext, AppContext, Axis, Element, Event, EventContext, LayoutContext, AfterLayoutContext, Axis, Element, ElementBox, Event, EventContext, LayoutContext,
MutableAppContext, PaintContext, SizeConstraint, Vector2FExt, PaintContext, SizeConstraint, Vector2FExt,
}; };
use pathfinder_geometry::vector::{vec2f, Vector2F}; use pathfinder_geometry::vector::{vec2f, Vector2F};
use std::any::Any;
pub struct Flex { pub struct Flex {
axis: Axis, axis: Axis,
children: Vec<Box<dyn Element>>, children: Vec<ElementBox>,
size: Option<Vector2F>,
origin: Option<Vector2F>,
} }
impl Flex { impl Flex {
@ -17,8 +16,6 @@ impl Flex {
Self { Self {
axis, axis,
children: Default::default(), children: Default::default(),
size: None,
origin: None,
} }
} }
@ -30,39 +27,41 @@ impl Flex {
Self::new(Axis::Vertical) Self::new(Axis::Vertical)
} }
fn child_flex<'b>(child: &dyn Element) -> Option<f32> { fn child_flex<'b>(child: &ElementBox) -> Option<f32> {
child child
.parent_data() .metadata()
.and_then(|d| d.downcast_ref::<FlexParentData>()) .and_then(|d| d.downcast_ref::<FlexParentData>())
.map(|data| data.flex) .map(|data| data.flex)
} }
} }
impl Extend<Box<dyn Element>> for Flex { impl Extend<ElementBox> for Flex {
fn extend<T: IntoIterator<Item = Box<dyn Element>>>(&mut self, children: T) { fn extend<T: IntoIterator<Item = ElementBox>>(&mut self, children: T) {
self.children.extend(children); self.children.extend(children);
} }
} }
impl Element for Flex { impl Element for Flex {
type LayoutState = ();
type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
ctx: &mut LayoutContext, ctx: &mut LayoutContext,
app: &AppContext, ) -> (Vector2F, Self::LayoutState) {
) -> Vector2F {
let mut total_flex = 0.0; let mut total_flex = 0.0;
let mut fixed_space = 0.0; let mut fixed_space = 0.0;
let cross_axis = self.axis.invert(); let cross_axis = self.axis.invert();
let mut cross_axis_max: f32 = 0.0; let mut cross_axis_max: f32 = 0.0;
for child in &mut self.children { for child in &mut self.children {
if let Some(flex) = Self::child_flex(child.as_ref()) { if let Some(flex) = Self::child_flex(&child) {
total_flex += flex; total_flex += flex;
} else { } else {
let child_constraint = let child_constraint =
SizeConstraint::strict_along(cross_axis, constraint.max_along(cross_axis)); SizeConstraint::strict_along(cross_axis, constraint.max_along(cross_axis));
let size = child.layout(child_constraint, ctx, app); let size = child.layout(child_constraint, ctx);
fixed_space += size.along(self.axis); fixed_space += size.along(self.axis);
cross_axis_max = cross_axis_max.max(size.along(cross_axis)); cross_axis_max = cross_axis_max.max(size.along(cross_axis));
} }
@ -77,7 +76,7 @@ impl Element for Flex {
let mut remaining_flex = total_flex; let mut remaining_flex = total_flex;
for child in &mut self.children { for child in &mut self.children {
let space_per_flex = remaining_space / remaining_flex; let space_per_flex = remaining_space / remaining_flex;
if let Some(flex) = Self::child_flex(child.as_ref()) { if let Some(flex) = Self::child_flex(&child) {
let child_max = space_per_flex * flex; let child_max = space_per_flex * flex;
let child_constraint = match self.axis { let child_constraint = match self.axis {
Axis::Horizontal => SizeConstraint::new( Axis::Horizontal => SizeConstraint::new(
@ -89,7 +88,7 @@ impl Element for Flex {
vec2f(constraint.max.x(), child_max), vec2f(constraint.max.x(), child_max),
), ),
}; };
let child_size = child.layout(child_constraint, ctx, app); let child_size = child.layout(child_constraint, ctx);
remaining_space -= child_size.along(self.axis); remaining_space -= child_size.along(self.axis);
remaining_flex -= flex; remaining_flex -= flex;
cross_axis_max = cross_axis_max.max(child_size.along(cross_axis)); cross_axis_max = cross_axis_max.max(child_size.along(cross_axis));
@ -110,45 +109,55 @@ impl Element for Flex {
if constraint.min.x().is_finite() { if constraint.min.x().is_finite() {
size.set_x(size.x().max(constraint.min.x())); size.set_x(size.x().max(constraint.min.x()));
} }
if constraint.min.y().is_finite() { if constraint.min.y().is_finite() {
size.set_y(size.y().max(constraint.min.y())); size.set_y(size.y().max(constraint.min.y()));
} }
self.size = Some(size); (size, ())
size
} }
fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &mut MutableAppContext) { fn after_layout(
&mut self,
_: Vector2F,
_: &mut Self::LayoutState,
ctx: &mut AfterLayoutContext,
) {
for child in &mut self.children { for child in &mut self.children {
child.after_layout(ctx, app); child.after_layout(ctx);
} }
} }
fn paint(&mut self, mut origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) { fn paint(
self.origin = Some(origin); &mut self,
bounds: pathfinder_geometry::rect::RectF,
_: &mut Self::LayoutState,
ctx: &mut PaintContext,
) -> Self::PaintState {
let mut child_origin = bounds.origin();
for child in &mut self.children { for child in &mut self.children {
child.paint(origin, ctx, app); child.paint(child_origin, ctx);
match self.axis { match self.axis {
Axis::Horizontal => origin += vec2f(child.size().unwrap().x(), 0.0), Axis::Horizontal => child_origin += vec2f(child.size().x(), 0.0),
Axis::Vertical => origin += vec2f(0.0, child.size().unwrap().y()), Axis::Vertical => child_origin += vec2f(0.0, child.size().y()),
} }
} }
} }
fn dispatch_event(&self, event: &Event, ctx: &mut EventContext, app: &AppContext) -> bool { fn dispatch_event(
&mut self,
event: &Event,
_: pathfinder_geometry::rect::RectF,
_: &mut Self::LayoutState,
_: &mut Self::PaintState,
ctx: &mut EventContext,
) -> bool {
let mut handled = false; let mut handled = false;
for child in &self.children { for child in &mut self.children {
if child.dispatch_event(event, ctx, app) { handled = child.dispatch_event(event, ctx) || handled;
handled = true;
}
} }
handled handled
} }
fn size(&self) -> Option<Vector2F> {
self.size
}
} }
struct FlexParentData { struct FlexParentData {
@ -156,46 +165,62 @@ struct FlexParentData {
} }
pub struct Expanded { pub struct Expanded {
parent_data: FlexParentData, metadata: FlexParentData,
child: Box<dyn Element>, child: ElementBox,
} }
impl Expanded { impl Expanded {
pub fn new(flex: f32, child: Box<dyn Element>) -> Self { pub fn new(flex: f32, child: ElementBox) -> Self {
Expanded { Expanded {
parent_data: FlexParentData { flex }, metadata: FlexParentData { flex },
child, child,
} }
} }
} }
impl Element for Expanded { impl Element for Expanded {
type LayoutState = ();
type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
ctx: &mut LayoutContext, ctx: &mut LayoutContext,
app: &AppContext, ) -> (Vector2F, Self::LayoutState) {
) -> Vector2F { let size = self.child.layout(constraint, ctx);
self.child.layout(constraint, ctx, app) (size, ())
} }
fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &mut MutableAppContext) { fn after_layout(
self.child.after_layout(ctx, app); &mut self,
_: Vector2F,
_: &mut Self::LayoutState,
ctx: &mut AfterLayoutContext,
) {
self.child.after_layout(ctx);
} }
fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) { fn paint(
self.child.paint(origin, ctx, app); &mut self,
bounds: pathfinder_geometry::rect::RectF,
_: &mut Self::LayoutState,
ctx: &mut PaintContext,
) -> Self::PaintState {
self.child.paint(bounds.origin(), ctx)
} }
fn dispatch_event(&self, event: &Event, ctx: &mut EventContext, app: &AppContext) -> bool { fn dispatch_event(
self.child.dispatch_event(event, ctx, app) &mut self,
event: &Event,
_: pathfinder_geometry::rect::RectF,
_: &mut Self::LayoutState,
_: &mut Self::PaintState,
ctx: &mut EventContext,
) -> bool {
self.child.dispatch_event(event, ctx)
} }
fn size(&self) -> Option<Vector2F> { fn metadata(&self) -> Option<&dyn Any> {
self.child.size() Some(&self.metadata)
}
fn parent_data(&self) -> Option<&dyn Any> {
Some(&self.parent_data)
} }
} }

View file

@ -3,8 +3,7 @@ use crate::{
fonts::{FamilyId, Properties}, fonts::{FamilyId, Properties},
geometry::vector::{vec2f, Vector2F}, geometry::vector::{vec2f, Vector2F},
text_layout::Line, text_layout::Line,
AfterLayoutContext, AppContext, Element, Event, EventContext, LayoutContext, MutableAppContext, AfterLayoutContext, Element, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
PaintContext, SizeConstraint,
}; };
use std::{ops::Range, sync::Arc}; use std::{ops::Range, sync::Arc};
@ -14,9 +13,11 @@ pub struct Label {
font_properties: Properties, font_properties: Properties,
font_size: f32, font_size: f32,
highlights: Option<Highlights>, highlights: Option<Highlights>,
layout_line: Option<Arc<Line>>, }
colors: Option<Vec<(Range<usize>, ColorU)>>,
size: Option<Vector2F>, pub struct LayoutState {
line: Arc<Line>,
colors: Vec<(Range<usize>, ColorU)>,
} }
pub struct Highlights { pub struct Highlights {
@ -33,9 +34,6 @@ impl Label {
font_properties: Properties::new(), font_properties: Properties::new(),
font_size, font_size,
highlights: None, highlights: None,
layout_line: None,
colors: None,
size: None,
} }
} }
@ -55,12 +53,14 @@ impl Label {
} }
impl Element for Label { impl Element for Label {
type LayoutState = LayoutState;
type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
ctx: &mut LayoutContext, ctx: &mut LayoutContext,
_: &AppContext, ) -> (Vector2F, Self::LayoutState) {
) -> Vector2F {
let font_id = ctx let font_id = ctx
.font_cache .font_cache
.select_font(self.family_id, &self.font_properties) .select_font(self.family_id, &self.font_properties)
@ -109,9 +109,7 @@ impl Element for Label {
colors = vec![(0..text_len, ColorU::black())]; colors = vec![(0..text_len, ColorU::black())];
} }
self.colors = Some(colors); let line = ctx.text_layout_cache.layout_str(
let layout_line = ctx.text_layout_cache.layout_str(
self.text.as_str(), self.text.as_str(),
self.font_size, self.font_size,
styles.as_slice(), styles.as_slice(),
@ -119,37 +117,33 @@ impl Element for Label {
); );
let size = vec2f( let size = vec2f(
layout_line line.width.max(constraint.min.x()).min(constraint.max.x()),
.width
.max(constraint.min.x())
.min(constraint.max.x()),
ctx.font_cache.line_height(font_id, self.font_size).ceil(), ctx.font_cache.line_height(font_id, self.font_size).ceil(),
); );
self.layout_line = Some(layout_line); (size, LayoutState { line, colors })
self.size = Some(size);
size
} }
fn after_layout(&mut self, _: &mut AfterLayoutContext, _: &mut MutableAppContext) {} fn after_layout(&mut self, _: Vector2F, _: &mut Self::LayoutState, _: &mut AfterLayoutContext) {
fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, _: &AppContext) {
// ctx.canvas.set_fill_style(FillStyle::Color(ColorU::black()));
// self.layout_line.as_ref().unwrap().paint(
// origin,
// RectF::new(origin, self.size.unwrap()),
// self.colors.as_ref().unwrap(),
// ctx.canvas,
// ctx.font_cache,
// );
} }
fn size(&self) -> Option<Vector2F> { fn paint(
self.size &mut self,
bounds: pathfinder_geometry::rect::RectF,
layout: &mut Self::LayoutState,
ctx: &mut PaintContext,
) -> Self::PaintState {
layout.line.paint(bounds, layout.colors.as_slice(), ctx)
} }
fn dispatch_event(&self, _: &Event, _: &mut EventContext, _: &AppContext) -> bool { fn dispatch_event(
&mut self,
_: &Event,
_: pathfinder_geometry::rect::RectF,
_: &mut Self::LayoutState,
_: &mut Self::PaintState,
_: &mut EventContext,
) -> bool {
false false
} }
} }

View file

@ -1,45 +1,42 @@
use super::{AppContext, Element, MutableAppContext};
use crate::{ use crate::{
fonts::{FamilyId, FontId, Properties}, fonts::{FamilyId, FontId, Properties},
geometry::vector::{vec2f, Vector2F}, geometry::vector::{vec2f, Vector2F},
AfterLayoutContext, Event, EventContext, LayoutContext, PaintContext, SizeConstraint, AfterLayoutContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
SizeConstraint,
}; };
pub struct LineBox { pub struct LineBox {
child: Box<dyn Element>, child: ElementBox,
family_id: FamilyId, family_id: FamilyId,
font_size: f32, font_size: f32,
font_properties: Properties, font_properties: Properties,
font_id: Option<FontId>,
size: Option<Vector2F>,
} }
impl LineBox { impl LineBox {
pub fn new(family_id: FamilyId, font_size: f32, child: Box<dyn Element>) -> Self { pub fn new(family_id: FamilyId, font_size: f32, child: ElementBox) -> Self {
Self { Self {
child, child,
family_id, family_id,
font_size, font_size,
font_properties: Properties::default(), font_properties: Properties::default(),
font_id: None,
size: None,
} }
} }
} }
impl Element for LineBox { impl Element for LineBox {
type LayoutState = Option<FontId>;
type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
ctx: &mut LayoutContext, ctx: &mut LayoutContext,
app: &AppContext, ) -> (Vector2F, Self::LayoutState) {
) -> Vector2F {
match ctx match ctx
.font_cache .font_cache
.select_font(self.family_id, &self.font_properties) .select_font(self.family_id, &self.font_properties)
{ {
Ok(font_id) => { Ok(font_id) => {
self.font_id = Some(font_id);
let line_height = ctx.font_cache.bounding_box(font_id, self.font_size).y(); let line_height = ctx.font_cache.bounding_box(font_id, self.font_size).y();
let child_max = vec2f( let child_max = vec2f(
constraint.max.x(), constraint.max.x(),
@ -49,36 +46,47 @@ impl Element for LineBox {
let child_size = self.child.layout( let child_size = self.child.layout(
SizeConstraint::new(constraint.min.min(child_max), child_max), SizeConstraint::new(constraint.min.min(child_max), child_max),
ctx, ctx,
app,
); );
let size = vec2f(child_size.x(), line_height); let size = vec2f(child_size.x(), line_height);
self.size = Some(size); (size, Some(font_id))
size
} }
Err(error) => { Err(error) => {
log::error!("can't layout LineBox: {}", error); log::error!("can't find font for LineBox: {}", error);
self.size = Some(constraint.min); (constraint.min, None)
constraint.min
} }
} }
} }
fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &mut MutableAppContext) { fn after_layout(
self.child.after_layout(ctx, app); &mut self,
_: Vector2F,
_: &mut Self::LayoutState,
ctx: &mut AfterLayoutContext,
) {
self.child.after_layout(ctx);
} }
fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) { fn paint(
if let Some(font_id) = self.font_id { &mut self,
let descent = ctx.font_cache.descent(font_id, self.font_size); bounds: pathfinder_geometry::rect::RectF,
self.child.paint(origin + vec2f(0.0, -descent), ctx, app); font_id: &mut Option<FontId>,
ctx: &mut PaintContext,
) -> Self::PaintState {
if let Some(font_id) = font_id {
let descent = ctx.font_cache.descent(*font_id, self.font_size);
self.child
.paint(bounds.origin() + vec2f(0.0, -descent), ctx);
} }
} }
fn size(&self) -> Option<Vector2F> { fn dispatch_event(
self.size &mut self,
} event: &Event,
_: pathfinder_geometry::rect::RectF,
fn dispatch_event(&self, event: &Event, ctx: &mut EventContext, app: &AppContext) -> bool { _: &mut Self::LayoutState,
self.child.dispatch_event(event, ctx, app) _: &mut Self::PaintState,
ctx: &mut EventContext,
) -> bool {
self.child.dispatch_event(event, ctx)
} }
} }

View file

@ -6,6 +6,7 @@ mod event_handler;
mod flex; mod flex;
mod label; mod label;
mod line_box; mod line_box;
mod new;
mod stack; mod stack;
mod svg; mod svg;
mod uniform_list; mod uniform_list;
@ -19,66 +20,33 @@ pub use event_handler::*;
pub use flex::*; pub use flex::*;
pub use label::*; pub use label::*;
pub use line_box::*; pub use line_box::*;
pub use new::*;
pub use stack::*; pub use stack::*;
pub use svg::*; pub use svg::*;
pub use uniform_list::*; pub use uniform_list::*;
use crate::{ use crate::{
AfterLayoutContext, AppContext, Event, EventContext, LayoutContext, MutableAppContext, AfterLayoutContext, AppContext, Event, EventContext, LayoutContext, PaintContext,
PaintContext, SizeConstraint, SizeConstraint,
}; };
use pathfinder_geometry::{rect::RectF, vector::Vector2F};
use std::any::Any;
pub trait Element { pub trait ParentElement<'a>: Extend<ElementBox> + Sized {
fn layout( fn add_children(&mut self, children: impl IntoIterator<Item = ElementBox>) {
&mut self,
constraint: SizeConstraint,
ctx: &mut LayoutContext,
app: &AppContext,
) -> Vector2F;
fn after_layout(&mut self, _: &mut AfterLayoutContext, _: &mut MutableAppContext) {}
fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext);
fn size(&self) -> Option<Vector2F>;
fn parent_data(&self) -> Option<&dyn Any> {
None
}
fn dispatch_event(&self, event: &Event, ctx: &mut EventContext, app: &AppContext) -> bool;
fn boxed(self) -> Box<dyn Element>
where
Self: 'static + Sized,
{
Box::new(self)
}
}
pub trait ParentElement<'a>: Extend<Box<dyn Element>> + Sized {
fn add_children(&mut self, children: impl IntoIterator<Item = Box<dyn Element>>) {
self.extend(children); self.extend(children);
} }
fn add_child(&mut self, child: Box<dyn Element>) { fn add_child(&mut self, child: ElementBox) {
self.add_children(Some(child)); self.add_children(Some(child));
} }
fn with_children(mut self, children: impl IntoIterator<Item = Box<dyn Element>>) -> Self { fn with_children(mut self, children: impl IntoIterator<Item = ElementBox>) -> Self {
self.add_children(children); self.add_children(children);
self self
} }
fn with_child(self, child: Box<dyn Element>) -> Self { fn with_child(self, child: ElementBox) -> Self {
self.with_children(Some(child)) self.with_children(Some(child))
} }
} }
impl<'a, T> ParentElement<'a> for T where T: Extend<Box<dyn Element>> {} impl<'a, T> ParentElement<'a> for T where T: Extend<ElementBox> {}
pub fn try_rect(origin: Option<Vector2F>, size: Option<Vector2F>) -> Option<RectF> {
origin.and_then(|origin| size.map(|size| RectF::new(origin, size)))
}

191
gpui/src/elements/new.rs Normal file
View file

@ -0,0 +1,191 @@
use crate::{
geometry::{rect::RectF, vector::Vector2F},
AfterLayoutContext, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
};
use core::panic;
use replace_with::replace_with_or_abort;
use std::any::Any;
trait AnyElement {
fn layout(&mut self, constraint: SizeConstraint, ctx: &mut LayoutContext) -> Vector2F;
fn after_layout(&mut self, _: &mut AfterLayoutContext) {}
fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext);
fn dispatch_event(&mut self, event: &Event, ctx: &mut EventContext) -> bool;
fn size(&self) -> Vector2F;
fn metadata(&self) -> Option<&dyn Any>;
}
pub trait Element {
type LayoutState;
type PaintState;
fn layout(
&mut self,
constraint: SizeConstraint,
ctx: &mut LayoutContext,
) -> (Vector2F, Self::LayoutState);
fn after_layout(
&mut self,
size: Vector2F,
layout: &mut Self::LayoutState,
ctx: &mut AfterLayoutContext,
);
fn paint(
&mut self,
bounds: RectF,
layout: &mut Self::LayoutState,
ctx: &mut PaintContext,
) -> Self::PaintState;
fn dispatch_event(
&mut self,
event: &Event,
bounds: RectF,
layout: &mut Self::LayoutState,
paint: &mut Self::PaintState,
ctx: &mut EventContext,
) -> bool;
fn metadata(&self) -> Option<&dyn Any> {
None
}
fn boxed(self) -> ElementBox
where
Self: 'static + Sized,
{
ElementBox(Box::new(Lifecycle::Init { element: self }))
}
}
pub enum Lifecycle<T: Element> {
Init {
element: T,
},
PostLayout {
element: T,
size: Vector2F,
layout: T::LayoutState,
},
PostPaint {
element: T,
bounds: RectF,
layout: T::LayoutState,
paint: T::PaintState,
},
}
pub struct ElementBox(Box<dyn AnyElement>);
impl<T: Element> AnyElement for Lifecycle<T> {
fn layout(&mut self, constraint: SizeConstraint, ctx: &mut LayoutContext) -> Vector2F {
let mut result = None;
replace_with_or_abort(self, |me| match me {
Lifecycle::Init { mut element }
| Lifecycle::PostLayout { mut element, .. }
| Lifecycle::PostPaint { mut element, .. } => {
let (size, layout) = element.layout(constraint, ctx);
result = Some(size);
Lifecycle::PostLayout {
element,
size,
layout,
}
}
});
result.unwrap()
}
fn after_layout(&mut self, ctx: &mut AfterLayoutContext) {
if let Lifecycle::PostLayout {
element,
size,
layout,
} = self
{
element.after_layout(*size, layout, ctx);
} else {
panic!("invalid element lifecycle state");
}
}
fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext) {
replace_with_or_abort(self, |me| {
if let Lifecycle::PostLayout {
mut element,
size,
mut layout,
} = me
{
let bounds = RectF::new(origin, size);
let paint = element.paint(bounds, &mut layout, ctx);
Lifecycle::PostPaint {
element,
bounds,
layout,
paint,
}
} else {
panic!("invalid element lifecycle state");
}
});
}
fn dispatch_event(&mut self, event: &Event, ctx: &mut EventContext) -> bool {
if let Lifecycle::PostPaint {
element,
bounds,
layout,
paint,
} = self
{
element.dispatch_event(event, *bounds, layout, paint, ctx)
} else {
panic!("invalid element lifecycle state");
}
}
fn size(&self) -> Vector2F {
match self {
Lifecycle::Init { .. } => panic!("invalid element lifecycle state"),
Lifecycle::PostLayout { size, .. } => *size,
Lifecycle::PostPaint { bounds, .. } => bounds.size(),
}
}
fn metadata(&self) -> Option<&dyn Any> {
match self {
Lifecycle::Init { element }
| Lifecycle::PostLayout { element, .. }
| Lifecycle::PostPaint { element, .. } => element.metadata(),
}
}
}
impl ElementBox {
pub fn layout(&mut self, constraint: SizeConstraint, ctx: &mut LayoutContext) -> Vector2F {
self.0.layout(constraint, ctx)
}
pub fn after_layout(&mut self, ctx: &mut AfterLayoutContext) {
self.0.after_layout(ctx);
}
pub fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext) {
self.0.paint(origin, ctx);
}
pub fn dispatch_event(&mut self, event: &Event, ctx: &mut EventContext) -> bool {
self.0.dispatch_event(event, ctx)
}
pub fn size(&self) -> Vector2F {
self.0.size()
}
pub fn metadata(&self) -> Option<&dyn Any> {
self.0.metadata()
}
}

View file

@ -1,65 +1,77 @@
use crate::{ use crate::{
geometry::vector::Vector2F, AfterLayoutContext, AppContext, Element, Event, EventContext, geometry::vector::Vector2F, AfterLayoutContext, Element, ElementBox, Event, EventContext,
LayoutContext, MutableAppContext, PaintContext, SizeConstraint, LayoutContext, PaintContext, SizeConstraint,
}; };
pub struct Stack { pub struct Stack {
children: Vec<Box<dyn Element>>, children: Vec<ElementBox>,
size: Option<Vector2F>,
} }
impl Stack { impl Stack {
pub fn new() -> Self { pub fn new() -> Self {
Stack { Stack {
children: Vec::new(), children: Vec::new(),
size: None,
} }
} }
} }
impl Element for Stack { impl Element for Stack {
type LayoutState = ();
type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
ctx: &mut LayoutContext, ctx: &mut LayoutContext,
app: &AppContext, ) -> (Vector2F, Self::LayoutState) {
) -> Vector2F {
let mut size = constraint.min; let mut size = constraint.min;
for child in &mut self.children { for child in &mut self.children {
size = size.max(child.layout(constraint, ctx, app)); size = size.max(child.layout(constraint, ctx));
} }
self.size = Some(size); (size, ())
size
} }
fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &mut MutableAppContext) { fn after_layout(
&mut self,
_: Vector2F,
_: &mut Self::LayoutState,
ctx: &mut AfterLayoutContext,
) {
for child in &mut self.children { for child in &mut self.children {
child.after_layout(ctx, app); child.after_layout(ctx);
} }
} }
fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) { fn paint(
&mut self,
bounds: pathfinder_geometry::rect::RectF,
_: &mut Self::LayoutState,
ctx: &mut PaintContext,
) -> Self::PaintState {
for child in &mut self.children { for child in &mut self.children {
child.paint(origin, ctx, app); child.paint(bounds.origin(), ctx);
} }
} }
fn dispatch_event(&self, event: &Event, ctx: &mut EventContext, app: &AppContext) -> bool { fn dispatch_event(
for child in self.children.iter().rev() { &mut self,
if child.dispatch_event(event, ctx, app) { event: &Event,
_: pathfinder_geometry::rect::RectF,
_: &mut Self::LayoutState,
_: &mut Self::PaintState,
ctx: &mut EventContext,
) -> bool {
for child in self.children.iter_mut().rev() {
if child.dispatch_event(event, ctx) {
return true; return true;
} }
} }
false false
} }
fn size(&self) -> Option<Vector2F> {
self.size
}
} }
impl Extend<Box<dyn Element>> for Stack { impl Extend<ElementBox> for Stack {
fn extend<T: IntoIterator<Item = Box<dyn Element>>>(&mut self, children: T) { fn extend<T: IntoIterator<Item = ElementBox>>(&mut self, children: T) {
self.children.extend(children) self.children.extend(children)
} }
} }

View file

@ -1,31 +1,27 @@
use crate::{ use crate::{
geometry::vector::Vector2F, AfterLayoutContext, AppContext, Element, Event, EventContext, geometry::vector::Vector2F, AfterLayoutContext, Element, Event, EventContext, LayoutContext,
LayoutContext, MutableAppContext, PaintContext, SizeConstraint, PaintContext, SizeConstraint,
}; };
pub struct Svg { pub struct Svg {
path: String, path: String,
// tree: Option<Rc<usvg::Tree>>,
size: Option<Vector2F>,
} }
impl Svg { impl Svg {
pub fn new(path: String) -> Self { pub fn new(path: String) -> Self {
Self { Self { path }
path,
// tree: None,
size: None,
}
} }
} }
impl Element for Svg { impl Element for Svg {
type LayoutState = ();
type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
constraint: SizeConstraint, _: SizeConstraint,
ctx: &mut LayoutContext, _: &mut LayoutContext,
_: &AppContext, ) -> (Vector2F, Self::LayoutState) {
) -> Vector2F {
// let size; // let size;
// match ctx.asset_cache.svg(&self.path) { // match ctx.asset_cache.svg(&self.path) {
// Ok(tree) => { // Ok(tree) => {
@ -52,25 +48,30 @@ impl Element for Svg {
// } // }
// }; // };
// self.size = Some(size);
// size // size
todo!() todo!()
} }
fn after_layout(&mut self, _: &mut AfterLayoutContext, _: &mut MutableAppContext) {} fn after_layout(&mut self, _: Vector2F, _: &mut Self::LayoutState, _: &mut AfterLayoutContext) {
fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, _: &AppContext) {
// if let Some(tree) = self.tree.as_ref() {
// ctx.canvas
// .draw_svg(tree, RectF::new(origin, self.size.unwrap()));
// }
} }
fn size(&self) -> Option<Vector2F> { fn paint(
self.size &mut self,
_: pathfinder_geometry::rect::RectF,
_: &mut Self::LayoutState,
_: &mut PaintContext,
) -> Self::PaintState {
} }
fn dispatch_event(&self, _: &Event, _: &mut EventContext, _: &AppContext) -> bool { fn dispatch_event(
&mut self,
_: &Event,
_: pathfinder_geometry::rect::RectF,
_: &mut Self::LayoutState,
_: &mut Self::PaintState,
_: &mut EventContext,
) -> bool {
false false
} }
} }

View file

@ -1,10 +1,13 @@
use super::{ use super::{
try_rect, AfterLayoutContext, AppContext, Element, Event, EventContext, LayoutContext, AfterLayoutContext, AppContext, Element, Event, EventContext, LayoutContext, PaintContext,
MutableAppContext, PaintContext, SizeConstraint, SizeConstraint,
}; };
use crate::geometry::{ use crate::{
rect::RectF, geometry::{
vector::{vec2f, Vector2F}, rect::RectF,
vector::{vec2f, Vector2F},
},
ElementBox,
}; };
use parking_lot::Mutex; use parking_lot::Mutex;
use std::{cmp, ops::Range, sync::Arc}; use std::{cmp, ops::Range, sync::Arc};
@ -12,11 +15,6 @@ use std::{cmp, ops::Range, sync::Arc};
#[derive(Clone)] #[derive(Clone)]
pub struct UniformListState(Arc<Mutex<StateInner>>); pub struct UniformListState(Arc<Mutex<StateInner>>);
struct StateInner {
scroll_top: f32,
scroll_to: Option<usize>,
}
impl UniformListState { impl UniformListState {
pub fn new() -> Self { pub fn new() -> Self {
Self(Arc::new(Mutex::new(StateInner { Self(Arc::new(Mutex::new(StateInner {
@ -28,34 +26,41 @@ impl UniformListState {
pub fn scroll_to(&self, item_ix: usize) { pub fn scroll_to(&self, item_ix: usize) {
self.0.lock().scroll_to = Some(item_ix); self.0.lock().scroll_to = Some(item_ix);
} }
pub fn scroll_top(&self) -> f32 {
self.0.lock().scroll_top
}
}
struct StateInner {
scroll_top: f32,
scroll_to: Option<usize>,
}
pub struct LayoutState {
scroll_max: f32,
item_height: f32,
items: Vec<ElementBox>,
} }
pub struct UniformList<F> pub struct UniformList<F>
where where
F: Fn(Range<usize>, &mut Vec<Box<dyn Element>>, &AppContext), F: Fn(Range<usize>, &mut Vec<ElementBox>, &AppContext),
{ {
state: UniformListState, state: UniformListState,
item_count: usize, item_count: usize,
append_items: F, append_items: F,
scroll_max: Option<f32>,
items: Vec<Box<dyn Element>>,
origin: Option<Vector2F>,
size: Option<Vector2F>,
} }
impl<F> UniformList<F> impl<F> UniformList<F>
where where
F: Fn(Range<usize>, &mut Vec<Box<dyn Element>>, &AppContext), F: Fn(Range<usize>, &mut Vec<ElementBox>, &AppContext),
{ {
pub fn new(state: UniformListState, item_count: usize, build_items: F) -> Self { pub fn new(state: UniformListState, item_count: usize, build_items: F) -> Self {
Self { Self {
state, state,
item_count, item_count,
append_items: build_items, append_items: build_items,
scroll_max: None,
items: Default::default(),
origin: None,
size: None,
} }
} }
@ -64,8 +69,8 @@ where
position: Vector2F, position: Vector2F,
delta: Vector2F, delta: Vector2F,
precise: bool, precise: bool,
scroll_max: f32,
ctx: &mut EventContext, ctx: &mut EventContext,
_: &AppContext,
) -> bool { ) -> bool {
if !self.rect().unwrap().contains_point(position) { if !self.rect().unwrap().contains_point(position) {
return false; return false;
@ -76,18 +81,15 @@ where
} }
let mut state = self.state.0.lock(); let mut state = self.state.0.lock();
state.scroll_top = (state.scroll_top - delta.y()) state.scroll_top = (state.scroll_top - delta.y()).max(0.0).min(scroll_max);
.max(0.0)
.min(self.scroll_max.unwrap());
ctx.dispatch_action("uniform_list:scroll", state.scroll_top); ctx.dispatch_action("uniform_list:scroll", state.scroll_top);
true true
} }
fn autoscroll(&mut self, list_height: f32, item_height: f32) { fn autoscroll(&mut self, scroll_max: f32, list_height: f32, item_height: f32) {
let mut state = self.state.0.lock(); let mut state = self.state.0.lock();
let scroll_max = self.item_count as f32 * item_height - list_height;
if state.scroll_top > scroll_max { if state.scroll_top > scroll_max {
state.scroll_top = scroll_max; state.scroll_top = scroll_max;
} }
@ -109,20 +111,23 @@ where
} }
fn rect(&self) -> Option<RectF> { fn rect(&self) -> Option<RectF> {
try_rect(self.origin, self.size) todo!()
// try_rect(self.origin, self.size)
} }
} }
impl<F> Element for UniformList<F> impl<F> Element for UniformList<F>
where where
F: Fn(Range<usize>, &mut Vec<Box<dyn Element>>, &AppContext), F: Fn(Range<usize>, &mut Vec<ElementBox>, &AppContext),
{ {
type LayoutState = LayoutState;
type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
ctx: &mut LayoutContext, ctx: &mut LayoutContext,
app: &AppContext, ) -> (Vector2F, Self::LayoutState) {
) -> Vector2F {
if constraint.max.y().is_infinite() { if constraint.max.y().is_infinite() {
unimplemented!( unimplemented!(
"UniformList does not support being rendered with an unconstrained height" "UniformList does not support being rendered with an unconstrained height"
@ -131,79 +136,91 @@ where
let mut size = constraint.max; let mut size = constraint.max;
let mut item_constraint = let mut item_constraint =
SizeConstraint::new(vec2f(size.x(), 0.0), vec2f(size.x(), f32::INFINITY)); SizeConstraint::new(vec2f(size.x(), 0.0), vec2f(size.x(), f32::INFINITY));
let mut item_height = 0.;
let mut scroll_max = 0.;
self.items.clear(); let mut items = Vec::new();
(self.append_items)(0..1, &mut self.items, app); (self.append_items)(0..1, &mut items, ctx.app);
if let Some(first_item) = self.items.first_mut() { if let Some(first_item) = items.first_mut() {
let mut item_size = first_item.layout(item_constraint, ctx, app); let mut item_size = first_item.layout(item_constraint, ctx);
item_size.set_x(size.x()); item_size.set_x(size.x());
item_constraint.min = item_size; item_constraint.min = item_size;
item_constraint.max = item_size; item_constraint.max = item_size;
item_height = item_size.y();
let scroll_height = self.item_count as f32 * item_size.y(); let scroll_height = self.item_count as f32 * item_height;
if scroll_height < size.y() { if scroll_height < size.y() {
size.set_y(size.y().min(scroll_height).max(constraint.min.y())); size.set_y(size.y().min(scroll_height).max(constraint.min.y()));
} }
self.autoscroll(size.y(), item_size.y()); scroll_max = item_height * self.item_count as f32 - size.y();
self.autoscroll(scroll_max, size.y(), item_height);
let start = cmp::min( items.clear();
(self.scroll_top() / item_size.y()) as usize, let start = cmp::min((self.scroll_top() / item_height) as usize, self.item_count);
self.item_count,
);
let end = cmp::min( let end = cmp::min(
self.item_count, self.item_count,
start + (size.y() / item_size.y()).ceil() as usize + 1, start + (size.y() / item_height).ceil() as usize + 1,
); );
self.items.clear(); (self.append_items)(start..end, &mut items, ctx.app);
(self.append_items)(start..end, &mut self.items, app); for item in &mut items {
item.layout(item_constraint, ctx);
self.scroll_max = Some(item_size.y() * self.item_count as f32 - size.y());
for item in &mut self.items {
item.layout(item_constraint, ctx, app);
} }
} }
self.size = Some(size); (
size size,
LayoutState {
item_height,
scroll_max,
items,
},
)
} }
fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &mut MutableAppContext) { fn after_layout(
for item in &mut self.items { &mut self,
item.after_layout(ctx, app); _: Vector2F,
layout: &mut Self::LayoutState,
ctx: &mut AfterLayoutContext,
) {
for item in &mut layout.items {
item.after_layout(ctx);
} }
} }
fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) { fn paint(
// self.origin = Some(origin); &mut self,
bounds: RectF,
layout: &mut Self::LayoutState,
ctx: &mut PaintContext,
) -> Self::PaintState {
// ctx.canvas.save();
// let mut clip_path = Path2D::new();
// clip_path.rect(RectF::new(origin, self.size.unwrap()));
// ctx.canvas.clip_path(clip_path, FillRule::Winding);
// if let Some(item) = self.items.first() { let mut item_origin =
// ctx.canvas.save(); bounds.origin() - vec2f(0.0, self.state.scroll_top() % layout.item_height);
// let mut clip_path = Path2D::new();
// clip_path.rect(RectF::new(origin, self.size.unwrap()));
// ctx.canvas.clip_path(clip_path, FillRule::Winding);
// let item_height = item.size().unwrap().y(); for item in &mut layout.items {
// let mut item_origin = origin - vec2f(0.0, self.state.0.lock().scroll_top % item_height); item.paint(item_origin, ctx);
// for item in &mut self.items { item_origin += vec2f(0.0, layout.item_height);
// item.paint(item_origin, ctx, app); }
// item_origin += vec2f(0.0, item_height); // ctx.canvas.restore();
// }
// ctx.canvas.restore();
// }
} }
fn size(&self) -> Option<Vector2F> { fn dispatch_event(
self.size &mut self,
} event: &Event,
_: RectF,
fn dispatch_event(&self, event: &Event, ctx: &mut EventContext, app: &AppContext) -> bool { layout: &mut Self::LayoutState,
_: &mut Self::PaintState,
ctx: &mut EventContext,
) -> bool {
let mut handled = false; let mut handled = false;
for item in &self.items { for item in &mut layout.items {
if item.dispatch_event(event, ctx, app) { handled = item.dispatch_event(event, ctx) || handled;
handled = true;
}
} }
match event { match event {
@ -212,7 +229,7 @@ where
delta, delta,
precise, precise,
} => { } => {
if self.scroll(*position, *delta, *precise, ctx, app) { if self.scroll(*position, *delta, *precise, layout.scroll_max, ctx) {
handled = true; handled = true;
} }
} }

View file

@ -11,7 +11,7 @@ pub use scene::{Border, Scene};
pub mod text_layout; pub mod text_layout;
pub use text_layout::TextLayoutCache; pub use text_layout::TextLayoutCache;
mod util; mod util;
pub use elements::Element; pub use elements::{Element, ElementBox};
pub mod executor; pub mod executor;
pub mod keymap; pub mod keymap;
pub mod platform; pub mod platform;

View file

@ -97,7 +97,6 @@ impl Renderer {
for quad_batch in layer.quads().chunks(batch_size) { for quad_batch in layer.quads().chunks(batch_size) {
for (ix, quad) in quad_batch.iter().enumerate() { for (ix, quad) in quad_batch.iter().enumerate() {
let bounds = quad.bounds * scene.scale_factor(); let bounds = quad.bounds * scene.scale_factor();
log::info!("render quad {:?}", quad);
let shader_quad = shaders::GPUIQuad { let shader_quad = shaders::GPUIQuad {
origin: bounds.origin().to_float2(), origin: bounds.origin().to_float2(),
size: bounds.size().to_float2(), size: bounds.size().to_float2(),

View file

@ -292,7 +292,6 @@ impl platform::WindowContext for WindowState {
} }
fn present_scene(&mut self, scene: Scene) { fn present_scene(&mut self, scene: Scene) {
log::info!("present scene");
self.scene_to_render = Some(scene); self.scene_to_render = Some(scene);
unsafe { unsafe {
let _: () = msg_send![self.native_window.contentView(), setNeedsDisplay: YES]; let _: () = msg_send![self.native_window.contentView(), setNeedsDisplay: YES];
@ -421,7 +420,6 @@ extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
} }
extern "C" fn display_layer(this: &Object, _: Sel, _: id) { extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
log::info!("display layer");
unsafe { unsafe {
let window_state = get_window_state(this); let window_state = get_window_state(this);
let mut window_state = window_state.as_ref().borrow_mut(); let mut window_state = window_state.as_ref().borrow_mut();

View file

@ -4,14 +4,14 @@ use crate::{
fonts::FontCache, fonts::FontCache,
platform::Event, platform::Event,
text_layout::TextLayoutCache, text_layout::TextLayoutCache,
AssetCache, Scene, AssetCache, ElementBox, Scene,
}; };
use pathfinder_geometry::vector::{vec2f, Vector2F}; use pathfinder_geometry::vector::{vec2f, Vector2F};
use std::{any::Any, collections::HashMap, sync::Arc}; use std::{any::Any, collections::HashMap, sync::Arc};
pub struct Presenter { pub struct Presenter {
window_id: usize, window_id: usize,
rendered_views: HashMap<usize, Box<dyn Element>>, rendered_views: HashMap<usize, ElementBox>,
parents: HashMap<usize, usize>, parents: HashMap<usize, usize>,
font_cache: Arc<FontCache>, font_cache: Arc<FontCache>,
text_layout_cache: TextLayoutCache, text_layout_cache: TextLayoutCache,
@ -68,13 +68,14 @@ impl Presenter {
if let Some(root_view_id) = app.root_view_id(self.window_id) { if let Some(root_view_id) = app.root_view_id(self.window_id) {
self.layout(window_size, app.downgrade()); self.layout(window_size, app.downgrade());
self.after_layout(app); self.after_layout(app);
let mut paint_ctx = PaintContext { let mut ctx = PaintContext {
scene: &mut scene, scene: &mut scene,
font_cache: &self.font_cache, font_cache: &self.font_cache,
text_layout_cache: &self.text_layout_cache, text_layout_cache: &self.text_layout_cache,
rendered_views: &mut self.rendered_views, rendered_views: &mut self.rendered_views,
app: app.downgrade(),
}; };
paint_ctx.paint(root_view_id, Vector2F::zero(), app.downgrade()); ctx.paint(root_view_id, Vector2F::zero());
self.text_layout_cache.finish_frame(); self.text_layout_cache.finish_frame();
} else { } else {
log::error!("could not find root_view_id for window {}", self.window_id); log::error!("could not find root_view_id for window {}", self.window_id);
@ -92,8 +93,9 @@ impl Presenter {
text_layout_cache: &self.text_layout_cache, text_layout_cache: &self.text_layout_cache,
asset_cache: &self.asset_cache, asset_cache: &self.asset_cache,
view_stack: Vec::new(), view_stack: Vec::new(),
app,
}; };
layout_ctx.layout(root_view_id, SizeConstraint::strict(size), app); layout_ctx.layout(root_view_id, SizeConstraint::strict(size));
} }
} }
@ -103,35 +105,27 @@ impl Presenter {
rendered_views: &mut self.rendered_views, rendered_views: &mut self.rendered_views,
font_cache: &self.font_cache, font_cache: &self.font_cache,
text_layout_cache: &self.text_layout_cache, text_layout_cache: &self.text_layout_cache,
app,
}; };
ctx.after_layout(root_view_id, app); ctx.after_layout(root_view_id);
} }
} }
pub fn responder_chain(&self, app: &AppContext) -> Option<Vec<usize>> { pub fn dispatch_event(&mut self, event: Event, app: &AppContext) -> Vec<ActionToDispatch> {
app.focused_view_id(self.window_id).map(|mut view_id| {
let mut chain = vec![view_id];
while let Some(parent_id) = self.parents.get(&view_id) {
view_id = *parent_id;
chain.push(view_id);
}
chain.reverse();
chain
})
}
pub fn dispatch_event(&self, event: Event, app: &AppContext) -> Vec<ActionToDispatch> {
let mut event_ctx = EventContext {
rendered_views: &self.rendered_views,
actions: Vec::new(),
font_cache: &self.font_cache,
text_layout_cache: &self.text_layout_cache,
view_stack: Vec::new(),
};
if let Some(root_view_id) = app.root_view_id(self.window_id) { if let Some(root_view_id) = app.root_view_id(self.window_id) {
event_ctx.dispatch_event_on_view(root_view_id, &event, app); let mut ctx = EventContext {
rendered_views: &mut self.rendered_views,
actions: Vec::new(),
font_cache: &self.font_cache,
text_layout_cache: &self.text_layout_cache,
view_stack: Vec::new(),
app,
};
ctx.dispatch_event(root_view_id, &event);
ctx.actions
} else {
Vec::new()
} }
event_ctx.actions
} }
} }
@ -142,22 +136,23 @@ pub struct ActionToDispatch {
} }
pub struct LayoutContext<'a> { pub struct LayoutContext<'a> {
rendered_views: &'a mut HashMap<usize, Box<dyn Element>>, rendered_views: &'a mut HashMap<usize, ElementBox>,
parents: &'a mut HashMap<usize, usize>, parents: &'a mut HashMap<usize, usize>,
pub font_cache: &'a FontCache, pub font_cache: &'a FontCache,
pub text_layout_cache: &'a TextLayoutCache, pub text_layout_cache: &'a TextLayoutCache,
pub asset_cache: &'a AssetCache, pub asset_cache: &'a AssetCache,
pub app: &'a AppContext,
view_stack: Vec<usize>, view_stack: Vec<usize>,
} }
impl<'a> LayoutContext<'a> { impl<'a> LayoutContext<'a> {
fn layout(&mut self, view_id: usize, constraint: SizeConstraint, app: &AppContext) -> Vector2F { fn layout(&mut self, view_id: usize, constraint: SizeConstraint) -> Vector2F {
if let Some(parent_id) = self.view_stack.last() { if let Some(parent_id) = self.view_stack.last() {
self.parents.insert(view_id, *parent_id); self.parents.insert(view_id, *parent_id);
} }
self.view_stack.push(view_id); self.view_stack.push(view_id);
let mut rendered_view = self.rendered_views.remove(&view_id).unwrap(); let mut rendered_view = self.rendered_views.remove(&view_id).unwrap();
let size = rendered_view.layout(constraint, self, app); let size = rendered_view.layout(constraint, self);
self.rendered_views.insert(view_id, rendered_view); self.rendered_views.insert(view_id, rendered_view);
self.view_stack.pop(); self.view_stack.pop();
size size
@ -165,55 +160,54 @@ impl<'a> LayoutContext<'a> {
} }
pub struct AfterLayoutContext<'a> { pub struct AfterLayoutContext<'a> {
rendered_views: &'a mut HashMap<usize, Box<dyn Element>>, rendered_views: &'a mut HashMap<usize, ElementBox>,
pub font_cache: &'a FontCache, pub font_cache: &'a FontCache,
pub text_layout_cache: &'a TextLayoutCache, pub text_layout_cache: &'a TextLayoutCache,
pub app: &'a mut MutableAppContext,
} }
impl<'a> AfterLayoutContext<'a> { impl<'a> AfterLayoutContext<'a> {
fn after_layout(&mut self, view_id: usize, app: &mut MutableAppContext) { fn after_layout(&mut self, view_id: usize) {
if let Some(mut view) = self.rendered_views.remove(&view_id) { if let Some(mut view) = self.rendered_views.remove(&view_id) {
view.after_layout(self, app); view.after_layout(self);
self.rendered_views.insert(view_id, view); self.rendered_views.insert(view_id, view);
} }
} }
} }
pub struct PaintContext<'a> { pub struct PaintContext<'a> {
rendered_views: &'a mut HashMap<usize, Box<dyn Element>>, rendered_views: &'a mut HashMap<usize, ElementBox>,
pub scene: &'a mut Scene, pub scene: &'a mut Scene,
pub font_cache: &'a FontCache, pub font_cache: &'a FontCache,
pub text_layout_cache: &'a TextLayoutCache, pub text_layout_cache: &'a TextLayoutCache,
pub app: &'a AppContext,
} }
impl<'a> PaintContext<'a> { impl<'a> PaintContext<'a> {
fn paint(&mut self, view_id: usize, origin: Vector2F, app: &AppContext) { fn paint(&mut self, view_id: usize, origin: Vector2F) {
if let Some(mut tree) = self.rendered_views.remove(&view_id) { if let Some(mut tree) = self.rendered_views.remove(&view_id) {
tree.paint(origin, self, app); tree.paint(origin, self);
self.rendered_views.insert(view_id, tree); self.rendered_views.insert(view_id, tree);
} }
} }
} }
pub struct EventContext<'a> { pub struct EventContext<'a> {
rendered_views: &'a HashMap<usize, Box<dyn Element>>, rendered_views: &'a mut HashMap<usize, ElementBox>,
actions: Vec<ActionToDispatch>, actions: Vec<ActionToDispatch>,
pub font_cache: &'a FontCache, pub font_cache: &'a FontCache,
pub text_layout_cache: &'a TextLayoutCache, pub text_layout_cache: &'a TextLayoutCache,
pub app: &'a AppContext,
view_stack: Vec<usize>, view_stack: Vec<usize>,
} }
impl<'a> EventContext<'a> { impl<'a> EventContext<'a> {
pub fn dispatch_event_on_view( fn dispatch_event(&mut self, view_id: usize, event: &Event) -> bool {
&mut self, if let Some(mut element) = self.rendered_views.remove(&view_id) {
view_id: usize,
event: &Event,
app: &AppContext,
) -> bool {
if let Some(element) = self.rendered_views.get(&view_id) {
self.view_stack.push(view_id); self.view_stack.push(view_id);
let result = element.dispatch_event(event, self, app); let result = element.dispatch_event(event, self);
self.view_stack.pop(); self.view_stack.pop();
self.rendered_views.insert(view_id, element);
result result
} else { } else {
false false
@ -298,47 +292,54 @@ impl SizeConstraint {
pub struct ChildView { pub struct ChildView {
view_id: usize, view_id: usize,
size: Option<Vector2F>,
origin: Option<Vector2F>,
} }
impl ChildView { impl ChildView {
pub fn new(view_id: usize) -> Self { pub fn new(view_id: usize) -> Self {
Self { Self { view_id }
view_id,
size: None,
origin: None,
}
} }
} }
impl Element for ChildView { impl Element for ChildView {
type LayoutState = ();
type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
ctx: &mut LayoutContext, ctx: &mut LayoutContext,
app: &AppContext, ) -> (Vector2F, Self::LayoutState) {
) -> Vector2F { let size = ctx.layout(self.view_id, constraint);
let size = ctx.layout(self.view_id, constraint, app); (size, ())
self.size = Some(size);
size
} }
fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &mut MutableAppContext) { fn after_layout(
ctx.after_layout(self.view_id, app); &mut self,
_: Vector2F,
_: &mut Self::LayoutState,
ctx: &mut AfterLayoutContext,
) {
ctx.after_layout(self.view_id);
} }
fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) { fn paint(
self.origin = Some(origin); &mut self,
ctx.paint(self.view_id, origin, app); bounds: pathfinder_geometry::rect::RectF,
_: &mut Self::LayoutState,
ctx: &mut PaintContext,
) -> Self::PaintState {
ctx.paint(self.view_id, bounds.origin());
} }
fn dispatch_event(&self, event: &Event, ctx: &mut EventContext, app: &AppContext) -> bool { fn dispatch_event(
ctx.dispatch_event_on_view(self.view_id, event, app) &mut self,
} event: &Event,
_: pathfinder_geometry::rect::RectF,
fn size(&self) -> Option<Vector2F> { _: &mut Self::LayoutState,
self.size _: &mut Self::PaintState,
ctx: &mut EventContext,
) -> bool {
ctx.dispatch_event(self.view_id, event)
} }
} }

View file

@ -2,7 +2,7 @@ use crate::{
color::ColorU, color::ColorU,
fonts::{FontCache, FontId, GlyphId}, fonts::{FontCache, FontId, GlyphId},
geometry::rect::RectF, geometry::rect::RectF,
scene::Scene, PaintContext,
}; };
use core_foundation::{ use core_foundation::{
attributed_string::CFMutableAttributedString, attributed_string::CFMutableAttributedString,
@ -188,11 +188,9 @@ impl Line {
pub fn paint( pub fn paint(
&self, &self,
_origin: Vector2F, _bounds: RectF,
_viewport_rect: RectF,
_colors: &[(Range<usize>, ColorU)], _colors: &[(Range<usize>, ColorU)],
_scene: Scene, _ctx: &mut PaintContext,
_font_cache: &FontCache,
) { ) {
// canvas.set_font_size(self.font_size); // canvas.set_font_size(self.font_size);
// let mut colors = colors.iter().peekable(); // let mut colors = colors.iter().peekable();

View file

@ -15,31 +15,25 @@ use std::{
pub struct BufferElement { pub struct BufferElement {
view: ViewHandle<BufferView>, view: ViewHandle<BufferView>,
layout: Option<LayoutState>,
paint: Option<PaintState>,
} }
impl BufferElement { impl BufferElement {
pub fn new(view: ViewHandle<BufferView>) -> Self { pub fn new(view: ViewHandle<BufferView>) -> Self {
Self { Self { view }
view,
layout: None,
paint: None,
}
} }
fn mouse_down( fn mouse_down(
&self, &self,
position: Vector2F, position: Vector2F,
cmd: bool, cmd: bool,
layout: &mut LayoutState,
paint: &mut PaintState,
ctx: &mut EventContext, ctx: &mut EventContext,
app: &AppContext,
) -> bool { ) -> bool {
let layout = self.layout.as_ref().unwrap(); if paint.text_bounds.contains_point(position) {
let paint = self.paint.as_ref().unwrap(); let view = self.view.as_ref(ctx.app);
if paint.text_rect.contains_point(position) { let position =
let view = self.view.as_ref(app); paint.point_for_position(view, layout, position, ctx.font_cache, ctx.app);
let position = paint.point_for_position(view, layout, position, ctx.font_cache, app);
ctx.dispatch_action("buffer:select", SelectAction::Begin { position, add: cmd }); ctx.dispatch_action("buffer:select", SelectAction::Begin { position, add: cmd });
true true
} else { } else {
@ -47,8 +41,8 @@ impl BufferElement {
} }
} }
fn mouse_up(&self, _position: Vector2F, ctx: &mut EventContext, app: &AppContext) -> bool { fn mouse_up(&self, _position: Vector2F, ctx: &mut EventContext) -> bool {
if self.view.as_ref(app).is_selecting() { if self.view.as_ref(ctx.app).is_selecting() {
ctx.dispatch_action("buffer:select", SelectAction::End); ctx.dispatch_action("buffer:select", SelectAction::End);
true true
} else { } else {
@ -56,13 +50,17 @@ impl BufferElement {
} }
} }
fn mouse_dragged(&self, position: Vector2F, ctx: &mut EventContext, app: &AppContext) -> bool { fn mouse_dragged(
let view = self.view.as_ref(app); &self,
let layout = self.layout.as_ref().unwrap(); position: Vector2F,
let paint = self.paint.as_ref().unwrap(); layout: &mut LayoutState,
paint: &mut PaintState,
ctx: &mut EventContext,
) -> bool {
let view = self.view.as_ref(ctx.app);
if view.is_selecting() { if view.is_selecting() {
let rect = self.paint.as_ref().unwrap().text_rect; let rect = paint.text_bounds;
let mut scroll_delta = Vector2F::zero(); let mut scroll_delta = Vector2F::zero();
let vertical_margin = view.line_height(ctx.font_cache).min(rect.height() / 3.0); let vertical_margin = view.line_height(ctx.font_cache).min(rect.height() / 3.0);
@ -92,15 +90,16 @@ impl BufferElement {
ctx.dispatch_action( ctx.dispatch_action(
"buffer:select", "buffer:select",
SelectAction::Update { SelectAction::Update {
position: paint.point_for_position(view, layout, position, ctx.font_cache, app), position: paint.point_for_position(
view,
layout,
position,
ctx.font_cache,
ctx.app,
),
scroll_position: (view.scroll_position() + scroll_delta).clamp( scroll_position: (view.scroll_position() + scroll_delta).clamp(
Vector2F::zero(), Vector2F::zero(),
self.layout.as_ref().unwrap().scroll_max( layout.scroll_max(view, ctx.font_cache, ctx.text_layout_cache, ctx.app),
view,
ctx.font_cache,
ctx.text_layout_cache,
app,
),
), ),
}, },
); );
@ -110,8 +109,8 @@ impl BufferElement {
} }
} }
fn key_down(&self, chars: &str, ctx: &mut EventContext, app: &AppContext) -> bool { fn key_down(&self, chars: &str, ctx: &mut EventContext) -> bool {
if self.view.is_focused(app) { if self.view.is_focused(ctx.app) {
if chars.is_empty() { if chars.is_empty() {
false false
} else { } else {
@ -132,12 +131,11 @@ impl BufferElement {
position: Vector2F, position: Vector2F,
delta: Vector2F, delta: Vector2F,
precise: bool, precise: bool,
layout: &mut LayoutState,
paint: &mut PaintState,
ctx: &mut EventContext, ctx: &mut EventContext,
app: &AppContext,
) -> bool { ) -> bool {
let paint = self.paint.as_ref().unwrap(); if !paint.bounds.contains_point(position) {
if !paint.rect.contains_point(position) {
return false; return false;
} }
@ -145,7 +143,7 @@ impl BufferElement {
todo!("still need to handle non-precise scroll events from a mouse wheel"); todo!("still need to handle non-precise scroll events from a mouse wheel");
} }
let view = self.view.as_ref(app); let view = self.view.as_ref(ctx.app);
let font_cache = &ctx.font_cache; let font_cache = &ctx.font_cache;
let layout_cache = &ctx.text_layout_cache; let layout_cache = &ctx.text_layout_cache;
let max_glyph_width = view.em_width(font_cache); let max_glyph_width = view.em_width(font_cache);
@ -155,10 +153,7 @@ impl BufferElement {
let y = (view.scroll_position().y() * line_height - delta.y()) / line_height; let y = (view.scroll_position().y() * line_height - delta.y()) / line_height;
let scroll_position = vec2f(x, y).clamp( let scroll_position = vec2f(x, y).clamp(
Vector2F::zero(), Vector2F::zero(),
self.layout layout.scroll_max(view, font_cache, layout_cache, ctx.app),
.as_ref()
.unwrap()
.scroll_max(view, font_cache, layout_cache, app),
); );
ctx.dispatch_action("buffer:scroll", scroll_position); ctx.dispatch_action("buffer:scroll", scroll_position);
@ -166,7 +161,7 @@ impl BufferElement {
true true
} }
fn paint_gutter(&mut self, rect: RectF, ctx: &mut PaintContext, app: &AppContext) { fn paint_gutter(&mut self, rect: RectF, ctx: &mut PaintContext) {
// if let Some(layout) = self.layout.as_ref() { // if let Some(layout) = self.layout.as_ref() {
// let view = self.view.as_ref(app); // let view = self.view.as_ref(app);
// let scene = &mut ctx.scene; // let scene = &mut ctx.scene;
@ -202,7 +197,7 @@ impl BufferElement {
// } // }
} }
fn paint_text(&mut self, rect: RectF, ctx: &mut PaintContext, app: &AppContext) { fn paint_text(&mut self, rect: RectF, ctx: &mut PaintContext) {
// if let Some(layout) = self.layout.as_ref() { // if let Some(layout) = self.layout.as_ref() {
// let scene = &mut ctx.scene; // let scene = &mut ctx.scene;
// let font_cache = &ctx.font_cache; // let font_cache = &ctx.font_cache;
@ -323,12 +318,15 @@ impl BufferElement {
} }
impl Element for BufferElement { impl Element for BufferElement {
type LayoutState = Option<LayoutState>;
type PaintState = Option<PaintState>;
fn layout( fn layout(
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
ctx: &mut LayoutContext, ctx: &mut LayoutContext,
app: &AppContext, ) -> (Vector2F, Self::LayoutState) {
) -> Vector2F { let app = ctx.app;
let mut size = constraint.max; let mut size = constraint.max;
if size.y().is_infinite() { if size.y().is_infinite() {
let view = self.view.as_ref(app); let view = self.view.as_ref(app);
@ -350,7 +348,7 @@ impl Element for BufferElement {
match view.max_line_number_width(ctx.font_cache, ctx.text_layout_cache, app) { match view.max_line_number_width(ctx.font_cache, ctx.text_layout_cache, app) {
Err(error) => { Err(error) => {
log::error!("error computing max line number width: {}", error); log::error!("error computing max line number width: {}", error);
return size; return (size, None);
} }
Ok(width) => gutter_width = width + gutter_padding * 2.0, Ok(width) => gutter_width = width + gutter_padding * 2.0,
} }
@ -368,7 +366,7 @@ impl Element for BufferElement {
match view.layout_line_numbers(size.y(), ctx.font_cache, ctx.text_layout_cache, app) { match view.layout_line_numbers(size.y(), ctx.font_cache, ctx.text_layout_cache, app) {
Err(error) => { Err(error) => {
log::error!("error laying out line numbers: {}", error); log::error!("error laying out line numbers: {}", error);
return size; return (size, None);
} }
Ok(layouts) => layouts, Ok(layouts) => layouts,
} }
@ -385,7 +383,7 @@ impl Element for BufferElement {
match view.layout_lines(start_row..end_row, font_cache, layout_cache, app) { match view.layout_lines(start_row..end_row, font_cache, layout_cache, app) {
Err(error) => { Err(error) => {
log::error!("error laying out lines: {}", error); log::error!("error laying out lines: {}", error);
return size; return (size, None);
} }
Ok(layouts) => { Ok(layouts) => {
for line in &layouts { for line in &layouts {
@ -398,84 +396,108 @@ impl Element for BufferElement {
} }
}; };
self.layout = Some(LayoutState { (
size, size,
gutter_size, Some(LayoutState {
gutter_padding, size,
text_size, gutter_size,
line_layouts, gutter_padding,
line_number_layouts, text_size,
max_visible_line_width, line_layouts,
autoscroll_horizontally, line_number_layouts,
}); max_visible_line_width,
autoscroll_horizontally,
size }),
)
} }
fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &mut MutableAppContext) { fn after_layout(
let layout = self.layout.as_ref().unwrap(); &mut self,
_: Vector2F,
layout: &mut Option<LayoutState>,
ctx: &mut AfterLayoutContext,
) {
if let Some(layout) = layout {
let app = ctx.app.downgrade();
let view = self.view.as_ref(app); let view = self.view.as_ref(app);
view.clamp_scroll_left( view.clamp_scroll_left(
layout layout
.scroll_max(view, ctx.font_cache, ctx.text_layout_cache, app.downgrade()) .scroll_max(view, ctx.font_cache, ctx.text_layout_cache, app)
.x(), .x(),
);
if layout.autoscroll_horizontally {
view.autoscroll_horizontally(
view.scroll_position().y() as u32,
layout.text_size.x(),
layout.scroll_width(view, ctx.font_cache, ctx.text_layout_cache, app.downgrade()),
view.em_width(ctx.font_cache),
&layout.line_layouts,
app.downgrade(),
); );
if layout.autoscroll_horizontally {
view.autoscroll_horizontally(
view.scroll_position().y() as u32,
layout.text_size.x(),
layout.scroll_width(view, ctx.font_cache, ctx.text_layout_cache, app),
view.em_width(ctx.font_cache),
&layout.line_layouts,
app,
);
}
} }
} }
fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) { fn paint(
let rect; &mut self,
let gutter_rect; bounds: RectF,
let text_rect; layout: &mut Self::LayoutState,
{ ctx: &mut PaintContext,
let layout = self.layout.as_ref().unwrap(); ) -> Self::PaintState {
rect = RectF::new(origin, layout.size); if let Some(layout) = layout {
gutter_rect = RectF::new(origin, layout.gutter_size); let gutter_bounds = RectF::new(bounds.origin(), layout.gutter_size);
text_rect = RectF::new( let text_bounds = RectF::new(
origin + vec2f(layout.gutter_size.x(), 0.0), bounds.origin() + vec2f(layout.gutter_size.x(), 0.0),
layout.text_size, layout.text_size,
); );
}
if self.view.as_ref(app).is_gutter_visible() { if self.view.as_ref(ctx.app).is_gutter_visible() {
self.paint_gutter(gutter_rect, ctx, app); self.paint_gutter(gutter_bounds, ctx);
} }
self.paint_text(text_rect, ctx, app); self.paint_text(text_bounds, ctx);
self.paint = Some(PaintState { rect, text_rect }); Some(PaintState {
} bounds,
text_bounds,
fn dispatch_event(&self, event: &Event, ctx: &mut EventContext, app: &AppContext) -> bool { })
match event { } else {
Event::LeftMouseDown { position, cmd } => self.mouse_down(*position, *cmd, ctx, app), None
Event::LeftMouseUp { position } => self.mouse_up(*position, ctx, app),
Event::LeftMouseDragged { position } => self.mouse_dragged(*position, ctx, app),
Event::ScrollWheel {
position,
delta,
precise,
} => self.scroll(*position, *delta, *precise, ctx, app),
Event::KeyDown { chars, .. } => self.key_down(chars, ctx, app),
} }
} }
fn size(&self) -> Option<Vector2F> { fn dispatch_event(
self.layout.as_ref().map(|layout| layout.size) &mut self,
event: &Event,
_: RectF,
layout: &mut Self::LayoutState,
paint: &mut Self::PaintState,
ctx: &mut EventContext,
) -> bool {
if let (Some(layout), Some(paint)) = (layout, paint) {
match event {
Event::LeftMouseDown { position, cmd } => {
self.mouse_down(*position, *cmd, layout, paint, ctx)
}
Event::LeftMouseUp { position } => self.mouse_up(*position, ctx),
Event::LeftMouseDragged { position } => {
self.mouse_dragged(*position, layout, paint, ctx)
}
Event::ScrollWheel {
position,
delta,
precise,
} => self.scroll(*position, *delta, *precise, layout, paint, ctx),
Event::KeyDown { chars, .. } => self.key_down(chars, ctx),
}
} else {
false
}
} }
} }
struct LayoutState { pub struct LayoutState {
size: Vector2F, size: Vector2F,
gutter_size: Vector2F, gutter_size: Vector2F,
gutter_padding: f32, gutter_padding: f32,
@ -518,9 +540,9 @@ impl LayoutState {
} }
} }
struct PaintState { pub struct PaintState {
rect: RectF, bounds: RectF,
text_rect: RectF, text_bounds: RectF,
} }
impl PaintState { impl PaintState {
@ -533,7 +555,7 @@ impl PaintState {
app: &AppContext, app: &AppContext,
) -> DisplayPoint { ) -> DisplayPoint {
let scroll_position = view.scroll_position(); let scroll_position = view.scroll_position();
let position = position - self.text_rect.origin(); let position = position - self.text_bounds.origin();
let y = position.y().max(0.0).min(layout.size.y()); let y = position.y().max(0.0).min(layout.size.y());
let row = ((y / view.line_height(font_cache)) + scroll_position.y()) as u32; let row = ((y / view.line_height(font_cache)) + scroll_position.y()) as u32;
let row = cmp::min(row, view.max_point(app).row()); let row = cmp::min(row, view.max_point(app).row());

View file

@ -8,7 +8,8 @@ use easy_parallel::Parallel;
use gpui::{ use gpui::{
fonts::{FontCache, Properties as FontProperties}, fonts::{FontCache, Properties as FontProperties},
keymap::Binding, keymap::Binding,
text_layout, App, AppContext, Element, Entity, ModelHandle, View, ViewContext, WeakViewHandle, text_layout, App, AppContext, Element, ElementBox, Entity, ModelHandle, View, ViewContext,
WeakViewHandle,
}; };
use gpui::{geometry::vector::Vector2F, TextLayoutCache}; use gpui::{geometry::vector::Vector2F, TextLayoutCache};
use parking_lot::Mutex; use parking_lot::Mutex;
@ -892,6 +893,7 @@ impl BufferView {
font_cache.scale_metric(bounds.width(), font_id, settings.buffer_font_size) font_cache.scale_metric(bounds.width(), font_id, settings.buffer_font_size)
} }
// TODO: Can we make this not return a result?
pub fn max_line_number_width( pub fn max_line_number_width(
&self, &self,
font_cache: &FontCache, font_cache: &FontCache,
@ -1140,7 +1142,7 @@ impl Entity for BufferView {
} }
impl View for BufferView { impl View for BufferView {
fn render<'a>(&self, app: &AppContext) -> Box<dyn Element> { fn render<'a>(&self, app: &AppContext) -> ElementBox {
BufferElement::new(self.handle.upgrade(app).unwrap()).boxed() BufferElement::new(self.handle.upgrade(app).unwrap()).boxed()
} }

View file

@ -57,7 +57,7 @@ impl View for FileFinder {
"FileFinder" "FileFinder"
} }
fn render(&self, _: &AppContext) -> Box<dyn Element> { fn render(&self, _: &AppContext) -> ElementBox {
Align::new( Align::new(
ConstrainedBox::new( ConstrainedBox::new(
Container::new( Container::new(
@ -98,7 +98,7 @@ impl View for FileFinder {
} }
impl FileFinder { impl FileFinder {
fn render_matches(&self) -> Box<dyn Element> { fn render_matches(&self) -> ElementBox {
if self.matches.is_empty() { if self.matches.is_empty() {
let settings = smol::block_on(self.settings.read()); let settings = smol::block_on(self.settings.read());
return Container::new( return Container::new(
@ -140,7 +140,7 @@ impl FileFinder {
path_match: &PathMatch, path_match: &PathMatch,
index: usize, index: usize,
app: &AppContext, app: &AppContext,
) -> Option<Box<dyn Element>> { ) -> Option<ElementBox> {
let tree_id = path_match.tree_id; let tree_id = path_match.tree_id;
let entry_id = path_match.entry_id; let entry_id = path_match.entry_id;
@ -227,7 +227,7 @@ impl FileFinder {
} }
EventHandler::new(container.boxed()) EventHandler::new(container.boxed())
.on_mouse_down(move |ctx, _| { .on_mouse_down(move |ctx| {
ctx.dispatch_action("file_finder:select", (tree_id, entry_id)); ctx.dispatch_action("file_finder:select", (tree_id, entry_id));
true true
}) })

View file

@ -173,7 +173,7 @@ impl Pane {
ctx.emit(Event::Split(direction)); ctx.emit(Event::Split(direction));
} }
fn render_tabs<'a>(&self, app: &AppContext) -> Box<dyn Element> { fn render_tabs<'a>(&self, app: &AppContext) -> ElementBox {
let settings = smol::block_on(self.settings.read()); let settings = smol::block_on(self.settings.read());
let border_color = ColorU::new(0xdb, 0xdb, 0xdc, 0xff); let border_color = ColorU::new(0xdb, 0xdb, 0xdc, 0xff);
@ -209,7 +209,7 @@ impl Pane {
1.0, 1.0,
ConstrainedBox::new( ConstrainedBox::new(
EventHandler::new(container.boxed()) EventHandler::new(container.boxed())
.on_mouse_down(move |ctx, _| { .on_mouse_down(move |ctx| {
ctx.dispatch_action("pane:activate_item", ix); ctx.dispatch_action("pane:activate_item", ix);
true true
}) })
@ -253,7 +253,7 @@ impl View for Pane {
"Pane" "Pane"
} }
fn render<'a>(&self, app: &AppContext) -> Box<dyn Element> { fn render<'a>(&self, app: &AppContext) -> ElementBox {
if let Some(active_item) = self.active_item() { if let Some(active_item) = self.active_item() {
Flex::column() Flex::column()
.with_child(self.render_tabs(app)) .with_child(self.render_tabs(app))

View file

@ -48,7 +48,7 @@ impl PaneGroup {
} }
} }
pub fn render<'a>(&self) -> Box<dyn Element> { pub fn render<'a>(&self) -> ElementBox {
self.root.render() self.root.render()
} }
} }
@ -77,7 +77,7 @@ impl Member {
Member::Axis(PaneAxis { axis, members }) Member::Axis(PaneAxis { axis, members })
} }
pub fn render<'a>(&self) -> Box<dyn Element> { pub fn render<'a>(&self) -> ElementBox {
match self { match self {
Member::Pane(view_id) => ChildView::new(*view_id).boxed(), Member::Pane(view_id) => ChildView::new(*view_id).boxed(),
Member::Axis(axis) => axis.render(), Member::Axis(axis) => axis.render(),
@ -168,7 +168,7 @@ impl PaneAxis {
} }
} }
fn render<'a>(&self) -> Box<dyn Element> { fn render<'a>(&self) -> ElementBox {
let last_member_ix = self.members.len() - 1; let last_member_ix = self.members.len() - 1;
Flex::new(self.axis) Flex::new(self.axis)
.with_children(self.members.iter().enumerate().map(|(ix, member)| { .with_children(self.members.iter().enumerate().map(|(ix, member)| {

View file

@ -141,7 +141,6 @@ impl WorkspaceView {
ctx.focus(&modal); ctx.focus(&modal);
self.modal = Some(modal.into()); self.modal = Some(modal.into());
} }
log::info!("toggle modal notify");
ctx.notify(); ctx.notify();
} }
@ -304,7 +303,7 @@ impl View for WorkspaceView {
"Workspace" "Workspace"
} }
fn render(&self, _: &AppContext) -> Box<dyn Element> { fn render(&self, _: &AppContext) -> ElementBox {
Container::new( Container::new(
// self.center.render(bump) // self.center.render(bump)
Stack::new() Stack::new()