1use std::any::{Any, TypeId};
2use std::cell::RefCell;
3use std::collections::HashMap;
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq)]
6pub enum TextDirection {
7 Ltr,
8 Rtl,
9}
10impl Default for TextDirection {
11 fn default() -> Self {
12 TextDirection::Ltr
13 }
14}
15
16thread_local! {
17 static LOCALS_STACK: RefCell<Vec<HashMap<TypeId, Box<dyn Any>>>> = RefCell::new(Vec::new());
18}
19
20pub fn with_text_direction<R>(dir: TextDirection, f: impl FnOnce() -> R) -> R {
21 with_locals_frame(|| {
22 set_local_boxed(std::any::TypeId::of::<TextDirection>(), Box::new(dir));
23 f()
24 })
25}
26
27pub fn text_direction() -> TextDirection {
28 LOCALS_STACK.with(|st| {
29 for frame in st.borrow().iter().rev() {
30 if let Some(v) = frame.get(&std::any::TypeId::of::<TextDirection>()) {
31 if let Some(d) = v.downcast_ref::<TextDirection>() {
32 return *d;
33 }
34 }
35 }
36 TextDirection::default()
37 })
38}
39
40fn with_locals_frame<R>(f: impl FnOnce() -> R) -> R {
41 LOCALS_STACK.with(|st| st.borrow_mut().push(HashMap::new()));
42 let out = f();
43 LOCALS_STACK.with(|st| st.borrow_mut().pop());
44 out
45}
46
47fn set_local_boxed(t: TypeId, v: Box<dyn Any>) {
48 LOCALS_STACK.with(|st| {
49 if let Some(top) = st.borrow_mut().last_mut() {
50 top.insert(t, v);
51 } else {
52 let mut m = HashMap::new();
54 m.insert(t, v);
55 st.borrow_mut().push(m);
56 }
57 });
58}
59
60#[derive(Clone, Copy, Debug)]
63pub struct Theme {
64 pub background: crate::Color,
65 pub surface: crate::Color,
66 pub on_surface: crate::Color,
67 pub primary: crate::Color,
68 pub on_primary: crate::Color,
69}
70impl Default for Theme {
71 fn default() -> Self {
72 Self {
73 background: crate::Color::from_hex("#121212"),
74 surface: crate::Color::from_hex("#1E1E1E"),
75 on_surface: crate::Color::from_hex("#DDDDDD"),
76 primary: crate::Color::from_hex("#34AF82"),
77 on_primary: crate::Color::WHITE,
78 }
79 }
80}
81
82#[derive(Clone, Copy, Debug)]
83pub struct Density {
84 pub scale: f32, }
86impl Default for Density {
87 fn default() -> Self {
88 Self { scale: 1.0 }
89 }
90}
91
92#[derive(Clone, Copy, Debug)]
93pub struct TextScale(pub f32);
94impl Default for TextScale {
95 fn default() -> Self {
96 Self(1.0)
97 }
98}
99
100pub fn with_theme<R>(theme: Theme, f: impl FnOnce() -> R) -> R {
103 with_locals_frame(|| {
104 set_local_boxed(TypeId::of::<Theme>(), Box::new(theme));
105 f()
106 })
107}
108
109pub fn with_density<R>(density: Density, f: impl FnOnce() -> R) -> R {
110 with_locals_frame(|| {
111 set_local_boxed(TypeId::of::<Density>(), Box::new(density));
112 f()
113 })
114}
115
116pub fn with_text_scale<R>(ts: TextScale, f: impl FnOnce() -> R) -> R {
117 with_locals_frame(|| {
118 set_local_boxed(TypeId::of::<TextScale>(), Box::new(ts));
119 f()
120 })
121}
122
123pub fn theme() -> Theme {
126 LOCALS_STACK.with(|st| {
127 for frame in st.borrow().iter().rev() {
128 if let Some(v) = frame.get(&TypeId::of::<Theme>()) {
129 if let Some(t) = v.downcast_ref::<Theme>() {
130 return *t;
131 }
132 }
133 }
134 Theme::default()
135 })
136}
137
138pub fn density() -> Density {
139 LOCALS_STACK.with(|st| {
140 for frame in st.borrow().iter().rev() {
141 if let Some(v) = frame.get(&TypeId::of::<Density>()) {
142 if let Some(d) = v.downcast_ref::<Density>() {
143 return *d;
144 }
145 }
146 }
147 Density::default()
148 })
149}
150
151pub fn text_scale() -> TextScale {
152 LOCALS_STACK.with(|st| {
153 for frame in st.borrow().iter().rev() {
154 if let Some(v) = frame.get(&TypeId::of::<TextScale>()) {
155 if let Some(ts) = v.downcast_ref::<TextScale>() {
156 return *ts;
157 }
158 }
159 }
160 TextScale::default()
161 })
162}
163
164pub fn dp(px: f32) -> f32 {
167 px * density().scale
168}