Skip to main content

kas_core/config/
font.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License in the LICENSE-APACHE file or at:
4//     https://www.apache.org/licenses/LICENSE-2.0
5
6//! Font configuration
7
8use crate::ConfigAction;
9use crate::text::fonts::FontSelector;
10use crate::theme::TextClass;
11use std::collections::BTreeMap;
12
13/// A message which may be used to update [`FontConfig`]
14#[derive(Clone, Debug)]
15#[non_exhaustive]
16pub enum FontConfigMsg {
17    /// Standard font size, in units of pixels-per-Em
18    Size(f32),
19}
20
21/// Font configuration
22#[derive(Clone, Debug, PartialEq)]
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24pub struct FontConfig {
25    /// Standard font size, in units of pixels-per-Em
26    #[cfg_attr(feature = "serde", serde(default = "defaults::size"))]
27    size: f32,
28
29    /// Standard fonts
30    ///
31    /// Changing this at run-tme is not currently supported.
32    ///
33    /// TODO: read/write support.
34    #[cfg_attr(feature = "serde", serde(default = "defaults::fonts"))]
35    fonts: BTreeMap<TextClass, FontSelector>,
36
37    /// Text glyph rastering settings
38    ///
39    /// Changing this at run-tme is not currently supported.
40    #[cfg_attr(feature = "serde", serde(default))]
41    raster: RasterConfig,
42}
43
44impl Default for FontConfig {
45    fn default() -> Self {
46        FontConfig {
47            size: defaults::size(),
48            fonts: defaults::fonts(),
49            raster: Default::default(),
50        }
51    }
52}
53
54/// Sub-pixel font rendering control
55#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
56#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
57pub enum SubpixelMode {
58    /// No sub-pixel rendering
59    ///
60    /// This is the default because it is the simplest, always supported and never wrong.
61    #[default]
62    None,
63    /// Horizontal RGB sub-pixels
64    ///
65    /// This is the most common LCD display type.
66    HorizontalRGB,
67}
68
69impl SubpixelMode {
70    /// Returns true if any subpixel rendering mode is enabled
71    pub fn any_subpixel(self) -> bool {
72        self != SubpixelMode::None
73    }
74}
75
76/// Font raster settings
77///
78/// These are not used by the theme, but passed through to the rendering
79/// backend.
80#[derive(Clone, Debug, PartialEq, Eq)]
81#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
82pub struct RasterConfig {
83    //// Raster mode/engine (backend dependent)
84    ////
85    /// The `mode` parameter selects rendering mode (though depending on crate
86    /// features, not all options will be available). The default mode is `4`.
87    /// In case the crate is built without the required `fontdue` or `swash`
88    /// feature, the rasterer falls back to mode `0`.
89    ///
90    /// -   `mode == 0`: use `ab_glyph` for rastering
91    /// -   `mode == 1`: use `ab_glyph` and align glyphs to side bearings
92    /// -   `mode == 2`: deprecated
93    /// -   `mode == 3`: use `swash` for rastering
94    /// -   `mode == 4`: use `swash` for rastering with hinting
95    #[cfg_attr(feature = "serde", serde(default = "defaults::mode"))]
96    pub mode: u8,
97    /// Subpixel positioning threshold
98    ///
99    /// Text with height `h` less than this threshold will use sub-pixel
100    /// positioning (see below), which should make letter spacing more
101    /// accurate for small fonts.
102    ///
103    /// Units: physical pixels per Em ("dpem"). Default value: 18.
104    #[cfg_attr(feature = "serde", serde(default = "defaults::subpixel_threshold"))]
105    pub subpixel_threshold: u8,
106    /// Subpixel steps (horizontal)
107    ///
108    /// The number of sub-pixel positioning steps to use on the x-axis for
109    /// horizontal text (when enabled; see [`Self::subpixel_threshold`]).
110    ///
111    /// Minimum: 1 (no sub-pixel positioning). Maximum: 16. Default: 3.
112    #[cfg_attr(feature = "serde", serde(default = "defaults::subpixel_x_steps"))]
113    pub subpixel_x_steps: u8,
114    /// Subpixel rendering mode
115    pub subpixel_mode: SubpixelMode,
116}
117
118impl Default for RasterConfig {
119    fn default() -> Self {
120        RasterConfig {
121            mode: defaults::mode(),
122            subpixel_threshold: defaults::subpixel_threshold(),
123            subpixel_x_steps: defaults::subpixel_x_steps(),
124            subpixel_mode: Default::default(),
125        }
126    }
127}
128
129/// Getters
130impl FontConfig {
131    /// Get font size
132    ///
133    /// Units: logical (unscaled) pixels per Em.
134    ///
135    /// To convert to Points, multiply by three quarters.
136    #[inline]
137    pub fn get_dpem(&self, class: TextClass) -> f32 {
138        if class != TextClass::Small {
139            self.size
140        } else {
141            self.size * 0.8
142        }
143    }
144
145    /// Get a [`FontSelector`] for `class`
146    #[inline]
147    pub fn get_font_selector(&self, class: TextClass) -> FontSelector {
148        self.fonts.get(&class).cloned().unwrap_or_default()
149    }
150}
151
152/// Setters
153impl FontConfig {
154    /// Set standard font size
155    ///
156    /// Units: logical (unscaled) pixels per Em.
157    ///
158    /// To convert to Points, multiply by three quarters.
159    pub fn set_size(&mut self, pt_size: f32) -> ConfigAction {
160        if self.size != pt_size {
161            self.size = pt_size;
162            ConfigAction::THEME
163        } else {
164            ConfigAction::empty()
165        }
166    }
167}
168
169/// Other functions
170impl FontConfig {
171    /// Apply config effects which only happen on startup
172    pub(super) fn init(&self) {}
173
174    /// Get raster config
175    #[inline]
176    pub fn raster(&self) -> &RasterConfig {
177        &self.raster
178    }
179}
180
181mod defaults {
182    use kas_text::fonts::FamilySelector;
183
184    use super::*;
185
186    pub fn size() -> f32 {
187        16.0
188    }
189
190    pub fn fonts() -> BTreeMap<TextClass, FontSelector> {
191        let list = [
192            (TextClass::Serif, FamilySelector::SERIF.into()),
193            (TextClass::SansSerif, FamilySelector::SANS_SERIF.into()),
194            (TextClass::Monospace, FamilySelector::MONOSPACE.into()),
195        ];
196        list.into_iter().collect()
197    }
198
199    pub fn mode() -> u8 {
200        4
201    }
202    pub fn subpixel_threshold() -> u8 {
203        18
204    }
205    pub fn subpixel_x_steps() -> u8 {
206        3
207    }
208}