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
20#[derive(Clone, Copy, Debug, PartialEq)]
22pub struct Dp(pub f32);
23
24impl Dp {
25 pub fn to_px(self) -> f32 {
27 self.0 * density().scale
28 }
29}
30
31pub fn dp_to_px(dp: f32) -> f32 {
33 Dp(dp).to_px()
34}
35
36pub fn with_text_direction<R>(dir: TextDirection, f: impl FnOnce() -> R) -> R {
37 with_locals_frame(|| {
38 set_local_boxed(std::any::TypeId::of::<TextDirection>(), Box::new(dir));
39 f()
40 })
41}
42
43pub fn text_direction() -> TextDirection {
44 LOCALS_STACK.with(|st| {
45 for frame in st.borrow().iter().rev() {
46 if let Some(v) = frame.get(&std::any::TypeId::of::<TextDirection>()) {
47 if let Some(d) = v.downcast_ref::<TextDirection>() {
48 return *d;
49 }
50 }
51 }
52 TextDirection::default()
53 })
54}
55
56fn with_locals_frame<R>(f: impl FnOnce() -> R) -> R {
57 struct Guard;
59 impl Drop for Guard {
60 fn drop(&mut self) {
61 LOCALS_STACK.with(|st| {
62 st.borrow_mut().pop();
63 });
64 }
65 }
66 LOCALS_STACK.with(|st| st.borrow_mut().push(HashMap::new()));
67 let _guard = Guard;
68 f()
69}
70
71fn set_local_boxed(t: TypeId, v: Box<dyn Any>) {
72 LOCALS_STACK.with(|st| {
73 if let Some(top) = st.borrow_mut().last_mut() {
74 top.insert(t, v);
75 } else {
76 let mut m = HashMap::new();
78 m.insert(t, v);
79 st.borrow_mut().push(m);
80 }
81 });
82}
83
84#[derive(Clone, Copy, Debug)]
87pub struct Theme {
88 pub background: crate::Color,
89 pub surface: crate::Color,
90 pub on_surface: crate::Color,
91 pub primary: crate::Color,
92 pub on_primary: crate::Color,
93 pub outline: crate::Color, pub focus: crate::Color, pub button_bg: crate::Color, pub button_bg_hover: crate::Color, pub button_bg_pressed: crate::Color, pub scrollbar_track: crate::Color, pub scrollbar_thumb: crate::Color, }
101impl Default for Theme {
102 fn default() -> Self {
103 Self {
104 background: crate::Color::from_hex("#121212"),
105 surface: crate::Color::from_hex("#1E1E1E"),
106 on_surface: crate::Color::from_hex("#DDDDDD"),
107 primary: crate::Color::from_hex("#34AF82"),
108 on_primary: crate::Color::WHITE,
109 outline: crate::Color::from_hex("#555555"),
110 focus: crate::Color::from_hex("#88CCFF"),
111 button_bg: crate::Color::from_hex("#34AF82"),
112 button_bg_hover: crate::Color::from_hex("#2A8F6A"),
113 button_bg_pressed: crate::Color::from_hex("#1F7556"),
114 scrollbar_track: crate::Color(0xDD, 0xDD, 0xDD, 32),
115 scrollbar_thumb: crate::Color(0xDD, 0xDD, 0xDD, 140),
116 }
117 }
118}
119
120#[derive(Clone, Copy, Debug)]
121pub struct Density {
122 pub scale: f32, }
124impl Default for Density {
125 fn default() -> Self {
126 Self { scale: 1.0 }
127 }
128}
129
130#[derive(Clone, Copy, Debug)]
131pub struct TextScale(pub f32);
132impl Default for TextScale {
133 fn default() -> Self {
134 Self(1.0)
135 }
136}
137
138pub fn with_theme<R>(theme: Theme, f: impl FnOnce() -> R) -> R {
141 with_locals_frame(|| {
142 set_local_boxed(TypeId::of::<Theme>(), Box::new(theme));
143 f()
144 })
145}
146
147pub fn with_density<R>(density: Density, f: impl FnOnce() -> R) -> R {
148 with_locals_frame(|| {
149 set_local_boxed(TypeId::of::<Density>(), Box::new(density));
150 f()
151 })
152}
153
154pub fn with_text_scale<R>(ts: TextScale, f: impl FnOnce() -> R) -> R {
155 with_locals_frame(|| {
156 set_local_boxed(TypeId::of::<TextScale>(), Box::new(ts));
157 f()
158 })
159}
160
161pub fn theme() -> Theme {
164 LOCALS_STACK.with(|st| {
165 for frame in st.borrow().iter().rev() {
166 if let Some(v) = frame.get(&TypeId::of::<Theme>()) {
167 if let Some(t) = v.downcast_ref::<Theme>() {
168 return *t;
169 }
170 }
171 }
172 Theme::default()
173 })
174}
175
176pub fn density() -> Density {
177 LOCALS_STACK.with(|st| {
178 for frame in st.borrow().iter().rev() {
179 if let Some(v) = frame.get(&TypeId::of::<Density>()) {
180 if let Some(d) = v.downcast_ref::<Density>() {
181 return *d;
182 }
183 }
184 }
185 Density::default()
186 })
187}
188
189pub fn text_scale() -> TextScale {
190 LOCALS_STACK.with(|st| {
191 for frame in st.borrow().iter().rev() {
192 if let Some(v) = frame.get(&TypeId::of::<TextScale>()) {
193 if let Some(ts) = v.downcast_ref::<TextScale>() {
194 return *ts;
195 }
196 }
197 }
198 TextScale::default()
199 })
200}