Skip to content

Commit

Permalink
Label now invalidates with more settings
Browse files Browse the repository at this point in the history
From discord, the text size being set for a label wasn't always causing
the label to be laid out correctly. This ended up being caused by the
text size not being included in the label cache key. I've refactored
this to support all of the font settings, and in theory make it harder
to break in the future.
  • Loading branch information
ecton committed Dec 20, 2024
1 parent dc86f82 commit dba57ef
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 33 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
made without observing any ill effects from the existing logic, but the logic
was not correct.
- `Input` and `Label` now honor `ConstraintLayout::Fill`.
- `Label` now properly invalidates itself when various font style components are
changed.

### Added

Expand Down Expand Up @@ -383,6 +385,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
to standardize a method of adding informational text to interfaces.
- `ImageScaling::layout_size` and `ImageScaling::render_area` are new functions
that expose the layout calculations the `Image` widget performed.
- `GraphicsContext::current_font_settings()` returns a new struct `FontSettings`
that contains the effective style components for the various font settings
supported by a `GraphicsContext`. `FontSettings::apply()` can be used to apply
settings in one step. `FontSettings` also implements `PartialEq` allowing it
to be used as a cache invalidation key.


[139]: https://github.com/khonsulabs/cushy/issues/139
Expand Down
55 changes: 48 additions & 7 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use figures::units::{Lp, Px, UPx};
use figures::{IntoSigned, Point, Rect, Round, ScreenScale, Size, Zero};
use kludgine::app::winit::event::{Ime, MouseButton, MouseScrollDelta, TouchPhase};
use kludgine::app::winit::window::Cursor;
use kludgine::cosmic_text::FamilyOwned;
use kludgine::cosmic_text::{FamilyOwned, Style, Weight};
use kludgine::shapes::{Shape, StrokeOptions};
use kludgine::{Color, Kludgine, KludgineId};

Expand All @@ -17,7 +17,7 @@ use crate::styles::components::{
CornerRadius, FontFamily, FontStyle, FontWeight, HighlightColor, LayoutOrder, LineHeight,
Opacity, OutlineWidth, TextSize, WidgetBackground,
};
use crate::styles::{ComponentDefinition, FontFamilyList, Styles, Theme, ThemePair};
use crate::styles::{ComponentDefinition, Dimension, FontFamilyList, Styles, Theme, ThemePair};
use crate::tree::Tree;
use crate::value::{IntoValue, Source, Value};
use crate::widget::{EventHandling, MountedWidget, RootBehavior, WidgetId, WidgetInstance};
Expand Down Expand Up @@ -709,14 +709,29 @@ impl<'clip, 'gfx, 'pass> GraphicsContext<'_, 'clip, 'gfx, 'pass> {
self.stroke_outline(color, StrokeOptions::px_wide(width));
}

/// Returns the widget context's current font settings.
///
/// The settings returned are from retrieving the values of these style
/// components:
///
/// The returned settings are not necessarily what is currently applied to
/// this graphics context. To apply these settings, consider using
/// [`Self::apply_current_font_settings()`] or [`FontSettings::apply`].
#[must_use]
pub fn current_font_settings(&self) -> FontSettings {
FontSettings {
family: self.widget.get(&FontFamily),
size: self.widget.get(&TextSize),
line_height: self.widget.get(&LineHeight),
style: self.widget.get(&FontStyle),
weight: self.widget.get(&FontWeight),
}
}

/// Applies the current style settings for font family, text size, font
/// style, and font weight.
pub fn apply_current_font_settings(&mut self) {
self.set_available_font_family(&self.widget.get(&FontFamily));
self.gfx.set_font_size(self.widget.get(&TextSize));
self.gfx.set_line_height(self.widget.get(&LineHeight));
self.gfx.set_font_style(self.widget.get(&FontStyle));
self.gfx.set_font_weight(self.widget.get(&FontWeight));
self.current_font_settings().apply(self);
}

/// Invokes [`Widget::redraw()`](crate::widget::Widget::redraw) on this
Expand Down Expand Up @@ -763,6 +778,32 @@ impl DerefMut for GraphicsContext<'_, '_, '_, '_> {
}
}

/// The font settings supported by a [`GraphicsContext`].
#[derive(PartialEq, Debug, Clone)]
pub struct FontSettings {
/// The list of font families.
pub family: FontFamilyList,
/// The font size.
pub size: Dimension,
/// The line height.
pub line_height: Dimension,
/// The font style.
pub style: Style,
/// The font weight (boldness/lightness).
pub weight: Weight,
}

