1use crate::{ColorsSrgb, ThemeConfig};
9use kas::text::fonts::{fonts, AddMode, FontSelector};
10use kas::theme::TextClass;
11use kas::TkAction;
12use std::collections::BTreeMap;
13use std::time::Duration;
14
15#[derive(Clone, Debug, PartialEq)]
17#[cfg_attr(feature = "config", derive(serde::Serialize, serde::Deserialize))]
18pub struct Config {
19 #[cfg_attr(feature = "config", serde(skip))]
20 dirty: bool,
21
22 #[cfg_attr(feature = "config", serde(default = "defaults::font_size"))]
24 font_size: f32,
25
26 #[cfg_attr(feature = "config", serde(default))]
28 active_scheme: String,
29
30 #[cfg_attr(feature = "config", serde(default = "defaults::color_schemes"))]
34 color_schemes: BTreeMap<String, ColorsSrgb>,
35
36 #[cfg_attr(feature = "config", serde(default))]
38 font_aliases: BTreeMap<String, FontAliases>,
39
40 #[cfg_attr(feature = "config", serde(default))]
42 fonts: BTreeMap<TextClass, FontSelector<'static>>,
43
44 #[cfg_attr(feature = "config", serde(default = "defaults::cursor_blink_rate_ms"))]
46 cursor_blink_rate_ms: u32,
47
48 #[cfg_attr(feature = "config", serde(default = "defaults::transition_fade_ms"))]
50 transition_fade_ms: u32,
51
52 #[cfg_attr(feature = "config", serde(default))]
54 raster: RasterConfig,
55}
56
57impl Default for Config {
58 fn default() -> Self {
59 Config {
60 dirty: false,
61 font_size: defaults::font_size(),
62 active_scheme: Default::default(),
63 color_schemes: defaults::color_schemes(),
64 font_aliases: Default::default(),
65 fonts: defaults::fonts(),
66 cursor_blink_rate_ms: defaults::cursor_blink_rate_ms(),
67 transition_fade_ms: defaults::transition_fade_ms(),
68 raster: Default::default(),
69 }
70 }
71}
72
73#[derive(Clone, Debug, PartialEq, Eq)]
78#[cfg_attr(feature = "config", derive(serde::Serialize, serde::Deserialize))]
79pub struct RasterConfig {
80 #[cfg_attr(feature = "config", serde(default))]
82 pub mode: u8,
83 #[cfg_attr(feature = "config", serde(default = "defaults::scale_steps"))]
89 pub scale_steps: u8,
90 #[cfg_attr(feature = "config", serde(default = "defaults::subpixel_threshold"))]
102 pub subpixel_threshold: u8,
103 #[cfg_attr(feature = "config", serde(default = "defaults::subpixel_steps"))]
113 pub subpixel_steps: u8,
114}
115
116impl Default for RasterConfig {
117 fn default() -> Self {
118 RasterConfig {
119 mode: 0,
120 scale_steps: defaults::scale_steps(),
121 subpixel_threshold: defaults::subpixel_threshold(),
122 subpixel_steps: defaults::subpixel_steps(),
123 }
124 }
125}
126
127impl Config {
129 #[inline]
133 pub fn font_size(&self) -> f32 {
134 self.font_size
135 }
136
137 #[inline]
141 pub fn active_scheme(&self) -> &str {
142 &self.active_scheme
143 }
144
145 #[inline]
147 pub fn color_schemes_iter(&self) -> impl Iterator<Item = (&str, &ColorsSrgb)> {
148 self.color_schemes.iter().map(|(s, t)| (s.as_str(), t))
149 }
150
151 #[inline]
153 pub fn get_color_scheme(&self, name: &str) -> Option<ColorsSrgb> {
154 self.color_schemes.get(name).cloned()
155 }
156
157 #[inline]
161 pub fn get_active_scheme(&self) -> Option<ColorsSrgb> {
162 self.color_schemes.get(&self.active_scheme).cloned()
163 }
164
165 #[inline]
167 pub fn iter_fonts(&self) -> impl Iterator<Item = (&TextClass, &FontSelector<'static>)> {
168 self.fonts.iter()
169 }
170
171 #[inline]
173 pub fn cursor_blink_rate(&self) -> Duration {
174 Duration::from_millis(self.cursor_blink_rate_ms as u64)
175 }
176
177 #[inline]
179 pub fn transition_fade_duration(&self) -> Duration {
180 Duration::from_millis(self.transition_fade_ms as u64)
181 }
182}
183
184impl Config {
186 pub fn set_font_size(&mut self, pt_size: f32) {
188 self.dirty = true;
189 self.font_size = pt_size;
190 }
191
192 pub fn set_active_scheme(&mut self, scheme: impl ToString) {
194 self.dirty = true;
195 self.active_scheme = scheme.to_string();
196 }
197}
198
199impl Config {
201 #[allow(clippy::float_cmp)]
203 pub fn apply_config(&mut self, other: &Config) -> TkAction {
204 let action = if self.font_size != other.font_size {
205 TkAction::RESIZE | TkAction::THEME_UPDATE
206 } else if self != other {
207 TkAction::REDRAW
208 } else {
209 TkAction::empty()
210 };
211
212 *self = other.clone();
213 action
214 }
215}
216
217impl ThemeConfig for Config {
218 #[cfg(feature = "config")]
219 #[inline]
220 fn is_dirty(&self) -> bool {
221 self.dirty
222 }
223
224 fn apply_startup(&self) {
226 if !self.font_aliases.is_empty() {
227 fonts().update_db(|db| {
228 for (family, aliases) in self.font_aliases.iter() {
229 db.add_aliases(
230 family.to_string().into(),
231 aliases.list.iter().map(|s| s.to_string().into()),
232 aliases.mode,
233 );
234 }
235 });
236 }
237 }
238
239 #[inline]
241 fn raster(&self) -> &RasterConfig {
242 &self.raster
243 }
244}
245
246#[allow(clippy::derive_partial_eq_without_eq)]
247#[derive(Clone, Debug, PartialEq)]
248#[cfg_attr(feature = "config", derive(serde::Serialize, serde::Deserialize))]
249pub struct FontAliases {
250 #[cfg_attr(feature = "config", serde(default = "defaults::add_mode"))]
251 mode: AddMode,
252 list: Vec<String>,
253}
254
255mod defaults {
256 use super::*;
257
258 #[cfg(feature = "config")]
259 pub fn add_mode() -> AddMode {
260 AddMode::Prepend
261 }
262
263 pub fn font_size() -> f32 {
264 10.0
265 }
266
267 pub fn color_schemes() -> BTreeMap<String, ColorsSrgb> {
268 let mut schemes = BTreeMap::new();
269 schemes.insert("light".to_string(), ColorsSrgb::light());
270 schemes.insert("dark".to_string(), ColorsSrgb::dark());
271 schemes.insert("blue".to_string(), ColorsSrgb::blue());
272 schemes
273 }
274
275 pub fn fonts() -> BTreeMap<TextClass, FontSelector<'static>> {
276 let mut selector = FontSelector::new();
277 selector.set_families(vec!["serif".into()]);
278 let list = [
279 (TextClass::Edit(false), selector.clone()),
280 (TextClass::Edit(true), selector),
281 ];
282 list.iter().cloned().collect()
283 }
284
285 pub fn cursor_blink_rate_ms() -> u32 {
286 600
287 }
288
289 pub fn transition_fade_ms() -> u32 {
290 150
291 }
292
293 pub fn scale_steps() -> u8 {
294 4
295 }
296 pub fn subpixel_threshold() -> u8 {
297 0
298 }
299 pub fn subpixel_steps() -> u8 {
300 5
301 }
302}