1use std::any::{Any, TypeId};
34use std::cell::RefCell;
35use std::collections::HashMap;
36
37use crate::Color;
38
39#[derive(Clone, Copy, Debug, PartialEq, Eq)]
40pub enum TextDirection {
41 Ltr,
42 Rtl,
43}
44impl Default for TextDirection {
45 fn default() -> Self {
46 TextDirection::Ltr
47 }
48}
49
50thread_local! {
51 static LOCALS_STACK: RefCell<Vec<HashMap<TypeId, Box<dyn Any>>>> = RefCell::new(Vec::new());
52}
53
54#[derive(Clone, Copy, Debug, PartialEq)]
56pub struct Dp(pub f32);
57
58impl Dp {
59 pub fn to_px(self) -> f32 {
61 self.0 * density().scale
62 }
63}
64
65pub fn dp_to_px(dp: f32) -> f32 {
67 Dp(dp).to_px()
68}
69
70pub fn with_text_direction<R>(dir: TextDirection, f: impl FnOnce() -> R) -> R {
71 with_locals_frame(|| {
72 set_local_boxed(std::any::TypeId::of::<TextDirection>(), Box::new(dir));
73 f()
74 })
75}
76
77pub fn text_direction() -> TextDirection {
78 LOCALS_STACK.with(|st| {
79 for frame in st.borrow().iter().rev() {
80 if let Some(v) = frame.get(&std::any::TypeId::of::<TextDirection>()) {
81 if let Some(d) = v.downcast_ref::<TextDirection>() {
82 return *d;
83 }
84 }
85 }
86 TextDirection::default()
87 })
88}
89
90fn with_locals_frame<R>(f: impl FnOnce() -> R) -> R {
91 struct Guard;
93 impl Drop for Guard {
94 fn drop(&mut self) {
95 LOCALS_STACK.with(|st| {
96 st.borrow_mut().pop();
97 });
98 }
99 }
100 LOCALS_STACK.with(|st| st.borrow_mut().push(HashMap::new()));
101 let _guard = Guard;
102 f()
103}
104
105fn set_local_boxed(t: TypeId, v: Box<dyn Any>) {
106 LOCALS_STACK.with(|st| {
107 if let Some(top) = st.borrow_mut().last_mut() {
108 top.insert(t, v);
109 } else {
110 let mut m = HashMap::new();
112 m.insert(t, v);
113 st.borrow_mut().push(m);
114 }
115 });
116}
117
118#[derive(Clone, Copy, Debug)]
126pub struct Theme {
127 pub background: Color,
129 pub surface: Color,
131 pub on_surface: Color,
133
134 pub primary: Color,
136 pub on_primary: Color,
138
139 pub outline: Color,
141 pub focus: Color,
143
144 pub button_bg: Color,
146 pub button_bg_hover: Color,
148 pub button_bg_pressed: Color,
150
151 pub scrollbar_track: Color,
153 pub scrollbar_thumb: Color,
155
156 pub error: Color,
158}
159
160impl Default for Theme {
161 fn default() -> Self {
162 Self {
163 background: Color::from_hex("#121212"),
164 surface: Color::from_hex("#1E1E1E"),
165 on_surface: Color::from_hex("#DDDDDD"),
166 primary: Color::from_hex("#34AF82"),
167 on_primary: Color::WHITE,
168 outline: Color::from_hex("#555555"),
169 focus: Color::from_hex("#88CCFF"),
170 button_bg: Color::from_hex("#34AF82"),
171 button_bg_hover: Color::from_hex("#2A8F6A"),
172 button_bg_pressed: Color::from_hex("#1F7556"),
173 scrollbar_track: Color(0xDD, 0xDD, 0xDD, 32),
174 scrollbar_thumb: Color(0xDD, 0xDD, 0xDD, 140),
175
176 error: Color::from_hex("#ae3636"),
177 }
178 }
179}
180
181#[derive(Clone, Copy, Debug)]
182pub struct Density {
183 pub scale: f32, }
185impl Default for Density {
186 fn default() -> Self {
187 Self { scale: 1.0 }
188 }
189}
190
191#[derive(Clone, Copy, Debug)]
192pub struct TextScale(pub f32);
193impl Default for TextScale {
194 fn default() -> Self {
195 Self(1.0)
196 }
197}
198
199pub fn with_theme<R>(theme: Theme, f: impl FnOnce() -> R) -> R {
202 with_locals_frame(|| {
203 set_local_boxed(TypeId::of::<Theme>(), Box::new(theme));
204 f()
205 })
206}
207
208pub fn with_density<R>(density: Density, f: impl FnOnce() -> R) -> R {
209 with_locals_frame(|| {
210 set_local_boxed(TypeId::of::<Density>(), Box::new(density));
211 f()
212 })
213}
214
215pub fn with_text_scale<R>(ts: TextScale, f: impl FnOnce() -> R) -> R {
216 with_locals_frame(|| {
217 set_local_boxed(TypeId::of::<TextScale>(), Box::new(ts));
218 f()
219 })
220}
221
222pub fn theme() -> Theme {
225 LOCALS_STACK.with(|st| {
226 for frame in st.borrow().iter().rev() {
227 if let Some(v) = frame.get(&TypeId::of::<Theme>()) {
228 if let Some(t) = v.downcast_ref::<Theme>() {
229 return *t;
230 }
231 }
232 }
233 Theme::default()
234 })
235}
236
237pub fn density() -> Density {
238 LOCALS_STACK.with(|st| {
239 for frame in st.borrow().iter().rev() {
240 if let Some(v) = frame.get(&TypeId::of::<Density>()) {
241 if let Some(d) = v.downcast_ref::<Density>() {
242 return *d;
243 }
244 }
245 }
246 Density::default()
247 })
248}
249
250pub fn text_scale() -> TextScale {
251 LOCALS_STACK.with(|st| {
252 for frame in st.borrow().iter().rev() {
253 if let Some(v) = frame.get(&TypeId::of::<TextScale>()) {
254 if let Some(ts) = v.downcast_ref::<TextScale>() {
255 return *ts;
256 }
257 }
258 }
259 TextScale::default()
260 })
261}