use super::theme::{Theme, ThemeManager};
use crate::reactive::{signal, Signal};
use crate::utils::lock::lock_or_recover;
use std::cell::RefCell;
use std::sync::{Arc, Mutex, OnceLock};
thread_local! {
static THEME_SIGNAL: RefCell<Option<Signal<Theme>>> = const { RefCell::new(None) };
}
static THEME_MANAGER: OnceLock<Arc<Mutex<ThemeManager>>> = OnceLock::new();
fn get_theme_signal() -> Signal<Theme> {
THEME_SIGNAL.with(|cell| {
let mut opt = cell.borrow_mut();
if opt.is_none() {
*opt = Some(signal(Theme::dark()));
}
opt.as_ref().unwrap().clone()
})
}
fn get_theme_manager() -> Arc<Mutex<ThemeManager>> {
THEME_MANAGER
.get_or_init(|| Arc::new(Mutex::new(ThemeManager::new())))
.clone()
}
pub fn use_theme() -> Signal<Theme> {
get_theme_signal()
}
pub fn set_theme(theme: Theme) {
get_theme_signal().set(theme);
}
pub fn set_theme_by_id(id: &str) -> bool {
let manager = get_theme_manager();
let guard = lock_or_recover(&manager);
if let Some(theme) = guard.get(id) {
let theme = theme.clone();
drop(guard); get_theme_signal().set(theme);
true
} else {
false
}
}
pub fn toggle_theme() {
let signal = get_theme_signal();
let current = signal.get();
let new_theme = if current.is_dark() {
Theme::light()
} else {
Theme::dark()
};
signal.set(new_theme);
}
pub fn cycle_theme() {
let manager = get_theme_manager();
let mut guard = lock_or_recover(&manager);
guard.cycle();
let theme = guard.current().clone();
drop(guard);
get_theme_signal().set(theme);
}
pub fn theme_ids() -> Vec<String> {
let manager = get_theme_manager();
let guard = lock_or_recover(&manager);
guard
.theme_ids()
.into_iter()
.map(|s| s.to_string())
.collect()
}
pub fn register_theme(id: impl Into<String>, theme: Theme) {
let manager = get_theme_manager();
let mut guard = lock_or_recover(&manager);
guard.register(id, theme);
}
pub fn get_theme(id: &str) -> Option<Theme> {
let manager = get_theme_manager();
let guard = lock_or_recover(&manager);
guard.get(id).cloned()
}
pub fn theme_to_css_variables(theme: &Theme) -> String {
use crate::style::Color;
fn color_to_css(color: &Color) -> String {
format!("#{:02x}{:02x}{:02x}", color.r, color.g, color.b)
}
format!(
r#":root {{
--theme-name: "{}";
--theme-bg: {};
--theme-surface: {};
--theme-text: {};
--theme-text-muted: {};
--theme-border: {};
--theme-divider: {};
--theme-selection: {};
--theme-selection-text: {};
--theme-focus: {};
--theme-primary: {};
--theme-secondary: {};
--theme-success: {};
--theme-warning: {};
--theme-error: {};
--theme-info: {};
}}"#,
theme.name,
color_to_css(&theme.colors.background),
color_to_css(&theme.colors.surface),
color_to_css(&theme.colors.text),
color_to_css(&theme.colors.text_muted),
color_to_css(&theme.colors.border),
color_to_css(&theme.colors.divider),
color_to_css(&theme.colors.selection),
color_to_css(&theme.colors.selection_text),
color_to_css(&theme.colors.focus),
color_to_css(&theme.palette.primary),
color_to_css(&theme.palette.secondary),
color_to_css(&theme.palette.success),
color_to_css(&theme.palette.warning),
color_to_css(&theme.palette.error),
color_to_css(&theme.palette.info),
)
}