duat_core/
form.rs

1//! Utilities for stylizing the text of Duat
2use std::sync::{OnceLock, RwLock, RwLockReadGuard};
3
4use FormType::*;
5use crossterm::style::{Attribute, Attributes, ContentStyle};
6pub use crossterm::{cursor::SetCursorStyle as CursorShape, style::Color};
7
8pub use self::global::*;
9pub(crate) use self::global::{colorscheme_exists, exists};
10use crate::{
11    hook::{self, FormSet},
12    session::DuatSender,
13    text::FormTag,
14};
15
16/// Lists of [`Form`]s to be applied by a name
17pub trait ColorScheme: Send + Sync + 'static {
18    /// Applies the [`Form`]s
19    ///
20    /// # Note
21    ///
22    /// This can technically do anything, mostly because one might
23    /// want to do a bunch of `if`s and `else`s in order to get to a
24    /// finalized [`ColorScheme`].
25    fn apply(&self);
26
27    /// The name of this [`ColorScheme`], shouldn't be altered
28    fn name(&self) -> &'static str;
29}
30
31static SENDER: OnceLock<DuatSender> = OnceLock::new();
32static BASE_FORMS: &[(&str, Form, FormType)] = &[
33    ("default", Form::new().0, Normal),
34    ("accent", Form::bold().0, Normal),
35    ("caret.main", Form::reverse().0, Normal),
36    ("caret.extra", Form::reverse().0, Ref(2)),
37    ("selection.main", Form::white().on_dark_grey().0, Normal),
38    ("selection.extra", Form::white().on_grey().0, Ref(5)),
39    ("cloak", Form::grey().on_black().0, Normal),
40    ("character.control", Form::grey().0, Normal),
41    ("param.buffer", Form::yellow().0, Normal),
42    ("param.buffer.open", Form::yellow().0, Ref(8)),
43    ("param.buffer.exists", Form::yellow().underlined().0, Normal),
44];
45
46/// The functions that will be exposed for public use.
47mod global {
48    use std::{
49        any::TypeId,
50        collections::HashMap,
51        sync::{LazyLock, Mutex, OnceLock, mpsc},
52        time::Duration,
53    };
54
55    use super::{BASE_FORMS, BuiltForm, ColorScheme, CursorShape, Form, FormId, Painter, Palette};
56    use crate::{
57        context,
58        hook::{self, ColorSchemeSet},
59    };
60
61    static PALETTE: OnceLock<&'static Palette> = OnceLock::new();
62    static FORMS: OnceLock<&'static Mutex<Vec<&str>>> = OnceLock::new();
63    static COLORSCHEMES: LazyLock<Mutex<Vec<Box<dyn ColorScheme>>>> = LazyLock::new(Mutex::default);
64
65    /// Either a [`Form`] or the name of a form
66    ///
67    /// Note that the referenced form does not need to exist for
68    /// [`form::set`] or [`form::set_weak`] to work properly. In that
69    /// case, a new form with that name will be created, as well as
70    /// any of its inherited parents (separated by `"."`).
71    ///
72    /// [`form::set`]: set
73    /// [`form::set_weak`]: set_weak
74    #[doc(hidden)]
75    pub trait FormFmt {
76        /// The kind of [`Form`] that this type represents
77        fn kind(&self) -> Kind;
78    }
79    impl FormFmt for Form {
80        fn kind(&self) -> Kind {
81            Kind::Form(*self)
82        }
83    }
84
85    impl FormFmt for BuiltForm {
86        fn kind(&self) -> Kind {
87            Kind::Form(self.0)
88        }
89    }
90
91    impl FormFmt for &str {
92        fn kind(&self) -> Kind {
93            Kind::Ref(self.to_string())
94        }
95    }
96
97    impl FormFmt for &mut str {
98        fn kind(&self) -> Kind {
99            Kind::Ref(self.to_string())
100        }
101    }
102
103    impl FormFmt for String {
104        fn kind(&self) -> Kind {
105            Kind::Ref(self.clone())
106        }
107    }
108
109    /// Sets the [`Form`] by the name of `name`
110    ///
111    /// This will create a new form or replace one that already
112    /// exists, and you can either set it to a [`Form`] directly, or
113    /// reference another form by its name:
114    ///
115    /// ```rust
116    /// # duat_core::form::set_initial(duat_core::form::get_initial());
117    /// # use duat_core::form::{self, Form};
118    /// // Creates a regular form
119    /// let reg_id = form::set("my_regular_form", Form::red());
120    /// // Creates a form that references the first
121    /// let ref_id = form::set("my_ref_form", "my_regular_form");
122    /// // Sets both "MyRegularForm" and "MyRefForm" to blue
123    /// form::set("my_regular_form", Form::blue());
124    /// ```
125    ///
126    /// If you are creating a plugin, or another kind of tool for
127    /// others using Duat, use [`form::set_weak`] instead of this
128    /// function.
129    ///
130    /// [`form::set_weak`]: set_weak
131    pub fn set(name: impl ToString, form: impl FormFmt) -> FormId {
132        let name = name.to_string();
133        let cloned_name = name.clone();
134
135        match form.kind() {
136            Kind::Form(form) => queue(move || PALETTE.get().unwrap().set_form(cloned_name, form)),
137            Kind::Ref(refed) => queue(move || PALETTE.get().unwrap().set_ref(cloned_name, refed)),
138        };
139
140        let mut forms = FORMS.get().unwrap().lock().unwrap();
141        if let Kind::Ref(refed) = form.kind() {
142            position_of_name(&mut forms, refed);
143        }
144        FormId(position_of_name(&mut forms, name) as u16)
145    }
146
147    /// Sets a form, "weakly"
148    ///
149    /// The difference between this function and [`form::set`] is
150    /// that this function will only trigger if the form didn't
151    /// already exist/was previously onle set with [`set_weak`].
152    ///
153    /// This is useful for plugins, since it prioritizes the user's
154    /// preferences, no matter in what order this function and
155    /// [`form::set`] are called:
156    ///
157    /// ```rust
158    /// # duat_core::form::set_initial(duat_core::form::get_initial());
159    /// use duat_core::form::{self, Form};
160    ///
161    /// // Creates a form "weakly"
162    /// form::set_weak("weak_form", Form::blue().on_white());
163    ///
164    /// // Sets that form "strongly"
165    /// form::set("weak_form", Form::red().on_grey());
166    ///
167    /// // Even if setting the form weakly afterwards, it won't change again.
168    /// form::set_weak("weak_form", Form::blue().underlined());
169    /// ```
170    ///
171    /// [`form::set`]: set
172    pub fn set_weak(name: impl ToString, form: impl FormFmt) -> FormId {
173        let name = name.to_string();
174        let cloned_name = name.clone();
175
176        match form.kind() {
177            Kind::Form(form) => {
178                queue(move || PALETTE.get().unwrap().set_weak_form(cloned_name, form))
179            }
180            Kind::Ref(refed) => {
181                queue(move || PALETTE.get().unwrap().set_weak_ref(cloned_name, refed))
182            }
183        };
184
185        let mut forms = FORMS.get().unwrap().lock().unwrap();
186        if let Kind::Ref(refed) = form.kind() {
187            position_of_name(&mut forms, refed);
188        }
189        FormId(position_of_name(&mut forms, name) as u16)
190    }
191
192    /// Returns a [`Form`], given a [`FormId`].
193    pub fn from_id(id: FormId) -> Form {
194        PALETTE
195            .get()
196            .unwrap()
197            .form_from_id(id)
198            .unwrap_or(Form::new().0)
199    }
200
201    /// The current main cursor, with the `"caret.main"` [`Form`]
202    pub fn main_cursor() -> (Form, Option<CursorShape>) {
203        PALETTE.get().unwrap().main_cursor()
204    }
205
206    /// The current extra cursor, with the `"caret.extra"` [`Form`]
207    pub fn extra_cursor() -> (Form, Option<CursorShape>) {
208        PALETTE.get().unwrap().extra_cursor()
209    }
210
211    /// Sets the main cursor's [shape]
212    ///
213    /// Cursors in Duat can either be a distinct [shape], or can be
214    /// defined as a [`Form`], just like the rest of the styling.
215    ///
216    /// This is done because some UIs (like a terminal) lack the
217    /// ability to show multiple cursors, so extra cursors are usually
218    /// printed as solid blocks with a background color.
219    ///
220    /// If you want to set the cursor's color, do something like this:
221    ///
222    /// ```rust
223    /// # use duat_core::form::{self, Form, Color};
224    /// # duat_core::form::set_initial(duat_core::form::get_initial());
225    /// form::set("caret.main", Form::black().on("rgb 240 210 200"));
226    /// ```
227    ///
228    /// However, if possible, Duat will still try to use the main
229    /// cursor's [shape]. If you don't want that to happen, see
230    /// [`form::unset_main_cursor`].
231    ///
232    /// [shape]: CursorShape
233    /// [`form::unset_main_cursor`]: unset_main_cursor
234    pub fn set_main_cursor(shape: CursorShape) {
235        queue(move || PALETTE.get().unwrap().set_main_cursor(shape));
236    }
237
238    /// Sets extra cursors's [shape]s
239    ///
240    /// Cursors in Duat can either be a distinct [shape], or can be
241    /// defined as a [`Form`], just like the rest of the styling.
242    ///
243    /// This is done because some UIs (like a terminal) lack the
244    /// ability to show multiple cursors, so extra cursors are usually
245    /// printed as solid blocks with a background color.
246    ///
247    /// If you want to set the cursor's color, do something like this:
248    ///
249    /// ```rust
250    /// # use duat_core::form::{self, Form, Color};
251    /// # duat_core::form::set_initial(duat_core::form::get_initial());
252    /// form::set("caret.extra", Form::black().on_cyan());
253    /// ```
254    ///
255    /// However, if possible, Duat will still try to use the main
256    /// cursor's [shape]. If you don't want that to happen, see
257    /// [`form::unset_extra_cursor`].
258    ///
259    /// [shape]: CursorShape
260    /// [`form::unset_extra_cursor`]: unset_extra_cursor
261    pub fn set_extra_cursor(shape: CursorShape) {
262        queue(move || PALETTE.get().unwrap().set_extra_cursor(shape));
263    }
264
265    /// Removes the main cursor's [shape]
266    ///
267    /// By doing this, you will force Duat to draw the main cursor by
268    /// use of the `"caret.main"` form.
269    ///
270    /// If you want to set the [shape] instead, see
271    /// [`form::set_main_cursor`].
272    ///
273    /// [shape]: CursorShape
274    /// [`form::set_main_cursor`]: set_main_cursor
275    pub fn unset_main_cursor() {
276        queue(move || PALETTE.get().unwrap().unset_main_cursor());
277    }
278
279    /// Removes extra cursors's [shape]s
280    ///
281    /// By doing this, you will force Duat to draw the extra cursor by
282    /// use of the `"caret.main"` form. Do note however that, in
283    /// something like a terminal, extra cursors would never be
284    /// printed as a [shape] anyways, since terminals can only
285    /// print one cursor at a time.
286    ///
287    /// If you want to set the [shape] instead, see
288    /// [`form::set_extra_cursor`].
289    ///
290    /// [shape]: CursorShape
291    /// [`form::set_extra_cursor`]: set_extra_cursor
292    pub fn unset_extra_cursor() {
293        queue(move || PALETTE.get().unwrap().unset_extra_cursor());
294    }
295
296    /// Removes all cursors's [shape]s
297    ///
298    /// Is the equivalent of calling [`unset_main_cursor`] and
299    /// [`unset_extra_cursor`].
300    ///
301    /// [shape]: CursorShape
302    pub fn unset_cursors() {
303        queue(move || {
304            PALETTE.get().unwrap().unset_main_cursor();
305            PALETTE.get().unwrap().unset_extra_cursor();
306        })
307    }
308
309    /// Creates a [`Painter`] with a mask
310    pub(crate) fn painter_with_mask<W: ?Sized + 'static>(mask: &'static str) -> Painter {
311        PALETTE.get().unwrap().painter(
312            default_id(TypeId::of::<W>(), crate::utils::duat_name::<W>()),
313            mask,
314        )
315    }
316
317    /// Enables the use of this mask
318    ///
319    /// A mask is essentially a remapping of [`Form`]s based on
320    /// suffix, this remapping doesn't take place outside of a
321    /// [`Painter`] (i.e. [`form::from_id`] won't be altered), here's
322    /// how it works:
323    ///
324    /// ```rust
325    /// # duat_core::doc_duat!(duat);
326    /// use duat::prelude::*;
327    ///
328    /// fn test<W: Widget>(pa: &mut Pass, handle: Handle<W>) {
329    ///     // Assume that a Form with the given name exists
330    ///     form::set("my_form", Form::red().on_blue());
331    ///
332    ///     // If I create a second Form like this one, they are separate
333    ///     form::set("my_form.suffix", Form::undercurled());
334    ///
335    ///     *handle.text_mut(pa) =
336    ///         txt!("[my_form]This is red on blue[], [my_form.suffix]undercurled");
337    ///
338    ///     // But if I enable the "suffix" mask that's at the end of the second Form
339    ///     form::enable_mask("suffix");
340    ///
341    ///     // After doing this, the Text should be printed with the "suffix" mask
342    ///     handle.set_mask("suffix");
343    ///
344    ///     // So when the widget is printed, it'd be equivalent to this:
345    ///     *handle.text_mut(pa) =
346    ///         txt!("[my_form.suffix]This is red on blue[], [my_form.suffix]undercurled");
347    /// }
348    /// ```
349    ///
350    /// Masks can serve a myriad of different purposes, but here's a
351    /// few:
352    ///
353    /// - When you want to temporarily change the [`Form`]s on a
354    ///   single [`Widget`]. This is, for example, used in the
355    ///   [`Notifications`] [`Widget`], which maps [`Form`]s in order
356    ///   to correspond to the [`Level`] of their severity.
357    /// - When you want to have [`Widget`]s change [`Form`] based on
358    ///   [hooks], so you could have, for example, an `"inactive"`
359    ///   mask for your [`Buffer`]s
360    /// - If you want to quickly cycle through [`Form`]s in a
361    ///   [`Text`], this is the most efficient way of doing that,
362    ///   since it relies on static remaps, not on changing the
363    ///   [`Form`]s themselves.
364    ///
365    /// When Duat first starts, the only available masks are
366    /// `"error"`, `"warn"` and `"info"`, but you can use this
367    /// function to add more of them.
368    ///
369    /// [`form::from_id`]: from_id
370    /// [`Widget`]: crate::ui::Widget
371    /// [`Notifications`]: https://docs.rs/duat/latest/duat
372    /// [`Level`]: crate::context::Level
373    /// [hooks]: crate::hook
374    /// [`Buffer`]: crate::buffer::Buffer
375    /// [`Text`]: crate::text::Text
376    pub fn enable_mask(mask: impl AsRef<str> + Send + Sync + 'static) {
377        queue(move || {
378            let mask = mask.as_ref();
379            let mut inner = PALETTE.get().unwrap().0.write().unwrap();
380            if !inner.masks.iter().any(|(m, _)| *m == mask) {
381                let mut remaps: Vec<u16> = (0..inner.forms.len() as u16).collect();
382
383                for (i, (name, ..)) in inner.forms.iter().enumerate() {
384                    if let Some((pref, suf)) = name.rsplit_once('.')
385                        && suf == mask
386                        && let Some(j) = inner.forms.iter().position(|(name, ..)| *name == pref)
387                    {
388                        remaps[j] = i as u16;
389                    }
390                }
391
392                inner.masks.push((mask.to_string().leak(), remaps));
393            }
394        })
395    }
396
397    /// Returns the [`FormId`] from the name of a [`Form`]
398    ///
399    /// If there is no [`Form`] with the given name, a new one is
400    /// created, which will behave according to the following
401    /// priority:
402    ///
403    /// - If the name contains a `'.'` character, it will reference
404    ///   the [`Form`] whose name is a suffix up to the last `'.'`.
405    ///   For example, `"Prefix.Middle.Suffix"` will reference
406    ///   `"Prefix.Middle"`;
407    /// - If the name does not contain a `'.'`, it will not reference
408    ///   anything, having the [default `Form`];
409    ///
410    /// If a referenced [`Form`] does not exist, it will be added,
411    /// following the same rules.
412    ///
413    /// # Note
414    ///
415    /// This is a macro because, in order to be as efficient as
416    /// possible, it is better to store this value inside of a
417    /// static variable, since it is guaranteed to not change. This
418    /// way, you only have to figure it out once, and it is much
419    /// faster than with a [`HashMap`] (how this is usually done).
420    ///
421    /// [`HashMap`]: std::collections::HashMap
422    /// [default `Form`]: Form::new
423    // SAFETY: Since _set_many always resolves to the same value, then the
424    // static muts should eventually be set to their correct values, after
425    // which no problems can occurr.
426    // Before that point, the absolute worst thing that could happen is
427    // DEFAULT_ID will be returned instead of the correct id (if the two
428    // unsafe setting statements are in the wrong order for some reason),
429    // but this should pretty much never happen.
430    pub macro id_of($form:expr) {{
431        use $crate::form::{_set_many, DEFAULT_ID, FormId};
432
433        static mut WAS_SET: bool = false;
434        static mut ID: FormId = DEFAULT_ID;
435        if unsafe { WAS_SET } {
436            unsafe { ID }
437        } else {
438            let name = $form.to_string();
439            let id = _set_many(true, vec![(name, None)])[0];
440            unsafe {
441                ID = id;
442                WAS_SET = true;
443            }
444            id
445        }
446    }}
447
448    /// Non static version of [`id_of!`]
449    ///
450    /// You should only use this if the names of the [`Form`]s in
451    /// question are not known at compile time. And if that is the
452    /// case, you should try to find a way to memoize around this
453    /// issue (usually with something like a [`HashMap`]).
454    pub fn id_of_non_static(name: impl ToString) -> FormId {
455        let name = name.to_string();
456        _set_many(true, vec![(name, None)])[0]
457    }
458
459    /// Non static version of [`id_of!`], for many [`Form`]s
460    ///
461    /// You should only use this if the names of the [`Form`]s in
462    /// question are not known at compile time. And if that is the
463    /// case, you should try to find a way to memoize around this
464    /// issue (usually with something like a [`HashMap`]).
465    pub fn ids_of_non_static(names: impl IntoIterator<Item = impl ToString>) -> Vec<FormId> {
466        let names: Vec<(String, Option<Kind>)> =
467            names.into_iter().map(|n| (n.to_string(), None)).collect();
468        _set_many(true, names)
469    }
470
471    /// Sets a bunch of [`Form`]s
472    #[doc(hidden)]
473    pub fn _set_many<S: AsRef<str> + Send + Sync + 'static>(
474        weak: bool,
475        sets: Vec<(S, Option<Kind>)>,
476    ) -> Vec<FormId> {
477        let mut ids = Vec::new();
478        let mut forms = FORMS.get().unwrap().lock().unwrap();
479        for (name, _) in sets.iter() {
480            ids.push(FormId(position_of_name(&mut forms, name) as u16));
481        }
482
483        queue(move || PALETTE.get().unwrap().set_many(weak, &sets));
484
485        ids
486    }
487
488    /// Adds a [`ColorScheme`] to the list of colorschemes
489    ///
490    /// This [`ColorScheme`] can then be added via
491    /// [`form::set_colorscheme`] or through the command
492    /// `colorscheme`.
493    ///
494    /// If a [`ColorScheme`] of the same name was already added, it
495    /// will be overritten.
496    ///
497    /// [`form::set_colorscheme`]: set_colorscheme
498    pub fn add_colorscheme(cs: impl ColorScheme) {
499        let mut colorschemes = COLORSCHEMES.lock().unwrap();
500        let name = cs.name();
501        if let Some(i) = colorschemes.iter().position(|cs| cs.name() == name) {
502            colorschemes[i] = Box::new(cs);
503        } else {
504            colorschemes.push(Box::new(cs));
505        }
506    }
507
508    /// Applies a [`ColorScheme`]
509    ///
510    /// This [`ColorScheme`] must've first been added via
511    /// [`form::add_colorscheme`].
512    ///
513    /// [`form::add_colorscheme`]: add_colorscheme
514    pub fn set_colorscheme(name: &str) {
515        let name = name.to_string();
516        let colorschemes = COLORSCHEMES.lock().unwrap();
517        if let Some(cs) = colorschemes.iter().find(|cs| cs.name() == name) {
518            cs.apply();
519            hook::queue(ColorSchemeSet(cs.name()));
520        } else {
521            context::error!("The colorscheme [a]{name}[] was not found");
522        }
523    }
524
525    /// Calls [`form::set`] on each tuple in the list
526    ///
527    /// This macro should primarily be used by colorschemes. If you
528    /// want to call a weak version of this macro (most useful for
529    /// other types of [`Plugin`]), then see [`set_many_weak!`].
530    ///
531    /// [`Plugin`]: crate::Plugin
532    /// [`form::set`]: set
533    pub macro set_many($(($name:literal, $form:expr)),+ $(,)?) {{
534        use $crate::form::FormFmt;
535        $crate::form::_set_many(false, vec![$( ($name, Some($form.kind())) ),+]);
536    }}
537
538    /// Calls [`form::set_weak`] on each tuple in the list
539    ///
540    /// This macro should be used when defining default colors in
541    /// [`Plugin`]s that are _not_ colorscheme [`Plugin`]s. This makes
542    /// it so the set [`Form`]s don't overrule the choices of the end
543    /// user, who might have called [`form::set`] or
544    /// [`form::set_colorscheme`] by the point that the [`Plugin`] is
545    /// added.
546    ///
547    /// [`Plugin`]: crate::Plugin
548    /// [`form::set_weak`]: set_weak
549    /// [`form::set`]: set
550    /// [`form::set_colorscheme`]: set_colorscheme
551    pub macro set_many_weak($(($name:literal, $form:expr)),+ $(,)?) {{
552        use $crate::form::FormFmt;
553        $crate::form::_set_many(true, vec![$( ($name, Some($form.kind())) ),+]);
554    }}
555
556    /// Wether or not a specific [`Form`] has been set
557    pub(crate) fn exists(name: &str) -> bool {
558        FORMS.get().unwrap().lock().unwrap().contains(&name)
559    }
560
561    /// Wether or not a specific [`ColorScheme`] was added
562    pub(crate) fn colorscheme_exists(name: &str) -> bool {
563        COLORSCHEMES
564            .lock()
565            .unwrap()
566            .iter()
567            .any(|cs| cs.name() == name)
568    }
569
570    /// The name of a form, given a [`FormId`]
571    pub(super) fn name_of(id: FormId) -> &'static str {
572        FORMS.get().unwrap().lock().unwrap()[id.0 as usize]
573    }
574
575    fn default_id(type_id: TypeId, type_name: &'static str) -> FormId {
576        static IDS: LazyLock<Mutex<HashMap<TypeId, FormId>>> = LazyLock::new(Mutex::default);
577        let mut ids = IDS.lock().unwrap();
578
579        if let Some(id) = ids.get(&type_id) {
580            *id
581        } else {
582            let name = format!("default.{type_name}");
583            let id = _set_many(true, vec![(name, None)])[0];
584            ids.insert(type_id, id);
585            id
586        }
587    }
588
589    fn position_of_name(names: &mut Vec<&'static str>, name: impl AsRef<str>) -> usize {
590        let name = name.as_ref();
591        if let Some((i, _)) = names.iter().enumerate().find(|(_, rhs)| **rhs == name) {
592            i
593        } else if let Some((refed, _)) = name.rsplit_once('.') {
594            position_of_name(names, refed);
595            names.push(name.to_string().leak());
596            names.len() - 1
597        } else {
598            names.push(name.to_string().leak());
599            names.len() - 1
600        }
601    }
602
603    fn queue<R>(f: impl FnOnce() -> R + Send + Sync + 'static) {
604        static SENDER: Mutex<Option<mpsc::Sender<Box<dyn FnOnce() + Send + Sync>>>> =
605            Mutex::new(None);
606
607        if context::will_reload_or_quit() {
608            return;
609        }
610
611        let f = Box::new(move || {
612            f();
613        });
614
615        let mut sender = SENDER.lock().unwrap();
616        let f = if let Some(tx) = sender.as_ref() {
617            let Err(err) = tx.send(f) else {
618                return;
619            };
620            err.0
621        } else {
622            f
623        };
624        let (tx, rx) = mpsc::channel();
625        tx.send(f).unwrap();
626        *sender = Some(tx);
627
628        std::thread::spawn(move || {
629            while let Ok(f) = rx.recv_timeout(Duration::from_micros(500)) {
630                f();
631            }
632        });
633    }
634
635    /// Gets a [`Mutex`] for the initial [`Form`]'s list of Duat
636    ///
637    /// ONLY MEANT TO BE USED BY THE DUAT EXECUTABLE
638    #[doc(hidden)]
639    pub fn get_initial() -> (&'static Mutex<Vec<&'static str>>, &'static Palette) {
640        let forms = Box::leak(Box::new(Mutex::new(
641            BASE_FORMS.iter().map(|(n, ..)| *n).collect(),
642        )));
643        let palette = Box::leak(Box::new(Palette::new()));
644        (forms, palette)
645    }
646
647    /// Sets the [`Mutex`] for the initial [`Form`]'s list of Duat
648    ///
649    /// ONLY MEANT TO BE USED BY THE DUAT EXECUTABLE
650    #[doc(hidden)]
651    pub fn set_initial((forms, palette): (&'static Mutex<Vec<&'static str>>, &'static Palette)) {
652        FORMS.set(forms).expect("Forms setup ran twice");
653        PALETTE.set(palette).expect("Forms setup ran twice");
654    }
655
656    /// A kind of [`Form`]
657    #[doc(hidden)]
658    pub enum Kind {
659        Form(Form),
660        Ref(String),
661    }
662}
663
664/// An identifier of a [`Form`]
665///
666/// [`Builder`] part: Applies the [`Form`] destructively
667///
668/// This struct is always going to point to the same form, since those
669/// cannot be destroyed.
670///
671/// The main use for keeping these things directly is in order to
672/// modify a buffer's text in an efficient manner, by adding tags
673/// directly, instead of using a macro like [`txt!`]
674///
675/// [`txt!`]: crate::text::txt
676/// [`Builder`]: crate::text::Builder
677#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
678pub struct FormId(u16);
679
680impl FormId {
681    /// Creates a [`Tag`] out of this [`FormId`]
682    ///
683    /// In order to push a [`Form`] to the [`Text`], it needs a
684    /// priority value, in order to properly sort the [`Form`]s within
685    /// the same byte.
686    ///
687    /// [`Tag`]: crate::text::Tag
688    /// [`Text`]: crate::text::Text
689    pub const fn to_tag(self, prio: u8) -> FormTag {
690        FormTag(self, prio)
691    }
692
693    /// The internal id of the [`FormId`]
694    ///
695    /// This may be useful in certain situations.
696    pub const fn to_u16(self) -> u16 {
697        self.0
698    }
699
700    /// The name of this [`FormId`]
701    pub fn name(self) -> &'static str {
702        name_of(self)
703    }
704}
705
706impl std::fmt::Debug for FormId {
707    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
708        write!(f, "FormId({})", name_of(*self))
709    }
710}
711
712/// A style for text.
713#[derive(Clone, Copy, PartialEq, Eq)]
714pub struct Form {
715    /// The actual [style](ContentStyle) that is applied
716    pub style: ContentStyle,
717}
718
719#[rustfmt::skip]
720impl Form {
721    mimic_method_new!(/**bold*/ bold Attribute::Bold);
722    mimic_method_new!(/**dim*/ dim Attribute::Dim);
723    mimic_method_new!(/**italic*/ italic Attribute::Italic);
724    mimic_method_new!(/**underlined*/ underlined Attribute::Underlined);
725    mimic_method_new!(/**double_underlined*/ double_underlined Attribute::DoubleUnderlined);
726    mimic_method_new!(/**undercurled*/ undercurled Attribute::Undercurled);
727    mimic_method_new!(/**underdashed*/ underdashed Attribute::Underdashed);
728    mimic_method_new!(/**reverse*/ reverse Attribute::Reverse);
729    mimic_method_new!(/**crossed_out*/ crossed_out Attribute::CrossedOut);
730    mimic_method_new!(/**black*/ black on_black underline_black Color::Black);
731    mimic_method_new!(/**dark_grey*/ dark_grey on_dark_grey underline_dark_grey Color::DarkGrey);
732    mimic_method_new!(/**red*/ red on_red underline_red Color::Red);
733    mimic_method_new!(/**dark_red*/ dark_red on_dark_red underline_dark_red Color::DarkRed);
734    mimic_method_new!(/**green*/ green on_green underline_green Color::Green);
735    mimic_method_new!(
736        /**dark_green*/ dark_green on_dark_green underline_dark_green Color::DarkGreen
737    );
738    mimic_method_new!(/**yellow*/ yellow on_yellow underline_yellow Color::Yellow);
739    mimic_method_new!(
740        /**dark_yellow*/ dark_yellow on_dark_yellow underline_dark_yellow Color::DarkYellow
741    );
742    mimic_method_new!(/**blue*/ blue on_blue underline_blue Color::Blue);
743    mimic_method_new!(/**dark_blue*/ dark_blue on_dark_blue underline_dark_blue Color::DarkBlue);
744    mimic_method_new!(/**magenta*/ magenta on_magenta underline_magenta Color::Magenta);
745    mimic_method_new!(
746        /**dark_magenta*/ dark_magenta on_dark_magenta underline_dark_magenta Color::DarkMagenta
747    );
748    mimic_method_new!(/**cyan*/ cyan on_cyan underline_cyan Color::Cyan);
749    mimic_method_new!(/**dark_cyan*/ dark_cyan on_dark_cyan underline_dark_cyan Color::DarkCyan);
750    mimic_method_new!(/**white*/ white on_white underline_white Color::White);
751    mimic_method_new!(/**grey*/ grey on_grey underline_grey Color::Grey);
752}
753
754impl Form {
755    /// Returns a new [`Form`] with a default style
756    ///
757    /// This method actually returns [`BuiltForm`]
758    #[allow(clippy::new_ret_no_self)]
759    pub const fn new() -> BuiltForm {
760        let style = ContentStyle {
761            foreground_color: None,
762            background_color: None,
763            underline_color: None,
764            attributes: Attributes::none(),
765        };
766        BuiltForm(Self { style })
767    }
768
769    /// Returns a new [`Form`] with the [`Reset`] attribute
770    ///
771    /// In Duat, the [`Reset`] attribute should remove only other
772    /// [`Attribute`]s, not any of the colors in use.
773    ///
774    /// [`Reset`]: Attribute::Reset
775    pub const fn reset() -> BuiltForm {
776        let mut built = Form::new();
777        built.0.style.attributes = built.0.style.attributes.with(Attribute::Reset);
778        built
779    }
780
781    /// New [`Form`] with a colored foreground
782    ///
783    /// This function accepts three color formats:
784    ///
785    /// - A hexcode, like `"#abcdef"`, capitalization is ignored;
786    /// - Three rgb values, like `"rgb 123 456 789"`;
787    /// - Three hsl values, like `"hsl {hue} {sat} {lit}"`, where
788    ///   {hue}, {sat} and {lit} can either be a number from `0..255`,
789    ///   or a percentage, followed by `'%'`, e.g. `"hsl 234 50% 42"`.
790    pub const fn with(str: &str) -> BuiltForm {
791        let mut built = Form::new();
792        built.0.style.foreground_color = match str_to_color(str) {
793            Ok(color) => Some(color),
794            Err(err) => panic!("{}", err),
795        };
796        built
797    }
798
799    /// New [`Form`] with a colored background
800    ///
801    /// This function accepts three color formats:
802    ///
803    /// - A hexcode, like `"#abcdef"`, capitalization is ignored;
804    /// - Three rgb values, like `"rgb 123 456 789"`;
805    /// - Three hsl values, like `"hsl {hue} {sat} {lit}"`, where
806    ///   {hue}, {sat} and {lit} can either be a number from `0..255`,
807    ///   or a percentage, followed by `'%'`, e.g. `"hsl 234 50% 42"`.
808    pub const fn on(str: &str) -> BuiltForm {
809        let mut built = Form::new();
810        built.0.style.background_color = match str_to_color(str) {
811            Ok(color) => Some(color),
812            Err(err) => panic!("{}", err),
813        };
814        built
815    }
816
817    /// New [`Form`] with a colored underlining
818    ///
819    /// This function accepts three color formats:
820    ///
821    /// - A hexcode, like `"#abcdef"`, capitalization is ignored;
822    /// - Three rgb values, like `"rgb 123 456 789"`;
823    /// - Three hsl values, like `"hsl {hue} {sat} {lit}"`, where
824    ///   {hue}, {sat} and {lit} can either be a number from `0..255`,
825    ///   or a percentage, followed by `'%'`, e.g. `"hsl 234 50% 42"`.
826    pub const fn underline(str: &str) -> BuiltForm {
827        let mut built = Form::new();
828        built.0.style.underline_color = match str_to_color(str) {
829            Ok(color) => Some(color),
830            Err(err) => panic!("{}", err),
831        };
832        built
833    }
834
835    /// The foreground color
836    pub const fn fg(&self) -> Option<Color> {
837        self.style.foreground_color
838    }
839
840    /// The background color
841    pub const fn bg(&self) -> Option<Color> {
842        self.style.background_color
843    }
844
845    /// The foreground color
846    pub const fn ul(&self) -> Option<Color> {
847        self.style.underline_color
848    }
849
850    /// The attributes
851    pub const fn attrs(&self) -> Attributes {
852        self.style.attributes
853    }
854}
855
856/// A convenience struct for [`Form`]s
857///
858/// This struct exists in order to have [`Form`] methods be
859/// initializers, while [`BuiltForm`] methods consume and return a
860/// [`BuiltForm`]s
861///
862/// This is their only difference, everywhere else, they are
863/// functionally identical.
864#[derive(Clone, Copy, Debug)]
865pub struct BuiltForm(Form);
866
867#[rustfmt::skip]
868impl BuiltForm {
869    mimic_method_mod!(/**bold*/ bold Attribute::Bold);
870    mimic_method_mod!(/**dim*/ dim Attribute::Dim);
871    mimic_method_mod!(/**italic*/ italic Attribute::Italic);
872    mimic_method_mod!(/**underlined*/ underlined Attribute::Underlined);
873    mimic_method_mod!(/**double_underlined*/ double_underlined Attribute::DoubleUnderlined);
874    mimic_method_mod!(/**undercurled*/ undercurled Attribute::Undercurled);
875    mimic_method_mod!(/**underdashed*/ underdashed Attribute::Underdashed);
876    mimic_method_mod!(/**reverse*/ reverse Attribute::Reverse);
877    mimic_method_mod!(/**crossed_out*/ crossed_out Attribute::CrossedOut);
878    mimic_method_mod!(/**overlined*/ overlined Attribute::OverLined);
879    mimic_method_mod!(/**black*/ black on_black underline_black Color::Black);
880    mimic_method_mod!(/**dark_grey*/ dark_grey on_dark_grey underline_dark_grey Color::DarkGrey);
881    mimic_method_mod!(/**red*/ red on_red underline_red Color::Red);
882    mimic_method_mod!(/**dark_red*/ dark_red on_dark_red underline_dark_red Color::DarkRed);
883    mimic_method_mod!(/**green*/ green on_green underline_green Color::Green);
884    mimic_method_mod!(
885        /**dark_green*/ dark_green on_dark_green underline_dark_green Color::DarkGreen
886    );
887    mimic_method_mod!(/**yellow*/ yellow on_yellow underline_yellow Color::Yellow);
888    mimic_method_mod!(
889        /**dark_yellow*/ dark_yellow on_dark_yellow underline_dark_yellow Color::DarkYellow
890    );
891    mimic_method_mod!(/**blue*/ blue on_blue underline_blue Color::Blue);
892    mimic_method_mod!(/**dark_blue*/ dark_blue on_dark_blue underline_dark_blue Color::DarkBlue);
893    mimic_method_mod!(/**magenta*/ magenta on_magenta underline_magenta Color::Magenta);
894    mimic_method_mod!(
895        /**dark_magenta*/ dark_magenta on_dark_magenta underline_dark_magenta Color::DarkMagenta
896    );
897    mimic_method_mod!(/**cyan*/ cyan on_cyan underline_cyan Color::Cyan);
898    mimic_method_mod!(/**dark_cyan*/ dark_cyan on_dark_cyan underline_dark_cyan Color::DarkCyan);
899    mimic_method_mod!(/**white*/ white on_white underline_white Color::White);
900    mimic_method_mod!(/**grey*/ grey on_grey underline_grey Color::Grey);
901}
902
903impl BuiltForm {
904    /// Adds the [`Reset`] attribute to this [`Form`]
905    ///
906    /// In Duat, the [`Reset`] attribute should remove only other
907    /// [`Attribute`]s, not any of the colors in use.
908    ///
909    /// [`Reset`]: Attribute::Reset
910    pub const fn reset(mut self) -> BuiltForm {
911        self.0.style.attributes = self.0.style.attributes.with(Attribute::Reset);
912        self
913    }
914
915    /// Colors the foreground of this [`Form`]
916    ///
917    /// This function accepts three color formats:
918    ///
919    /// - A hexcode, like `"#abcdef"`, capitalization is ignored;
920    /// - Three rgb values, like `"rgb 123 456 789"`;
921    /// - Three hsl values, like `"hsl {hue} {sat} {lit}"`, where
922    ///   {hue}, {sat} and {lit} can either be a number from `0..255`,
923    ///   or a percentage, followed by `'%'`, e.g. `"hsl 234 50% 42"`.
924    pub const fn with(mut self, str: &str) -> Self {
925        self.0.style.foreground_color = match str_to_color(str) {
926            Ok(color) => Some(color),
927            Err(err) => panic!("{}", err),
928        };
929        self
930    }
931
932    /// Colors the background of this [`Form`]
933    ///
934    /// This function accepts three color formats:
935    ///
936    /// - A hexcode, like `"#abcdef"`, capitalization is ignored;
937    /// - Three rgb values, like `"rgb 123 456 789"`;
938    /// - Three hsl values, like `"hsl {hue} {sat} {lit}"`, where
939    ///   {hue}, {sat} and {lit} can either be a number from `0..255`,
940    ///   or a percentage, followed by `'%'`, e.g. `"hsl 234 50% 42"`.
941    pub const fn on(mut self, str: &str) -> Self {
942        self.0.style.background_color = match str_to_color(str) {
943            Ok(color) => Some(color),
944            Err(err) => panic!("{}", err),
945        };
946        self
947    }
948
949    /// Colors the underlining of this [`Form`]
950    ///
951    /// This function accepts three color formats:
952    ///
953    /// - A hexcode, like `"#abcdef"`, capitalization is ignored;
954    /// - Three rgb values, like `"rgb 123 456 789"`;
955    /// - Three hsl values, like `"hsl {hue} {sat} {lit}"`, where
956    ///   {hue}, {sat} and {lit} can either be a number from `0..255`,
957    ///   or a percentage, followed by `'%'`, e.g. `"hsl 234 50% 42"`.
958    pub const fn underline(mut self, str: &str) -> Self {
959        self.0.style.underline_color = match str_to_color(str) {
960            Ok(color) => Some(color),
961            Err(err) => panic!("{}", err),
962        };
963        self
964    }
965}
966
967impl std::ops::Deref for BuiltForm {
968    type Target = Form;
969
970    fn deref(&self) -> &Self::Target {
971        &self.0
972    }
973}
974
975impl std::ops::DerefMut for BuiltForm {
976    fn deref_mut(&mut self) -> &mut Self::Target {
977        &mut self.0
978    }
979}
980
981impl From<BuiltForm> for Form {
982    fn from(value: BuiltForm) -> Self {
983        value.0
984    }
985}
986
987/// The [`FormId`] of `"default"`
988pub const DEFAULT_ID: FormId = FormId(0);
989/// The [`FormId`] of `"accent"`
990pub const ACCENT_ID: FormId = FormId(1);
991/// The [`FormId`] of `"caret.main"`
992pub const M_CAR_ID: FormId = FormId(2);
993/// The [`FormId`] of `"caret.extra"`
994pub const E_CAR_ID: FormId = FormId(3);
995/// The [`FormId`] of `"slection.main"`
996pub const M_SEL_ID: FormId = FormId(4);
997/// The [`FormId`] of `"selection.extra"`
998pub const E_SEL_ID: FormId = FormId(5);
999/// The [`FormId`] of `"character.control"`
1000pub const CONTROL_CHAR_ID: FormId = FormId(7);
1001
1002/// The list of forms to be used when rendering.
1003///
1004/// ONLY MEANT TO BE ACCESSED BY DUAT AND DUAT-CORE.
1005#[derive(Debug)]
1006#[doc(hidden)]
1007pub struct Palette(RwLock<InnerPalette>);
1008
1009impl Palette {
1010    /// Returns a new instance of [`FormPalette`]
1011    fn new() -> Self {
1012        let main_cursor = Some(CursorShape::DefaultUserShape);
1013        Self(RwLock::new(InnerPalette {
1014            main_cursor,
1015            extra_cursor: main_cursor,
1016            forms: BASE_FORMS.to_vec(),
1017            masks: vec![(
1018                "".to_string().leak(),
1019                (0..BASE_FORMS.len() as u16).collect(),
1020            )],
1021        }))
1022    }
1023
1024    /// Sets a [`Form`]
1025    fn set_form(&self, name: impl AsRef<str>, form: Form) {
1026        let name = name.as_ref();
1027        self.0.write().unwrap().set_form(name, form);
1028    }
1029
1030    /// Sets a [`Form`] "weakly"
1031    fn set_weak_form(&self, name: impl AsRef<str>, form: Form) {
1032        let name = name.as_ref();
1033        self.0.write().unwrap().set_weak_form(name, form);
1034    }
1035
1036    /// Makes a [`Form`] reference another
1037    fn set_ref(&self, name: impl AsRef<str>, refed: impl AsRef<str>) {
1038        let (name, refed) = (name.as_ref(), refed.as_ref());
1039        self.0.write().unwrap().set_ref(name, refed);
1040    }
1041
1042    /// Makes a [`Form`] reference another "weakly"
1043    fn set_weak_ref(&self, name: impl AsRef<str>, refed: impl AsRef<str>) {
1044        let (name, refed) = (name.as_ref(), refed.as_ref());
1045        self.0.write().unwrap().set_weak_ref(name, refed);
1046    }
1047
1048    /// Sets many [`Form`]s
1049    fn set_many<S: AsRef<str>>(&self, weak: bool, sets: &[(S, Option<self::global::Kind>)]) {
1050        let mut inner = self.0.write().unwrap();
1051        for (name, kind) in sets {
1052            match (weak, kind) {
1053                (false, Some(Kind::Form(form))) => inner.set_form(name.as_ref(), *form),
1054                (false, Some(Kind::Ref(refed))) => inner.set_ref(name.as_ref(), refed),
1055                (true, Some(Kind::Form(form))) => inner.set_weak_form(name.as_ref(), *form),
1056                (true, Some(Kind::Ref(refed))) => inner.set_weak_ref(name.as_ref(), refed),
1057                (_, None) => {
1058                    position_and_form(&mut inner.forms, name);
1059                }
1060            }
1061        }
1062    }
1063
1064    /// Returns a form, given a [`FormId`].
1065    fn form_from_id(&self, id: FormId) -> Option<Form> {
1066        let inner = self.0.read().unwrap();
1067        inner.forms.get(id.0 as usize).map(|(_, form, _)| *form)
1068    }
1069
1070    /// The [`Form`] and [`CursorShape`] of the main cursor
1071    fn main_cursor(&self) -> (Form, Option<CursorShape>) {
1072        let form = self.form_from_id(M_CAR_ID).unwrap();
1073        (form, self.0.read().unwrap().main_cursor)
1074    }
1075
1076    /// The [`Form`] and [`CursorShape`] of extra cursors
1077    fn extra_cursor(&self) -> (Form, Option<CursorShape>) {
1078        let form = self.form_from_id(E_CAR_ID).unwrap();
1079        (form, self.0.read().unwrap().extra_cursor)
1080    }
1081
1082    /// Sets the [`CursorShape`] of the main cursor
1083    fn set_main_cursor(&self, shape: CursorShape) {
1084        self.0.write().unwrap().main_cursor = Some(shape);
1085        if let Some(sender) = SENDER.get() {
1086            sender.send_form_changed().unwrap()
1087        }
1088    }
1089
1090    /// Sets the [`CursorShape`] of extra cursors
1091    fn set_extra_cursor(&self, shape: CursorShape) {
1092        self.0.write().unwrap().extra_cursor = Some(shape);
1093        if let Some(sender) = SENDER.get() {
1094            sender.send_form_changed().unwrap()
1095        }
1096    }
1097
1098    /// Unsets the [`CursorShape`] of the main cursor
1099    fn unset_main_cursor(&self) {
1100        self.0.write().unwrap().main_cursor = None;
1101        if let Some(sender) = SENDER.get() {
1102            sender.send_form_changed().unwrap()
1103        }
1104    }
1105
1106    /// Unsets the [`CursorShape`] of the extra cursors
1107    fn unset_extra_cursor(&self) {
1108        self.0.write().unwrap().extra_cursor = None;
1109        if let Some(sender) = SENDER.get() {
1110            sender.send_form_changed().unwrap()
1111        }
1112    }
1113
1114    /// Returns a [`Painter`]
1115    fn painter(&'static self, default_id: FormId, mask: &str) -> Painter {
1116        let inner = self.0.read().unwrap();
1117        let mask_i = inner
1118            .masks
1119            .iter()
1120            .position(|(m, _)| *m == mask)
1121            .unwrap_or_default();
1122
1123        let default = inner
1124            .forms
1125            .get(match inner.masks[mask_i].1.get(default_id.0 as usize) {
1126                Some(i) => *i as usize,
1127                None => default_id.0 as usize,
1128            })
1129            .map(|(_, f, _)| *f)
1130            .unwrap_or(Form::new().0);
1131
1132        Painter {
1133            inner,
1134            mask_i,
1135            default,
1136            forms: Vec::new(),
1137            set_fg: true,
1138            set_bg: true,
1139            set_ul: true,
1140            reset_attrs: false,
1141            prev_style: None,
1142        }
1143    }
1144}
1145
1146struct InnerPalette {
1147    main_cursor: Option<CursorShape>,
1148    extra_cursor: Option<CursorShape>,
1149    forms: Vec<(&'static str, Form, FormType)>,
1150    masks: Vec<(&'static str, Vec<u16>)>,
1151}
1152
1153impl InnerPalette {
1154    /// Sets a [`Form`]
1155    fn set_form(&mut self, name: &str, form: Form) {
1156        let (i, _) = position_and_form(&mut self.forms, name);
1157
1158        self.forms[i].1 = form;
1159        self.forms[i].2 = FormType::Normal;
1160
1161        for refed in refs_of(self, i) {
1162            self.forms[refed].1 = form;
1163        }
1164
1165        if let Some(sender) = SENDER.get() {
1166            sender.send_form_changed().unwrap()
1167        }
1168
1169        mask_form(name, i, self);
1170        hook::queue(FormSet((self.forms[i].0, FormId(i as u16), form)));
1171    }
1172
1173    /// Sets a [`Form`] "weakly"
1174    fn set_weak_form(&mut self, name: &str, form: Form) {
1175        let (i, _) = position_and_form(&mut self.forms, name);
1176
1177        let (_, f, f_ty) = &mut self.forms[i];
1178        if let FormType::Weakest | FormType::WeakestRef(_) = f_ty {
1179            *f = form;
1180            *f_ty = FormType::Normal;
1181
1182            if let Some(sender) = SENDER.get() {
1183                sender.send_form_changed().unwrap()
1184            }
1185            for refed in refs_of(self, i) {
1186                self.forms[refed].1 = form;
1187            }
1188
1189            mask_form(name, i, self);
1190        }
1191    }
1192
1193    /// Makes a [`Form`] reference another
1194    fn set_ref(&mut self, name: &str, refed: &str) {
1195        let (refed, form) = position_and_form(&mut self.forms, refed);
1196        let (i, _) = position_and_form(&mut self.forms, name);
1197
1198        self.forms[i].1 = form;
1199        for refed in refs_of(self, i) {
1200            self.forms[refed].1 = form;
1201        }
1202
1203        // If it would be circular, we just don't reference anything.
1204        if would_be_circular(self, i, refed) {
1205            self.forms[i].2 = FormType::Normal;
1206        } else {
1207            self.forms[i].2 = FormType::Ref(refed);
1208        }
1209
1210        if let Some(sender) = SENDER.get() {
1211            sender.send_form_changed().unwrap()
1212        }
1213
1214        mask_form(name, i, self);
1215        hook::queue(FormSet((self.forms[i].0, FormId(i as u16), form)));
1216    }
1217
1218    /// Makes a [`Form`] reference another "weakly"
1219    fn set_weak_ref(&mut self, name: &str, refed: &str) {
1220        let (refed, form) = position_and_form(&mut self.forms, refed);
1221        let (i, _) = position_and_form(&mut self.forms, name);
1222
1223        // For weak refs, no checks are done, since a form is only set if it
1224        // doesn't exist, and for there to be refs to it, it must exist.
1225        let (_, f, f_ty) = &mut self.forms[i];
1226        if let FormType::Weakest | FormType::WeakestRef(_) = f_ty {
1227            *f = form;
1228            *f_ty = FormType::WeakestRef(refed);
1229
1230            if let Some(sender) = SENDER.get() {
1231                sender.send_form_changed().unwrap()
1232            }
1233            for refed in refs_of(self, i) {
1234                self.forms[refed].1 = form;
1235            }
1236
1237            mask_form(name, i, self);
1238        }
1239    }
1240}
1241
1242/// If setting a form with an existing mask suffix, mask its prefix
1243fn mask_form(name: &str, form_i: usize, inner: &mut InnerPalette) {
1244    if inner.masks[0].1.len() < inner.forms.len() {
1245        for (_, remaps) in inner.masks.iter_mut() {
1246            remaps.extend(remaps.len() as u16..inner.forms.len() as u16);
1247        }
1248    }
1249
1250    if let Some((pref, mask)) = name.rsplit_once(".")
1251        && let Some((_, remaps)) = inner.masks.iter_mut().find(|(m, _)| *m == mask)
1252        && let Some(j) = inner.forms.iter().position(|(name, ..)| *name == pref)
1253    {
1254        remaps[j] = form_i as u16;
1255    }
1256}
1257
1258/// A struct to create [`Form`]s from [`RawTag`] in a [`Text`]
1259///
1260/// This [`Painter`] not only prints the [`Form`]s in the [`Text`],
1261/// but within it there is also a "mask". This mask will remap
1262/// [`Form`]s based on suffix, like in the following example:
1263///
1264/// ```rust
1265/// # duat_core::doc_duat!(duat);
1266/// use duat::prelude::*;
1267///
1268/// fn test<W: Widget>(pa: &mut Pass, handle: Handle<W>) {
1269///     // Assume that a Form with the given name exists
1270///     form::set("my_form", Form::red().on_blue());
1271///
1272///     // If I create a second Form like this one, they are separate
1273///     form::set("my_form.suffix", Form::undercurled());
1274///
1275///     *handle.text_mut(pa) =
1276///         txt!("[my_form]This text is red on blue[], [my_form.suffix]undercurled");
1277///
1278///     // But if I enable the "suffix" mask that's at the end of the second Form
1279///     form::enable_mask("suffix");
1280///
1281///     // After doing this, the Text should be printed with the "suffix" mask
1282///     handle.set_mask("suffix");
1283///
1284///     // So when the widget is printed, it'd be equivalent to this:
1285///     *handle.text_mut(pa) =
1286///         txt!("[my_form.suffix]This text is red on blue[], [my_form.suffix]undercurled");
1287/// }
1288/// ```
1289///
1290/// Masks can serve a myriad of different purposes, but here's a
1291/// few:
1292///
1293/// - When you want to temporarily change the [`Form`]s on a single
1294///   [`Widget`]. This is, for example, used in the [`Notifications`]
1295///   [`Widget`], which maps [`Form`]s in order to correspond to the
1296///   [`Level`] of their severity.
1297/// - When you want to have [`Widget`]s change [`Form`] based on
1298///   [hooks], so you could have, for example, an `"inactive"` mask
1299///   for your [`Buffer`]s
1300/// - If you want to quickly cycle through [`Form`]s in a [`Text`],
1301///   this is the most efficient way of doing that, since it relies on
1302///   static remaps, not on changing the [`Form`]s themselves.
1303///
1304/// Do note that no suffix, except `"error"`, `"warn"` and
1305/// `"info"` is a mask when Duat starts. In order to enable more
1306/// masks, see [`enable_mask`].
1307///
1308/// [`Widget`]: crate::ui::Widget
1309/// [`Notifications`]: https://docs.rs/duat/latest/duat
1310/// [`Level`]: crate::context::Level
1311/// [hooks]: crate::hook
1312/// [`Buffer`]: crate::buffer::Buffer
1313/// [`Text`]: crate::text::Text
1314///
1315/// [`RawTag`]: crate::text::RawTag
1316/// [`Text`]: crate::text::Text
1317pub struct Painter {
1318    inner: RwLockReadGuard<'static, InnerPalette>,
1319    mask_i: usize,
1320    default: Form,
1321    forms: Vec<(Form, FormId, u8)>,
1322    set_fg: bool,
1323    set_bg: bool,
1324    set_ul: bool,
1325    reset_attrs: bool,
1326    prev_style: Option<ContentStyle>,
1327}
1328
1329impl Painter {
1330    /// Applies the `Form` with the given `id` and returns the result,
1331    /// given previous triggers.
1332    ///
1333    /// Will return a [`Form`] _relative_ to what the previous
1334    /// [`Form`] was, that is, if the new [`Form`] doesn't include a
1335    /// background, its combination with the other [`Form`]s also
1336    /// won't, since it wasn't changed.
1337    #[inline(always)]
1338    pub fn apply(&mut self, id: FormId, prio: u8) {
1339        let (_, mask) = &self.inner.masks[self.mask_i];
1340        let id = FormId(mask.get(id.0 as usize).copied().unwrap_or(id.0));
1341
1342        let forms = &self.inner.forms;
1343        let form = forms
1344            .get(id.0 as usize)
1345            .map(|(_, f, _)| *f)
1346            .unwrap_or(Form::new().0);
1347
1348        let gt = |(.., p): &&(_, _, u8)| *p > prio;
1349        let i = self.forms.len() - self.forms.iter().rev().take_while(gt).count();
1350        self.forms.insert(i, (form, id, prio));
1351
1352        self.set_fg |= form.fg().is_some();
1353        self.set_bg |= form.bg().is_some();
1354        self.set_ul |= form.ul().is_some();
1355        self.reset_attrs |= form.attrs().has(Attribute::Reset);
1356    }
1357
1358    /// Removes the [`Form`] with the given `id` and returns the
1359    /// result, given previous triggers
1360    #[inline(always)]
1361    pub fn remove(&mut self, id: FormId) {
1362        let mask = &self.inner.masks[self.mask_i].1;
1363        let id = FormId(mask.get(id.0 as usize).copied().unwrap_or(id.0));
1364
1365        let mut applied_forms = self.forms.iter().enumerate();
1366        if let Some((i, &(form, ..))) = applied_forms.rfind(|(_, (_, lhs, _))| *lhs == id) {
1367            self.forms.remove(i);
1368
1369            self.set_fg |= form.fg().is_some();
1370            self.set_bg |= form.bg().is_some();
1371            self.set_ul |= form.ul().is_some();
1372            self.reset_attrs |= !form.attrs().is_empty();
1373        };
1374    }
1375
1376    /// Removes all [`Form`]s except the default one
1377    ///
1378    /// Should be used when a [`ResetState`] part is printed
1379    ///
1380    /// [`ResetState`]: crate::text::Part::ResetState
1381    #[inline(always)]
1382    pub fn reset(&mut self) -> ContentStyle {
1383        self.forms.clear();
1384        self.absolute_style()
1385    }
1386
1387    /// Generates the absolute [`ContentStyle`] to be set
1388    ///
1389    /// This function assumes that all previous styling is not being
1390    /// carried over, i.e., we're styling from scratch.
1391    #[inline(always)]
1392    pub fn absolute_style(&self) -> ContentStyle {
1393        let mut style = self.default.style;
1394
1395        for &(form, ..) in &self.forms {
1396            style.foreground_color = form.fg().or(style.foreground_color);
1397            style.background_color = form.bg().or(style.background_color);
1398            style.underline_color = form.ul().or(style.underline_color);
1399            style.attributes = if form.attrs().has(Attribute::Reset) {
1400                form.attrs()
1401            } else {
1402                form.attrs() | style.attributes
1403            }
1404        }
1405
1406        style
1407    }
1408
1409    /// Generates the relative [`ContentStyle`] to be set
1410    ///
1411    /// This function assumes that previously printed styles are being
1412    /// carried over, influencing this one.
1413    ///
1414    /// You should strive to use this function more than
1415    /// [`absolute_style`], since it "theoretically" should be less
1416    /// work to change just one aspect of the style, rather than
1417    /// replacing the whole thing.
1418    ///
1419    /// [`absolute_style`]: Painter::absolute_style
1420    #[inline(always)]
1421    pub fn relative_style(&mut self) -> Option<ContentStyle> {
1422        let abs_style = self.absolute_style();
1423        let mut style = abs_style;
1424
1425        if style.attributes.has(Attribute::Reset) || self.reset_attrs {
1426            style.attributes.set(Attribute::Reset);
1427        // Only when we are certain that all forms have been
1428        // printed, can we cull unnecessary colors for efficiency
1429        // (this happens most of the time).
1430        } else {
1431            style.foreground_color = self
1432                .set_fg
1433                .then_some(style.foreground_color.unwrap_or(Color::Reset))
1434                .filter(|fg| Some(*fg) != self.prev_style.and_then(|s| s.foreground_color));
1435            style.background_color = self
1436                .set_bg
1437                .then_some(style.background_color.unwrap_or(Color::Reset))
1438                .filter(|bg| Some(*bg) != self.prev_style.and_then(|s| s.background_color));
1439            style.underline_color = self
1440                .set_ul
1441                .then_some(style.underline_color.unwrap_or(Color::Reset))
1442                .filter(|ul| Some(*ul) != self.prev_style.and_then(|s| s.underline_color));
1443        }
1444
1445        self.set_fg = false;
1446        self.set_bg = false;
1447        self.set_ul = false;
1448        self.reset_attrs = false;
1449
1450        if let Some(prev_style) = self.prev_style.replace(abs_style) {
1451            (style != prev_style && style != Default::default()).then_some(style)
1452        } else {
1453            Some(style)
1454        }
1455    }
1456
1457    /// Makes it so the next call to [`relative_style`] returns the
1458    /// same thing as a call to [`absolute_style`]
1459    ///
1460    /// [`relative_style`]: Self::relative_style
1461    /// [`absolute_style`]: Self::absolute_style
1462    pub fn reset_prev_style(&mut self) {
1463        self.prev_style = None;
1464        self.set_fg = true;
1465        self.set_bg = true;
1466        self.set_ul = true;
1467        self.reset_attrs = true;
1468    }
1469
1470    /// Applies the `"caret.main"` [`Form`]
1471    #[inline(always)]
1472    pub fn apply_main_cursor(&mut self) {
1473        self.apply(M_CAR_ID, 100);
1474    }
1475
1476    /// Removes the `"caret.main"` [`Form`]
1477    #[inline(always)]
1478    pub fn remove_main_caret(&mut self) {
1479        self.remove(M_CAR_ID);
1480    }
1481
1482    /// Applies the `"caret.extra"` [`Form`]
1483    #[inline(always)]
1484    pub fn apply_extra_cursor(&mut self) {
1485        self.apply(E_CAR_ID, 100);
1486    }
1487
1488    /// Removes the `"caret.extra"` [`Form`]
1489    #[inline(always)]
1490    pub fn remove_extra_caret(&mut self) {
1491        self.remove(E_CAR_ID);
1492    }
1493
1494    /// The [`Form`] "caret.extra", and its shape.
1495    pub fn main_cursor(&self) -> Option<CursorShape> {
1496        self.inner.main_cursor
1497    }
1498
1499    /// The [`Form`] "caret.extra", and its shape.
1500    pub fn extra_cursor(&self) -> Option<CursorShape> {
1501        self.inner.extra_cursor
1502    }
1503
1504    /// The `"default"` form's [`Form`]
1505    pub fn get_default(&self) -> Form {
1506        self.default
1507    }
1508}
1509
1510pub(crate) fn set_sender(sender: DuatSender) {
1511    SENDER
1512        .set(sender)
1513        .unwrap_or_else(|_| panic!("Sender set more than once"));
1514}
1515
1516/// An enum that helps in the modification of forms
1517#[derive(Clone)]
1518enum FormType {
1519    Normal,
1520    Ref(usize),
1521    Weakest,
1522    WeakestRef(usize),
1523}
1524
1525/// The position of each form that eventually references the `n`th
1526fn refs_of(inner: &InnerPalette, refed: usize) -> Vec<usize> {
1527    let mut refs = Vec::new();
1528    for (i, (.., f_ty)) in inner.forms.iter().enumerate() {
1529        if let FormType::Ref(ref_id) | FormType::WeakestRef(ref_id) = f_ty
1530            && *ref_id == refed
1531        {
1532            refs.push(i);
1533            refs.extend(refs_of(inner, i));
1534        }
1535    }
1536    refs
1537}
1538
1539/// If form references would eventually lead to a loop
1540fn would_be_circular(inner: &InnerPalette, referee: usize, refed: usize) -> bool {
1541    if let (.., FormType::Ref(refed_ref) | FormType::WeakestRef(refed_ref)) = inner.forms[refed] {
1542        match refed_ref == referee {
1543            true => true,
1544            false => would_be_circular(inner, referee, refed_ref),
1545        }
1546    } else {
1547        false
1548    }
1549}
1550
1551fn position_and_form(
1552    forms: &mut Vec<(&str, Form, FormType)>,
1553    name: impl AsRef<str>,
1554) -> (usize, Form) {
1555    let name = name.as_ref();
1556    if let Some((i, (_, form, _))) = forms.iter().enumerate().find(|(_, (lhs, ..))| *lhs == name) {
1557        (i, *form)
1558    } else if let Some((refed, _)) = name.rsplit_once('.') {
1559        let (i, form) = position_and_form(forms, refed);
1560        forms.push((name.to_string().leak(), form, FormType::WeakestRef(i)));
1561        (forms.len() - 1, form)
1562    } else {
1563        forms.push((name.to_string().leak(), Form::new().0, FormType::Weakest));
1564        (forms.len() - 1, Form::new().0)
1565    }
1566}
1567
1568/// Converts a string to a color, supporst hex, RGB and HSL
1569const fn str_to_color(str: &str) -> std::result::Result<Color, &'static str> {
1570    const fn strip_prefix<'a>(prefix: &str, str: &'a str) -> Option<&'a str> {
1571        let prefix = prefix.as_bytes();
1572
1573        let mut i = 0;
1574        while i < prefix.len() {
1575            if str.as_bytes()[i] != prefix[i] {
1576                return None;
1577            }
1578            i += 1;
1579        }
1580
1581        Some(str.split_at(prefix.len()).1)
1582    }
1583    const fn strip_suffix<'a>(suffix: &str, str: &'a str) -> Option<&'a str> {
1584        let prefix = suffix.as_bytes();
1585
1586        let mut i = str.len() - 1;
1587        while i >= str.len() - prefix.len() {
1588            if str.as_bytes()[i] != prefix[i - (str.len() - prefix.len())] {
1589                return None;
1590            }
1591            i += 1;
1592        }
1593
1594        Some(str.split_at(str.len() - suffix.len()).0)
1595    }
1596    const fn split_space(str: &str) -> Option<(&str, &str)> {
1597        if str.is_empty() {
1598            return None;
1599        }
1600
1601        let mut i = 0;
1602        while i < str.len() {
1603            if str.as_bytes()[i] == b' ' {
1604                break;
1605            }
1606            i += 1;
1607        }
1608
1609        let (cut, rest) = str.split_at(i);
1610        let (_, rest) = rest.split_at(if rest.is_empty() { 0 } else { 1 });
1611        Some((cut, rest))
1612    }
1613    const fn hue_to_rgb(p: f32, q: f32, mut t: f32) -> f32 {
1614        t = if t < 0.0 { t + 1.0 } else { t };
1615        t = if t > 1.0 { t - 1.0 } else { t };
1616        if t < 1.0 / 6.0 {
1617            p + (q - p) * 6.0 * t
1618        } else if t < 1.0 / 2.0 {
1619            q
1620        } else if t < 2.0 / 3.0 {
1621            p + (q - p) * (2.0 / 3.0 - t) * 6.0
1622        } else {
1623            p
1624        }
1625    }
1626
1627    // Expects "#{red:x}{green:x}{blue:x}"
1628    if let Some(hex) = strip_prefix("#", str) {
1629        let total = match u32::from_str_radix(hex, 16) {
1630            Ok(total) if hex.len() == 6 => total,
1631            _ => return Err("Hexcode does not contain 6 hex values"),
1632        };
1633        let r = (total >> 16) as u8;
1634        let g = (total >> 8) as u8;
1635        let b = total as u8;
1636
1637        Ok(Color::Rgb { r, g, b })
1638        // Expects "rgb {red} {green} {blue}"
1639    } else if let Some(mut rgb) = strip_prefix("rgb ", str) {
1640        let mut values = [0, 0, 0];
1641        let mut i = 0;
1642
1643        while i < values.len() {
1644            if let Some((cut, rest)) = split_space(rgb) {
1645                rgb = rest;
1646                values[i] = match u8::from_str_radix(cut, 10) {
1647                    Ok(value) => value,
1648                    Err(_) => return Err("Rgb format value could not be parsed"),
1649                }
1650            } else {
1651                return Err("Missing value in rgb format");
1652            }
1653            i += 1;
1654        }
1655
1656        let [r, g, b] = values;
1657        Ok(Color::Rgb { r, g, b })
1658        // Expects "hsl {hue%?} {saturation%?} {lightness%?}"
1659    } else if let Some(mut hsl) = strip_prefix("hsl ", str) {
1660        let mut values = [0.0, 0.0, 0.0];
1661        let mut i = 0;
1662        while i < values.len() {
1663            if let Some((cut, rest)) = split_space(hsl) {
1664                hsl = rest;
1665                let (num, div) = match strip_suffix("%", cut) {
1666                    Some(perc) => (perc, 100),
1667                    None => (cut, 255),
1668                };
1669                values[i] = match u8::from_str_radix(num, 10) {
1670                    Ok(value) if value <= div => value as f32 / div as f32,
1671                    _ => return Err("Hsl format property could not be parsed"),
1672                }
1673            } else {
1674                return Err("Missing value in hsl format");
1675            }
1676            i += 1;
1677        }
1678        let [hue, sat, lit] = values;
1679
1680        let (r, g, b) = if sat == 0.0 {
1681            (lit, lit, lit)
1682        } else {
1683            let q = if lit < 0.5 {
1684                lit * (1.0 + sat)
1685            } else {
1686                lit + sat - lit * sat
1687            };
1688            let p = 2.0 * lit - q;
1689            let r = hue_to_rgb(p, q, hue + 1.0 / 3.0);
1690            let g = hue_to_rgb(p, q, hue);
1691            let b = hue_to_rgb(p, q, hue - 1.0 / 3.0);
1692            (r, g, b)
1693        };
1694
1695        // + 0.5 because `as` rounds floats down.
1696        let r = (0.5 + r * 255.0) as u8;
1697        let g = (0.5 + g * 255.0) as u8;
1698        let b = (0.5 + b * 255.0) as u8;
1699        Ok(Color::Rgb { r, g, b })
1700    } else {
1701        Err("Color format was not recognized")
1702    }
1703}
1704
1705impl std::fmt::Debug for Form {
1706    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1707        struct DebugColor(Option<Color>);
1708        impl std::fmt::Debug for DebugColor {
1709            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1710                match self.0 {
1711                    Some(Color::Rgb { r, g, b }) => write!(f, "Some(Rgb({r}, {g}, {b}))"),
1712                    Some(Color::AnsiValue(ansi)) => write!(f, "Some(Ansi({ansi}))"),
1713                    Some(color) => write!(f, "Some({color:?})"),
1714                    None => f.write_str("None"),
1715                }
1716            }
1717        }
1718
1719        struct DebugAttributes(Attributes);
1720        impl std::fmt::Debug for DebugAttributes {
1721            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1722                if self.0.is_empty() {
1723                    f.write_str("None")
1724                } else {
1725                    let mut is_first = true;
1726                    for attr in Attribute::iterator() {
1727                        if self.0.has(attr) {
1728                            if !is_first {
1729                                f.write_str(" | ")?;
1730                            }
1731                            is_first = false;
1732                            write!(f, "{attr:?}")?;
1733                        }
1734                    }
1735                    Ok(())
1736                }
1737            }
1738        }
1739
1740        f.debug_struct("Form")
1741            .field("fg", &DebugColor(self.style.foreground_color))
1742            .field("bg", &DebugColor(self.style.background_color))
1743            .field("ul", &DebugColor(self.style.underline_color))
1744            .field("attr", &DebugAttributes(self.style.attributes))
1745            .finish()
1746    }
1747}
1748
1749impl std::fmt::Debug for InnerPalette {
1750    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1751        struct DebugForms<'a>(&'a [(&'static str, Form, FormType)]);
1752        impl std::fmt::Debug for DebugForms<'_> {
1753            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1754                if f.alternate() {
1755                    f.write_str("[\n")?;
1756                    let max = self.0.len().ilog10() as usize + 3;
1757                    for (n, (name, form, ty)) in self.0.iter().enumerate() {
1758                        let num = format!("{n}:");
1759                        writeln!(f, "{num:<max$}({name}, {ty:?}, {form:#?})")?;
1760                    }
1761                    f.write_str("]")
1762                } else {
1763                    write!(f, "{:?}", self.0)
1764                }
1765            }
1766        }
1767
1768        struct DebugCursorShape(CursorShape);
1769        impl std::fmt::Debug for DebugCursorShape {
1770            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1771                match self.0 {
1772                    CursorShape::DefaultUserShape => f.write_str("DefaultUserShape"),
1773                    CursorShape::BlinkingBlock => f.write_str("BlinkingBlock"),
1774                    CursorShape::SteadyBlock => f.write_str("SteadyBlock"),
1775                    CursorShape::BlinkingUnderScore => f.write_str("BlinkingUnderScore"),
1776                    CursorShape::SteadyUnderScore => f.write_str("SteadyUnderScore"),
1777                    CursorShape::BlinkingBar => f.write_str("BlinkingBar"),
1778                    CursorShape::SteadyBar => f.write_str("SteadyBar"),
1779                }
1780            }
1781        }
1782
1783        f.debug_struct("InnerPalette")
1784            .field("main_cursor", &self.main_cursor.map(DebugCursorShape))
1785            .field("extra_cursor", &self.extra_cursor.map(DebugCursorShape))
1786            .field("forms", &DebugForms(&self.forms))
1787            .field("masks", &self.masks)
1788            .finish()
1789    }
1790}
1791
1792impl std::fmt::Debug for FormType {
1793    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1794        match self {
1795            Self::Normal => write!(f, "Normal"),
1796            Self::Ref(refed) => write!(f, "Ref({refed})"),
1797            Self::Weakest => write!(f, "Weakest"),
1798            Self::WeakestRef(refed) => write!(f, "WeakestRef({refed})"),
1799        }
1800    }
1801}
1802
1803/// Mimics [`ContentStyle`] methods for the [`Form`] type
1804macro mimic_method_new {
1805    (#[$attr:meta] $method:ident $attrib:expr) => {
1806        /// New [`Form`] with the
1807        #[$attr]
1808        /// attribute
1809        pub const fn $method() -> BuiltForm {
1810            let mut built = Form::new();
1811            built.0.style.attributes = built.0.style.attributes.with($attrib);
1812            built
1813        }
1814    },
1815
1816    (#[$attr:meta] $fg:ident $bg:ident $ul:ident $color:expr) => {
1817        /// New [`Form`] with a
1818        #[$attr]
1819        /// foreground
1820        pub const fn $fg() -> BuiltForm {
1821            let mut built = Form::new();
1822            built.0.style.foreground_color = Some($color);
1823            built
1824        }
1825
1826        /// New [`Form`] with a
1827        #[$attr]
1828        /// background
1829        pub const fn $bg() -> BuiltForm {
1830            let mut built = Form::new();
1831            built.0.style.background_color = Some($color);
1832            built
1833        }
1834
1835        /// New [`Form`] with a
1836        #[$attr]
1837        /// underlining
1838        ///
1839        /// Do note that this feature may not be supported in all `Ui`s,
1840        /// for example, various terminals don't support this feature,
1841        /// since it is a part of the kitty protocol, and hasn't been
1842        /// universally accepted yet.
1843        ///
1844        /// `Ui`: crate::ui::traits::RawUi
1845        pub const fn $ul() -> BuiltForm {
1846            let mut built = Form::new();
1847            built.0.style.underline_color = Some($color);
1848            built
1849        }
1850    }
1851}
1852
1853macro mimic_method_mod {
1854    (#[$attr:meta] $method:ident $attrib:expr) => {
1855        /// Applies the
1856        #[$attr]
1857        /// attribute to this [`Form`]
1858        pub const fn $method(mut self) -> Self {
1859            self.0.style.attributes = self.0.style.attributes.with($attrib);
1860            self
1861        }
1862    },
1863
1864    (#[$attr:meta] $fg:ident $bg:ident $ul:ident $color:expr) => {
1865        /// Turns the foreground of this [`Form`]
1866        #[$attr]
1867        pub const fn $fg(mut self) -> Self {
1868            self.0.style.foreground_color = Some($color);
1869            self
1870        }
1871
1872        /// Turns the background of this [`Form`]
1873        #[$attr]
1874        pub const fn $bg(mut self) -> Self {
1875            self.0.style.background_color = Some($color);
1876            self
1877        }
1878
1879        /// Turns the underlining of this [`Form`]
1880        #[$attr]
1881        ///
1882        /// Do note that this feature may not be supported in all `Ui`s,
1883        /// for example, various terminals don't support this feature,
1884        /// since it is a part of the kitty protocol, and hasn't been
1885        /// universally accepted yet.
1886        ///
1887        /// `Ui`: crate::ui::traits::RawUi
1888        pub const fn $ul(mut self) -> Self {
1889            self.0.style.underline_color = Some($color);
1890            self
1891        }
1892    }
1893}