Improve text widget API

This commit is contained in:
Joscha 2024-03-09 13:28:13 +01:00
parent dd40058e71
commit 0f21c3701e
5 changed files with 108 additions and 53 deletions

1
Cargo.lock generated
View file

@ -1167,6 +1167,7 @@ dependencies = [
"cosmic-text", "cosmic-text",
"image", "image",
"palette", "palette",
"showbits-assets",
"taffy", "taffy",
] ]

View file

@ -1,2 +1,3 @@
pub const UNIFONT: &[u8] = include_bytes!("../data/unifont-15.1.05.otf"); pub const UNIFONT: &[u8] = include_bytes!("../data/unifont-15.1.05.otf");
pub const UNIFONT_NAME: &str = "Unifont"; pub const UNIFONT_NAME: &str = "Unifont";
pub const UNIFONT_SIZE: f32 = 16.0;

View file

@ -8,6 +8,7 @@ anyhow.workspace = true
cosmic-text.workspace = true cosmic-text.workspace = true
image.workspace = true image.workspace = true
palette.workspace = true palette.workspace = true
showbits-assets.workspace = true
taffy.workspace = true taffy.workspace = true
[lints] [lints]

View file

@ -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 palette::Srgba;
use showbits_assets::{UNIFONT, UNIFONT_NAME, UNIFONT_SIZE};
use taffy::{ use taffy::{
prelude::{AvailableSpace, Size}, prelude::{AvailableSpace, Size},
Layout, Layout,
@ -16,8 +17,13 @@ pub struct FontStuff {
impl FontStuff { impl FontStuff {
pub fn new() -> Self { 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 { Self {
font_system: FontSystem::new(), font_system,
swash_cache: SwashCache::new(), swash_cache: SwashCache::new(),
} }
} }
@ -34,63 +40,112 @@ pub trait HasFontStuff {
} }
pub struct Text { pub struct Text {
buffer: Buffer, metrics: Metrics,
default_attrs: AttrsOwned,
chunks: Vec<(AttrsOwned, String)>,
shaping: Shaping,
color: Srgba, color: Srgba,
} }
impl Text { impl Text {
/// Default text color. pub const fn default_metrics() -> Metrics {
const COLOR: Srgba = Srgba::new(0.0, 0.0, 0.0, 1.0); Metrics::new(UNIFONT_SIZE, UNIFONT_SIZE)
}
// Default shaping strategy. pub fn default_attrs<'a>() -> Attrs<'a> {
const SHAPING: Shaping = Shaping::Advanced; Attrs::new().family(Family::Monospace)
}
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 new() -> Self {
Self { Self {
buffer, metrics: Self::default_metrics(),
color: Self::COLOR, default_attrs: AttrsOwned::new(Self::default_attrs()),
chunks: vec![],
shaping: Shaping::Basic,
color: color::BLACK,
} }
} }
pub fn rich<'r, 's, I>( pub fn with_metrics(mut self, metrics: Metrics) -> Self {
font_stuff: &mut FontStuff, self.metrics = metrics;
metrics: Metrics, self
default_attrs: Attrs<'_>,
spans: I,
) -> Self
where
I: IntoIterator<Item = (&'s str, Attrs<'r>)>,
{
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 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.color = color;
self self
} }
pub fn chunk_plain<S: ToString>(mut self, text: S) -> Self {
let chunk = (self.default_attrs.clone(), text.to_string());
self.chunks.push(chunk);
self
}
pub fn chunk_rich<S: ToString>(mut self, attrs: Attrs<'_>, text: S) -> Self {
let chunk = (AttrsOwned::new(attrs), text.to_string());
self.chunks.push(chunk);
self
}
pub fn chunks<I>(mut self, chunks: I) -> Self
where
I: IntoIterator<Item = (AttrsOwned, String)>,
{
self.chunks.extend(chunks);
self
}
pub fn widget<C: HasFontStuff>(self, font_stuff: &mut FontStuff) -> impl Widget<C> {
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<C: HasFontStuff> Widget<C> for Text { impl Default for Text {
fn default() -> Self {
Self::new()
}
}
struct TextWidget {
buffer: Buffer,
color: Srgba,
}
impl<C: HasFontStuff> Widget<C> for TextWidget {
fn size( fn size(
&mut self, &mut self,
ctx: &mut C, ctx: &mut C,

View file

@ -1,4 +1,3 @@
use cosmic_text::{Attrs, Metrics};
use palette::Srgba; use palette::Srgba;
use showbits_common::{ use showbits_common::{
color, color,
@ -69,12 +68,10 @@ impl Drawer {
fn on_test(&mut self) -> anyhow::Result<()> { fn on_test(&mut self) -> anyhow::Result<()> {
let mut tree = Tree::<Context>::new(Srgba::new(1.0, 1.0, 1.0, 1.0)); let mut tree = Tree::<Context>::new(Srgba::new(1.0, 1.0, 1.0, 1.0));
let text = Text::simple( let text = Text::new()
&mut self.ctx.font_stuff, .chunk_plain("Hello\nworld!")
Metrics::new(16.0, 24.0), .with_metrics(Text::default_metrics().scale(2.0))
Attrs::new(), .widget(&mut self.ctx.font_stuff)
"Hello\nworld!",
)
.node() .node()
.margin_horiz(length(8.0)) .margin_horiz(length(8.0))
.margin_vert(length(2.0)) .margin_vert(length(2.0))