feat(theme): themes as files (theme crate: toml subset + ints, contrast check, wire); whimsy dark + light; display reads themes/, serves THEME/THEME_NAME, init SET_THEME -> THEME_CHANGED to all windows, bar themed; k-ui follows; settings appearance; init keeps choice in sealed users/<id>/settings/, reapplies at sign-in + display restart

This commit is contained in:
2026-10-28 13:36:57 +01:00
parent 29becf297c
commit 6827e1455d
43 changed files with 1140 additions and 179 deletions

View File

@@ -1,5 +1,6 @@
//! Settings: what each app may use (allow or revoke, through the
//! permission manager), who is signed in, and signing out. A system app:
//! permission manager), the theme (the display's list, kept per user by
//! init), who is signed in, and signing out. A system app:
//! `permissions` and `session` are only ever granted to apps on the system
//! image.
@@ -29,6 +30,7 @@ enum Screen {
Apps,
/// One app, by its place in the list.
App(usize),
Appearance,
About,
}
@@ -37,6 +39,8 @@ enum Event {
Show(Screen),
/// Allow or revoke this capability of the app on screen.
Flip(Capability),
/// Use the display's `n`th theme.
Theme(usize),
SignOut,
}
@@ -54,6 +58,9 @@ struct Settings {
rows: Vec<AppRow>,
/// Who is signed in, and how.
who: Option<(String, Role)>,
/// The display's themes, and which is in use.
themes: Vec<String>,
theme: usize,
/// What went wrong last, shown until the next change.
trouble: Option<String>,
}
@@ -99,6 +106,23 @@ impl Settings {
self.reload();
}
fn appearance(&self) -> Element<Event> {
let rows = self.themes.iter().enumerate().map(|(i, name)| {
let item = list_item(name.clone(), Event::Theme(i));
if i == self.theme { item.subtitle("In use") } else { item }
});
let view = Self::hint(Self::heading("Appearance"), "Esc goes back").children(rows);
self.with_trouble(view).into()
}
fn choose_theme(&mut self, index: usize) {
let (Some(name), Some(session)) = (self.themes.get(index), libkos::start::session()) else { return };
match libkos::session::set_theme(session, name.as_bytes()) {
true => self.theme = index,
false => self.trouble = Some("That theme could not be kept.".to_string()),
}
}
fn heading(title: &str) -> k_ui::element::FlexBuilder<Event> {
column().padding(Spacing::Xl).gap(Spacing::Md).child(text(title).style(TextStyle::Heading))
}
@@ -110,6 +134,7 @@ impl Settings {
fn home(&self) -> Element<Event> {
Self::heading("Settings")
.child(list_item("Apps", Event::Show(Screen::Apps)).subtitle("What each app may use"))
.child(list_item("Appearance", Event::Show(Screen::Appearance)).subtitle("The theme everything is drawn in"))
.child(list_item("About", Event::Show(Screen::About)).subtitle("Who is signed in, and this device"))
.child(button("Sign out", Event::SignOut).variant(ButtonVariant::Destructive))
.into()
@@ -176,7 +201,15 @@ impl App for Settings {
let who = libkos::start::session()
.and_then(|s| libkos::session::who(s, &mut name))
.map(|(len, role)| (String::from_utf8_lossy(&name[..len]).into_owned(), role));
let mut s = Settings { screen: Screen::Home, apps, rows: Vec::new(), who, trouble: None };
let window = libkos::start::display();
let themes = window.map_or(Vec::new(), |w| {
(0..)
.map_while(|i| libkos::display::theme_name(w, i))
.map(|(n, len)| String::from_utf8_lossy(&n[..len]).into_owned())
.collect()
});
let theme = window.and_then(libkos::display::theme).map_or(0, |(i, _)| i);
let mut s = Settings { screen: Screen::Home, apps, rows: Vec::new(), who, themes, theme, trouble: None };
s.reload();
s
}
@@ -195,6 +228,7 @@ impl App for Settings {
self.flip(i, cap);
}
}
Event::Theme(i) => self.choose_theme(i),
Event::SignOut => {
if let Some(s) = libkos::start::session() {
libkos::session::sign_out(s);
@@ -208,6 +242,7 @@ impl App for Settings {
Screen::Home => self.home(),
Screen::Apps => self.app_list(),
Screen::App(i) => self.app(i),
Screen::Appearance => self.appearance(),
Screen::About => self.about(),
}
}
@@ -217,7 +252,7 @@ impl App for Settings {
self.screen = match self.screen {
Screen::Home => return false,
Screen::App(_) => Screen::Apps,
Screen::Apps | Screen::About => Screen::Home,
Screen::Apps | Screen::Appearance | Screen::About => Screen::Home,
};
true
}
@@ -227,7 +262,8 @@ impl App for Settings {
Screen::Home => 0,
Screen::Apps => 1,
Screen::About => 2,
Screen::App(i) => 3 + i,
Screen::Appearance => 3,
Screen::App(i) => 4 + i,
}
}
}