From 06643211252e002dadb82a2a6e21c134868425e7 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 6 Apr 2021 23:50:13 -0600 Subject: [PATCH] Add ability to debug element trees as JSON --- Cargo.lock | 6 ++- gpui/Cargo.toml | 2 + gpui/examples/text.rs | 17 +++++-- gpui/src/color.rs | 9 ++++ gpui/src/elements/align.rs | 21 +++++++- gpui/src/elements/canvas.rs | 16 ++++++- gpui/src/elements/constrained_box.rs | 23 +++++++-- gpui/src/elements/container.rs | 72 ++++++++++++++++++++++++++++ gpui/src/elements/empty.rs | 23 +++++++-- gpui/src/elements/event_handler.rs | 24 ++++++++-- gpui/src/elements/flex.rs | 48 ++++++++++++++++--- gpui/src/elements/label.rs | 34 ++++++++++++- gpui/src/elements/line_box.rs | 26 ++++++++-- gpui/src/elements/new.rs | 28 ++++++++++- gpui/src/elements/stack.rs | 19 +++++++- gpui/src/elements/svg.rs | 23 ++++++++- gpui/src/elements/uniform_list.rs | 19 ++++++++ gpui/src/font_cache.rs | 9 ++++ gpui/src/fonts.rs | 57 +++++++++++++++++++++- gpui/src/geometry.rs | 15 +++++- gpui/src/json.rs | 15 ++++++ gpui/src/lib.rs | 7 +-- gpui/src/presenter.rs | 45 +++++++++++++++++ gpui/src/scene.rs | 22 +++++++++ zed/src/editor/buffer_element.rs | 15 ++++++ 25 files changed, 554 insertions(+), 41 deletions(-) create mode 100644 gpui/src/color.rs create mode 100644 gpui/src/json.rs diff --git a/Cargo.lock b/Cargo.lock index 38d75ed246..b6d5b045e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -930,6 +930,8 @@ dependencies = [ "rand 0.8.3", "replace_with", "resvg", + "serde", + "serde_json", "simplelog", "smallvec", "smol", @@ -1691,9 +1693,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.124" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd761ff957cb2a45fbb9ab3da6512de9de55872866160b23c25f1a841e99d29f" +checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" [[package]] name = "serde_json" diff --git a/gpui/Cargo.toml b/gpui/Cargo.toml index 2b0e90bedc..7bf1913372 100644 --- a/gpui/Cargo.toml +++ b/gpui/Cargo.toml @@ -18,6 +18,8 @@ pathfinder_geometry = "0.5" rand = "0.8.3" replace_with = "0.1.7" resvg = "0.14" +serde = "1.0.125" +serde_json = "1.0.64" smallvec = "1.6.1" smol = "1.2" tiny-skia = "0.5" diff --git a/gpui/examples/text.rs b/gpui/examples/text.rs index e481ce5e2f..14bc198270 100644 --- a/gpui/examples/text.rs +++ b/gpui/examples/text.rs @@ -2,9 +2,10 @@ use gpui::{ color::ColorU, fonts::{Properties, Weight}, platform::{current as platform, Runner}, - Element as _, Quad, + DebugContext, Element as _, Quad, }; use log::LevelFilter; +use pathfinder_geometry::rect::RectF; use simplelog::SimpleLogger; fn main() { @@ -59,7 +60,7 @@ impl gpui::Element for TextElement { fn paint( &mut self, - bounds: pathfinder_geometry::rect::RectF, + bounds: RectF, _: &mut Self::LayoutState, ctx: &mut gpui::PaintContext, ) -> Self::PaintState { @@ -109,11 +110,21 @@ impl gpui::Element for TextElement { fn dispatch_event( &mut self, _: &gpui::Event, - _: pathfinder_geometry::rect::RectF, + _: RectF, _: &mut Self::LayoutState, _: &mut Self::PaintState, _: &mut gpui::EventContext, ) -> bool { false } + + fn debug( + &self, + _: RectF, + _: &Self::LayoutState, + _: &Self::PaintState, + _: &DebugContext, + ) -> gpui::json::Value { + todo!() + } } diff --git a/gpui/src/color.rs b/gpui/src/color.rs new file mode 100644 index 0000000000..95b966493e --- /dev/null +++ b/gpui/src/color.rs @@ -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)) + } +} diff --git a/gpui/src/elements/align.rs b/gpui/src/elements/align.rs index a8277d2469..9d5c905eb5 100644 --- a/gpui/src/elements/align.rs +++ b/gpui/src/elements/align.rs @@ -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", + "alignment": self.alignment.to_json(), + "bounds": bounds.to_json(), + "child": self.child.debug(ctx), + }) + } } diff --git a/gpui/src/elements/canvas.rs b/gpui/src/elements/canvas.rs index 4788ab9b9d..a82ef50c90 100644 --- a/gpui/src/elements/canvas.rs +++ b/gpui/src/elements/canvas.rs @@ -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()}) + } } diff --git a/gpui/src/elements/constrained_box.rs b/gpui/src/elements/constrained_box.rs index dc4c0be7fd..95b6d29637 100644 --- a/gpui/src/elements/constrained_box.rs +++ b/gpui/src/elements/constrained_box.rs @@ -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)}) + } } diff --git a/gpui/src/elements/container.rs b/gpui/src/elements/container.rs index 83b3e820e3..dcded86f38 100644 --- a/gpui/src/elements/container.rs +++ b/gpui/src/elements/container.rs @@ -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", + "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(), + }, + "bounds": bounds.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() + }) + } +} diff --git a/gpui/src/elements/empty.rs b/gpui/src/elements/empty.rs index f5d038bb50..fe9ff3c9b9 100644 --- a/gpui/src/elements/empty.rs +++ b/gpui/src/elements/empty.rs @@ -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(), + }) + } } diff --git a/gpui/src/elements/event_handler.rs b/gpui/src/elements/event_handler.rs index b558bd0254..717d7db1f6 100644 --- a/gpui/src/elements/event_handler.rs +++ b/gpui/src/elements/event_handler.rs @@ -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), + }) + } } diff --git a/gpui/src/elements/flex.rs b/gpui/src/elements/flex.rs index 86cc9447c5..abed2aabc5 100644 --- a/gpui/src/elements/flex.rs +++ b/gpui/src/elements/flex.rs @@ -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", + "axis": self.axis.to_json(), + "bounds": bounds.to_json(), + "children": self.children.iter().map(|child| child.debug(ctx)).collect::>() + }) + } } 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) + }) + } } diff --git a/gpui/src/elements/label.rs b/gpui/src/elements/label.rs index bc5281e15a..952bc0eab0 100644 --- a/gpui/src/elements/label.rs +++ b/gpui/src/elements/label.rs @@ -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", + "font_size": self.font_size, + "bounds": bounds.to_json(), + "text": &self.text, + "family_id": ctx.font_cache.family_name(self.family_id).unwrap(), + "font_properties": self.font_properties.to_json(), + "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(), + }) + } } diff --git a/gpui/src/elements/line_box.rs b/gpui/src/elements/line_box.rs index 7f1a3bc628..eec76cbeb4 100644 --- a/gpui/src/elements/line_box.rs +++ b/gpui/src/elements/line_box.rs @@ -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(), + "family_id": 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), + }) + } } diff --git a/gpui/src/elements/new.rs b/gpui/src/elements/new.rs index 9cde494171..446948e3cc 100644 --- a/gpui/src/elements/new.rs +++ b/gpui/src/elements/new.rs @@ -1,6 +1,7 @@ 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; @@ -11,6 +12,7 @@ trait AnyElement { 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,6 +55,14 @@ 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, @@ -165,6 +175,18 @@ impl AnyElement for Lifecycle { | 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 { @@ -191,4 +213,8 @@ impl ElementBox { pub fn metadata(&self) -> Option<&dyn Any> { self.0.metadata() } + + pub fn debug(&self, ctx: &DebugContext) -> json::Value { + self.0.debug(ctx) + } } diff --git a/gpui/src/elements/stack.rs b/gpui/src/elements/stack.rs index 0cfcf48cc5..6fdae68010 100644 --- a/gpui/src/elements/stack.rs +++ b/gpui/src/elements/stack.rs @@ -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::>() + }) + } } impl Extend for Stack { diff --git a/gpui/src/elements/svg.rs b/gpui/src/elements/svg.rs index 9afc5eeab8..12b08b154c 100644 --- a/gpui/src/elements/svg.rs +++ b/gpui/src/elements/svg.rs @@ -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), diff --git a/gpui/src/elements/uniform_list.rs b/gpui/src/elements/uniform_list.rs index 637dfdbd55..c1b9b86ca7 100644 --- a/gpui/src/elements/uniform_list.rs +++ b/gpui/src/elements/uniform_list.rs @@ -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::>() + + }) + } } diff --git a/gpui/src/font_cache.rs b/gpui/src/font_cache.rs index 205a450670..4e109ef240 100644 --- a/gpui/src/font_cache.rs +++ b/gpui/src/font_cache.rs @@ -36,6 +36,15 @@ impl FontCache { })) } + pub fn family_name(&self, family_id: FamilyId) -> Result { + 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 { for name in names { let state = self.0.upgradable_read(); diff --git a/gpui/src/fonts.rs b/gpui/src/fonts.rs index 599f018526..e7e6b5dedb 100644 --- a/gpui/src/fonts.rs +++ b/gpui/src/fonts.rs @@ -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) + } +} diff --git a/gpui/src/geometry.rs b/gpui/src/geometry.rs index ba6cabfea1..cf8ec9cb40 100644 --- a/gpui/src/geometry.rs +++ b/gpui/src/geometry.rs @@ -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()}) + } +} diff --git a/gpui/src/json.rs b/gpui/src/json.rs new file mode 100644 index 0000000000..8d7625acec --- /dev/null +++ b/gpui/src/json.rs @@ -0,0 +1,15 @@ +pub use serde_json::*; + +pub trait ToJson { + fn to_json(&self) -> Value; +} + +impl ToJson for Option { + fn to_json(&self) -> Value { + if let Some(value) = self.as_ref() { + value.to_json() + } else { + json!(null) + } + } +} diff --git a/gpui/src/lib.rs b/gpui/src/lib.rs index bdf7096506..7b0ecdddef 100644 --- a/gpui/src/lib.rs +++ b/gpui/src/lib.rs @@ -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, }; diff --git a/gpui/src/presenter.rs b/gpui/src/presenter.rs index 197fdaf637..293e528aba 100644 --- a/gpui/src/presenter.rs +++ b/gpui/src/presenter.rs @@ -2,11 +2,13 @@ use crate::{ app::{AppContext, MutableAppContext, WindowInvalidation}, elements::Element, font_cache::FontCache, + json::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 { @@ -224,6 +226,12 @@ impl<'a> EventContext<'a> { } } +pub struct DebugContext<'a> { + rendered_views: &'a mut HashMap, + pub font_cache: &'a FontCache, + pub app: &'a AppContext, +} + #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Axis { Horizontal, @@ -239,6 +247,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 +308,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 +368,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)] diff --git a/gpui/src/scene.rs b/gpui/src/scene.rs index cde5dc60d0..44f9fcd1a0 100644 --- a/gpui/src/scene.rs +++ b/gpui/src/scene.rs @@ -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 + } +} diff --git a/zed/src/editor/buffer_element.rs b/zed/src/editor/buffer_element.rs index a4a9d63a33..e00c11f1fd 100644 --- a/zed/src/editor/buffer_element.rs +++ b/zed/src/editor/buffer_element.rs @@ -6,10 +6,12 @@ use gpui::{ vector::{vec2f, Vector2F}, PathBuilder, }, + json::{self, ToJson}, text_layout::{self, TextLayoutCache}, AfterLayoutContext, AppContext, Border, Element, Event, EventContext, FontCache, LayoutContext, PaintContext, Quad, Scene, SizeConstraint, ViewHandle, }; +use json::json; use smallvec::SmallVec; use std::cmp::Ordering; use std::{ @@ -477,6 +479,19 @@ impl Element for BufferElement { false } } + + fn debug( + &self, + bounds: RectF, + _: &Self::LayoutState, + _: &Self::PaintState, + _: &gpui::DebugContext, + ) -> json::Value { + json!({ + "type": "BufferElement", + "bounds": bounds.to_json() + }) + } } pub struct LayoutState {