ltk/theme/
mod.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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>

//! Theming for ltk-based applications.
//!
//! A *theme* is a directory on disk holding a `theme.json` and any
//! supporting assets (wallpapers, fonts, …). The JSON declares `light`
//! and `dark` modes — each with its own slot table plus wallpaper,
//! lockscreen, launcher and window-controls blocks — plus a shared
//! `fonts` registry. The shell picks which mode is active via
//! [`ThemeMode`].
//!
//! # On-disk layout
//!
//! ```text
//! /usr/share/ltk/themes/<id>/             (system overlay, lower priority)
//! ~/.local/share/ltk/themes/<id>/         (user overlay,   higher priority)
//!     theme.json
//!     background-light.png
//!     background-dark.png
//!     fonts/
//!         Sora-Regular.ttf
//! ```
//!
//! Paths inside `theme.json` are interpreted relative to the theme's
//! directory and are eagerly resolved to absolute paths at load time.
//!
//! # Process-wide active state
//!
//! The active theme is published process-wide so widgets can consult their
//! colours without every caller threading the palette through their own
//! state. Use [`set_active_document`] / [`set_active_mode`] to change it,
//! and [`active_document`] / [`active_mode`] / [`active_theme_id`] to read
//! it back. Per-slot shorthand accessors ([`color`], [`paint()`], [`surface()`],
//! [`palette()`], …) cover the common patterns without going through the
//! full document.
//!
//! There is **no in-code fallback**: if `ensure_active` cannot locate the
//! `default` theme in any search path, the process aborts with a message
//! pointing at the `ltk-theme-default` Debian package (it `Provides:
//! ltk-theme`) or at the `LTK_THEMES_DIR` environment variable for
//! development installations.

use std::sync::{ Arc, OnceLock };

// ─── Submodules ──────────────────────────────────────────────────────────────
//
// Paint / Shadow / Surface / TextStyle are the building blocks of the
// slot-typed theme schema; the SlotStore (`slots`) indexes them by id;
// the JSON loader (`schema`) parses the on-disk shape. `document` holds
// the top-level `ThemeDocument` and per-mode `Mode` types. `fonts` +
// `font_registry` cover the typed font references and their
// runtime-loaded counterparts. `gradient_lut` is the small CPU helper
// the GPU gradient shaders consume.

pub mod paint;
pub mod shadow;
pub mod surface;
pub mod text_style;

pub mod fonts;
pub mod font_registry;
pub mod gradient_lut;
pub mod slots;
pub mod document;
pub ( crate ) mod schema;
pub ( crate ) mod fallback;

pub mod accessors;
pub mod active;
pub mod assets;
pub mod error;
pub mod palette;
pub mod prefs;
pub mod search;
pub mod typography;

pub use paint::{ ColorStop, GradientSpace, LinearGradient, Paint, RadialGradient };
pub use shadow::{ BlendMode, InsetShadow, Shadow, ShadowsRef };
pub use surface::Surface;
pub use text_style::{ FontRef, FontStyle, LineHeight, TextDecoration, TextStyle, TextTransform };

pub use fonts::{ FontFamilyDef, FontSource };
pub use font_registry::{ FontKey, FontLoadError, FontRegistry };
pub use slots::{ Metadata, Slot, SlotStore };
pub use document::{ Mode, ThemeDocument };

pub use accessors::{ color, color_or, paint, palette, resolve_surface, shadows, surface, text_style, window_controls };
pub use active::{ active_document, active_mode, active_theme_id, is_fallback_active, set_active_document, set_active_mode };
pub use assets::{ app_default_icon, app_icon, branding_asset, branding_image, branding_raster, decode_svg_bytes, icon_path, icon_rgba, launcher_icon, lockscreen, logo, logo_horizontal, logo_square, tint_symbolic, wallpaper };
pub use error::ThemeError;
pub use palette::Palette;
pub use prefs::{ LauncherSpec, ThemeMode, ThemePreference, WallpaperFit, WallpaperSpec, WindowControlsSpec };
pub use search::{ build_font_registry, search_paths };

/// Shared `usvg` font database, lazily populated from the host's system
/// fonts. resvg's default `Options::fontdb` is empty, so SVGs containing
/// `<text>` would otherwise rasterise with no glyphs at all.
pub ( crate ) fn system_fontdb() -> Arc<resvg::usvg::fontdb::Database>
{
	static DB: OnceLock<Arc<resvg::usvg::fontdb::Database>> = OnceLock::new();
	DB.get_or_init( || {
		let mut db = resvg::usvg::fontdb::Database::new();
		db.load_system_fonts();
		Arc::new( db )
	} ).clone()
}