Merge branch 'master' into menus
This commit is contained in:
+41
-10
@@ -324,6 +324,7 @@ pub struct MutableAppContext {
|
||||
window_invalidations: HashMap<usize, WindowInvalidation>,
|
||||
invalidation_callbacks:
|
||||
HashMap<usize, Box<dyn FnMut(WindowInvalidation, &mut MutableAppContext)>>,
|
||||
debug_elements_callbacks: HashMap<usize, Box<dyn Fn(&AppContext) -> crate::json::Value>>,
|
||||
foreground: Rc<executor::Foreground>,
|
||||
future_handlers: Rc<RefCell<HashMap<usize, FutureHandler>>>,
|
||||
stream_handlers: Rc<RefCell<HashMap<usize, StreamHandler>>>,
|
||||
@@ -361,6 +362,7 @@ impl MutableAppContext {
|
||||
observations: HashMap::new(),
|
||||
window_invalidations: HashMap::new(),
|
||||
invalidation_callbacks: HashMap::new(),
|
||||
debug_elements_callbacks: HashMap::new(),
|
||||
foreground,
|
||||
future_handlers: Default::default(),
|
||||
stream_handlers: Default::default(),
|
||||
@@ -391,16 +393,29 @@ impl MutableAppContext {
|
||||
&self.ctx.background
|
||||
}
|
||||
|
||||
pub fn on_window_invalidated<F: 'static + FnMut(WindowInvalidation, &mut MutableAppContext)>(
|
||||
&mut self,
|
||||
window_id: usize,
|
||||
callback: F,
|
||||
) {
|
||||
pub fn on_window_invalidated<F>(&mut self, window_id: usize, callback: F)
|
||||
where
|
||||
F: 'static + FnMut(WindowInvalidation, &mut MutableAppContext),
|
||||
{
|
||||
self.invalidation_callbacks
|
||||
.insert(window_id, Box::new(callback));
|
||||
self.update_windows();
|
||||
}
|
||||
|
||||
pub fn on_debug_elements<F>(&mut self, window_id: usize, callback: F)
|
||||
where
|
||||
F: 'static + Fn(&AppContext) -> crate::json::Value,
|
||||
{
|
||||
self.debug_elements_callbacks
|
||||
.insert(window_id, Box::new(callback));
|
||||
}
|
||||
|
||||
pub fn debug_elements(&self, window_id: usize) -> Option<crate::json::Value> {
|
||||
self.debug_elements_callbacks
|
||||
.get(&window_id)
|
||||
.map(|debug_elements| debug_elements(&self.ctx))
|
||||
}
|
||||
|
||||
pub fn add_action<S, V, T, F>(&mut self, name: S, mut handler: F)
|
||||
where
|
||||
S: Into<String>,
|
||||
@@ -710,11 +725,19 @@ impl MutableAppContext {
|
||||
}));
|
||||
}
|
||||
|
||||
self.on_window_invalidated(window_id, move |invalidation, ctx| {
|
||||
let mut presenter = presenter.borrow_mut();
|
||||
presenter.invalidate(invalidation, ctx.downgrade());
|
||||
let scene = presenter.build_scene(window.size(), window.scale_factor(), ctx);
|
||||
window.present_scene(scene);
|
||||
{
|
||||
let presenter = presenter.clone();
|
||||
self.on_window_invalidated(window_id, move |invalidation, ctx| {
|
||||
let mut presenter = presenter.borrow_mut();
|
||||
presenter.invalidate(invalidation, ctx.downgrade());
|
||||
let scene =
|
||||
presenter.build_scene(window.size(), window.scale_factor(), ctx);
|
||||
window.present_scene(scene);
|
||||
});
|
||||
}
|
||||
|
||||
self.on_debug_elements(window_id, move |ctx| {
|
||||
presenter.borrow().debug_elements(ctx).unwrap()
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1115,6 +1138,10 @@ impl MutableAppContext {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy(&self, text: &str) {
|
||||
self.platform.copy(text);
|
||||
}
|
||||
}
|
||||
|
||||
impl ModelAsRef for MutableAppContext {
|
||||
@@ -1591,6 +1618,10 @@ impl<'a, T: View> ViewContext<'a, T> {
|
||||
&self.app.ctx.background
|
||||
}
|
||||
|
||||
pub fn debug_elements(&self) -> crate::json::Value {
|
||||
self.app.debug_elements(self.window_id).unwrap()
|
||||
}
|
||||
|
||||
pub fn focus<S>(&mut self, handle: S)
|
||||
where
|
||||
S: Into<AnyViewHandle>,
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
use crate::json::ToJson;
|
||||
pub use pathfinder_color::*;
|
||||
use serde_json::json;
|
||||
|
||||
impl ToJson for ColorU {
|
||||
fn to_json(&self) -> serde_json::Value {
|
||||
json!(format!("0x{:x}{:x}{:x}", self.r, self.g, self.b))
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
use crate::{
|
||||
AfterLayoutContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
|
||||
SizeConstraint,
|
||||
json, AfterLayoutContext, DebugContext, Element, ElementBox, Event, EventContext,
|
||||
LayoutContext, PaintContext, SizeConstraint,
|
||||
};
|
||||
use json::ToJson;
|
||||
use pathfinder_geometry::vector::{vec2f, Vector2F};
|
||||
use serde_json::json;
|
||||
|
||||
pub struct Align {
|
||||
child: ElementBox,
|
||||
@@ -79,4 +81,19 @@ impl Element for Align {
|
||||
) -> bool {
|
||||
self.child.dispatch_event(event, ctx)
|
||||
}
|
||||
|
||||
fn debug(
|
||||
&self,
|
||||
bounds: pathfinder_geometry::rect::RectF,
|
||||
_: &Self::LayoutState,
|
||||
_: &Self::PaintState,
|
||||
ctx: &DebugContext,
|
||||
) -> json::Value {
|
||||
json!({
|
||||
"type": "Align",
|
||||
"bounds": bounds.to_json(),
|
||||
"alignment": self.alignment.to_json(),
|
||||
"child": self.child.debug(ctx),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
use super::Element;
|
||||
use crate::PaintContext;
|
||||
use crate::{
|
||||
json::{self, json},
|
||||
DebugContext, PaintContext,
|
||||
};
|
||||
use json::ToJson;
|
||||
use pathfinder_geometry::{
|
||||
rect::RectF,
|
||||
vector::{vec2f, Vector2F},
|
||||
@@ -70,4 +74,14 @@ where
|
||||
) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn debug(
|
||||
&self,
|
||||
bounds: RectF,
|
||||
_: &Self::LayoutState,
|
||||
_: &Self::PaintState,
|
||||
_: &DebugContext,
|
||||
) -> json::Value {
|
||||
json!({"type": "Canvas", "bounds": bounds.to_json()})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
use json::ToJson;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{
|
||||
AfterLayoutContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
|
||||
SizeConstraint,
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json, AfterLayoutContext, DebugContext, Element, ElementBox, Event, EventContext,
|
||||
LayoutContext, PaintContext, SizeConstraint,
|
||||
};
|
||||
use pathfinder_geometry::vector::Vector2F;
|
||||
|
||||
pub struct ConstrainedBox {
|
||||
child: ElementBox,
|
||||
@@ -63,7 +66,7 @@ impl Element for ConstrainedBox {
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
bounds: pathfinder_geometry::rect::RectF,
|
||||
bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
ctx: &mut PaintContext,
|
||||
) -> Self::PaintState {
|
||||
@@ -73,11 +76,21 @@ impl Element for ConstrainedBox {
|
||||
fn dispatch_event(
|
||||
&mut self,
|
||||
event: &Event,
|
||||
_: pathfinder_geometry::rect::RectF,
|
||||
_: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
_: &mut Self::PaintState,
|
||||
ctx: &mut EventContext,
|
||||
) -> bool {
|
||||
self.child.dispatch_event(event, ctx)
|
||||
}
|
||||
|
||||
fn debug(
|
||||
&self,
|
||||
_: RectF,
|
||||
_: &Self::LayoutState,
|
||||
_: &Self::PaintState,
|
||||
ctx: &DebugContext,
|
||||
) -> json::Value {
|
||||
json!({"type": "ConstrainedBox", "constraint": self.constraint.to_json(), "child": self.child.debug(ctx)})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
use pathfinder_geometry::rect::RectF;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{
|
||||
color::ColorU,
|
||||
geometry::vector::{vec2f, Vector2F},
|
||||
json::ToJson,
|
||||
scene::{self, Border, Quad},
|
||||
AfterLayoutContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
|
||||
SizeConstraint,
|
||||
@@ -189,6 +191,28 @@ impl Element for Container {
|
||||
) -> bool {
|
||||
self.child.dispatch_event(event, ctx)
|
||||
}
|
||||
|
||||
fn debug(
|
||||
&self,
|
||||
bounds: RectF,
|
||||
_: &Self::LayoutState,
|
||||
_: &Self::PaintState,
|
||||
ctx: &crate::DebugContext,
|
||||
) -> serde_json::Value {
|
||||
json!({
|
||||
"type": "Container",
|
||||
"bounds": bounds.to_json(),
|
||||
"details": {
|
||||
"margin": self.margin.to_json(),
|
||||
"padding": self.padding.to_json(),
|
||||
"background_color": self.background_color.to_json(),
|
||||
"border": self.border.to_json(),
|
||||
"corner_radius": self.corner_radius,
|
||||
"shadow": self.shadow.to_json(),
|
||||
},
|
||||
"child": self.child.debug(ctx),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -199,6 +223,25 @@ pub struct Margin {
|
||||
right: f32,
|
||||
}
|
||||
|
||||
impl ToJson for Margin {
|
||||
fn to_json(&self) -> serde_json::Value {
|
||||
let mut value = json!({});
|
||||
if self.top > 0. {
|
||||
value["top"] = json!(self.top);
|
||||
}
|
||||
if self.right > 0. {
|
||||
value["right"] = json!(self.right);
|
||||
}
|
||||
if self.bottom > 0. {
|
||||
value["bottom"] = json!(self.bottom);
|
||||
}
|
||||
if self.left > 0. {
|
||||
value["left"] = json!(self.left);
|
||||
}
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Padding {
|
||||
top: f32,
|
||||
@@ -207,9 +250,38 @@ pub struct Padding {
|
||||
right: f32,
|
||||
}
|
||||
|
||||
impl ToJson for Padding {
|
||||
fn to_json(&self) -> serde_json::Value {
|
||||
let mut value = json!({});
|
||||
if self.top > 0. {
|
||||
value["top"] = json!(self.top);
|
||||
}
|
||||
if self.right > 0. {
|
||||
value["right"] = json!(self.right);
|
||||
}
|
||||
if self.bottom > 0. {
|
||||
value["bottom"] = json!(self.bottom);
|
||||
}
|
||||
if self.left > 0. {
|
||||
value["left"] = json!(self.left);
|
||||
}
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Shadow {
|
||||
offset: Vector2F,
|
||||
blur: f32,
|
||||
color: ColorU,
|
||||
}
|
||||
|
||||
impl ToJson for Shadow {
|
||||
fn to_json(&self) -> serde_json::Value {
|
||||
json!({
|
||||
"offset": self.offset.to_json(),
|
||||
"blur": self.blur,
|
||||
"color": self.color.to_json()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
use crate::geometry::{
|
||||
rect::RectF,
|
||||
vector::{vec2f, Vector2F},
|
||||
use crate::{
|
||||
geometry::{
|
||||
rect::RectF,
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
json::{json, ToJson},
|
||||
DebugContext,
|
||||
};
|
||||
use crate::{
|
||||
AfterLayoutContext, Element, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
|
||||
@@ -58,4 +62,17 @@ impl Element for Empty {
|
||||
) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn debug(
|
||||
&self,
|
||||
bounds: RectF,
|
||||
_: &Self::LayoutState,
|
||||
_: &Self::PaintState,
|
||||
_: &DebugContext,
|
||||
) -> serde_json::Value {
|
||||
json!({
|
||||
"type": "Empty",
|
||||
"bounds": bounds.to_json(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
use pathfinder_geometry::rect::RectF;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{
|
||||
geometry::vector::Vector2F, AfterLayoutContext, Element, ElementBox, Event, EventContext,
|
||||
LayoutContext, PaintContext, SizeConstraint,
|
||||
geometry::vector::Vector2F, AfterLayoutContext, DebugContext, Element, ElementBox, Event,
|
||||
EventContext, LayoutContext, PaintContext, SizeConstraint,
|
||||
};
|
||||
|
||||
pub struct EventHandler {
|
||||
@@ -49,7 +52,7 @@ impl Element for EventHandler {
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
bounds: pathfinder_geometry::rect::RectF,
|
||||
bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
ctx: &mut PaintContext,
|
||||
) -> Self::PaintState {
|
||||
@@ -59,7 +62,7 @@ impl Element for EventHandler {
|
||||
fn dispatch_event(
|
||||
&mut self,
|
||||
event: &Event,
|
||||
bounds: pathfinder_geometry::rect::RectF,
|
||||
bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
_: &mut Self::PaintState,
|
||||
ctx: &mut EventContext,
|
||||
@@ -80,4 +83,17 @@ impl Element for EventHandler {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn debug(
|
||||
&self,
|
||||
_: RectF,
|
||||
_: &Self::LayoutState,
|
||||
_: &Self::PaintState,
|
||||
ctx: &DebugContext,
|
||||
) -> serde_json::Value {
|
||||
json!({
|
||||
"type": "EventHandler",
|
||||
"child": self.child.debug(ctx),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
use std::any::Any;
|
||||
|
||||
use crate::{
|
||||
AfterLayoutContext, Axis, Element, ElementBox, Event, EventContext, LayoutContext,
|
||||
PaintContext, SizeConstraint, Vector2FExt,
|
||||
json::{self, ToJson, Value},
|
||||
AfterLayoutContext, Axis, DebugContext, Element, ElementBox, Event, EventContext,
|
||||
LayoutContext, PaintContext, SizeConstraint, Vector2FExt,
|
||||
};
|
||||
use pathfinder_geometry::vector::{vec2f, Vector2F};
|
||||
use pathfinder_geometry::{
|
||||
rect::RectF,
|
||||
vector::{vec2f, Vector2F},
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
pub struct Flex {
|
||||
axis: Axis,
|
||||
@@ -130,7 +135,7 @@ impl Element for Flex {
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
bounds: pathfinder_geometry::rect::RectF,
|
||||
bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
ctx: &mut PaintContext,
|
||||
) -> Self::PaintState {
|
||||
@@ -147,7 +152,7 @@ impl Element for Flex {
|
||||
fn dispatch_event(
|
||||
&mut self,
|
||||
event: &Event,
|
||||
_: pathfinder_geometry::rect::RectF,
|
||||
_: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
_: &mut Self::PaintState,
|
||||
ctx: &mut EventContext,
|
||||
@@ -158,6 +163,21 @@ impl Element for Flex {
|
||||
}
|
||||
handled
|
||||
}
|
||||
|
||||
fn debug(
|
||||
&self,
|
||||
bounds: RectF,
|
||||
_: &Self::LayoutState,
|
||||
_: &Self::PaintState,
|
||||
ctx: &DebugContext,
|
||||
) -> json::Value {
|
||||
json!({
|
||||
"type": "Flex",
|
||||
"bounds": bounds.to_json(),
|
||||
"axis": self.axis.to_json(),
|
||||
"children": self.children.iter().map(|child| child.debug(ctx)).collect::<Vec<json::Value>>()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct FlexParentData {
|
||||
@@ -202,7 +222,7 @@ impl Element for Expanded {
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
bounds: pathfinder_geometry::rect::RectF,
|
||||
bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
ctx: &mut PaintContext,
|
||||
) -> Self::PaintState {
|
||||
@@ -212,7 +232,7 @@ impl Element for Expanded {
|
||||
fn dispatch_event(
|
||||
&mut self,
|
||||
event: &Event,
|
||||
_: pathfinder_geometry::rect::RectF,
|
||||
_: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
_: &mut Self::PaintState,
|
||||
ctx: &mut EventContext,
|
||||
@@ -223,4 +243,18 @@ impl Element for Expanded {
|
||||
fn metadata(&self) -> Option<&dyn Any> {
|
||||
Some(&self.metadata)
|
||||
}
|
||||
|
||||
fn debug(
|
||||
&self,
|
||||
_: RectF,
|
||||
_: &Self::LayoutState,
|
||||
_: &Self::PaintState,
|
||||
ctx: &DebugContext,
|
||||
) -> Value {
|
||||
json!({
|
||||
"type": "Expanded",
|
||||
"flex": self.metadata.flex,
|
||||
"child": self.child.debug(ctx)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{
|
||||
color::ColorU,
|
||||
font_cache::FamilyId,
|
||||
@@ -6,8 +8,10 @@ use crate::{
|
||||
rect::RectF,
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
json::{ToJson, Value},
|
||||
text_layout::Line,
|
||||
AfterLayoutContext, Element, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
|
||||
AfterLayoutContext, DebugContext, Element, Event, EventContext, LayoutContext, PaintContext,
|
||||
SizeConstraint,
|
||||
};
|
||||
use std::{ops::Range, sync::Arc};
|
||||
|
||||
@@ -152,4 +156,32 @@ impl Element for Label {
|
||||
) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn debug(
|
||||
&self,
|
||||
bounds: RectF,
|
||||
_: &Self::LayoutState,
|
||||
_: &Self::PaintState,
|
||||
ctx: &DebugContext,
|
||||
) -> Value {
|
||||
json!({
|
||||
"type": "Label",
|
||||
"bounds": bounds.to_json(),
|
||||
"font_family": ctx.font_cache.family_name(self.family_id).unwrap(),
|
||||
"font_size": self.font_size,
|
||||
"font_properties": self.font_properties.to_json(),
|
||||
"text": &self.text,
|
||||
"highlights": self.highlights.to_json(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJson for Highlights {
|
||||
fn to_json(&self) -> Value {
|
||||
json!({
|
||||
"color": self.color.to_json(),
|
||||
"indices": self.indices,
|
||||
"font_properties": self.font_properties.to_json(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
use crate::{
|
||||
font_cache::FamilyId,
|
||||
fonts::Properties,
|
||||
geometry::vector::{vec2f, Vector2F},
|
||||
AfterLayoutContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
|
||||
SizeConstraint,
|
||||
geometry::{
|
||||
rect::RectF,
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
json::{json, ToJson},
|
||||
AfterLayoutContext, DebugContext, Element, ElementBox, Event, EventContext, LayoutContext,
|
||||
PaintContext, SizeConstraint,
|
||||
};
|
||||
|
||||
pub struct LineBox {
|
||||
@@ -85,4 +89,20 @@ impl Element for LineBox {
|
||||
) -> bool {
|
||||
self.child.dispatch_event(event, ctx)
|
||||
}
|
||||
|
||||
fn debug(
|
||||
&self,
|
||||
bounds: RectF,
|
||||
_: &Self::LayoutState,
|
||||
_: &Self::PaintState,
|
||||
ctx: &DebugContext,
|
||||
) -> serde_json::Value {
|
||||
json!({
|
||||
"bounds": bounds.to_json(),
|
||||
"font_family": ctx.font_cache.family_name(self.family_id).unwrap(),
|
||||
"font_size": self.font_size,
|
||||
"font_properties": self.font_properties.to_json(),
|
||||
"child": self.child.debug(ctx),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+63
-10
@@ -1,16 +1,18 @@
|
||||
use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
AfterLayoutContext, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
|
||||
json, AfterLayoutContext, DebugContext, Event, EventContext, LayoutContext, PaintContext,
|
||||
SizeConstraint,
|
||||
};
|
||||
use core::panic;
|
||||
use replace_with::replace_with_or_abort;
|
||||
use std::any::Any;
|
||||
use std::{any::Any, borrow::Cow};
|
||||
|
||||
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 debug(&self, ctx: &DebugContext) -> serde_json::Value;
|
||||
|
||||
fn size(&self) -> Vector2F;
|
||||
fn metadata(&self) -> Option<&dyn Any>;
|
||||
@@ -53,11 +55,32 @@ pub trait Element {
|
||||
None
|
||||
}
|
||||
|
||||
fn debug(
|
||||
&self,
|
||||
bounds: RectF,
|
||||
layout: &Self::LayoutState,
|
||||
paint: &Self::PaintState,
|
||||
ctx: &DebugContext,
|
||||
) -> serde_json::Value;
|
||||
|
||||
fn boxed(self) -> ElementBox
|
||||
where
|
||||
Self: 'static + Sized,
|
||||
{
|
||||
ElementBox(Box::new(Lifecycle::Init { element: self }))
|
||||
ElementBox {
|
||||
name: None,
|
||||
element: Box::new(Lifecycle::Init { element: self }),
|
||||
}
|
||||
}
|
||||
|
||||
fn named(self, name: impl Into<Cow<'static, str>>) -> ElementBox
|
||||
where
|
||||
Self: 'static + Sized,
|
||||
{
|
||||
ElementBox {
|
||||
name: Some(name.into()),
|
||||
element: Box::new(Lifecycle::Init { element: self }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +100,10 @@ pub enum Lifecycle<T: Element> {
|
||||
paint: T::PaintState,
|
||||
},
|
||||
}
|
||||
pub struct ElementBox(Box<dyn AnyElement>);
|
||||
pub struct ElementBox {
|
||||
name: Option<Cow<'static, str>>,
|
||||
element: Box<dyn AnyElement>,
|
||||
}
|
||||
|
||||
impl<T: Element> AnyElement for Lifecycle<T> {
|
||||
fn layout(&mut self, constraint: SizeConstraint, ctx: &mut LayoutContext) -> Vector2F {
|
||||
@@ -165,30 +191,57 @@ impl<T: Element> AnyElement for Lifecycle<T> {
|
||||
| Lifecycle::PostPaint { element, .. } => element.metadata(),
|
||||
}
|
||||
}
|
||||
|
||||
fn debug(&self, ctx: &DebugContext) -> serde_json::Value {
|
||||
match self {
|
||||
Lifecycle::PostPaint {
|
||||
element,
|
||||
bounds,
|
||||
layout,
|
||||
paint,
|
||||
} => element.debug(*bounds, layout, paint, ctx),
|
||||
_ => panic!("invalid element lifecycle state"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ElementBox {
|
||||
pub fn layout(&mut self, constraint: SizeConstraint, ctx: &mut LayoutContext) -> Vector2F {
|
||||
self.0.layout(constraint, ctx)
|
||||
self.element.layout(constraint, ctx)
|
||||
}
|
||||
|
||||
pub fn after_layout(&mut self, ctx: &mut AfterLayoutContext) {
|
||||
self.0.after_layout(ctx);
|
||||
self.element.after_layout(ctx);
|
||||
}
|
||||
|
||||
pub fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext) {
|
||||
self.0.paint(origin, ctx);
|
||||
self.element.paint(origin, ctx);
|
||||
}
|
||||
|
||||
pub fn dispatch_event(&mut self, event: &Event, ctx: &mut EventContext) -> bool {
|
||||
self.0.dispatch_event(event, ctx)
|
||||
self.element.dispatch_event(event, ctx)
|
||||
}
|
||||
|
||||
pub fn size(&self) -> Vector2F {
|
||||
self.0.size()
|
||||
self.element.size()
|
||||
}
|
||||
|
||||
pub fn metadata(&self) -> Option<&dyn Any> {
|
||||
self.0.metadata()
|
||||
self.element.metadata()
|
||||
}
|
||||
|
||||
pub fn debug(&self, ctx: &DebugContext) -> json::Value {
|
||||
let mut value = self.element.debug(ctx);
|
||||
|
||||
if let Some(name) = &self.name {
|
||||
if let json::Value::Object(map) = &mut value {
|
||||
let mut new_map: crate::json::Map<String, serde_json::Value> = Default::default();
|
||||
new_map.insert("name".into(), json::Value::String(name.to_string()));
|
||||
new_map.append(map);
|
||||
return json::Value::Object(new_map);
|
||||
}
|
||||
}
|
||||
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
AfterLayoutContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
|
||||
SizeConstraint,
|
||||
json::{self, json, ToJson},
|
||||
AfterLayoutContext, DebugContext, Element, ElementBox, Event, EventContext, LayoutContext,
|
||||
PaintContext, SizeConstraint,
|
||||
};
|
||||
|
||||
pub struct Stack {
|
||||
@@ -71,6 +72,20 @@ impl Element for Stack {
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn debug(
|
||||
&self,
|
||||
bounds: RectF,
|
||||
_: &Self::LayoutState,
|
||||
_: &Self::PaintState,
|
||||
ctx: &DebugContext,
|
||||
) -> json::Value {
|
||||
json!({
|
||||
"type": "Stack",
|
||||
"bounds": bounds.to_json(),
|
||||
"children": self.children.iter().map(|child| child.debug(ctx)).collect::<Vec<json::Value>>()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Extend<ElementBox> for Stack {
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{
|
||||
color::ColorU,
|
||||
geometry::{
|
||||
rect::RectF,
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
scene, AfterLayoutContext, Element, Event, EventContext, LayoutContext, PaintContext,
|
||||
SizeConstraint,
|
||||
scene, AfterLayoutContext, DebugContext, Element, Event, EventContext, LayoutContext,
|
||||
PaintContext, SizeConstraint,
|
||||
};
|
||||
|
||||
pub struct Svg {
|
||||
@@ -86,8 +88,25 @@ impl Element for Svg {
|
||||
) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn debug(
|
||||
&self,
|
||||
bounds: RectF,
|
||||
_: &Self::LayoutState,
|
||||
_: &Self::PaintState,
|
||||
_: &DebugContext,
|
||||
) -> serde_json::Value {
|
||||
json!({
|
||||
"type": "Svg",
|
||||
"bounds": bounds.to_json(),
|
||||
"path": self.path,
|
||||
"color": self.color.to_json(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
use crate::json::ToJson;
|
||||
|
||||
fn from_usvg_rect(rect: usvg::Rect) -> RectF {
|
||||
RectF::new(
|
||||
vec2f(rect.x() as f32, rect.y() as f32),
|
||||
|
||||
@@ -7,8 +7,10 @@ use crate::{
|
||||
rect::RectF,
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
json::{self, json},
|
||||
ElementBox,
|
||||
};
|
||||
use json::ToJson;
|
||||
use parking_lot::Mutex;
|
||||
use std::{cmp, ops::Range, sync::Arc};
|
||||
|
||||
@@ -236,4 +238,21 @@ where
|
||||
|
||||
handled
|
||||
}
|
||||
|
||||
fn debug(
|
||||
&self,
|
||||
bounds: RectF,
|
||||
layout: &Self::LayoutState,
|
||||
_: &Self::PaintState,
|
||||
ctx: &crate::DebugContext,
|
||||
) -> json::Value {
|
||||
json!({
|
||||
"type": "UniformList",
|
||||
"bounds": bounds.to_json(),
|
||||
"scroll_max": layout.scroll_max,
|
||||
"item_height": layout.item_height,
|
||||
"items": layout.items.iter().map(|item| item.debug(ctx)).collect::<Vec<json::Value>>()
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,15 @@ impl FontCache {
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn family_name(&self, family_id: FamilyId) -> Result<String> {
|
||||
self.0
|
||||
.read()
|
||||
.families
|
||||
.get(family_id.0)
|
||||
.ok_or_else(|| anyhow!("invalid family id"))
|
||||
.map(|family| family.name.clone())
|
||||
}
|
||||
|
||||
pub fn load_family(&self, names: &[&str]) -> Result<FamilyId> {
|
||||
for name in names {
|
||||
let state = self.0.upgradable_read();
|
||||
|
||||
+56
-1
@@ -1,7 +1,62 @@
|
||||
use crate::json::json;
|
||||
pub use font_kit::metrics::Metrics;
|
||||
pub use font_kit::properties::{Properties, Weight};
|
||||
pub use font_kit::properties::{Properties, Stretch, Style, Weight};
|
||||
|
||||
use crate::json::ToJson;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub struct FontId(pub usize);
|
||||
|
||||
pub type GlyphId = u32;
|
||||
|
||||
impl ToJson for Properties {
|
||||
fn to_json(&self) -> crate::json::Value {
|
||||
json!({
|
||||
"style": self.style.to_json(),
|
||||
"weight": self.weight.to_json(),
|
||||
"stretch": self.stretch.to_json(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJson for Style {
|
||||
fn to_json(&self) -> crate::json::Value {
|
||||
match self {
|
||||
Style::Normal => json!("normal"),
|
||||
Style::Italic => json!("italic"),
|
||||
Style::Oblique => json!("oblique"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJson for Weight {
|
||||
fn to_json(&self) -> crate::json::Value {
|
||||
if self.0 == Weight::THIN.0 {
|
||||
json!("thin")
|
||||
} else if self.0 == Weight::EXTRA_LIGHT.0 {
|
||||
json!("extra light")
|
||||
} else if self.0 == Weight::LIGHT.0 {
|
||||
json!("light")
|
||||
} else if self.0 == Weight::NORMAL.0 {
|
||||
json!("normal")
|
||||
} else if self.0 == Weight::MEDIUM.0 {
|
||||
json!("medium")
|
||||
} else if self.0 == Weight::SEMIBOLD.0 {
|
||||
json!("semibold")
|
||||
} else if self.0 == Weight::BOLD.0 {
|
||||
json!("bold")
|
||||
} else if self.0 == Weight::EXTRA_BOLD.0 {
|
||||
json!("extra bold")
|
||||
} else if self.0 == Weight::BLACK.0 {
|
||||
json!("black")
|
||||
} else {
|
||||
json!(self.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJson for Stretch {
|
||||
fn to_json(&self) -> serde_json::Value {
|
||||
json!(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
+14
-1
@@ -1,7 +1,8 @@
|
||||
use super::scene::{Path, PathVertex};
|
||||
use crate::color::ColorU;
|
||||
use crate::{color::ColorU, json::ToJson};
|
||||
pub use pathfinder_geometry::*;
|
||||
use rect::RectF;
|
||||
use serde_json::json;
|
||||
use vector::{vec2f, Vector2F};
|
||||
|
||||
pub struct PathBuilder {
|
||||
@@ -106,3 +107,15 @@ impl PathBuilder {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJson for Vector2F {
|
||||
fn to_json(&self) -> serde_json::Value {
|
||||
json!([self.x(), self.y()])
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJson for RectF {
|
||||
fn to_json(&self) -> serde_json::Value {
|
||||
json!({"origin": self.origin().to_json(), "size": self.size().to_json()})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
pub use serde_json::*;
|
||||
|
||||
pub trait ToJson {
|
||||
fn to_json(&self) -> Value;
|
||||
}
|
||||
|
||||
impl<T: ToJson> ToJson for Option<T> {
|
||||
fn to_json(&self) -> Value {
|
||||
if let Some(value) = self.as_ref() {
|
||||
value.to_json()
|
||||
} else {
|
||||
json!(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -18,11 +18,12 @@ mod util;
|
||||
pub use elements::{Element, ElementBox};
|
||||
pub mod executor;
|
||||
pub use executor::Task;
|
||||
pub mod color;
|
||||
pub mod json;
|
||||
pub mod keymap;
|
||||
pub mod platform;
|
||||
pub use pathfinder_color as color;
|
||||
pub use platform::Event;
|
||||
pub use presenter::{
|
||||
AfterLayoutContext, Axis, EventContext, LayoutContext, PaintContext, SizeConstraint,
|
||||
Vector2FExt,
|
||||
AfterLayoutContext, Axis, DebugContext, EventContext, LayoutContext, PaintContext,
|
||||
SizeConstraint, Vector2FExt,
|
||||
};
|
||||
|
||||
@@ -2,12 +2,12 @@ use super::{BoolExt as _, Dispatcher, FontSystem, Window};
|
||||
use crate::{executor, platform};
|
||||
use anyhow::Result;
|
||||
use cocoa::{
|
||||
appkit::{NSApplication, NSOpenPanel, NSModalResponse},
|
||||
appkit::{NSApplication, NSModalResponse, NSOpenPanel, NSPasteboard, NSPasteboardTypeString},
|
||||
base::nil,
|
||||
foundation::{NSArray, NSString, NSURL},
|
||||
foundation::{NSArray, NSData, NSString, NSURL},
|
||||
};
|
||||
use objc::{msg_send, sel, sel_impl};
|
||||
use std::{path::PathBuf, rc::Rc, sync::Arc};
|
||||
use std::{ffi::c_void, path::PathBuf, rc::Rc, sync::Arc};
|
||||
|
||||
pub struct App {
|
||||
dispatcher: Arc<Dispatcher>,
|
||||
@@ -84,4 +84,17 @@ impl platform::App for App {
|
||||
let _: () = msg_send![app, terminate: nil];
|
||||
}
|
||||
}
|
||||
|
||||
fn copy(&self, text: &str) {
|
||||
unsafe {
|
||||
let data = NSData::dataWithBytes_length_(
|
||||
nil,
|
||||
text.as_ptr() as *const c_void,
|
||||
text.len() as u64,
|
||||
);
|
||||
let pasteboard = NSPasteboard::generalPasteboard(nil);
|
||||
pasteboard.clearContents();
|
||||
pasteboard.setData_forType(data, NSPasteboardTypeString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ pub trait App {
|
||||
fn prompt_for_paths(&self, options: PathPromptOptions) -> Option<Vec<PathBuf>>;
|
||||
fn fonts(&self) -> Arc<dyn FontSystem>;
|
||||
fn quit(&self);
|
||||
fn copy(&self, text: &str);
|
||||
}
|
||||
|
||||
pub trait Dispatcher: Send + Sync {
|
||||
|
||||
@@ -52,6 +52,8 @@ impl super::App for App {
|
||||
fn prompt_for_paths(&self, _: super::PathPromptOptions) -> Option<Vec<std::path::PathBuf>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn copy(&self, _: &str) {}
|
||||
}
|
||||
|
||||
impl Window {
|
||||
|
||||
@@ -2,11 +2,13 @@ use crate::{
|
||||
app::{AppContext, MutableAppContext, WindowInvalidation},
|
||||
elements::Element,
|
||||
font_cache::FontCache,
|
||||
json::{self, ToJson},
|
||||
platform::Event,
|
||||
text_layout::TextLayoutCache,
|
||||
AssetCache, ElementBox, Scene,
|
||||
};
|
||||
use pathfinder_geometry::vector::{vec2f, Vector2F};
|
||||
use serde_json::json;
|
||||
use std::{any::Any, collections::HashMap, sync::Arc};
|
||||
|
||||
pub struct Presenter {
|
||||
@@ -128,6 +130,18 @@ impl Presenter {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn debug_elements(&self, ctx: &AppContext) -> Option<json::Value> {
|
||||
ctx.root_view_id(self.window_id)
|
||||
.and_then(|root_view_id| self.rendered_views.get(&root_view_id))
|
||||
.map(|root_element| {
|
||||
root_element.debug(&DebugContext {
|
||||
rendered_views: &self.rendered_views,
|
||||
font_cache: &self.font_cache,
|
||||
app: ctx,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ActionToDispatch {
|
||||
@@ -224,6 +238,12 @@ impl<'a> EventContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DebugContext<'a> {
|
||||
rendered_views: &'a HashMap<usize, ElementBox>,
|
||||
pub font_cache: &'a FontCache,
|
||||
pub app: &'a AppContext,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Axis {
|
||||
Horizontal,
|
||||
@@ -239,6 +259,15 @@ impl Axis {
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJson for Axis {
|
||||
fn to_json(&self) -> serde_json::Value {
|
||||
match self {
|
||||
Axis::Horizontal => json!("horizontal"),
|
||||
Axis::Vertical => json!("vertical"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Vector2FExt {
|
||||
fn along(self, axis: Axis) -> f32;
|
||||
}
|
||||
@@ -291,6 +320,15 @@ impl SizeConstraint {
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJson for SizeConstraint {
|
||||
fn to_json(&self) -> serde_json::Value {
|
||||
json!({
|
||||
"min": self.min.to_json(),
|
||||
"max": self.max.to_json(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ChildView {
|
||||
view_id: usize,
|
||||
}
|
||||
@@ -342,6 +380,25 @@ impl Element for ChildView {
|
||||
) -> bool {
|
||||
ctx.dispatch_event(self.view_id, event)
|
||||
}
|
||||
|
||||
fn debug(
|
||||
&self,
|
||||
bounds: pathfinder_geometry::rect::RectF,
|
||||
_: &Self::LayoutState,
|
||||
_: &Self::PaintState,
|
||||
ctx: &DebugContext,
|
||||
) -> serde_json::Value {
|
||||
json!({
|
||||
"type": "ChildView",
|
||||
"view_id": self.view_id,
|
||||
"bounds": bounds.to_json(),
|
||||
"child": if let Some(view) = ctx.rendered_views.get(&self.view_id) {
|
||||
view.debug(ctx)
|
||||
} else {
|
||||
json!(null)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{
|
||||
color::ColorU,
|
||||
fonts::{FontId, GlyphId},
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json::ToJson,
|
||||
};
|
||||
|
||||
pub struct Scene {
|
||||
@@ -258,3 +261,22 @@ impl Border {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJson for Border {
|
||||
fn to_json(&self) -> serde_json::Value {
|
||||
let mut value = json!({});
|
||||
if self.top {
|
||||
value["top"] = json!(self.width);
|
||||
}
|
||||
if self.right {
|
||||
value["right"] = json!(self.width);
|
||||
}
|
||||
if self.bottom {
|
||||
value["bottom"] = json!(self.width);
|
||||
}
|
||||
if self.left {
|
||||
value["left"] = json!(self.width);
|
||||
}
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user