Wygląd jako dane
Motywy i tokeny
W libLunie wygląd nie jest rozsiany po widgetach. Jest w Theme: palecie, odstępach, promieniach, typografii i metrykach kontrolek. Zmiana motywu to podmiana danych — nie przepisywanie komponentów.
Trzy warstwy wyglądu
Wariant
ThemePreference: System, Light lub Dark. Wybiera bazową paletę.
Akcent
Accent: 9 kolorów. Podmienia colors.accent i accent_fg.
Metryki
Metrics: wysokości, paddingi i rozmiary per widget. Geometria też jest tokenem.
Theme w kodzie
use libluna::prelude::*;
// Dwa gotowe warianty
let light = Theme::light();
let dark = Theme::dark();
// Ten sam motyw z innym akcentem
let purple = Theme::light().accented(
Accent::Purple.color(),
Accent::Purple.foreground(),
);
// Preferencja użytkownika + stan systemu → konkretny motyw
let theme = ThemePreference::System.resolve(true, Accent::Teal);
Pole Theme | Typ | Co zawiera |
|---|---|---|
colors | ColorScheme | Paleta jasna lub ciemna. |
spacing | Spacing | Skala odstępów 3/6/12/18/24. |
radius | Radius | Promienie 6/9/12/pill. |
typography | Typography | Style: title, heading, body, caption. |
metrics | Metrics | Geometria 32 grup widgetów. |
appearance | Appearance | Light / Dark. |
Paleta
Nazwy tokenów są wspólne dla obu wariantów; różnią się wartości. Poniżej paleta jasna:
W ciemnym motywie window_bg to #242424, card_bg #303030, a text #ffffff. Wartości pochodzą z ColorScheme::dark().
Kolory akcentu — spróbuj tutaj
Kliknięcie zmienia akcent tej strony (i zapisuje wybór w localStorage). W aplikacji robi to Frame::set_accent(...). Ten sam akcent trafia do Theme::accented.
for accent in Accent::ALL {
println!("{} → {:?}", accent.name(), accent.color());
}
frame.set_accent(Accent::Purple);
frame.set_preference(ThemePreference::Dark);
Odstępy, promienie, typografia
Spacing
xs— 3 pxsm— 6 pxmd— 12 pxlg— 18 pxxl— 24 px
Radius
small— 6 pxmedium— 9 pxlarge— 12 pxpill— 999 px
Typography
title— 20 px / 700 / 26heading— 15 px / 700 / 20body— 14 px / 400 / 20caption— 12 px / 400 / 16
FontStyle
size— rozmiar w px logicznychweight— 400 / 500 / 700line_height— wysokość wierszamonospace— czcionka stałej szerokości
let pad = theme.spacing.md;
let radius = theme.radius.large;
let title = theme.typography.title;
scene.fill_rounded_rect(rect, radius, theme.colors.card_bg);
scene.text(
TextRun::new(Point::new(rect.x() + pad, rect.y() + pad + title.size),
"Tytuł", title.size)
.with_weight(title.weight)
.with_color(theme.colors.text),
);
Metryki kontrolek
Widgety nie mają zaszytych wysokości. Czytają je z theme.metrics, więc styl może zmienić np. wysokość paska tytułu albo rozmiar przełącznika. Przykładowe wartości standardowe:
| Grupa | Klucz | Standard |
|---|---|---|
headerbar | height / control | 38 / 30 px |
button | height / pad_x | 34 / 14 px |
switch | width / height | 44 / 24 px |
slider | track_height / knob_r | 6 / 9 px |
entry | height / pad_x | 34 / 12 px |
tabs | height / min_width | 34 / 120 px |
itemview | tile_w / tile_h | 100 / 76 px |
toast | height / margin_bottom | 40 / 40 px |
stack (metryki kontenerów) | container.gap | 8 px |
Pełna lista 32 grup jest w indeksie API i w rustdoc.
Style z plików .theme
Wygląd można też zainstalować jak w GNOME/KDE. Format jest liniowy, z kropkowymi kluczami; nieznane klucze są ignorowane (zgodność w przód).
__name__ = Nord
appearance = dark
accent = teal
colors.card_bg = #3b4252
colors.text = #eceff4
colors.window_bg = #2e3440
colors.sidebar_bg = #2e3440
radius.small = 5
radius.large = 10
metrics.button.height = 36
metrics.headerbar.height = 48
metrics.switch.width = 48
| Katalog | Priorytet |
|---|---|
~/.config/luna/themes | najwyższy |
~/.local/share/luna/themes | średni |
/usr/share/luna/themes | systemowy |
LUNA_THEMES_DIR | dodatkowe katalogi (dev/testy) |
use libluna::core::{available_themes, ThemePatch};
for style in available_themes() {
println!("{} ({})", style.name, style.id);
}
let patch = ThemePatch::from_file(path)?;
let theme = patch.build(Theme::light());
// albo na istniejącym motywie
let mut theme = Theme::dark();
patch.apply(&mut theme);
Styl steruje nie tylko kolorami, ale i geometrią: metrics.* zmienia rozmiary kontrolek, a spacing.* i radius.* — odstępy i zaokrąglenia.
Zmiana motywu z aplikacji
Frame trzyma bieżące ThemeSettings i zapisuje zmiany od razu. Po zmianie następna klatka dostaje już nowy theme.
if clicked_light {
frame.set_preference(ThemePreference::Light);
}
if clicked_dark {
frame.set_preference(ThemePreference::Dark);
}
if clicked_auto {
frame.set_preference(ThemePreference::System);
}
if clicked_purple {
frame.set_accent(Accent::Purple);
}
if clicked_custom_style {
frame.set_style(Some("nord".into()));
}
Ustawienia trafiają do ~/.config/luna/theme.conf. W trybie System ciemność pochodzi z LUNA_SYSTEM_DARK (docelowo portal D-Bus).