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,
}
}