1use std::{
4 fmt::{self, Write},
5 num::{NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128},
6 ops::Deref,
7 sync::Arc,
8};
9
10use parking_lot::MappedRwLockReadGuard;
11use smallstr::SmallString;
12
13use crate::{prelude::*, reactive::VarReadGuard};
14
15#[doc(hidden)]
16pub type StackString = SmallString<[u8; 64]>;
17
18#[macro_export]
26macro_rules! ui_format {
27 ($val:expr, $fmt:literal) => {{
28 let var = ($val).clone();
29 UIString::__deferred_stack(move || {
30 use std::fmt::Write;
31 use $crate::data::StackString;
32
33 let v = var.read()?;
34 let mut buf = StackString::new();
35 let _ = write!(&mut buf, $fmt, &*v);
36 Some(buf)
37 })
38 }};
39}
40
41pub enum UIStringRef<'a> {
45 Borrowed(&'a str),
47 Guard(MappedRwLockReadGuard<'a, str>),
49 VarString(VarReadGuard<'a, String>),
51 OwnedStackString(StackString),
53 OwnedString(String),
55}
56
57impl<'a> Deref for UIStringRef<'a> {
58 type Target = str;
59
60 fn deref(&self) -> &Self::Target {
61 match self {
62 UIStringRef::Borrowed(s) => s,
63 UIStringRef::Guard(g) => g,
64 UIStringRef::VarString(g) => g.as_str(),
65 UIStringRef::OwnedStackString(s) => s.as_str(),
66 UIStringRef::OwnedString(s) => s.as_str(),
67 }
68 }
69}
70
71impl<'a> From<UIStringRef<'a>> for Box<str> {
72 fn from(value: UIStringRef<'a>) -> Self {
73 match value {
74 UIStringRef::OwnedString(s) => s.into_boxed_str(),
75 UIStringRef::Borrowed(s) => s.to_owned().into_boxed_str(),
76 UIStringRef::Guard(g) => g.to_owned().into_boxed_str(),
77 UIStringRef::VarString(g) => g.to_owned().into_boxed_str(),
78 UIStringRef::OwnedStackString(s) => s.as_str().to_owned().into_boxed_str(),
79 }
80 }
81}
82
83#[derive(Clone, Debug)]
103pub struct UIString(UIStringInner);
104
105#[derive(Clone)]
106enum UIStringInner {
107 Static(&'static str),
108 Owned(String),
109 VarStaticStr(WeakVar<&'static str>),
110 VarString(WeakVar<String>),
111 Localized(LocalizedString),
112 DeferredStack(Arc<dyn Fn() -> Option<StackString> + Send + Sync + 'static>),
113 DeferredHeap(Arc<dyn for<'a> Fn(&'a TranslationMap) -> String + Send + Sync + 'static>),
114}
115
116impl fmt::Debug for UIStringInner {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 match self {
119 UIStringInner::Owned(s) => f.debug_tuple("Owned").field(s).finish(),
120 UIStringInner::Localized(_) => f.debug_tuple("Localized").field(&"<localized>").finish(),
121 UIStringInner::Static(s) => f.debug_tuple("Static").field(s).finish(),
122 UIStringInner::VarString(_) => f.debug_tuple("VarString").field(&"<var>").finish(),
123 UIStringInner::VarStaticStr(_) => f.debug_tuple("VarStaticStr").field(&"<var>").finish(),
124 UIStringInner::DeferredStack(_) => f.debug_tuple("DeferredStack").field(&"<deferred>").finish(),
125 UIStringInner::DeferredHeap(_) => f.debug_tuple("DeferredHeap").field(&"<deferred>").finish(),
126 }
127 }
128}
129
130impl UIString {
131 #[doc(hidden)]
133 pub fn __deferred_stack<F>(f: F) -> Self
134 where
135 F: Fn() -> Option<SmallString<[u8; 64]>> + Send + Sync + 'static,
136 {
137 UIString(UIStringInner::DeferredStack(Arc::new(f)))
138 }
139
140 pub fn deferred<F>(f: F) -> Self
142 where
143 F: for<'a> Fn(&'a TranslationMap) -> String + Send + Sync + 'static,
144 {
145 UIString(UIStringInner::DeferredHeap(Arc::new(f)))
146 }
147
148 pub fn resolve<'a>(&'a self, translation_map: &'a TranslationMap) -> Option<UIStringRef<'a>> {
152 match &self.0 {
153 UIStringInner::Owned(s) => Some(UIStringRef::Borrowed(s.as_str())),
154 UIStringInner::Localized(localized_string) => Some(UIStringRef::Guard(localized_string.resolve(translation_map))),
155 UIStringInner::Static(string) => Some(UIStringRef::Borrowed(string)),
156 UIStringInner::VarString(var) => {
157 let guard = var.read()?;
158 Some(UIStringRef::VarString(guard))
159 }
160 UIStringInner::VarStaticStr(var) => {
161 let guard = var.read()?;
162 Some(UIStringRef::Borrowed(*guard))
164 }
165 UIStringInner::DeferredHeap(f) => Some(UIStringRef::OwnedString((f)(translation_map))),
166 UIStringInner::DeferredStack(f) => Some(UIStringRef::OwnedStackString((f)()?)),
167 }
168 }
169}
170
171impl From<&'static str> for UIString {
172 fn from(value: &'static str) -> Self {
173 UIString(UIStringInner::Static(value))
174 }
175}
176
177impl From<String> for UIString {
178 fn from(value: String) -> Self {
179 UIString(UIStringInner::Owned(value))
180 }
181}
182
183impl From<WeakVar<&'static str>> for UIString {
184 fn from(value: WeakVar<&'static str>) -> Self {
185 UIString(UIStringInner::VarStaticStr(value))
186 }
187}
188
189impl From<WeakVar<String>> for UIString {
190 fn from(value: WeakVar<String>) -> Self {
191 UIString(UIStringInner::VarString(value))
192 }
193}
194
195impl From<LocalizedString> for UIString {
196 fn from(value: LocalizedString) -> Self {
197 UIString(UIStringInner::Localized(value))
198 }
199}
200
201mod sealed {
203 pub trait Sealed {}
204}
205
206#[doc(hidden)]
210pub trait UIVarDisplay: sealed::Sealed + fmt::Display + Send + Sync + 'static {}
211impl<T> UIVarDisplay for T where T: sealed::Sealed + fmt::Display + Send + Sync + 'static {}
212
213macro_rules! impl_ui_var_display {
214 ($($ty:ty),* $(,)?) => {
215 $(
216 impl sealed::Sealed for $ty {}
217 )*
218 };
219}
220
221#[rustfmt::skip]
222impl_ui_var_display!(
223 f32, f64,
224 bool, char,
225 usize, isize,
226 u8, u16, u32, u64, u128,
227 i8, i16, i32, i64, i128,
228 NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128,
229 NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128,
230);
231
232impl<T> From<WeakVar<T>> for UIString
237where
238 T: UIVarDisplay,
239{
240 fn from(var: WeakVar<T>) -> Self {
241 UIString::__deferred_stack(move || {
242 let v = var.read()?;
243 let mut buf = SmallString::<[u8; 64]>::new();
244 let _ = write!(&mut buf, "{}", &*v);
245 Some(buf)
246 })
247 }
248}
249
250#[derive(Copy, Clone)]
265pub enum UIParam<T: Send + Sync + 'static> {
266 Static(T),
267 Dynamic(WeakVar<T>),
268}
269
270impl<T: Send + Sync + 'static> From<T> for UIParam<T> {
271 fn from(value: T) -> Self {
272 UIParam::Static(value)
273 }
274}
275
276impl<T: Send + Sync + 'static> From<WeakVar<T>> for UIParam<T> {
277 fn from(var: WeakVar<T>) -> Self {
278 UIParam::Dynamic(var)
279 }
280}
281
282impl<T: Clone + PartialEq + Send + Sync + 'static> UIParam<T> {
283 pub fn with_mut<R>(&mut self, func: impl FnOnce(&mut T) -> R) -> Option<R> {
284 match self {
285 UIParam::Static(value) => Some(func(value)),
286 UIParam::Dynamic(var) => {
287 let mut guard = var.write()?;
288 Some(func(&mut guard))
289 }
290 }
291 }
292
293 pub fn get(&self) -> Option<T> {
294 match self {
295 UIParam::Static(value) => Some(value.clone()),
296 UIParam::Dynamic(var) => var.get(),
297 }
298 }
299
300 pub fn get_or(&self, default: T) -> T {
301 match self {
302 UIParam::Static(value) => value.clone(),
303 UIParam::Dynamic(var) => var.get().unwrap_or(default),
304 }
305 }
306}