duat_core/widgets/status_line/
state.rs

1//! The acceptable patterns in a [`StatusLine`]
2//!
3//! The patterns that can be put in a [`StatusLine`] are largely the
4//! same as the ones that can be put inside a [`text!`] macro,
5//! however, some additions are made.
6//!
7//! Specifically, arguments that read from the [`File`] and its
8//! related structs are also accepted by [`status!`].
9//!
10//! In addition, arguments with arbitrary update schedules are also
11//! accepted, such as the [data] types, and parser/checker function
12//! pairs.
13//!
14//! [`StatusLine`]: super::StatusLine
15//! [`status!`]: super::status
16//! [`File`]: super::File
17//! [data]: crate::data
18use std::{fmt::Display, marker::PhantomData};
19
20use super::Reader;
21use crate::{
22    data::{DataMap, RwData},
23    text::{AlignCenter, AlignLeft, AlignRight, Builder, BuilderPart, Ghost, Spacer, Text},
24    ui::Ui,
25    widgets::Widget,
26};
27
28/// A struct that reads state in order to return [`Text`].
29enum Appender<U: Ui, _T: Clone = (), D: Display + Send + Clone = String, W = ()> {
30    NoArgs(Box<dyn FnMut(&mut Builder) + Send + 'static>),
31    FromWidget(WidgetAreaFn<W, U>),
32    Part(BuilderPart<D, _T>),
33}
34
35/// A part of the [`StatusLine`]
36///
37/// This can either be a static part, like [`Text`], [`impl Display`]
38/// type, or it can be a reader of the [`File`] and its structs, or it
39/// can update independently.
40///
41/// [`StatusLine`]: super::StatusLine
42/// [`impl Display`]: std::fmt::Display
43/// [`File`]: crate::widgets::File
44pub struct State<U: Ui, _T: Clone = (), D: Display + Send + Clone = String, W: 'static = ()> {
45    appender: Appender<U, _T, D, W>,
46    checker: Option<Box<dyn Fn() -> bool + Send + Sync>>,
47    ghost: PhantomData<_T>,
48}
49
50impl<U, _T, D, W> State<U, _T, D, W>
51where
52    U: Ui,
53    _T: Send + Clone + 'static,
54    D: Display + Send + Clone + 'static,
55    W: 'static,
56{
57    pub fn fns(self) -> (ReaderFn<U>, Box<dyn Fn() -> bool + Send + Sync>) {
58        (
59            match self.appender {
60                Appender::NoArgs(mut f) => Box::new(move |b, _| f(b)),
61                Appender::FromWidget(mut f) => Box::new(move |b, reader| {
62                    reader.inspect_related(|w, a| f(b, w, a));
63                }),
64                Appender::Part(builder_part) => Box::new(move |b, _| b.push(builder_part.clone())),
65            },
66            Box::new(move || self.checker.as_ref().is_some_and(|check| check())),
67        )
68    }
69}
70
71impl<D: Display + Send + Clone + 'static, U: Ui> From<D> for State<U, D, D> {
72    fn from(value: D) -> Self {
73        Self {
74            appender: Appender::Part(BuilderPart::from(value)),
75            checker: None,
76            ghost: PhantomData,
77        }
78    }
79}
80
81impl<U: Ui> From<Text> for State<U, Text> {
82    fn from(value: Text) -> Self {
83        Self {
84            appender: Appender::Part(BuilderPart::from(value)),
85            checker: None,
86            ghost: PhantomData,
87        }
88    }
89}
90
91impl<D: Display + Send + Clone, U: Ui> From<RwData<D>> for State<U, DataArg<D>> {
92    fn from(value: RwData<D>) -> Self {
93        Self {
94            appender: Appender::NoArgs({
95                let value = value.clone();
96                Box::new(move |b| b.push(value.read().clone()))
97            }),
98            checker: Some(Box::new(value.checker())),
99            ghost: PhantomData,
100        }
101    }
102}
103
104impl<U: Ui> From<RwData<Text>> for State<U, DataArg<Text>> {
105    fn from(value: RwData<Text>) -> Self {
106        Self {
107            appender: Appender::NoArgs({
108                let value = value.clone();
109                Box::new(move |b| b.push(value.read().clone()))
110            }),
111            checker: Some(Box::new(value.checker())),
112            ghost: PhantomData,
113        }
114    }
115}
116
117impl<U, I, O> From<DataMap<I, O>> for State<U, DataArg<String>>
118where
119    U: Ui,
120    I: ?Sized + Send,
121    O: Display + 'static,
122{
123    fn from(value: DataMap<I, O>) -> Self {
124        let (mut reader, checker) = value.fns();
125        State {
126            appender: Appender::NoArgs(Box::new(move |b| b.push(reader()))),
127            checker: Some(checker),
128            ghost: PhantomData,
129        }
130    }
131}
132
133impl<U: Ui, I: ?Sized + Send> From<DataMap<I, Text>> for State<U, DataArg<Text>> {
134    fn from(value: DataMap<I, Text>) -> Self {
135        let (mut reader, checker) = value.fns();
136        State {
137            appender: Appender::NoArgs(Box::new(move |b| b.push(reader()))),
138            checker: Some(checker),
139            ghost: PhantomData,
140        }
141    }
142}
143
144impl<U, F, I, O> From<F> for State<U, IntoDataArg<String>>
145where
146    U: Ui,
147    F: FnOnce() -> DataMap<I, O>,
148    I: ?Sized + Send + 'static,
149    O: Display + 'static,
150{
151    fn from(value: F) -> Self {
152        let (mut reader, checker) = value().fns();
153        State {
154            appender: Appender::NoArgs(Box::new(move |b| b.push(reader()))),
155            checker: Some(checker),
156            ghost: PhantomData,
157        }
158    }
159}
160
161impl<U, F, I> From<F> for State<U, IntoDataArg<Text>>
162where
163    U: Ui,
164    F: FnOnce() -> DataMap<I, Text>,
165    I: ?Sized + Send + 'static,
166{
167    fn from(value: F) -> Self {
168        let (mut reader, checker) = value().fns();
169        State {
170            appender: Appender::NoArgs(Box::new(move |b| b.push(reader()))),
171            checker: Some(checker),
172            ghost: PhantomData,
173        }
174    }
175}
176
177impl<D, Reader, Checker, U> From<(Reader, Checker)> for State<U, NoArg<String>>
178where
179    D: Display,
180    Reader: Fn() -> D + Send + 'static,
181    Checker: Fn() -> bool + Send + Sync + 'static,
182    U: Ui,
183{
184    fn from((reader, checker): (Reader, Checker)) -> Self {
185        State {
186            appender: Appender::NoArgs(Box::new(move |b| b.push(reader()))),
187            checker: Some(Box::new(checker)),
188            ghost: PhantomData,
189        }
190    }
191}
192
193impl<Reader, Checker, U> From<(Reader, Checker)> for State<U, NoArg<Text>>
194where
195    Reader: Fn() -> Text + Send + 'static,
196    Checker: Fn() -> bool + Send + Sync + 'static,
197    U: Ui,
198{
199    fn from((reader, checker): (Reader, Checker)) -> Self {
200        State {
201            appender: Appender::NoArgs(Box::new(move |b| b.push(reader()))),
202            checker: Some(Box::new(checker)),
203            ghost: PhantomData,
204        }
205    }
206}
207
208impl<D, W, ReadFn, U> From<ReadFn> for State<U, WidgetArg<String>, String, W>
209where
210    D: Display + Send + 'static,
211    W: Widget<U> + Sized,
212    ReadFn: Fn(&W) -> D + Send + 'static,
213    U: Ui,
214{
215    fn from(value: ReadFn) -> Self {
216        State {
217            appender: Appender::FromWidget(Box::new(move |b, widget, _| b.push(value(widget)))),
218            checker: None,
219            ghost: PhantomData,
220        }
221    }
222}
223
224impl<W, ReadFn, U> From<ReadFn> for State<U, WidgetArg<Text>, String, W>
225where
226    W: Widget<U> + Sized,
227    ReadFn: Fn(&W) -> Text + Send + 'static,
228    U: Ui,
229{
230    fn from(value: ReadFn) -> Self {
231        State {
232            appender: Appender::FromWidget(Box::new(move |b, widget, _| b.push(value(widget)))),
233            checker: None,
234            ghost: PhantomData,
235        }
236    }
237}
238
239impl<D, W, ReadFn, U> From<ReadFn> for State<U, WidgetAreaArg<String>, String, W>
240where
241    D: Display + Send + 'static,
242    W: Widget<U> + Sized,
243    ReadFn: Fn(&W, &U::Area) -> D + Send + 'static,
244    U: Ui,
245{
246    fn from(value: ReadFn) -> Self {
247        State {
248            appender: Appender::FromWidget(Box::new(move |b, widget, area| {
249                b.push(value(widget, area))
250            })),
251            checker: None,
252            ghost: PhantomData,
253        }
254    }
255}
256
257impl<W, ReadFn, U> From<ReadFn> for State<U, WidgetAreaArg<Text>, String, W>
258where
259    W: Widget<U> + Sized,
260    ReadFn: Fn(&W, &U::Area) -> Text + Send + 'static,
261    U: Ui,
262{
263    fn from(value: ReadFn) -> Self {
264        State {
265            appender: Appender::FromWidget(Box::new(move |b, widget, area| {
266                b.push(value(widget, area))
267            })),
268            checker: None,
269            ghost: PhantomData,
270        }
271    }
272}
273
274impl<U: Ui> From<AlignLeft> for State<U, AlignLeft> {
275    fn from(_: AlignLeft) -> Self {
276        Self {
277            appender: Appender::Part(BuilderPart::AlignLeft),
278            checker: None,
279            ghost: PhantomData,
280        }
281    }
282}
283
284impl<U: Ui> From<AlignCenter> for State<U, AlignCenter> {
285    fn from(_: AlignCenter) -> Self {
286        Self {
287            appender: Appender::Part(BuilderPart::AlignCenter),
288            checker: None,
289            ghost: PhantomData,
290        }
291    }
292}
293
294impl<U: Ui> From<AlignRight> for State<U, AlignRight> {
295    fn from(_: AlignRight) -> Self {
296        Self {
297            appender: Appender::Part(BuilderPart::AlignRight),
298            checker: None,
299            ghost: PhantomData,
300        }
301    }
302}
303
304impl<U: Ui> From<Spacer> for State<U, Spacer> {
305    fn from(_: Spacer) -> Self {
306        Self {
307            appender: Appender::Part(BuilderPart::from(Spacer)),
308            checker: None,
309            ghost: PhantomData,
310        }
311    }
312}
313
314impl<U: Ui> From<Ghost> for State<U, Ghost> {
315    fn from(value: Ghost) -> Self {
316        Self {
317            appender: Appender::Part(BuilderPart::from(value)),
318            checker: None,
319            ghost: PhantomData,
320        }
321    }
322}
323
324// Dummy structs to prevent implementation conflicts.
325#[doc(hidden)]
326#[derive(Clone)]
327pub struct DataArg<T>(PhantomData<T>);
328#[doc(hidden)]
329#[derive(Clone)]
330pub struct IntoDataArg<T>(PhantomData<T>);
331#[doc(hidden)]
332#[derive(Clone)]
333pub struct NoArg<T>(PhantomData<T>);
334#[doc(hidden)]
335#[derive(Clone)]
336pub struct WidgetArg<W>(PhantomData<W>);
337#[doc(hidden)]
338#[derive(Clone)]
339pub struct WidgetAreaArg<W>(PhantomData<W>);
340
341// The various types of function aliases
342type WidgetAreaFn<W, U> = Box<dyn FnMut(&mut Builder, &W, &<U as Ui>::Area) + Send + 'static>;
343type ReaderFn<U> = Box<dyn FnMut(&mut Builder, &mut Reader<U>) + Send>;