telar_theme_core/
context.rs1use std::mem::ManuallyDrop;
2use std::rc::Rc;
3
4use geometry_core::Color;
5use reactive_core::{RwSignal, signal};
6
7pub trait Theme: 'static {
9 fn as_any(&self) -> &dyn std::any::Any;
10}
11
12pub trait ThemeTokens: 'static {
14 fn primary(&self) -> Color;
15 fn on_primary(&self) -> Color;
16
17 fn muted(&self) -> Color {
18 Color::rgba(0.5, 0.5, 0.6, 0.6)
19 }
20 fn scrollbar(&self) -> Color {
21 Color::rgba(0.5, 0.5, 0.6, 0.6)
22 }
23
24 fn ink(&self) -> Color {
27 Color::rgba(0.15, 0.15, 0.2, 1.0)
28 }
29 fn surface_alt(&self) -> Color {
31 Color::rgba(0.5, 0.5, 0.55, 0.1)
32 }
33 fn border(&self) -> Color {
35 Color::rgba(0.5, 0.5, 0.55, 0.35)
36 }
37
38 fn success(&self) -> Color {
40 Color::rgba(0.4, 0.7, 0.4, 1.0)
41 }
42 fn warning(&self) -> Color {
43 Color::rgba(0.9, 0.75, 0.4, 1.0)
44 }
45 fn error(&self) -> Color {
46 Color::rgba(0.8, 0.35, 0.4, 1.0)
47 }
48 fn info(&self) -> Color {
49 Color::rgba(0.4, 0.6, 0.8, 1.0)
50 }
51
52 fn highlight_low(&self) -> Color {
55 Color::rgba(0.5, 0.5, 0.55, 0.06)
56 }
57 fn highlight_med(&self) -> Color {
58 Color::rgba(0.5, 0.5, 0.55, 0.12)
59 }
60 fn highlight_high(&self) -> Color {
61 Color::rgba(0.5, 0.5, 0.55, 0.20)
62 }
63}
64
65thread_local! {
66 static THEME: ManuallyDrop<RwSignal<Option<Rc<dyn Theme>>>> =
68 ManuallyDrop::new(signal(None));
69 static THEME_TOKENS: ManuallyDrop<RwSignal<Option<Rc<dyn ThemeTokens>>>> =
70 ManuallyDrop::new(signal(None));
71}
72
73pub fn set_theme<T: Theme + ThemeTokens + Clone + 'static>(theme: T) {
74 let theme = Rc::new(theme);
75 let as_theme: Rc<dyn Theme> = theme.clone();
76 let as_tokens: Rc<dyn ThemeTokens> = theme;
77 THEME.with(|s| s.set(Some(as_theme)));
78 THEME_TOKENS.with(|s| s.set(Some(as_tokens)));
79}
80
81pub fn use_theme<T: Theme + Clone + 'static>() -> T {
82 THEME.with(|s| {
83 let theme = s.get().unwrap_or_else(|| {
84 panic!(
85 "use_theme::<{}> called but no theme has been set; call set_theme first",
86 std::any::type_name::<T>()
87 )
88 });
89 theme
90 .as_any()
91 .downcast_ref::<T>()
92 .unwrap_or_else(|| {
93 panic!(
94 "use_theme::<{}> called but a theme of a different type is set",
95 std::any::type_name::<T>()
96 )
97 })
98 .clone()
99 })
100}
101
102pub fn use_theme_tokens() -> Option<Rc<dyn ThemeTokens>> {
103 THEME_TOKENS.with(|s| s.get())
104}