From 0f21c3701e2f38eca1d6385c1d7cdeeef141c87a Mon Sep 17 00:00:00 2001 From: Joscha Date: Sat, 9 Mar 2024 13:28:13 +0100 Subject: [PATCH] Improve text widget API --- Cargo.lock | 1 + showbits-assets/src/lib.rs | 1 + showbits-common/Cargo.toml | 1 + showbits-common/src/widgets/text.rs | 139 +++++++++++++++++-------- showbits-thermal-printer/src/drawer.rs | 19 ++-- 5 files changed, 108 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a9b91e1..37d7399 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1167,6 +1167,7 @@ dependencies = [ "cosmic-text", "image", "palette", + "showbits-assets", "taffy", ] diff --git a/showbits-assets/src/lib.rs b/showbits-assets/src/lib.rs index a07bf60..7890ee7 100644 --- a/showbits-assets/src/lib.rs +++ b/showbits-assets/src/lib.rs @@ -1,2 +1,3 @@ pub const UNIFONT: &[u8] = include_bytes!("../data/unifont-15.1.05.otf"); pub const UNIFONT_NAME: &str = "Unifont"; +pub const UNIFONT_SIZE: f32 = 16.0; diff --git a/showbits-common/Cargo.toml b/showbits-common/Cargo.toml index 6dc324f..32e85f3 100644 --- a/showbits-common/Cargo.toml +++ b/showbits-common/Cargo.toml @@ -8,6 +8,7 @@ anyhow.workspace = true cosmic-text.workspace = true image.workspace = true palette.workspace = true +showbits-assets.workspace = true taffy.workspace = true [lints] diff --git a/showbits-common/src/widgets/text.rs b/showbits-common/src/widgets/text.rs index ff3931c..45615c1 100644 --- a/showbits-common/src/widgets/text.rs +++ b/showbits-common/src/widgets/text.rs @@ -1,5 +1,6 @@ -use cosmic_text::{Attrs, Buffer, FontSystem, Metrics, Shaping, SwashCache}; +use cosmic_text::{Attrs, AttrsOwned, Buffer, Family, FontSystem, Metrics, Shaping, SwashCache}; use palette::Srgba; +use showbits_assets::{UNIFONT, UNIFONT_NAME, UNIFONT_SIZE}; use taffy::{ prelude::{AvailableSpace, Size}, Layout, @@ -16,8 +17,13 @@ pub struct FontStuff { impl FontStuff { pub fn new() -> Self { + let mut font_system = FontSystem::new(); + let db = font_system.db_mut(); + db.load_font_data(UNIFONT.to_vec()); + db.set_monospace_family(UNIFONT_NAME); + Self { - font_system: FontSystem::new(), + font_system, swash_cache: SwashCache::new(), } } @@ -34,63 +40,112 @@ pub trait HasFontStuff { } pub struct Text { - buffer: Buffer, + metrics: Metrics, + default_attrs: AttrsOwned, + chunks: Vec<(AttrsOwned, String)>, + shaping: Shaping, color: Srgba, } impl Text { - /// Default text color. - const COLOR: Srgba = Srgba::new(0.0, 0.0, 0.0, 1.0); + pub const fn default_metrics() -> Metrics { + Metrics::new(UNIFONT_SIZE, UNIFONT_SIZE) + } - // Default shaping strategy. - const SHAPING: Shaping = Shaping::Advanced; - - pub fn simple( - font_stuff: &mut FontStuff, - metrics: Metrics, - attrs: Attrs<'_>, - text: &str, - ) -> Self { - let fs = &mut font_stuff.font_system; - - let mut buffer = Buffer::new_empty(metrics); - buffer.set_size(fs, f32::INFINITY, f32::INFINITY); - buffer.set_text(fs, text, attrs, Self::SHAPING); + pub fn default_attrs<'a>() -> Attrs<'a> { + Attrs::new().family(Family::Monospace) + } + pub fn new() -> Self { Self { - buffer, - color: Self::COLOR, + metrics: Self::default_metrics(), + default_attrs: AttrsOwned::new(Self::default_attrs()), + chunks: vec![], + shaping: Shaping::Basic, + color: color::BLACK, } } - pub fn rich<'r, 's, I>( - font_stuff: &mut FontStuff, - metrics: Metrics, - default_attrs: Attrs<'_>, - spans: I, - ) -> Self - where - I: IntoIterator)>, - { - let fs = &mut font_stuff.font_system; - - let mut buffer = Buffer::new_empty(metrics); - buffer.set_size(fs, f32::INFINITY, f32::INFINITY); - buffer.set_rich_text(fs, spans, default_attrs, Self::SHAPING); - - Self { - buffer, - color: Self::COLOR, - } + pub fn with_metrics(mut self, metrics: Metrics) -> Self { + self.metrics = metrics; + self } - pub fn color(mut self, color: Srgba) -> Self { + pub fn with_font_size(mut self, size: f32) -> Self { + self.metrics.font_size = size; + self + } + + pub fn with_line_height(mut self, height: f32) -> Self { + self.metrics.line_height = height; + self + } + + pub fn with_default_attrs(mut self, attrs: Attrs<'_>) -> Self { + self.default_attrs = AttrsOwned::new(attrs); + self + } + + pub fn with_shaping(mut self, shaping: Shaping) -> Self { + self.shaping = shaping; + self + } + + pub fn with_color(mut self, color: Srgba) -> Self { self.color = color; self } + + pub fn chunk_plain(mut self, text: S) -> Self { + let chunk = (self.default_attrs.clone(), text.to_string()); + self.chunks.push(chunk); + self + } + + pub fn chunk_rich(mut self, attrs: Attrs<'_>, text: S) -> Self { + let chunk = (AttrsOwned::new(attrs), text.to_string()); + self.chunks.push(chunk); + self + } + + pub fn chunks(mut self, chunks: I) -> Self + where + I: IntoIterator, + { + self.chunks.extend(chunks); + self + } + + pub fn widget(self, font_stuff: &mut FontStuff) -> impl Widget { + let fs = &mut font_stuff.font_system; + let mut buffer = Buffer::new_empty(self.metrics); + buffer.set_size(fs, f32::INFINITY, f32::INFINITY); + + let spans = self + .chunks + .iter() + .map(|(attrs, text)| (text as &str, attrs.as_attrs())); + buffer.set_rich_text(fs, spans, self.default_attrs.as_attrs(), self.shaping); + + TextWidget { + buffer, + color: self.color, + } + } } -impl Widget for Text { +impl Default for Text { + fn default() -> Self { + Self::new() + } +} + +struct TextWidget { + buffer: Buffer, + color: Srgba, +} + +impl Widget for TextWidget { fn size( &mut self, ctx: &mut C, diff --git a/showbits-thermal-printer/src/drawer.rs b/showbits-thermal-printer/src/drawer.rs index 62d085d..77e5284 100644 --- a/showbits-thermal-printer/src/drawer.rs +++ b/showbits-thermal-printer/src/drawer.rs @@ -1,4 +1,3 @@ -use cosmic_text::{Attrs, Metrics}; use palette::Srgba; use showbits_common::{ color, @@ -69,16 +68,14 @@ impl Drawer { fn on_test(&mut self) -> anyhow::Result<()> { let mut tree = Tree::::new(Srgba::new(1.0, 1.0, 1.0, 1.0)); - let text = Text::simple( - &mut self.ctx.font_stuff, - Metrics::new(16.0, 24.0), - Attrs::new(), - "Hello\nworld!", - ) - .node() - .margin_horiz(length(8.0)) - .margin_vert(length(2.0)) - .register(&mut tree)?; + let text = Text::new() + .chunk_plain("Hello\nworld!") + .with_metrics(Text::default_metrics().scale(2.0)) + .widget(&mut self.ctx.font_stuff) + .node() + .margin_horiz(length(8.0)) + .margin_vert(length(2.0)) + .register(&mut tree)?; let wrap = Block::new() .border(color::BLACK)