use std::cell::Cell;
use crate::reactive::{create_root, Scope, Signal};
use crate::theme::{default_theme, Theme};
thread_local! {
static THEME_SIGNAL: Cell<Option<Signal<&'static Theme>>> = const { Cell::new(None) };
}
fn theme_signal() -> Signal<&'static Theme> {
THEME_SIGNAL.with(|slot| {
if let Some(sig) = slot.get() {
return sig;
}
let (root, sig) = create_root(|cx| cx.signal(default_theme()));
std::mem::forget(root);
slot.set(Some(sig));
sig
})
}
pub fn use_theme(_cx: Scope) -> Signal<&'static Theme> {
theme_signal()
}
pub fn current_theme() -> &'static Theme {
theme_signal().get_untracked()
}
pub fn set_theme(theme: &'static Theme) -> &'static Theme {
let sig = theme_signal();
let prev = sig.get_untracked();
sig.set(theme);
prev
}
pub fn set_theme_by_id(id: &str) -> bool {
match crate::theme::get(id) {
Some(t) => {
set_theme(t);
true
}
None => false,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::reactive::create_root;
#[test]
fn theme_defaults_and_switches_reactively() {
assert_eq!(current_theme().id, crate::theme::DEFAULT_THEME_ID);
let runs = std::rc::Rc::new(std::cell::RefCell::new(Vec::new()));
let r2 = runs.clone();
let (_root, ()) = create_root(|cx| {
let theme = use_theme(cx);
cx.effect(move || r2.borrow_mut().push(theme.get().id));
});
let target = crate::theme::get("nord").expect("nord registered");
let prev = set_theme(target);
assert_eq!(runs.borrow().last().copied(), Some("nord"));
set_theme(prev);
assert_eq!(runs.borrow().len(), 3, "initial + switch + restore");
}
#[test]
fn set_theme_by_id_rejects_unknown() {
assert!(!set_theme_by_id("no-such-theme"));
assert_eq!(current_theme().id, crate::theme::DEFAULT_THEME_ID);
}
}