ltk/theme/fallback/
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
108
109
110
111
112
// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>

//! "Last resort" assets used when the canonical `default` theme or the
//! system font search chain come up empty. Two pieces:
//!
//! * [`document`] builds a black/white [`ThemeDocument`] with the
//!   eight canonical palette slots populated — no wallpaper, no
//!   launcher spec, no decorated surface slots. Widgets that look up
//!   surface or shadow slots fall through to their flat-colour branch;
//!   the UI is usable but not decorated.
//! * [`FALLBACK_FONT`] bundles Sora Regular (~50 KB, SIL OFL 1.1) so
//!   `Canvas::new` / `Canvas::new_gles` can always load a font even
//!   on systems without `fonts-sora`, `fonts-liberation` or
//!   `fonts-dejavu`.
//!
//! Both are activated by [`super::ensure_active`] when
//! `ThemeDocument::find("default")` fails. The crate stamps every
//! frame with a red banner so the user can't miss the
//! "install `ltk-theme-default`" signal.
//!
//! # Font licence
//!
//! `Sora-Regular.otf` is distributed under the SIL Open Font Licence
//! 1.1. The full licence text is in
//! [`Sora-LICENSE.txt`](./Sora-LICENSE.txt) next to this source
//! file. A binary-only redistribution must carry the OFL text
//! alongside — see the crate's `debian/copyright`.

use std::collections::HashMap;

use crate::types::Color;

use super::slots::{ Metadata, Slot, SlotStore };
use super::{ Mode, ThemeDocument };

/// Sora Regular, OFL 1.1. Loaded by `Canvas::new` / `Canvas::new_gles`
/// when no system font is found through the candidate chain. ~50 KB,
/// one weight, one style — enough to render the fallback banner and
/// any app that only asks for default typography.
pub( crate ) const FALLBACK_FONT: &[u8] = include_bytes!( "Sora-Regular.otf" );

/// Build the B/W fallback document. Eight canonical palette slots per
/// mode (`bg-page`, `surface`, `surface-alt`, `text-primary`,
/// `text-secondary`, `accent`, `divider`, `icon`), no wallpaper, no
/// launcher, no window_controls, no fonts registered (the default
/// canvas font is used for every widget).
///
/// Light mode: pure white backgrounds, pure black text + accent.
/// Dark mode: pure black backgrounds, pure white text + accent.
/// `accent` doubles as brand colour and focus-ring colour; setting
/// it to black/white keeps the fallback visually consistent with the
/// rest of the palette and gives the focus ring maximum contrast.
pub( super ) fn document() -> ThemeDocument
{
	ThemeDocument
	{
		id:    "fallback".to_string(),
		name:  "Fallback (B/W)".to_string(),
		root:  None,
		fonts: HashMap::new(),
		light: light_mode(),
		dark:  dark_mode(),
	}
}

fn mk_color_slot( c: Color ) -> Slot
{
	Slot::Color { value: c, meta: Metadata::default() }
}

fn light_mode() -> Mode
{
	let mut slots = SlotStore::new();
	slots.insert( "bg-page",        mk_color_slot( Color::WHITE ) );
	slots.insert( "surface",        mk_color_slot( Color::WHITE ) );
	slots.insert( "surface-alt",    mk_color_slot( Color::rgb( 0.95, 0.95, 0.95 ) ) );
	slots.insert( "text-primary",   mk_color_slot( Color::BLACK ) );
	slots.insert( "text-secondary", mk_color_slot( Color::rgba( 0.0, 0.0, 0.0, 0.6 ) ) );
	slots.insert( "accent",         mk_color_slot( Color::BLACK ) );
	slots.insert( "divider",        mk_color_slot( Color::rgba( 0.0, 0.0, 0.0, 0.1 ) ) );
	slots.insert( "icon",           mk_color_slot( Color::BLACK ) );
	Mode
	{
		wallpaper:       None,
		lockscreen:      None,
		launcher:        None,
		window_controls: None,
		slots,
	}
}

fn dark_mode() -> Mode
{
	let mut slots = SlotStore::new();
	slots.insert( "bg-page",        mk_color_slot( Color::BLACK ) );
	slots.insert( "surface",        mk_color_slot( Color::BLACK ) );
	slots.insert( "surface-alt",    mk_color_slot( Color::rgb( 0.1, 0.1, 0.1 ) ) );
	slots.insert( "text-primary",   mk_color_slot( Color::WHITE ) );
	slots.insert( "text-secondary", mk_color_slot( Color::rgba( 1.0, 1.0, 1.0, 0.6 ) ) );
	slots.insert( "accent",         mk_color_slot( Color::WHITE ) );
	slots.insert( "divider",        mk_color_slot( Color::rgba( 1.0, 1.0, 1.0, 0.1 ) ) );
	slots.insert( "icon",           mk_color_slot( Color::WHITE ) );
	Mode
	{
		wallpaper:       None,
		lockscreen:      None,
		launcher:        None,
		window_controls: None,
		slots,
	}
}