ltk/theme/
fonts.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>

//! Font family definitions as declared by the theme document.
//!
//! This module only models the **declaration**: a family name, a set of
//! source files indexed by weight/style, and a fallback chain. Loading the
//! `.ttf` bytes into `fontdue` and resolving text styles against a live
//! registry is the runtime registry's job (see [`super::FontRegistry`]).

use std::path::PathBuf;

use super::text_style::FontStyle;

// ─── FontFamilyDef ───────────────────────────────────────────────────────────

/// A font family as declared by the theme document.
///
/// `sources` is a flat list indexed by weight + style, not a nested map, so
/// the JSON round-trips without ambiguity on ordering.
#[ derive( Debug, Clone, PartialEq ) ]
pub struct FontFamilyDef
{
	/// Human-readable family name (e.g. `"Sora"`). Shown in telemetry and
	/// in any eventual font-picker UI.
	pub name:      String,
	/// Fallback chain if the family or a given weight/style is missing.
	/// Resolved in order: the first entry that the font registry can
	/// satisfy is used.
	pub fallbacks: Vec<String>,
	/// One source per weight/style the family ships. The runtime font
	/// registry registers each source in `fontdue` at load time.
	pub sources:   Vec<FontSource>,
}

// ─── FontSource ──────────────────────────────────────────────────────────────

/// A single font file, specified by its numeric weight and style.
#[ derive( Debug, Clone, PartialEq, Eq ) ]
pub struct FontSource
{
	/// CSS numeric weight (100..=900).
	pub weight: u16,
	/// Italic vs upright.
	pub style:  FontStyle,
	/// Path to the `.ttf` / `.otf` file. Resolved to absolute at load time
	/// (relative paths are taken to be relative to the theme directory root).
	pub path:   PathBuf,
}

// ─── Tests ───────────────────────────────────────────────────────────────────

#[ cfg( test ) ]
mod tests
{
	use super::*;

	#[ test ]
	fn family_def_holds_multiple_weights_of_same_family()
	{
		let sora = FontFamilyDef
		{
			name:      "Sora".to_string(),
			fallbacks: vec![ "system-ui".to_string(), "sans-serif".to_string() ],
			sources:   vec!
			[
				FontSource { weight: 300, style: FontStyle::Normal, path: "fonts/Sora-Light.ttf".into()    },
				FontSource { weight: 400, style: FontStyle::Normal, path: "fonts/Sora-Regular.ttf".into()  },
				FontSource { weight: 600, style: FontStyle::Normal, path: "fonts/Sora-SemiBold.ttf".into() },
				FontSource { weight: 700, style: FontStyle::Normal, path: "fonts/Sora-Bold.ttf".into()     },
			],
		};
		assert_eq!( sora.sources.len(), 4 );
		assert_eq!( sora.sources[2].weight, 600 );
		assert_eq!( sora.fallbacks[0], "system-ui" );
	}
}