mirror of https://github.com/linebender/xilem
Disable hinting whilst a `VariableLabel` animation is ongoing (#535)
If using hinting during an animation, a shimmering effect can occur.
This commit is contained in:
parent
ff7635e4c2
commit
c77c6ecb68
|
@ -59,41 +59,72 @@ pub struct TextLayout<T> {
|
|||
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<peniko::Brush> for TextBrush {
|
||||
fn from(value: peniko::Brush) -> Self {
|
||||
Self::Normal(value)
|
||||
Self::Normal(value, Hinting::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Gradient> for TextBrush {
|
||||
fn from(value: Gradient) -> Self {
|
||||
Self::Normal(value.into())
|
||||
Self::Normal(value.into(), Hinting::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Color> 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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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::{
|
||||
|
|
|
@ -42,6 +42,7 @@ impl<T: Selectable> TextWithSelection<T> {
|
|||
highlight_brush: TextBrush::Highlight {
|
||||
text: Color::WHITE.into(),
|
||||
fill: Color::LIGHT_BLUE.into(),
|
||||
hinting: Default::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,9 +76,13 @@ pub fn render_text(
|
|||
.iter()
|
||||
.map(|coord| vello::skrifa::instance::NormalizedCoord::from_bits(*coord))
|
||||
.collect::<Vec<_>>();
|
||||
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,
|
||||
|
|
|
@ -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| {
|
||||
|
|
Loading…
Reference in New Issue