diff --git a/Cargo.lock b/Cargo.lock index 8cabfe11a8..39b348c43a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5304,6 +5304,7 @@ dependencies = [ "gpui", "log", "optional_struct", + "parking_lot 0.11.2", "playground_macros", "serde", "simplelog", diff --git a/crates/gpui/playground/Cargo.toml b/crates/gpui/playground/Cargo.toml index bdf131d089..f9b2d5e110 100644 --- a/crates/gpui/playground/Cargo.toml +++ b/crates/gpui/playground/Cargo.toml @@ -14,6 +14,7 @@ gpui = { path = ".." } log.workspace = true optional_struct = "0.3.1" playground_macros = { path = "../playground_macros" } +parking_lot.workspace = true serde.workspace = true simplelog = "0.9" smallvec.workspace = true diff --git a/crates/gpui/playground/src/color.rs b/crates/gpui/playground/src/color.rs index b0aa10dc3f..14c60248aa 100644 --- a/crates/gpui/playground/src/color.rs +++ b/crates/gpui/playground/src/color.rs @@ -116,6 +116,15 @@ pub fn hsla(h: f32, s: f32, l: f32, a: f32) -> Hsla { } } +pub fn black() -> Hsla { + Hsla { + h: 0., + s: 0., + l: 0., + a: 1., + } +} + impl From for Hsla { fn from(color: Rgba) -> Self { let r = color.r; diff --git a/crates/gpui/playground/src/components.rs b/crates/gpui/playground/src/components.rs index d92583866d..780f6bd88c 100644 --- a/crates/gpui/playground/src/components.rs +++ b/crates/gpui/playground/src/components.rs @@ -1,11 +1,12 @@ use crate::{ element::{Element, ElementMetadata}, frame, + text::ArcCow, themes::rose_pine, }; use gpui::ViewContext; use playground_macros::Element; -use std::{borrow::Cow, marker::PhantomData, rc::Rc}; +use std::{marker::PhantomData, rc::Rc}; struct ButtonHandlers { click: Option>, @@ -22,8 +23,8 @@ impl Default for ButtonHandlers { pub struct Button { metadata: ElementMetadata, handlers: ButtonHandlers, - label: Option>, - icon: Option>, + label: Option>, + icon: Option>, data: Rc, view_type: PhantomData, } @@ -56,17 +57,17 @@ impl Button { // Impl block for *any* button. impl Button { - fn label(mut self, label: impl Into>) -> Self { + pub fn label(mut self, label: impl Into>) -> Self { self.label = Some(label.into()); self } - fn icon(mut self, icon: impl Into>) -> Self { + pub fn icon(mut self, icon: impl Into>) -> Self { self.icon = Some(icon.into()); self } - fn click(self, handler: impl Fn(&mut V, &D) + 'static) -> Self { + pub fn click(self, handler: impl Fn(&mut V, &D) + 'static) -> Self { let data = self.data.clone(); Element::click(self, move |view, _| { handler(view, data.as_ref()); @@ -80,8 +81,11 @@ pub fn button() -> Button { impl Button { fn render(&mut self, view: &mut V, cx: &mut ViewContext) -> impl Element { - // TODO: Drive from the context - let button = frame().fill(rose_pine::dawn().error(0.5)).h_5().w_9(); + // TODO: Drive theme from the context + let button = frame() + .fill(rose_pine::dawn().error(0.5)) + .h_4() + .children(self.label.clone()); if let Some(handler) = self.handlers.click.clone() { let data = self.data.clone(); diff --git a/crates/gpui/playground/src/element.rs b/crates/gpui/playground/src/element.rs index 26fef21a21..03e97b56ea 100644 --- a/crates/gpui/playground/src/element.rs +++ b/crates/gpui/playground/src/element.rs @@ -1,5 +1,6 @@ use crate::{ adapter::Adapter, + color::Hsla, style::{Display, ElementStyle, Fill, Overflow, Position}, }; use anyhow::Result; @@ -8,7 +9,7 @@ pub use gpui::LayoutContext; use gpui::{ geometry::{DefinedLength, Length}, scene::MouseClick, - EngineLayout, PaintContext as LegacyPaintContext, + EngineLayout, PaintContext as LegacyPaintContext, RenderContext, }; use playground_macros::tailwind_lengths; use std::{any::Any, rc::Rc}; @@ -22,6 +23,20 @@ pub struct PaintContext<'a, 'b, 'c, 'd, V> { pub(crate) scene: &'d mut gpui::SceneBuilder, } +impl RenderContext for PaintContext<'_, '_, '_, '_, V> { + fn text_style(&self) -> gpui::fonts::TextStyle { + self.legacy_cx.text_style() + } + + fn push_text_style(&mut self, style: gpui::fonts::TextStyle) { + self.legacy_cx.push_text_style(style) + } + + fn pop_text_style(&mut self) { + self.legacy_cx.pop_text_style() + } +} + pub struct Layout<'a, E: ?Sized> { pub from_engine: EngineLayout, pub from_element: &'a mut E, @@ -303,9 +318,18 @@ pub trait Element: 'static { self.style_mut().fill = fill.into(); self } + + fn text_color(mut self, color: impl Into) -> Self + where + Self: Sized, + { + self.style_mut().text_color = Some(color.into()); + self + } } -pub trait ElementObject { +// Object-safe counterpart of Element used by AnyElement to store elements as trait objects. +trait ElementObject { fn style_mut(&mut self) -> &mut ElementStyle; fn handlers_mut(&mut self) -> &mut ElementHandlers; fn layout(&mut self, view: &mut V, cx: &mut LayoutContext) @@ -360,12 +384,33 @@ pub struct AnyElement { impl AnyElement { pub fn layout(&mut self, view: &mut V, cx: &mut LayoutContext) -> Result { + let pushed_text_style = self.push_text_style(cx); + let (node_id, layout) = self.element.layout(view, cx)?; self.layout = Some((node_id, layout)); + + if pushed_text_style { + cx.pop_text_style(); + } + Ok(node_id) } + pub fn push_text_style(&mut self, cx: &mut impl RenderContext) -> bool { + let text_style = self.element.style_mut().text_style(); + if let Some(text_style) = text_style { + let mut current_text_style = cx.text_style(); + text_style.apply(&mut current_text_style); + cx.push_text_style(current_text_style); + true + } else { + false + } + } + pub fn paint(&mut self, view: &mut V, cx: &mut PaintContext) -> Result<()> { + let pushed_text_style = self.push_text_style(cx); + let (layout_node_id, element_layout) = self.layout.as_mut().expect("paint called before layout"); @@ -378,7 +423,12 @@ impl AnyElement { from_element: element_layout.as_mut(), }; - self.element.paint(layout, view, cx) + self.element.paint(layout, view, cx)?; + if pushed_text_style { + cx.pop_text_style(); + } + + Ok(()) } } @@ -405,3 +455,16 @@ impl Element for AnyElement { self.paint(view, cx) } } + +pub trait IntoElement { + type Element: Element; + + fn into_element(self) -> Self::Element; + + fn into_any_element(self) -> AnyElement + where + Self: Sized, + { + self.into_element().into_any() + } +} diff --git a/crates/gpui/playground/src/frame.rs b/crates/gpui/playground/src/frame.rs index 7dba849085..4b32bc33d6 100644 --- a/crates/gpui/playground/src/frame.rs +++ b/crates/gpui/playground/src/frame.rs @@ -1,11 +1,17 @@ use crate::{ - element::{AnyElement, Element, ElementHandlers, Layout, LayoutContext, NodeId, PaintContext}, + element::{ + AnyElement, Element, ElementHandlers, IntoElement, Layout, LayoutContext, NodeId, + PaintContext, + }, style::ElementStyle, }; use anyhow::{anyhow, Result}; use gpui::LayoutNodeId; +use playground_macros::IntoElement; -pub struct Frame { +#[derive(IntoElement)] +#[element_crate = "crate"] +pub struct Frame { style: ElementStyle, handlers: ElementHandlers, children: Vec>, @@ -66,8 +72,18 @@ impl Element for Frame { } impl Frame { - pub fn child(mut self, child: impl Element) -> Self { - self.children.push(child.into_any()); + pub fn child(mut self, child: impl IntoElement) -> Self { + self.children.push(child.into_any_element()); + self + } + + pub fn children(mut self, children: I) -> Self + where + I: IntoIterator, + E: IntoElement, + { + self.children + .extend(children.into_iter().map(|e| e.into_any_element())); self } } diff --git a/crates/gpui/playground/src/playground.rs b/crates/gpui/playground/src/playground.rs index 7b98ac3026..702487a881 100644 --- a/crates/gpui/playground/src/playground.rs +++ b/crates/gpui/playground/src/playground.rs @@ -1,9 +1,10 @@ #![allow(dead_code, unused_variables)] +use color::black; use components::button; use element::Element; use frame::frame; use gpui::{ - geometry::{percent, rect::RectF, vector::vec2f}, + geometry::{rect::RectF, vector::vec2f}, platform::WindowOptions, }; use log::LevelFilter; @@ -35,19 +36,21 @@ fn main() { center: true, ..Default::default() }, - |_| view(|_| workspace(&rose_pine::moon())), + |_| view(|_| playground(&rose_pine::moon())), ); cx.platform().activate(true); }); } -fn workspace(theme: &ThemeColors) -> impl Element { +fn playground(theme: &ThemeColors) -> impl Element { frame() + .text_color(black()) .h_full() - .w(percent(50.)) + .w_half() .fill(theme.success(0.5)) - .child(button()) + .child(button().label("Hello").click(|_, _| (println!("hey!")))) } + // todo!() // // column() // // .size(auto()) diff --git a/crates/gpui/playground/src/style.rs b/crates/gpui/playground/src/style.rs index 08f914f69a..915b4a958d 100644 --- a/crates/gpui/playground/src/style.rs +++ b/crates/gpui/playground/src/style.rs @@ -66,6 +66,8 @@ pub struct ElementStyle { /// The fill color of this element pub fill: Fill, + /// The color of text within this element. Cascades to children unless overridden. + pub text_color: Option, } impl ElementStyle { @@ -103,6 +105,7 @@ impl ElementStyle { l: 0., a: 0., }), + text_color: None, }; pub fn new() -> Self { @@ -136,6 +139,16 @@ impl ElementStyle { ..Default::default() // Ignore grid properties for now } } + + pub fn text_style(&self) -> Option { + if self.text_color.is_some() { + Some(OptionalTextStyle { + color: self.text_color, + }) + } else { + None + } + } } impl Default for ElementStyle { @@ -144,6 +157,18 @@ impl Default for ElementStyle { } } +pub struct OptionalTextStyle { + color: Option, +} + +impl OptionalTextStyle { + pub fn apply(&self, style: &mut gpui::fonts::TextStyle) { + if let Some(color) = self.color { + style.color = color.into(); + } + } +} + #[derive(Clone)] pub enum Fill { Color(Hsla), diff --git a/crates/gpui/playground/src/text.rs b/crates/gpui/playground/src/text.rs index 561d5506a6..876edc0a3c 100644 --- a/crates/gpui/playground/src/text.rs +++ b/crates/gpui/playground/src/text.rs @@ -1,36 +1,155 @@ -use std::borrow::Cow; +use crate::element::{Element, ElementMetadata, IntoElement}; +use gpui::{geometry::Size, text_layout::LineLayout, RenderContext}; +use parking_lot::Mutex; +use std::sync::Arc; -use crate::element::Element; +impl>> IntoElement for S { + type Element = Text; -impl Element for S -where - V: 'static, - S: 'static + Into>, -{ - type Layout = Cow<'static, str>; + fn into_element(self) -> Self::Element { + Text { + text: self.into(), + metadata: Default::default(), + } + } +} + +pub struct Text { + text: ArcCow<'static, str>, + metadata: ElementMetadata, +} + +impl Element for Text { + type Layout = Arc>>; fn style_mut(&mut self) -> &mut crate::style::ElementStyle { - todo!() + &mut self.metadata.style } fn handlers_mut(&mut self) -> &mut crate::element::ElementHandlers { - todo!() + &mut self.metadata.handlers } fn layout( &mut self, view: &mut V, - cx: &mut crate::element::LayoutContext, + cx: &mut gpui::LayoutContext, ) -> anyhow::Result<(taffy::tree::NodeId, Self::Layout)> { - todo!() + let rem_size = cx.rem_pixels(); + let fonts = cx.platform().fonts(); + let text_style = cx.text_style(); + let line_height = cx.font_cache().line_height(text_style.font_size); + let layout_engine = cx.layout_engine().expect("no layout engine present"); + let text = self.text.clone(); + let layout = Arc::new(Mutex::new(None)); + + let node_id = layout_engine.add_measured_node(self.metadata.style.to_taffy(rem_size), { + let layout = layout.clone(); + move |params| { + let line_layout = fonts.layout_line( + text.as_ref(), + text_style.font_size, + &[(text.len(), text_style.to_run())], + ); + + let size = Size { + width: line_layout.width, + height: line_height, + }; + + layout.lock().replace(TextLayout { + line_layout: Arc::new(line_layout), + line_height, + }); + + size + } + })?; + + Ok((node_id, layout)) } fn paint<'a>( &mut self, - layout: crate::element::Layout, + layout: crate::element::Layout>>>, view: &mut V, cx: &mut crate::element::PaintContext, ) -> anyhow::Result<()> { - todo!() + let element_layout_lock = layout.from_element.lock(); + let element_layout = element_layout_lock + .as_ref() + .expect("layout has not been performed"); + let line_layout = element_layout.line_layout.clone(); + let line_height = element_layout.line_height; + drop(element_layout_lock); + + let text_style = cx.text_style(); + let line = + gpui::text_layout::Line::new(line_layout, &[(self.text.len(), text_style.to_run())]); + line.paint( + cx.scene, + layout.from_engine.bounds.origin(), + layout.from_engine.bounds, + line_height, + cx.legacy_cx, + ); + Ok(()) + } +} + +pub struct TextLayout { + line_layout: Arc, + line_height: f32, +} + +pub enum ArcCow<'a, T: ?Sized> { + Borrowed(&'a T), + Owned(Arc), +} + +impl<'a, T: ?Sized> Clone for ArcCow<'a, T> { + fn clone(&self) -> Self { + match self { + Self::Borrowed(borrowed) => Self::Borrowed(borrowed), + Self::Owned(owned) => Self::Owned(owned.clone()), + } + } +} + +impl<'a, T: ?Sized> From<&'a T> for ArcCow<'a, T> { + fn from(s: &'a T) -> Self { + Self::Borrowed(s) + } +} + +impl From> for ArcCow<'_, T> { + fn from(s: Arc) -> Self { + Self::Owned(s) + } +} + +impl From for ArcCow<'_, str> { + fn from(value: String) -> Self { + Self::Owned(value.into()) + } +} + +impl std::ops::Deref for ArcCow<'_, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + match self { + ArcCow::Borrowed(s) => s, + ArcCow::Owned(s) => s.as_ref(), + } + } +} + +impl AsRef for ArcCow<'_, T> { + fn as_ref(&self) -> &T { + match self { + ArcCow::Borrowed(borrowed) => borrowed, + ArcCow::Owned(owned) => owned.as_ref(), + } } } diff --git a/crates/gpui/playground_macros/src/derive_element.rs b/crates/gpui/playground_macros/src/derive_element.rs index 546267f1bf..0d436c95af 100644 --- a/crates/gpui/playground_macros/src/derive_element.rs +++ b/crates/gpui/playground_macros/src/derive_element.rs @@ -5,6 +5,8 @@ use syn::{ WhereClause, }; +use crate::derive_into_element::impl_into_element; + pub fn derive_element(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); let type_name = ast.ident; @@ -62,6 +64,15 @@ pub fn derive_element(input: TokenStream) -> TokenStream { } } + let impl_into_element = impl_into_element( + &impl_generics, + &crate_name, + &view_type_name, + &type_name, + &type_generics, + &where_clause, + ); + let gen = quote! { impl #impl_generics #crate_name::element::Element<#view_type_name> for #type_name #type_generics #where_clause @@ -96,6 +107,8 @@ pub fn derive_element(input: TokenStream) -> TokenStream { Ok(()) } } + + #impl_into_element }; gen.into() diff --git a/crates/gpui/playground_macros/src/derive_into_element.rs b/crates/gpui/playground_macros/src/derive_into_element.rs new file mode 100644 index 0000000000..b88a0b907a --- /dev/null +++ b/crates/gpui/playground_macros/src/derive_into_element.rs @@ -0,0 +1,95 @@ +use proc_macro::TokenStream; +use quote::{format_ident, quote}; +use syn::{ + parse_macro_input, parse_quote, DeriveInput, GenericParam, Generics, Ident, Lit, Meta, + WhereClause, +}; + +pub fn derive_into_element(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as DeriveInput); + let type_name = ast.ident; + + let crate_name: String = ast + .attrs + .iter() + .find_map(|attr| { + if attr.path.is_ident("element_crate") { + match attr.parse_meta() { + Ok(Meta::NameValue(nv)) => { + if let Lit::Str(s) = nv.lit { + Some(s.value()) + } else { + None + } + } + _ => None, + } + } else { + None + } + }) + .unwrap_or_else(|| String::from("playground")); + + let crate_name = format_ident!("{}", crate_name); + + let placeholder_view_generics: Generics = parse_quote! { }; + let placeholder_view_type_name: Ident = parse_quote! { V }; + let view_type_name: Ident; + let impl_generics: syn::ImplGenerics<'_>; + let type_generics: Option>; + let where_clause: Option<&'_ WhereClause>; + + match ast.generics.params.iter().find_map(|param| { + if let GenericParam::Type(type_param) = param { + Some(type_param.ident.clone()) + } else { + None + } + }) { + Some(type_name) => { + view_type_name = type_name; + let generics = ast.generics.split_for_impl(); + impl_generics = generics.0; + type_generics = Some(generics.1); + where_clause = generics.2; + } + _ => { + view_type_name = placeholder_view_type_name; + let generics = placeholder_view_generics.split_for_impl(); + impl_generics = generics.0; + type_generics = None; + where_clause = generics.2; + } + } + + impl_into_element( + &impl_generics, + &crate_name, + &view_type_name, + &type_name, + &type_generics, + &where_clause, + ) + .into() +} + +pub fn impl_into_element( + impl_generics: &syn::ImplGenerics<'_>, + crate_name: &Ident, + view_type_name: &Ident, + type_name: &Ident, + type_generics: &Option>, + where_clause: &Option<&WhereClause>, +) -> proc_macro2::TokenStream { + quote! { + impl #impl_generics #crate_name::element::IntoElement<#view_type_name> for #type_name #type_generics + #where_clause + { + type Element = Self; + + fn into_element(self) -> Self { + self + } + } + } +} diff --git a/crates/gpui/playground_macros/src/playground_macros.rs b/crates/gpui/playground_macros/src/playground_macros.rs index 446b476981..4b0de71015 100644 --- a/crates/gpui/playground_macros/src/playground_macros.rs +++ b/crates/gpui/playground_macros/src/playground_macros.rs @@ -1,14 +1,20 @@ use proc_macro::TokenStream; mod derive_element; +mod derive_into_element; mod tailwind_lengths; -#[proc_macro_attribute] -pub fn tailwind_lengths(attr: TokenStream, item: TokenStream) -> TokenStream { - tailwind_lengths::tailwind_lengths(attr, item) -} - #[proc_macro_derive(Element, attributes(element_crate))] pub fn derive_element(input: TokenStream) -> TokenStream { derive_element::derive_element(input) } + +#[proc_macro_derive(IntoElement, attributes(element_crate))] +pub fn derive_into_element(input: TokenStream) -> TokenStream { + derive_into_element::derive_into_element(input) +} + +#[proc_macro_attribute] +pub fn tailwind_lengths(attr: TokenStream, item: TokenStream) -> TokenStream { + tailwind_lengths::tailwind_lengths(attr, item) +} diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index be29b7029e..3c1bee5b0b 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -3361,11 +3361,21 @@ impl BorrowWindowContext for ViewContext<'_, '_, V> { } } +/// Methods shared by both LayoutContext and PaintContext +/// +/// It's that PaintContext should be implemented in terms of layout context and +/// deref to it, in which case we wouldn't need this. +pub trait RenderContext { + fn text_style(&self) -> TextStyle; + fn push_text_style(&mut self, style: TextStyle); + fn pop_text_style(&mut self); +} + pub struct LayoutContext<'a, 'b, 'c, V> { view_context: &'c mut ViewContext<'a, 'b, V>, new_parents: &'c mut HashMap, views_to_notify_if_ancestors_change: &'c mut HashMap>, - text_style_stack: Vec>, + text_style_stack: Vec, pub refreshing: bool, } @@ -3433,24 +3443,8 @@ impl<'a, 'b, 'c, V> LayoutContext<'a, 'b, 'c, V> { .push(self_view_id); } - pub fn text_style(&self) -> Arc { - self.text_style_stack - .last() - .cloned() - .unwrap_or(Arc::new(TextStyle::default(&self.font_cache))) - } - - pub fn push_text_style>>(&mut self, style: S) { - self.text_style_stack.push(style.into()); - } - - pub fn pop_text_style(&mut self) { - self.text_style_stack.pop(); - } - - pub fn with_text_style(&mut self, style: S, f: F) -> T + pub fn with_text_style(&mut self, style: TextStyle, f: F) -> T where - S: Into>, F: FnOnce(&mut Self) -> T, { self.push_text_style(style); @@ -3460,6 +3454,23 @@ impl<'a, 'b, 'c, V> LayoutContext<'a, 'b, 'c, V> { } } +impl<'a, 'b, 'c, V> RenderContext for LayoutContext<'a, 'b, 'c, V> { + fn text_style(&self) -> TextStyle { + self.text_style_stack + .last() + .cloned() + .unwrap_or(TextStyle::default(&self.font_cache)) + } + + fn push_text_style(&mut self, style: TextStyle) { + self.text_style_stack.push(style); + } + + fn pop_text_style(&mut self) { + self.text_style_stack.pop(); + } +} + impl<'a, 'b, 'c, V> Deref for LayoutContext<'a, 'b, 'c, V> { type Target = ViewContext<'a, 'b, V>; @@ -3516,7 +3527,7 @@ impl BorrowWindowContext for LayoutContext<'_, '_, '_, V> { pub struct PaintContext<'a, 'b, 'c, V> { view_context: &'c mut ViewContext<'a, 'b, V>, - text_style_stack: Vec>, + text_style_stack: Vec, } impl<'a, 'b, 'c, V> PaintContext<'a, 'b, 'c, V> { @@ -3526,23 +3537,22 @@ impl<'a, 'b, 'c, V> PaintContext<'a, 'b, 'c, V> { text_style_stack: Vec::new(), } } +} - pub fn text_style(&self) -> Arc { +impl<'a, 'b, 'c, V> RenderContext for PaintContext<'a, 'b, 'c, V> { + fn text_style(&self) -> TextStyle { self.text_style_stack .last() .cloned() - .unwrap_or(Arc::new(TextStyle::default(&self.font_cache))) + .unwrap_or(TextStyle::default(&self.font_cache)) } - pub fn with_text_style(&mut self, style: S, f: F) -> T - where - S: Into>, - F: FnOnce(&mut Self) -> T, - { - self.text_style_stack.push(style.into()); - let result = f(self); + fn push_text_style(&mut self, style: TextStyle) { + self.text_style_stack.push(style); + } + + fn pop_text_style(&mut self) { self.text_style_stack.pop(); - result } } diff --git a/crates/gpui/src/app/window.rs b/crates/gpui/src/app/window.rs index d8a6c2d55e..f2475b1372 100644 --- a/crates/gpui/src/app/window.rs +++ b/crates/gpui/src/app/window.rs @@ -1300,6 +1300,7 @@ where } } +#[derive(Debug, Clone)] pub struct EngineLayout { pub bounds: RectF, pub order: u32, diff --git a/crates/gpui/src/text_layout.rs b/crates/gpui/src/text_layout.rs index 25b5c3f430..7b79376434 100644 --- a/crates/gpui/src/text_layout.rs +++ b/crates/gpui/src/text_layout.rs @@ -212,7 +212,7 @@ pub struct Glyph { } impl Line { - fn new(layout: Arc, runs: &[(usize, RunStyle)]) -> Self { + pub fn new(layout: Arc, runs: &[(usize, RunStyle)]) -> Self { let mut style_runs = SmallVec::new(); for (len, style) in runs { style_runs.push(StyleRun { @@ -364,13 +364,13 @@ impl Line { origin: glyph_origin, }); } else { - scene.push_glyph(scene::Glyph { + scene.push_glyph(dbg!(scene::Glyph { font_id: run.font_id, font_size: self.layout.font_size, id: glyph.id, origin: glyph_origin, color, - }); + })); } } }