diff --git a/masonry/src/text/layout.rs b/masonry/src/text/layout.rs index 5575d4c3..0c2e9cab 100644 --- a/masonry/src/text/layout.rs +++ b/masonry/src/text/layout.rs @@ -59,41 +59,72 @@ pub struct TextLayout { scratch_scene: Scene, } +/// Whether a section of text should be hinted. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] +pub enum Hinting { + #[default] + Yes, + No, +} + +impl Hinting { + /// Whether the + pub fn should_hint(self) -> bool { + match self { + Hinting::Yes => true, + Hinting::No => false, + } + } +} + /// A custom brush for `Parley`, enabling using Parley to pass-through /// which glyphs are selected/highlighted #[derive(Clone, Debug, PartialEq)] pub enum TextBrush { - Normal(peniko::Brush), + Normal(peniko::Brush, Hinting), Highlight { text: peniko::Brush, fill: peniko::Brush, + hinting: Hinting, }, } +impl TextBrush { + pub fn set_hinting(&mut self, hinting: Hinting) { + match self { + TextBrush::Normal(_, should_hint) => *should_hint = hinting, + TextBrush::Highlight { + hinting: should_hint, + .. + } => *should_hint = hinting, + } + } +} + impl BrushTrait for TextBrush {} impl From for TextBrush { fn from(value: peniko::Brush) -> Self { - Self::Normal(value) + Self::Normal(value, Hinting::default()) } } impl From for TextBrush { fn from(value: Gradient) -> Self { - Self::Normal(value.into()) + Self::Normal(value.into(), Hinting::default()) } } impl From for TextBrush { fn from(value: Color) -> Self { - Self::Normal(value.into()) + Self::Normal(value.into(), Hinting::default()) } } // Parley requires their Brush implementations to implement Default impl Default for TextBrush { fn default() -> Self { - Self::Normal(Default::default()) + Self::Normal(Default::default(), Hinting::default()) } } diff --git a/masonry/src/text/mod.rs b/masonry/src/text/mod.rs index 716b84b3..0a9bbe70 100644 --- a/masonry/src/text/mod.rs +++ b/masonry/src/text/mod.rs @@ -14,7 +14,7 @@ mod store; pub use store::{Link, TextStorage}; mod layout; -pub use layout::{LayoutMetrics, TextBrush, TextLayout}; +pub use layout::{Hinting, LayoutMetrics, TextBrush, TextLayout}; mod selection; pub use selection::{ diff --git a/masonry/src/text/selection.rs b/masonry/src/text/selection.rs index 13cfac76..eceb9e77 100644 --- a/masonry/src/text/selection.rs +++ b/masonry/src/text/selection.rs @@ -42,6 +42,7 @@ impl TextWithSelection { highlight_brush: TextBrush::Highlight { text: Color::WHITE.into(), fill: Color::LIGHT_BLUE.into(), + hinting: Default::default(), }, } } diff --git a/masonry/src/text_helpers.rs b/masonry/src/text_helpers.rs index 33c5bf50..829a517b 100644 --- a/masonry/src/text_helpers.rs +++ b/masonry/src/text_helpers.rs @@ -76,9 +76,13 @@ pub fn render_text( .iter() .map(|coord| vello::skrifa::instance::NormalizedCoord::from_bits(*coord)) .collect::>(); - let text_brush = match &style.brush { - TextBrush::Normal(text_brush) => text_brush, - TextBrush::Highlight { text, fill } => { + let (text_brush, hinting) = match &style.brush { + TextBrush::Normal(text_brush, hinting) => (text_brush, hinting), + TextBrush::Highlight { + text, + fill, + hinting, + } => { scene.fill( Fill::EvenOdd, transform, @@ -95,13 +99,13 @@ pub fn render_text( ), ); - text + (text, hinting) } }; scratch_scene .draw_glyphs(font) .brush(text_brush) - .hint(true) + .hint(hinting.should_hint()) .transform(transform) .glyph_transform(glyph_xform) .font_size(font_size) @@ -121,7 +125,8 @@ pub fn render_text( ); if let Some(underline) = &style.underline { let underline_brush = match &underline.brush { - TextBrush::Normal(text) => text, + // Underlines aren't hinted + TextBrush::Normal(text, _) => text, // It doesn't make sense for an underline to have a highlight colour, so we // just use the text colour for the colour TextBrush::Highlight { text, .. } => text, @@ -154,7 +159,8 @@ pub fn render_text( } if let Some(strikethrough) = &style.strikethrough { let strikethrough_brush = match &strikethrough.brush { - TextBrush::Normal(text) => text, + // Strikethroughs aren't hinted + TextBrush::Normal(text, _) => text, // It doesn't make sense for an underline to have a highlight colour, so we // just use the text colour for the colour TextBrush::Highlight { text, .. } => text, diff --git a/masonry/src/widget/variable_label.rs b/masonry/src/widget/variable_label.rs index 5d414f75..b239de97 100644 --- a/masonry/src/widget/variable_label.rs +++ b/masonry/src/widget/variable_label.rs @@ -15,7 +15,7 @@ use vello::kurbo::{Affine, Point, Size}; use vello::peniko::BlendMode; use vello::Scene; -use crate::text::{TextBrush, TextLayout, TextStorage}; +use crate::text::{Hinting, TextBrush, TextLayout, TextStorage}; use crate::widget::WidgetMut; use crate::{ AccessCtx, AccessEvent, ArcStr, BoxConstraints, EventCtx, LayoutCtx, LifeCycle, LifeCycleCtx, @@ -50,6 +50,11 @@ impl AnimatedF32 { } } + /// Is this animation finished? + pub fn is_completed(&self) -> bool { + self.target == self.value + } + /// Move this value to the `target` over `over_millis` milliseconds. /// Might change the current value, if `over_millis` is zero. /// @@ -186,7 +191,7 @@ impl VariableLabel { } /// Set the initial font weight for this text. pub fn with_initial_weight(mut self, weight: f32) -> Self { - self.weight.value = weight; + self.weight = AnimatedF32::stable(weight); self } @@ -194,6 +199,19 @@ impl VariableLabel { pub fn empty() -> Self { Self::new("") } + + fn brush(&self, disabled: bool) -> TextBrush { + if disabled { + crate::theme::DISABLED_TEXT_COLOR.into() + } else { + let mut brush = self.brush.clone(); + if !self.weight.is_completed() { + brush.set_hinting(Hinting::No); + } + // N.B. if hinting is No externally, we don't want to overwrite it to yes. + brush + } + } } // --- MARK: WIDGETMUT --- @@ -227,8 +245,9 @@ impl WidgetMut<'_, VariableLabel> { let brush = brush.into(); self.widget.brush = brush; if !self.ctx.is_disabled() { - let brush = self.widget.brush.clone(); - self.set_text_properties(|layout| layout.set_brush(brush)); + self.widget.text_layout.invalidate(); + self.ctx.request_layout(); + self.ctx.request_paint(); } } /// Set the font size for this text. @@ -343,6 +362,8 @@ impl Widget for VariableLabel { }; self.text_layout.set_max_advance(max_advance); if self.text_layout.needs_rebuild() { + self.text_layout + .set_brush(self.brush(ctx.widget_state.is_disabled())); let (font_ctx, layout_ctx) = ctx.text_contexts(); self.text_layout .rebuild_with_attributes(font_ctx, layout_ctx, |mut builder| {