impl FontSettings {
/// Applies these font settings to `context`.
pub fn apply(&self, context: &mut GraphicsContext<'_, '_, '_, '_>) {
context.set_available_font_family(&self.family);
context.gfx.set_font_size(self.size);
context.gfx.set_line_height(self.line_height);
context.gfx.set_font_style(self.style);
context.gfx.set_font_weight(self.weight);
}
}

/// A context to a function that is rendering a widget.
pub struct LayoutContext<'context, 'clip, 'gfx, 'pass> {
/// The graphics context that this layout operation is being performed
Expand Down
69 changes: 43 additions & 26 deletions src/widgets/label.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ use kludgine::text::{MeasuredText, Text, TextOrigin};
use kludgine::{cosmic_text, CanRenderTo, Color, DrawableExt};

use super::input::CowString;
use crate::context::{GraphicsContext, LayoutContext, Trackable, WidgetContext};
use crate::context::{FontSettings, GraphicsContext, LayoutContext, Trackable, WidgetContext};
use crate::styles::components::{HorizontalAlignment, TextColor, VerticalAlignment};
use crate::styles::{FontFamilyList, HorizontalAlign, VerticalAlign};
use crate::styles::{HorizontalAlign, VerticalAlign};
use crate::value::{
Dynamic, DynamicReader, Generation, IntoDynamic, IntoReadOnly, IntoValue, ReadOnly, Value,
};
Expand All @@ -28,7 +28,7 @@ pub struct Label<T> {
/// single line.
pub overflow: Value<LabelOverflow>,
displayed: String,
prepared_text: WindowLocal<LabelCacheKey>,
prepared_text: WindowLocal<LabelCache>,
}

impl<T> Label<T>
Expand Down Expand Up @@ -61,7 +61,6 @@ where
mut width: Px,
align: HorizontalAlign,
) -> &MeasuredText<Px> {
let is_left_aligned = align == HorizontalAlign::Left;
let align = match align {
HorizontalAlign::Left => cosmic_text::Align::Left,
HorizontalAlign::Center => cosmic_text::Align::Center,
Expand All @@ -71,22 +70,20 @@ where
if overflow == LabelOverflow::Clip {
width = Px::MAX;
}
let check_generation = self.display.generation();
let check_display_generation = self.display.map(|display| display.generation(context));
context.apply_current_font_settings();
let current_families = context.current_family_list();

let mut cache_key = LabelCacheKey {
generation: self.display.generation(),
display_generation: self.display.map(|display| display.generation(context)),
width,
color,
settings: context.current_font_settings(),
align,
};

match self.prepared_text.get(context) {
Some(cache)
if cache.text.can_render_to(&context.gfx)
&& cache.generation == check_generation
&& cache.display_generation == check_display_generation
&& cache.color == color
&& cache.align == align
&& ((is_left_aligned
&& width <= cache.width
&& cache.text.size.width <= width)
|| (!is_left_aligned && width == cache.width))
&& cache.families == current_families => {}
if cache.text.can_render_to(&context.gfx) && cache_key.is_valid_for(cache) => {}
_ => {
let (measured, display_generation) = self.display.map(|text| {
self.displayed.clear();
Expand All @@ -100,16 +97,12 @@ where
text.generation(context),
)
});
cache_key.display_generation = display_generation;
self.prepared_text.set(
context,
LabelCacheKey {
LabelCache {
text: measured,
generation: check_generation,
display_generation,
width,
color,
families: current_families,
align,
key: cache_key,
},
);
}
Expand Down Expand Up @@ -218,16 +211,40 @@ pub enum LabelOverflow {
}

#[derive(Debug)]
struct LabelCacheKey {
struct LabelCache {
text: MeasuredText<Px>,
key: LabelCacheKey,
}

#[derive(Debug)]
struct LabelCacheKey {
generation: Option<Generation>,
display_generation: Option<Generation>,
width: Px,
color: Color,
families: FontFamilyList,
settings: FontSettings,
align: cosmic_text::Align,
}

impl LabelCacheKey {
pub fn is_valid_for(&self, cache: &LabelCache) -> bool {
if self.generation == cache.key.generation
&& self.display_generation == cache.key.display_generation
&& self.color == cache.key.color
&& self.settings == cache.key.settings
&& self.align == cache.key.align
{
if self.align == cosmic_text::Align::Left {
self.width <= cache.key.width && cache.text.size.width <= self.width
} else {
self.width == cache.key.width
}
} else {
false
}
}
}

/// A context-aware [`Display`] implementation.
///
/// This trait is automatically implemented for all types that implement
Expand Down

0 comments on commit dba57ef

Please sign in to comment.