From 17f4e9f4f2655f6883b962102d665363ed57af7c Mon Sep 17 00:00:00 2001 From: Aaron Muir Hamilton Date: Thu, 10 Oct 2024 11:46:12 -0400 Subject: [PATCH] Make StyleProperty::FontStack more ergonomic. (#129) --- CHANGELOG.md | 3 +++ examples/swash_render/src/main.rs | 17 ++++++-------- examples/tiny_skia_render/src/main.rs | 32 +++++++++++++-------------- examples/vello_editor/src/main.rs | 11 ++++----- parley/src/builder.rs | 12 ++++++---- parley/src/layout/editor.rs | 2 +- parley/src/lib.rs | 10 ++++----- parley/src/style/font.rs | 30 +++++++++++++++++++++++++ parley/src/style/mod.rs | 24 ++++++++++++++++++++ 9 files changed, 97 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2063c66f..154c382f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ This release has an [MSRV] of 1.75. - An example with Vello on Winit which shows a basic text editor ([#106] by [@dfrg]) - `PlainEditor`, a basic action-based text editor based on Parley `Selection` and `Cursor` ([#126] by [@xorgy]) - Tree style builder ([#76] by [@nicoburns]) +- Conversions for `FontFamily`, `FontStack`, and `StyleProperty` to make styling more ergonomic ([#129] by [@xorgy]) ### Changed @@ -35,6 +36,7 @@ This release has an [MSRV] of 1.75. #### Parley - Emoji clusters now get an Emoji family added by default ([#56] by [@dfrg]) +- Style builders now accept `Into>` so you can push a `GenericFamily` or `FontStack` directly. ([#129] by [@xorgy]) #### Fontique @@ -78,6 +80,7 @@ This release has an [MSRV] of 1.70. [#85]: https://github.com/linebender/parley/pull/85 [#106]: https://github.com/linebender/parley/pull/106 [#126]: https://github.com/linebender/parley/pull/126 +[#129]: https://github.com/linebender/parley/pull/129 [Unreleased]: https://github.com/linebender/parley/compare/v0.1.0...HEAD [0.1.0]: https://github.com/linebender/parley/releases/tag/v0.1.0 diff --git a/examples/swash_render/src/main.rs b/examples/swash_render/src/main.rs index c95e6bf6..bb5c1b1d 100644 --- a/examples/swash_render/src/main.rs +++ b/examples/swash_render/src/main.rs @@ -10,7 +10,6 @@ use parley::layout::{Alignment, Glyph, GlyphRun, Layout, PositionedLayoutItem}; use parley::style::{FontStack, FontWeight, StyleProperty, TextStyle}; use parley::{FontContext, InlineBox, LayoutContext}; use peniko::Color; -use std::borrow::Cow; use std::fs::File; use swash::scale::image::Content; use swash::scale::{Render, ScaleContext, Scaler, Source, StrikeWith}; @@ -47,10 +46,8 @@ fn main() { // Setup some Parley text styles let brush_style = StyleProperty::Brush(text_color); - let font_stack = FontStack::Source(Cow::Borrowed("system-ui")); - let font_stack_style: StyleProperty = StyleProperty::FontStack(font_stack.clone()); - let bold = FontWeight::new(600.0); - let bold_style = StyleProperty::FontWeight(bold); + let font_stack = FontStack::from("system-ui"); + let bold_style = StyleProperty::FontWeight(FontWeight::new(600.0)); let mut layout = if std::env::args().any(|arg| arg == "--tree") { // TREE BUILDER @@ -104,15 +101,15 @@ fn main() { let mut builder = layout_cx.ranged_builder(&mut font_cx, &text, display_scale); // Set default text colour styles (set foreground text color) - builder.push_default(&brush_style); + builder.push_default(brush_style); // Set default font family - builder.push_default(&font_stack_style); - builder.push_default(&StyleProperty::LineHeight(1.3)); - builder.push_default(&StyleProperty::FontSize(16.0)); + builder.push_default(font_stack); + builder.push_default(StyleProperty::LineHeight(1.3)); + builder.push_default(StyleProperty::FontSize(16.0)); // Set the first 4 characters to bold - builder.push(&bold_style, 0..4); + builder.push(bold_style, 0..4); builder.push_inline_box(InlineBox { id: 0, diff --git a/examples/tiny_skia_render/src/main.rs b/examples/tiny_skia_render/src/main.rs index a1690ec4..565a2e5f 100644 --- a/examples/tiny_skia_render/src/main.rs +++ b/examples/tiny_skia_render/src/main.rs @@ -7,16 +7,17 @@ //! Note: Emoji rendering is not currently implemented in this example. See the swash example //! if you need emoji rendering. -use std::borrow::Cow; - -use parley::layout::{Alignment, GlyphRun, Layout, PositionedLayoutItem}; -use parley::style::{FontStack, FontWeight, StyleProperty}; -use parley::{FontContext, InlineBox, LayoutContext}; +use parley::{ + Alignment, FontContext, FontWeight, GenericFamily, GlyphRun, InlineBox, Layout, LayoutContext, + PositionedLayoutItem, StyleProperty, +}; use peniko::Color as PenikoColor; -use skrifa::instance::{LocationRef, NormalizedCoord, Size}; -use skrifa::outline::{DrawSettings, OutlinePen}; -use skrifa::raw::FontRef as ReadFontsRef; -use skrifa::{GlyphId, MetadataProvider, OutlineGlyph}; +use skrifa::{ + instance::{LocationRef, NormalizedCoord, Size}, + outline::{DrawSettings, OutlinePen}, + raw::FontRef as ReadFontsRef, + GlyphId, MetadataProvider, OutlineGlyph, +}; use tiny_skia::{ Color as TinySkiaColor, FillRule, Paint, PathBuilder, Pixmap, PixmapMut, Rect, Transform, }; @@ -52,19 +53,16 @@ fn main() { // Set default text colour styles (set foreground text color) let brush_style = StyleProperty::Brush(foreground_color); - builder.push_default(&brush_style); + builder.push_default(brush_style); // Set default font family - let font_stack = FontStack::Source(Cow::Borrowed("system-ui")); - let font_stack_style = StyleProperty::FontStack(font_stack); - builder.push_default(&font_stack_style); - builder.push_default(&StyleProperty::LineHeight(1.3)); - builder.push_default(&StyleProperty::FontSize(16.0)); + builder.push_default(GenericFamily::SystemUi); + builder.push_default(StyleProperty::LineHeight(1.3)); + builder.push_default(StyleProperty::FontSize(16.0)); // Set the first 4 characters to bold let bold = FontWeight::new(600.0); - let bold_style = StyleProperty::FontWeight(bold); - builder.push(&bold_style, 0..4); + builder.push(StyleProperty::FontWeight(bold), 0..4); builder.push_inline_box(InlineBox { id: 0, diff --git a/examples/vello_editor/src/main.rs b/examples/vello_editor/src/main.rs index 3ec0e372..5b0bbc3e 100644 --- a/examples/vello_editor/src/main.rs +++ b/examples/vello_editor/src/main.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT use anyhow::Result; -use std::borrow::Cow; use std::num::NonZeroUsize; use std::sync::Arc; use vello::peniko::Color; @@ -17,7 +16,7 @@ use winit::window::Window; // #[path = "text2.rs"] mod text; -use parley::layout::editor::PlainEditorOp; +use parley::{GenericFamily, PlainEditorOp, StyleProperty}; // Simple struct to hold the state of the renderer pub struct ActiveRenderState<'s> { @@ -133,11 +132,9 @@ impl ApplicationHandler for SimpleVelloApp<'_> { PlainEditorOp::SetScale(1.0), PlainEditorOp::SetWidth(size.width as f32 - 2f32 * text::INSET), PlainEditorOp::SetDefaultStyle(Arc::new([ - parley::style::StyleProperty::FontSize(32.0), - parley::style::StyleProperty::LineHeight(1.2), - parley::style::StyleProperty::FontStack(parley::style::FontStack::Source( - Cow::Borrowed("system-ui"), - )), + StyleProperty::FontSize(32.0), + StyleProperty::LineHeight(1.2), + GenericFamily::SystemUi.into(), ])), ]); } diff --git a/parley/src/builder.rs b/parley/src/builder.rs index 980787ad..ceec9872 100644 --- a/parley/src/builder.rs +++ b/parley/src/builder.rs @@ -22,19 +22,23 @@ pub struct RangedBuilder<'a, B: Brush> { } impl RangedBuilder<'_, B> { - pub fn push_default(&mut self, property: &StyleProperty) { + pub fn push_default<'a>(&mut self, property: impl Into>) { let resolved = self .lcx .rcx - .resolve_property(self.fcx, property, self.scale); + .resolve_property(self.fcx, &property.into(), self.scale); self.lcx.ranged_style_builder.push_default(resolved); } - pub fn push(&mut self, property: &StyleProperty, range: impl RangeBounds) { + pub fn push<'a>( + &mut self, + property: impl Into>, + range: impl RangeBounds, + ) { let resolved = self .lcx .rcx - .resolve_property(self.fcx, property, self.scale); + .resolve_property(self.fcx, &property.into(), self.scale); self.lcx.ranged_style_builder.push(resolved, range); } diff --git a/parley/src/layout/editor.rs b/parley/src/layout/editor.rs index 248a5247..bd041521 100644 --- a/parley/src/layout/editor.rs +++ b/parley/src/layout/editor.rs @@ -445,7 +445,7 @@ where fn update_layout(&mut self, font_cx: &mut FontContext, layout_cx: &mut LayoutContext) { let mut builder = layout_cx.ranged_builder(font_cx, &self.buffer, self.scale); for prop in self.default_style.iter() { - builder.push_default(prop); + builder.push_default(prop.to_owned()); } builder.build_into(&mut self.layout, &self.buffer); self.layout.break_all_lines(Some(self.width)); diff --git a/parley/src/lib.rs b/parley/src/lib.rs index d5f59190..d2bb4119 100644 --- a/parley/src/lib.rs +++ b/parley/src/lib.rs @@ -10,10 +10,10 @@ //! - [`RangedBuilder`] and [`TreeBuilder`] which are builders for creating a [`Layout`]. //! - [`RangedBuilder`] allows styles to be specified as a flat `Vec` of spans //! - [`TreeBuilder`] allows styles to be specified as a tree of spans -//! +//! //! They are constructed using the [`ranged_builder`](LayoutContext::ranged_builder) and [`tree_builder`](LayoutContext::ranged_builder) methods on [`LayoutContext`]. //! - [`Layout`] which represents styled paragraph(s) of text and can perform shaping, line-breaking, bidi-reordering, and alignment of that text. -//! +//! //! `Layout` supports re-linebreaking and re-aligning many times (in case the width at which wrapping should occur changes). But if the text content or //! the styles applied to that content change then a new `Layout` must be created using a new `RangedBuilder` or `TreeBuilder`. //! @@ -38,11 +38,11 @@ //! let mut builder = layout_cx.ranged_builder(&mut font_cx, &TEXT, DISPLAY_SCALE); //! //! // Set default styles that apply to the entire layout -//! builder.push_default(&StyleProperty::LineHeight(1.3)); -//! builder.push_default(&StyleProperty::FontSize(16.0)); +//! builder.push_default(StyleProperty::LineHeight(1.3)); +//! builder.push_default(StyleProperty::FontSize(16.0)); //! //! // Set a style that applies to the first 4 characters -//! builder.push(&StyleProperty::FontWeight(FontWeight::new(600.0)), 0..4); +//! builder.push(StyleProperty::FontWeight(FontWeight::new(600.0)), 0..4); //! //! // Add a box to be laid out inline with the text //! builder.push_inline_box(InlineBox { id: 0, index: 5, width: 50.0, height: 50.0 }); diff --git a/parley/src/style/font.rs b/parley/src/style/font.rs index 8101a30b..18181aae 100644 --- a/parley/src/style/font.rs +++ b/parley/src/style/font.rs @@ -83,6 +83,36 @@ impl<'a> FontFamily<'a> { } } +impl<'a> From for FontFamily<'a> { + fn from(f: GenericFamily) -> Self { + FontFamily::Generic(f) + } +} + +impl<'a> From for FontStack<'a> { + fn from(f: GenericFamily) -> Self { + FontStack::Single(f.into()) + } +} + +impl<'a> From> for FontStack<'a> { + fn from(f: FontFamily<'a>) -> Self { + FontStack::Single(f) + } +} + +impl<'a> From<&'a str> for FontStack<'a> { + fn from(s: &'a str) -> Self { + FontStack::Source(Cow::Borrowed(s)) + } +} + +impl<'a> From<&'a [FontFamily<'a>]> for FontStack<'a> { + fn from(fs: &'a [FontFamily<'a>]) -> Self { + FontStack::List(Cow::Borrowed(fs)) + } +} + impl fmt::Display for FontFamily<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { diff --git a/parley/src/style/mod.rs b/parley/src/style/mod.rs index 6efc1cb0..11d8f282 100644 --- a/parley/src/style/mod.rs +++ b/parley/src/style/mod.rs @@ -136,3 +136,27 @@ impl Default for TextStyle<'_, B> { } } } + +impl<'a, B: Brush> From> for StyleProperty<'a, B> { + fn from(fs: FontStack<'a>) -> Self { + StyleProperty::FontStack(fs) + } +} + +impl<'a, B: Brush> From<&'a [FontFamily<'a>]> for StyleProperty<'a, B> { + fn from(fs: &'a [FontFamily<'a>]) -> Self { + StyleProperty::FontStack(fs.into()) + } +} + +impl<'a, B: Brush> From> for StyleProperty<'a, B> { + fn from(f: FontFamily<'a>) -> Self { + StyleProperty::FontStack(FontStack::from(f)) + } +} + +impl<'a, B: Brush> From for StyleProperty<'a, B> { + fn from(f: GenericFamily) -> Self { + StyleProperty::FontStack(f.into()) + } +}