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:
parent
046fe3fff9
commit
119aa452b6
27 changed files with 931 additions and 642 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
191
gpui/src/elements/new.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
})
|
})
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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)| {
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue