duat_core/
form.rs

1//! Utilities for stylizing the text of Duat
2use std::sync::{LazyLock, OnceLock};
3
4use FormType::*;
5use crossterm::style::{Attribute, Attributes, ContentStyle};
6pub use crossterm::{cursor::SetCursorStyle as CursorShape, style::Color};
7use parking_lot::{RwLock, RwLockWriteGuard};
8
9pub use self::global::{
10    FormFmt, add_colorscheme, extra_cursor, from_id, id_of, inner_to_id, main_cursor, name_of,
11    painter, set, set_colorscheme, set_extra_cursor, set_main_cursor, set_many, set_weak,
12    unset_extra_cursor, unset_main_cursor,
13};
14pub(crate) use self::global::{colorscheme_exists, exists};
15use crate::{
16    data::RwLockReadGuard,
17    hooks::{self, FormSet},
18    ui::Sender,
19};
20
21pub trait ColorScheme: Send + Sync + 'static {
22    fn apply(&self);
23
24    fn name(&self) -> &'static str;
25}
26
27static SENDER: OnceLock<Sender> = OnceLock::new();
28static BASE_FORMS: &[(&str, Form, FormType)] = &[
29    ("Default", Form::new().0, Normal),
30    ("Accent", Form::bold().0, Normal),
31    ("DefaultOk", Form::blue().0, Normal),
32    ("AccentOk", Form::cyan().0, Normal),
33    ("DefaultErr", Form::red().0, Normal),
34    ("AccentErr", Form::red().bold().0, Normal),
35    ("DefaultHint", Form::grey().0, Normal),
36    ("AccentHint", Form::grey().bold().0, Normal),
37    ("MainCursor", Form::reverse().0, Normal),
38    ("ExtraCursor", Form::reverse().0, Ref(M_CUR_ID)),
39    ("MainSelection", Form::on_dark_grey().0, Normal),
40    ("ExtraSelection", Form::on_dark_grey().0, Ref(M_SEL_ID)),
41    ("Inactive", Form::grey().0, Normal),
42    // Tree sitter Forms
43    ("type", Form::yellow().italic().0, Normal),
44    ("type.builtin", Form::yellow().reset().0, Normal),
45    ("function", Form::blue().reset().0, Normal),
46    ("comment", Form::grey().0, Normal),
47    ("comment.documentation", Form::grey().bold().0, Normal),
48    ("punctuation.bracket", Form::red().0, Normal),
49    ("punctuation.delimiter", Form::cyan().0, Normal),
50    ("constant", Form::grey().0, Normal),
51    ("constant.builtin", Form::dark_yellow().0, Normal),
52    ("variable.parameter", Form::italic().0, Normal),
53    ("variable.builtin", Form::dark_yellow().0, Normal),
54    ("label", Form::green().0, Normal),
55    ("keyword", Form::magenta().0, Normal),
56    ("string", Form::green().0, Normal),
57    ("escape", Form::dark_yellow().0, Normal),
58    ("attribute", Form::magenta().0, Normal),
59    ("operator", Form::cyan().0, Normal),
60    ("constructor", Form::yellow().0, Normal),
61    ("module", Form::blue().italic().0, Normal),
62    // Markup Forms
63    ("markup", Form::new().0, Ref(DEFAULT_ID)),
64    ("markup.strong", Form::cyan().bold().0, Normal),
65    ("markup.italic", Form::cyan().italic().0, Normal),
66    ("markup.strikethrough", Form::cyan().crossed_out().0, Normal),
67    (
68        "markup.underline",
69        Form::underline_cyan().underlined().0,
70        Normal,
71    ),
72    ("markup.heading", Form::blue().bold().0, Normal),
73    ("markup.math", Form::yellow().0, Normal),
74    ("markup.quote", Form::grey().italic().0, Normal),
75    ("markup.link", Form::blue().underlined().0, Normal),
76    ("markup.raw", Form::cyan().0, Normal),
77    ("markup.list", Form::yellow().0, Normal),
78    ("markup.list.checked", Form::green().0, Normal),
79    ("markup.list.unchecked", Form::grey().0, Normal),
80];
81
82/// The functions that will be exposed for public use.
83mod global {
84    use std::sync::LazyLock;
85
86    use parking_lot::Mutex;
87
88    use super::{BASE_FORMS, BuiltForm, ColorScheme, CursorShape, Form, FormId, Painter, Palette};
89    use crate::{
90        hooks::{self, ColorSchemeSet},
91        text::err,
92    };
93
94    static PALETTE: Palette = Palette::new();
95    static FORMS: LazyLock<Mutex<Vec<&str>>> =
96        LazyLock::new(|| Mutex::new(BASE_FORMS.iter().map(|(name, ..)| *name).collect()));
97    static COLORSCHEMES: LazyLock<Mutex<Vec<Box<dyn ColorScheme>>>> = LazyLock::new(Mutex::default);
98
99    /// Either a [`Form`] or a name of a form
100    ///
101    /// Note that the referenced form does not need to exist for
102    /// [`form::set`] or [`form::set_weak`] to work properly.
103    ///
104    /// [`form::set`]: set
105    /// [`form::set_weak`]: set_weak
106    pub trait FormFmt: InnerFormFmt {}
107    impl FormFmt for Form {}
108    impl FormFmt for BuiltForm {}
109    impl FormFmt for &str {}
110    impl FormFmt for &mut str {}
111    impl FormFmt for String {}
112
113    /// Sets the [`Form`] by the name of `name`
114    ///
115    /// This will create a new form or replace one that already
116    /// exists, and you can either set it to a [`Form`] directly, or
117    /// reference another form by its name:
118    ///
119    /// ```rust
120    /// # use duat_core::form::{self, Form};
121    /// // Creates a regular form
122    /// form::set("MyRegularForm", Form::red());
123    /// // Creates a form that references the first
124    /// form::set("MyRefForm", "MyRegularForm");
125    /// // Sets both "MyRegularForm" and "MyRefForm" to blue
126    /// form::set("MyRegularForm", Form::blue());
127    /// ```
128    ///
129    /// If you are creating a plugin, or another kind of tool for
130    /// others using Duat, use [`form::set_weak`] instead of this
131    /// function.
132    ///
133    /// [`form::set_weak`]: set_weak
134    pub fn set(name: impl ToString, form: impl FormFmt) -> FormId {
135        let kind = form.kind();
136        let name: &'static str = name.to_string().leak();
137
138        match kind {
139            Kind::Form(form) => crate::thread::queue(move || PALETTE.set_form(name, form)),
140            Kind::Ref(refed) => crate::thread::queue(move || PALETTE.set_ref(name, refed)),
141        }
142
143        let mut forms = FORMS.lock();
144        if let Kind::Ref(refed) = kind
145            && !forms.contains(&refed)
146        {
147            forms.push(refed);
148        }
149
150        if let Some(id) = forms.iter().position(|form| *form == name) {
151            FormId(id as u16)
152        } else {
153            forms.push(name);
154            FormId(forms.len() as u16 - 1)
155        }
156    }
157
158    /// Sets a form, "weakly"
159    ///
160    /// The difference between this function and [`form::set`] is
161    /// that this function will only trigger if the form didn't
162    /// already exist.
163    ///
164    /// This is useful for plugins, since it prioritizes the user's
165    /// preferences, no matter in what order this function and
166    /// [`form::set`] are called:
167    ///
168    /// ```rust
169    /// use duat_core::form::{self, Form};
170    /// // Creates a form
171    /// form::set_weak("WeakForm", Form::blue().on_white());
172    /// // Creates a form "strongly"
173    /// form::set("StrongForm", Form::new().bold());
174    /// // Does nothing
175    /// form::set_weak("StrongForm", "Default");
176    /// ```
177    ///
178    /// [`form::set`]: set
179    pub fn set_weak(name: impl ToString, form: impl FormFmt) -> FormId {
180        let kind = form.kind();
181        let name: &'static str = name.to_string().leak();
182
183        match kind {
184            Kind::Form(form) => crate::thread::queue(move || PALETTE.set_weak_form(name, form)),
185            Kind::Ref(refed) => crate::thread::queue(move || PALETTE.set_weak_ref(name, refed)),
186        }
187
188        let mut forms = FORMS.lock();
189        if let Kind::Ref(refed) = kind
190            && !forms.contains(&refed)
191        {
192            forms.push(refed);
193        }
194
195        if let Some(id) = forms.iter().position(|form| *form == name) {
196            FormId(id as u16)
197        } else {
198            forms.push(name);
199            FormId(forms.len() as u16 - 1)
200        }
201    }
202
203    /// Returns a [`Form`], given a [`FormId`].
204    ///
205    /// If you are thinking of using this for printing purposes,
206    /// consider using [`form::painter`] instead.
207    ///
208    /// [`form::painter`]: painter
209    pub fn from_id(id: FormId) -> Form {
210        PALETTE.form_from_id(id).unwrap_or(Form::new().0)
211    }
212
213    /// The name of a form, given a [`FormId`]
214    pub fn name_of(id: FormId) -> &'static str {
215        PALETTE.name_from_id(id)
216    }
217
218    /// The current main cursor, with the `"MainCursor"` [`Form`]
219    pub fn main_cursor() -> (Form, Option<CursorShape>) {
220        PALETTE.main_cursor()
221    }
222
223    /// The current extra cursor, with the `"ExtraCursor"` [`Form`]
224    pub fn extra_cursor() -> (Form, Option<CursorShape>) {
225        PALETTE.extra_cursor()
226    }
227
228    /// Sets the main cursor's [shape]
229    ///
230    /// Cursors in Duat can either be a distinct [shape], or can be
231    /// defined as a [`Form`], just like the rest of the styling.
232    ///
233    /// This is done because some UIs (like a terminal) lack the
234    /// ability to show multiple cursors, so extra cursors are usually
235    /// printed as solid blocks with a background color.
236    ///
237    /// If you want to set the cursor's color, do something like this:
238    ///
239    /// ```rust
240    /// # use duat_core::form::{self, Form, Color};
241    /// form::set("MainCursor", Form::black().on("rgb 240 210 200"));
242    /// ```
243    ///
244    /// However, if possible, Duat will still try to use the main
245    /// cursor's [shape]. If you don't want that to happen, see
246    /// [`form::unset_main_cursor`].
247    ///
248    /// [shape]: CursorShape
249    /// [`form::unset_main_cursor`]: unset_main_cursor
250    pub fn set_main_cursor(shape: CursorShape) {
251        crate::thread::queue(move || PALETTE.set_main_cursor(shape));
252    }
253
254    /// Sets extra cursors's [shape]s
255    ///
256    /// Cursors in Duat can either be a distinct [shape], or can be
257    /// defined as a [`Form`], just like the rest of the styling.
258    ///
259    /// This is done because some UIs (like a terminal) lack the
260    /// ability to show multiple cursors, so extra cursors are usually
261    /// printed as solid blocks with a background color.
262    ///
263    /// If you want to set the cursor's color, do something like this:
264    ///
265    /// ```rust
266    /// # use duat_core::form::{self, Form, Color};
267    /// form::set("ExtraCursor", Form::black().on_cyan());
268    /// ```
269    ///
270    /// However, if possible, Duat will still try to use the main
271    /// cursor's [shape]. If you don't want that to happen, see
272    /// [`form::unset_extra_cursor`].
273    ///
274    /// [shape]: CursorShape
275    /// [`form::unset_extra_cursor`]: unset_extra_cursor
276    pub fn set_extra_cursor(shape: CursorShape) {
277        crate::thread::queue(move || PALETTE.set_extra_cursor(shape));
278    }
279
280    /// Removes the main cursor's [shape]
281    ///
282    /// By doing this, you will force Duat to draw the main cursor by
283    /// use of the `"MainCursor"` form.
284    ///
285    /// If you want to set the [shape] instead, see
286    /// [`form::set_main_cursor`].
287    ///
288    /// [shape]: CursorShape
289    /// [`form::set_main_cursor`]: set_main_cursor
290    pub fn unset_main_cursor() {
291        crate::thread::queue(move || PALETTE.unset_main_cursor());
292    }
293
294    /// Removes extra cursors's [shape]s
295    ///
296    /// By doing this, you will force Duat to draw the extra cursor by
297    /// use of the `"MainCursor"` form. Do note however that, in
298    /// something like a terminal, extra cursors would never be
299    /// printed as a [shape] anyways, since terminals can only
300    /// print one cursor at a time.
301    ///
302    /// If you want to set the [shape] instead, see
303    /// [`form::set_extra_cursor`].
304    ///
305    /// [shape]: CursorShape
306    /// [`form::set_extra_cursor`]: set_extra_cursor
307    pub fn unset_extra_cursor() {
308        crate::thread::queue(move || PALETTE.unset_extra_cursor());
309    }
310
311    /// A [`Painter`] for coloring text efficiently
312    ///
313    /// This function will be used primarily when printing widgets to
314    /// the screen, which is something that only [`Ui`]s should be
315    /// doing.
316    ///
317    /// One thing to note is that a [`Painter`] will lock the form
318    /// palette while it is being used. This means that, if a form is
319    /// changed while a widget is in the middle of printing, the
320    /// printed form will be the old version, not the new one. Only
321    /// after said widget is done printing will the new form come into
322    /// effect.
323    ///
324    /// [`Ui`]: crate::ui::Ui
325    pub fn painter() -> Painter {
326        PALETTE.painter()
327    }
328
329    /// Returns the [`FormId`] from the name of a [`Form`]
330    ///
331    /// You can also pass multiple names, in order to get a list of
332    /// ids.
333    ///
334    /// # Note
335    ///
336    /// This is a macro because, in order to be as efficient as
337    /// possible, it is better to store this value inside of a
338    /// static variable, since it is guaranteed to not change. This
339    /// way, you only have to figure it out once, and it is much
340    /// faster than with a [`HashMap`] (how this is usually done).
341    ///
342    /// [`HashMap`]: std::collections::HashMap
343    pub macro id_of {
344        ($form:expr) => {{
345            static ID: LazyLock<FormId> = LazyLock::new(|| inner_to_id($form));
346            *ID
347        }},
348        ($($form:expr),+) => {{
349            static IDS: LazyLock<&[FormId]> = LazyLock::new(|| {
350    			let mut ids = Vec::new();
351    			$(
352        			ids.push(inner_to_id($form));
353    			)+
354    			ids.leak()
355			});
356			*ID
357        }}
358    }
359
360    /// Returns the [`FormId`] of the form's name
361    pub fn inner_to_id(name: impl ToString) -> FormId {
362        let name: &'static str = name.to_string().leak();
363
364        crate::thread::queue(move || PALETTE.id_from_name(name));
365
366        let mut forms = FORMS.lock();
367        if let Some(id) = forms.iter().position(|form| *form == name) {
368            FormId(id as u16)
369        } else {
370            forms.push(name);
371            FormId(forms.len() as u16 - 1)
372        }
373    }
374
375    /// Adds a [`ColorScheme`] to the list of colorschemes
376    ///
377    /// This [`ColorScheme`] can then be added via
378    /// [`form::set_colorscheme`] or through the command
379    /// `colorscheme`.
380    ///
381    /// If a [`ColorScheme`] of the same name was already added, it
382    /// will be overritten.
383    ///
384    /// [`form::set_colorscheme`]: set_colorscheme
385    pub fn add_colorscheme(cs: impl ColorScheme) {
386        let mut colorschemes = COLORSCHEMES.lock();
387        let name = cs.name();
388        if let Some(i) = colorschemes.iter().position(|cs| cs.name() == name) {
389            colorschemes[i] = Box::new(cs);
390        } else {
391            colorschemes.push(Box::new(cs));
392        }
393    }
394
395    /// Applies a [`ColorScheme`]
396    ///
397    /// This [`ColorScheme`] must've first been added via
398    /// [`form::add_colorscheme`].
399    ///
400    /// [`form::add_colorscheme`]: add_colorscheme
401    pub fn set_colorscheme(name: &str) {
402        let colorschemes = COLORSCHEMES.lock();
403        if let Some(cs) = colorschemes.iter().find(|cs| cs.name() == name) {
404            cs.apply();
405            hooks::trigger::<ColorSchemeSet>(cs.name());
406        } else {
407            crate::context::notify(err!("The colorscheme " [*a] name [] " was not found"));
408        }
409    }
410
411    /// Calls [`form::set`] on each tuple in the list
412    ///
413    /// [`form::set`]: set
414    pub macro set_many($(($name:literal, $form:expr)),+ $(,)?) {{
415        $(
416            set($name, $form);
417        )+
418    }}
419
420    /// Wether or not a specific [`Form`] has been set
421    pub(crate) fn exists(name: &str) -> bool {
422        FORMS.lock().contains(&name)
423    }
424
425    /// Wether or not a specific [`ColorScheme`] was added
426    pub(crate) fn colorscheme_exists(name: &str) -> bool {
427        COLORSCHEMES.lock().iter().any(|cs| cs.name() == name)
428    }
429
430    /// A kind of [`Form`]
431    #[derive(Clone, Copy)]
432    enum Kind {
433        Form(Form),
434        Ref(&'static str),
435    }
436
437    /// So [`Form`]s and [`impl ToString`]s are arguments for [`set`]
438    ///
439    /// [`impl ToString`]: ToString
440    trait InnerFormFmt {
441        /// The kind of [`Form`] that this type represents
442        fn kind(self) -> Kind;
443    }
444
445    impl InnerFormFmt for Form {
446        fn kind(self) -> Kind {
447            Kind::Form(self)
448        }
449    }
450
451    impl InnerFormFmt for BuiltForm {
452        fn kind(self) -> Kind {
453            Kind::Form(self.0)
454        }
455    }
456
457    impl InnerFormFmt for &str {
458        fn kind(self) -> Kind {
459            Kind::Ref(self.to_string().leak())
460        }
461    }
462
463    impl InnerFormFmt for &mut str {
464        fn kind(self) -> Kind {
465            Kind::Ref(self.to_string().leak())
466        }
467    }
468
469    impl InnerFormFmt for String {
470        fn kind(self) -> Kind {
471            Kind::Ref(self.leak())
472        }
473    }
474}
475
476/// An identifier of a [`Form`]
477///
478/// This struct is always going to point to the same form, since those
479/// cannot be destroyed.
480///
481/// The main use for keeping these things directly is in order to
482/// modify a file's text in an efficient manner, by adding tags
483/// directly, instead of using a macro like [`text!`]
484///
485/// [`text!`]: crate::text::text
486#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
487pub struct FormId(u16);
488
489impl FormId {
490    /// The internal id of the [`FormId`]
491    ///
492    /// This may be useful in certain situations.
493    pub fn to_u16(self) -> u16 {
494        self.0
495    }
496}
497
498impl std::fmt::Debug for FormId {
499    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
500        write!(f, "FormId({})", name_of(*self))
501    }
502}
503
504/// A style for text.
505#[derive(Clone, Copy, Debug, PartialEq, Eq)]
506pub struct Form {
507    pub style: ContentStyle,
508}
509
510#[rustfmt::skip]
511impl Form {
512    mimic_method_new!(/**bold*/ bold Attribute::Bold);
513    mimic_method_new!(/**dim*/ dim Attribute::Dim);
514    mimic_method_new!(/**italic*/ italic Attribute::Italic);
515    mimic_method_new!(/**underlined*/ underlined Attribute::Underlined);
516    mimic_method_new!(/**double_underlined*/ double_underlined Attribute::DoubleUnderlined);
517    mimic_method_new!(/**undercurled*/ undercurled Attribute::Undercurled);
518    mimic_method_new!(/**underdashed*/ underdashed Attribute::Underdashed);
519    mimic_method_new!(/**reverse*/ reverse Attribute::Reverse);
520    mimic_method_new!(/**crossed_out*/ crossed_out Attribute::CrossedOut);
521    mimic_method_new!(/**black*/ black on_black underline_black Color::Black);
522    mimic_method_new!(/**dark_grey*/ dark_grey on_dark_grey underline_dark_grey Color::DarkGrey);
523    mimic_method_new!(/**red*/ red on_red underline_red Color::Red);
524    mimic_method_new!(/**dark_red*/ dark_red on_dark_red underline_dark_red Color::DarkRed);
525    mimic_method_new!(/**green*/ green on_green underline_green Color::Green);
526    mimic_method_new!(
527        /**dark_green*/ dark_green on_dark_green underline_dark_green Color::DarkGreen
528    );
529    mimic_method_new!(/**yellow*/ yellow on_yellow underline_yellow Color::Yellow);
530    mimic_method_new!(
531        /**dark_yellow*/ dark_yellow on_dark_yellow underline_dark_yellow Color::DarkYellow
532    );
533    mimic_method_new!(/**blue*/ blue on_blue underline_blue Color::Blue);
534    mimic_method_new!(/**dark_blue*/ dark_blue on_dark_blue underline_dark_blue Color::DarkBlue);
535    mimic_method_new!(/**magenta*/ magenta on_magenta underline_magenta Color::Magenta);
536    mimic_method_new!(
537        /**dark_magenta*/ dark_magenta on_dark_magenta underline_dark_magenta Color::DarkMagenta
538    );
539    mimic_method_new!(/**cyan*/ cyan on_cyan underline_cyan Color::Cyan);
540    mimic_method_new!(/**dark_cyan*/ dark_cyan on_dark_cyan underline_dark_cyan Color::DarkCyan);
541    mimic_method_new!(/**white*/ white on_white underline_white Color::White);
542    mimic_method_new!(/**grey*/ grey on_grey underline_grey Color::Grey);
543}
544
545impl Form {
546    /// Returns a new [`Form`] with a default style
547    ///
548    /// This method actually returns [`BuiltForm`]
549    #[allow(clippy::new_ret_no_self)]
550    pub const fn new() -> BuiltForm {
551        let style = ContentStyle {
552            foreground_color: None,
553            background_color: None,
554            underline_color: None,
555            attributes: Attributes::none(),
556        };
557        BuiltForm(Self { style })
558    }
559
560    /// Returns a new [`Form`] with the [`Reset`] attribute
561    ///
562    /// In Duat, the [`Reset`] attribute should remove only other
563    /// [`Attribute`]s, not any of the colors in use.
564    ///
565    /// [`Reset`]: Attribute::Reset
566    pub const fn reset() -> BuiltForm {
567        let mut built = Form::new();
568        built.0.style.attributes = built.0.style.attributes.with(Attribute::Reset);
569        built
570    }
571
572    /// New [`Form`] with a colored foreground
573    ///
574    /// This function accepts three color formats:
575    ///
576    /// - A hexcode, like `"#abcdef"`, capitalization is ignored;
577    /// - Three rgb values, like `"rgb 123 456 789"`;
578    /// - Three hsl values, like `"hsl {hue} {sat} {lit}"`, where
579    ///   {hue}, {sat} and {lit} can either be a number from `0..255`,
580    ///   or a percentage, followed by `'%'`, e.g. `"hsl 234 50% 42"`.
581    pub const fn with(str: &str) -> BuiltForm {
582        let mut built = Form::new();
583        built.0.style.foreground_color = match str_to_color(str) {
584            Ok(color) => Some(color),
585            Err(err) => panic!("{}", err),
586        };
587        built
588    }
589
590    /// New [`Form`] with a colored background
591    ///
592    /// This function accepts three color formats:
593    ///
594    /// - A hexcode, like `"#abcdef"`, capitalization is ignored;
595    /// - Three rgb values, like `"rgb 123 456 789"`;
596    /// - Three hsl values, like `"hsl {hue} {sat} {lit}"`, where
597    ///   {hue}, {sat} and {lit} can either be a number from `0..255`,
598    ///   or a percentage, followed by `'%'`, e.g. `"hsl 234 50% 42"`.
599    pub const fn on(str: &str) -> BuiltForm {
600        let mut built = Form::new();
601        built.0.style.background_color = match str_to_color(str) {
602            Ok(color) => Some(color),
603            Err(err) => panic!("{}", err),
604        };
605        built
606    }
607
608    /// New [`Form`] with a colored underlining
609    ///
610    /// This function accepts three color formats:
611    ///
612    /// - A hexcode, like `"#abcdef"`, capitalization is ignored;
613    /// - Three rgb values, like `"rgb 123 456 789"`;
614    /// - Three hsl values, like `"hsl {hue} {sat} {lit}"`, where
615    ///   {hue}, {sat} and {lit} can either be a number from `0..255`,
616    ///   or a percentage, followed by `'%'`, e.g. `"hsl 234 50% 42"`.
617    pub const fn underline(str: &str) -> BuiltForm {
618        let mut built = Form::new();
619        built.0.style.underline_color = match str_to_color(str) {
620            Ok(color) => Some(color),
621            Err(err) => panic!("{}", err),
622        };
623        built
624    }
625
626    /// The foreground color
627    pub const fn fg(&self) -> Option<Color> {
628        self.style.foreground_color
629    }
630
631    /// The background color
632    pub const fn bg(&self) -> Option<Color> {
633        self.style.background_color
634    }
635
636    /// The foreground color
637    pub const fn ul(&self) -> Option<Color> {
638        self.style.underline_color
639    }
640
641    /// The attributes
642    pub const fn attr(&self) -> Attributes {
643        self.style.attributes
644    }
645}
646
647/// A convenience struct for [`Form`]s
648///
649/// This struct exists in order to have [`Form`] methods be
650/// initializers, while [`BuiltForm`] methods consume and return a
651/// [`BuiltForm`]s
652///
653/// This is their only difference, everywhere else, they are
654/// functionally identical.
655pub struct BuiltForm(Form);
656
657#[rustfmt::skip]
658impl BuiltForm {
659    mimic_method_mod!(/**bold*/ bold Attribute::Bold);
660    mimic_method_mod!(/**dim*/ dim Attribute::Dim);
661    mimic_method_mod!(/**italic*/ italic Attribute::Italic);
662    mimic_method_mod!(/**underlined*/ underlined Attribute::Underlined);
663    mimic_method_mod!(/**double_underlined*/ double_underlined Attribute::DoubleUnderlined);
664    mimic_method_mod!(/**undercurled*/ undercurled Attribute::Undercurled);
665    mimic_method_mod!(/**underdashed*/ underdashed Attribute::Underdashed);
666    mimic_method_mod!(/**reverse*/ reverse Attribute::Reverse);
667    mimic_method_mod!(/**crossed_out*/ crossed_out Attribute::CrossedOut);
668    mimic_method_mod!(/**overlined*/ overlined Attribute::OverLined);
669    mimic_method_mod!(/**black*/ black on_black underline_black Color::Black);
670    mimic_method_mod!(/**dark_grey*/ dark_grey on_dark_grey underline_dark_grey Color::DarkGrey);
671    mimic_method_mod!(/**red*/ red on_red underline_red Color::Red);
672    mimic_method_mod!(/**dark_red*/ dark_red on_dark_red underline_dark_red Color::DarkRed);
673    mimic_method_mod!(/**green*/ green on_green underline_green Color::Green);
674    mimic_method_mod!(
675        /**dark_green*/ dark_green on_dark_green underline_dark_green Color::DarkGreen
676    );
677    mimic_method_mod!(/**yellow*/ yellow on_yellow underline_yellow Color::Yellow);
678    mimic_method_mod!(
679        /**dark_yellow*/ dark_yellow on_dark_yellow underline_dark_yellow Color::DarkYellow
680    );
681    mimic_method_mod!(/**blue*/ blue on_blue underline_blue Color::Blue);
682    mimic_method_mod!(/**dark_blue*/ dark_blue on_dark_blue underline_dark_blue Color::DarkBlue);
683    mimic_method_mod!(/**magenta*/ magenta on_magenta underline_magenta Color::Magenta);
684    mimic_method_mod!(
685        /**dark_magenta*/ dark_magenta on_dark_magenta underline_dark_magenta Color::DarkMagenta
686    );
687    mimic_method_mod!(/**cyan*/ cyan on_cyan underline_cyan Color::Cyan);
688    mimic_method_mod!(/**dark_cyan*/ dark_cyan on_dark_cyan underline_dark_cyan Color::DarkCyan);
689    mimic_method_mod!(/**white*/ white on_white underline_white Color::White);
690    mimic_method_mod!(/**grey*/ grey on_grey underline_grey Color::Grey);
691}
692
693impl BuiltForm {
694    /// Adds the [`Reset`] attribute to this [`Form`]
695    ///
696    /// In Duat, the [`Reset`] attribute should remove only other
697    /// [`Attribute`]s, not any of the colors in use.
698    ///
699    /// [`Reset`]: Attribute::Reset
700    pub const fn reset(mut self) -> BuiltForm {
701        self.0.style.attributes = self.0.style.attributes.with(Attribute::Reset);
702        self
703    }
704
705    /// Colors the foreground of this [`Form`]
706    ///
707    /// This function accepts three color formats:
708    ///
709    /// - A hexcode, like `"#abcdef"`, capitalization is ignored;
710    /// - Three rgb values, like `"rgb 123 456 789"`;
711    /// - Three hsl values, like `"hsl {hue} {sat} {lit}"`, where
712    ///   {hue}, {sat} and {lit} can either be a number from `0..255`,
713    ///   or a percentage, followed by `'%'`, e.g. `"hsl 234 50% 42"`.
714    pub const fn with(mut self, str: &str) -> Self {
715        self.0.style.foreground_color = match str_to_color(str) {
716            Ok(color) => Some(color),
717            Err(err) => panic!("{}", err),
718        };
719        self
720    }
721
722    /// Colors the background of this [`Form`]
723    ///
724    /// This function accepts three color formats:
725    ///
726    /// - A hexcode, like `"#abcdef"`, capitalization is ignored;
727    /// - Three rgb values, like `"rgb 123 456 789"`;
728    /// - Three hsl values, like `"hsl {hue} {sat} {lit}"`, where
729    ///   {hue}, {sat} and {lit} can either be a number from `0..255`,
730    ///   or a percentage, followed by `'%'`, e.g. `"hsl 234 50% 42"`.
731    pub const fn on(mut self, str: &str) -> Self {
732        self.0.style.background_color = match str_to_color(str) {
733            Ok(color) => Some(color),
734            Err(err) => panic!("{}", err),
735        };
736        self
737    }
738
739    /// Colors the underlining of this [`Form`]
740    ///
741    /// This function accepts three color formats:
742    ///
743    /// - A hexcode, like `"#abcdef"`, capitalization is ignored;
744    /// - Three rgb values, like `"rgb 123 456 789"`;
745    /// - Three hsl values, like `"hsl {hue} {sat} {lit}"`, where
746    ///   {hue}, {sat} and {lit} can either be a number from `0..255`,
747    ///   or a percentage, followed by `'%'`, e.g. `"hsl 234 50% 42"`.
748    pub const fn underline(mut self, str: &str) -> Self {
749        self.0.style.underline_color = match str_to_color(str) {
750            Ok(color) => Some(color),
751            Err(err) => panic!("{}", err),
752        };
753        self
754    }
755}
756
757impl std::ops::Deref for BuiltForm {
758    type Target = Form;
759
760    fn deref(&self) -> &Self::Target {
761        &self.0
762    }
763}
764
765impl std::ops::DerefMut for BuiltForm {
766    fn deref_mut(&mut self) -> &mut Self::Target {
767        &mut self.0
768    }
769}
770
771/// The [`FormId`] of the `"Default"` form
772pub const DEFAULT_ID: FormId = FormId(0);
773/// The [`FormId`] of the `"MainCursor"` form
774pub const M_CUR_ID: FormId = FormId(8);
775/// The [`FormId`] of the `"ExtraCursor"` form
776pub const E_CUR_ID: FormId = FormId(9);
777/// The [`FormId`] of the `"MainSelection"` form
778pub const M_SEL_ID: FormId = FormId(10);
779/// The [`FormId`] of the `"ExtraSelection"` form
780pub const E_SEL_ID: FormId = FormId(11);
781/// The [`FormId`] of the `"Inactive"` form
782pub const INACTIVE_ID: FormId = FormId(12);
783
784struct InnerPalette {
785    main_cursor: Option<CursorShape>,
786    extra_cursor: Option<CursorShape>,
787    forms: Vec<(&'static str, Form, FormType)>,
788}
789
790/// The list of forms to be used when rendering.
791struct Palette(LazyLock<RwLock<InnerPalette>>);
792
793impl Palette {
794    /// Returns a new instance of [`FormPalette`]
795    const fn new() -> Self {
796        Self(LazyLock::new(|| {
797            let main_cursor = Some(CursorShape::DefaultUserShape);
798
799            RwLock::new(InnerPalette {
800                main_cursor,
801                extra_cursor: main_cursor,
802                forms: BASE_FORMS.to_vec(),
803            })
804        }))
805    }
806
807    /// Sets a [`Form`]
808    fn set_form(&self, name: &'static str, form: Form) {
809        let mut inner = self.0.write();
810
811        let id = if let Some(i) = inner.forms.iter().position(|(cmp, ..)| *cmp == name) {
812            inner.forms[i] = (name, form, FormType::Normal);
813
814            for refed in refs_of(&inner, i, i) {
815                inner.forms[refed].1 = form;
816            }
817
818            if let Some(sender) = SENDER.get() {
819                sender.send_form_changed().unwrap()
820            }
821            FormId(i as u16)
822        } else {
823            inner.forms.push((name, form, FormType::Normal));
824            FormId(inner.forms.len() as u16 - 1)
825        };
826
827        hooks::trigger::<FormSet>((name, id, form));
828    }
829
830    /// Sets a [`Form`] "weakly"
831    fn set_weak_form(&self, name: &'static str, form: Form) {
832        let mut inner = self.0.write();
833
834        if let Some(i) = inner.forms.iter().position(|(cmp, ..)| *cmp == name) {
835            let (_, f, ty) = &mut inner.forms[i];
836            if let FormType::Weakest = ty {
837                *f = form;
838                *ty = FormType::Normal;
839
840                if let Some(sender) = SENDER.get() {
841                    sender.send_form_changed().unwrap()
842                }
843            }
844        } else {
845            inner.forms.push((name, form, FormType::Normal));
846            let i = inner.forms.len() - 1;
847            for refed in refs_of(&inner, i, i) {
848                inner.forms[refed].1 = form;
849            }
850        }
851    }
852
853    /// Makes a [`Form`] reference another
854    fn set_ref(&self, name: &'static str, refed: impl AsRef<str>) {
855        let refed = {
856            let refed: &'static str = refed.as_ref().to_string().leak();
857            self.id_from_name(refed)
858        };
859
860        let mut inner = self.0.write();
861        let (_, form, _) = inner.forms[refed.0 as usize];
862
863        if let Some(i) = inner.forms.iter().position(|(cmp, ..)| *cmp == name) {
864            for refed in refs_of(&inner, i, i) {
865                inner.forms[refed].1 = form;
866            }
867
868            // If it would be circular, we just don't reference anything.
869            if would_be_circular(&inner, i, refed.0 as usize) {
870                inner.forms[i] = (name, form, FormType::Normal);
871            } else {
872                inner.forms[i] = (name, form, FormType::Ref(refed));
873            }
874
875            if let Some(sender) = SENDER.get() {
876                sender.send_form_changed().unwrap()
877            }
878            hooks::trigger::<FormSet>((name, FormId(i as u16), form));
879        } else {
880            // If the form didn't previously exist, nothing was referencing it, so
881            // no checks are done.
882            inner.forms.push((name, form, FormType::Ref(refed)));
883            hooks::trigger::<FormSet>((name, FormId(inner.forms.len() as u16 - 1), form));
884        }
885    }
886
887    /// Makes a [`Form`] reference another "weakly"
888    fn set_weak_ref(&self, name: &'static str, refed: impl AsRef<str>) {
889        let refed = {
890            let refed: &'static str = refed.as_ref().to_string().leak();
891            self.id_from_name(refed)
892        };
893
894        let mut inner = self.0.write();
895        let (_, form, _) = inner.forms[refed.0 as usize];
896
897        // For weak refs, no checks are done, since a form is only set if it
898        // doesn't exist, and for there to be refs to it, it must exist.
899        if let Some(i) = inner.forms.iter().position(|(cmp, ..)| *cmp == name) {
900            let (_, f, ty) = &mut inner.forms[i];
901            if let FormType::Weakest = ty {
902                *f = form;
903                *ty = FormType::Ref(refed);
904
905                if let Some(sender) = SENDER.get() {
906                    sender.send_form_changed().unwrap()
907                }
908            }
909        } else {
910            inner.forms.push((name, form, FormType::Ref(refed)));
911        }
912    }
913
914    /// Returns the [`FormId`] from a given `name`
915    ///
916    /// If the named form doesn't exist, create it.
917    fn id_from_name(&self, name: &'static str) -> FormId {
918        let mut inner = self.0.write();
919
920        if let Some(id) = inner.forms.iter().position(|(cmp, ..)| *cmp == name) {
921            FormId(id as u16)
922        } else {
923            inner.forms.push((name, Form::new().0, FormType::Weakest));
924            FormId((inner.forms.len() - 1) as u16)
925        }
926    }
927
928    /// Returns a form, given a [`FormId`].
929    fn form_from_id(&self, id: FormId) -> Option<Form> {
930        let inner = self.0.read_recursive();
931        inner.forms.get(id.0 as usize).map(|(_, form, _)| *form)
932    }
933
934    /// Returns the name of the [`FormId`]
935    fn name_from_id(&self, id: FormId) -> &'static str {
936        let inner = self.0.read_recursive();
937        let nth = inner.forms.get(id.0 as usize).map(|(name, ..)| name);
938
939        let Some(ret) = nth else {
940            unreachable!("Form with id {} not found, this should never happen", id.0);
941        };
942        ret
943    }
944
945    /// The [`Form`] and [`CursorShape`] of the main cursor
946    fn main_cursor(&self) -> (Form, Option<CursorShape>) {
947        let form = self.form_from_id(M_CUR_ID).unwrap();
948        (form, self.0.read_recursive().main_cursor)
949    }
950
951    /// The [`Form`] and [`CursorShape`] of extra cursors
952    fn extra_cursor(&self) -> (Form, Option<CursorShape>) {
953        let form = self.form_from_id(E_CUR_ID).unwrap();
954        (form, self.0.read_recursive().extra_cursor)
955    }
956
957    /// Sets the [`CursorShape`] of the main cursor
958    fn set_main_cursor(&self, shape: CursorShape) {
959        self.0.write().main_cursor = Some(shape);
960        if let Some(sender) = SENDER.get() {
961            sender.send_form_changed().unwrap()
962        }
963    }
964
965    /// Sets the [`CursorShape`] of extra cursors
966    fn set_extra_cursor(&self, shape: CursorShape) {
967        self.0.write().extra_cursor = Some(shape);
968        if let Some(sender) = SENDER.get() {
969            sender.send_form_changed().unwrap()
970        }
971    }
972
973    /// Unsets the [`CursorShape`] of the main cursor
974    fn unset_main_cursor(&self) {
975        self.0.write().main_cursor = None;
976        if let Some(sender) = SENDER.get() {
977            sender.send_form_changed().unwrap()
978        }
979    }
980
981    /// Unsets the [`CursorShape`] of the extra cursors
982    fn unset_extra_cursor(&self) {
983        self.0.write().extra_cursor = None;
984        if let Some(sender) = SENDER.get() {
985            sender.send_form_changed().unwrap()
986        }
987    }
988
989    /// Returns a [`Painter`]
990    fn painter(&'static self) -> Painter {
991        let inner = self.0.read();
992        let default = inner.forms[DEFAULT_ID.0 as usize].1;
993        Painter {
994            inner,
995            forms: vec![(default, DEFAULT_ID)],
996            reset_count: 0,
997            final_form_start: 1,
998            still_on_same_byte: false,
999            reset_is_needed: false,
1000        }
1001    }
1002}
1003
1004pub struct Painter {
1005    inner: RwLockReadGuard<'static, InnerPalette>,
1006    forms: Vec<(Form, FormId)>,
1007    reset_count: usize,
1008    final_form_start: usize,
1009    still_on_same_byte: bool,
1010    reset_is_needed: bool,
1011}
1012
1013impl Painter {
1014    /// Applies the `Form` with the given `id` and returns the result,
1015    /// given previous triggers.
1016    ///
1017    /// Will return a [`Form`] _relative_ to what the previous
1018    /// [`Form`] was, that is, if the new [`Form`] doesn't include a
1019    /// background, its combination with the other [`Form`]s also
1020    /// won't, since it wasn't changed.
1021    #[inline(always)]
1022    pub fn apply(&mut self, id: FormId) -> ContentStyle {
1023        let i = id.0 as usize;
1024        let forms = &self.inner.forms;
1025        let form = forms.get(i).map(|(_, f, _)| *f).unwrap_or(Form::new().0);
1026
1027        self.reset_count += form.style.attributes.has(Attribute::Reset) as usize;
1028
1029        self.forms.insert(self.final_form_start, (form, id));
1030        if !(id == M_SEL_ID || id == E_SEL_ID) {
1031            self.final_form_start += 1;
1032        }
1033
1034        let mut style = self.make_style();
1035        if self.reset_is_needed || self.reset_count > 0 {
1036            self.still_on_same_byte = true;
1037            self.reset_is_needed = true;
1038            style.attributes.set(Attribute::Reset);
1039        // Only when we are certain that all forms have been
1040        // printed, can we cull unnecessary colors for efficiency
1041        // (this happens most of the time).
1042        } else if !self.still_on_same_byte {
1043            self.still_on_same_byte = true;
1044            style.foreground_color = form.fg().and(style.foreground_color.or(Some(Color::Reset)));
1045            style.background_color = form.bg().and(style.background_color.or(Some(Color::Reset)));
1046            style.underline_color = form.ul().and(style.underline_color.or(Some(Color::Reset)));
1047        }
1048        style
1049    }
1050
1051    /// Removes the [`Form`] with the given `id` and returns the
1052    /// result, given previous triggers
1053    #[inline(always)]
1054    pub fn remove(&mut self, id: FormId) -> ContentStyle {
1055        let mut applied_forms = self.forms.iter().enumerate();
1056        let Some((i, &(form, _))) = applied_forms.rfind(|(_, (_, i))| *i == id) else {
1057            return absolute_style(&self.forms);
1058        };
1059
1060        self.reset_count -= form.style.attributes.has(Attribute::Reset) as usize;
1061        self.forms.remove(i);
1062        if id != M_SEL_ID && id != E_SEL_ID {
1063            self.final_form_start -= 1;
1064        }
1065
1066        let mut style = self.make_style();
1067        if !form.style.attributes.is_empty() || self.reset_is_needed || self.reset_count > 0 {
1068            self.still_on_same_byte = true;
1069            self.reset_is_needed = true;
1070            style.attributes.set(Attribute::Reset);
1071        // Only when we are certain that all forms have been
1072        // printed, can we cull unnecessary colors for efficiency
1073        // (this happens most of the time).
1074        } else if !self.still_on_same_byte {
1075            self.still_on_same_byte = true;
1076            style.foreground_color = form.fg().and(style.foreground_color.or(Some(Color::Reset)));
1077            style.background_color = form.bg().and(style.background_color.or(Some(Color::Reset)));
1078            style.underline_color = form.ul().and(style.underline_color.or(Some(Color::Reset)));
1079        }
1080        style
1081    }
1082
1083    /// Removes all [`Form`]s except the default one
1084    ///
1085    /// Should be used when a [`ResetState`] part is printed
1086    ///
1087    /// [`ResetState`]: crate::text::Part::ResetState
1088    #[inline(always)]
1089    pub fn reset(&mut self) -> ContentStyle {
1090        self.forms.splice(1.., []);
1091        self.make_style()
1092    }
1093
1094    /// Generates the form to be printed, given all the previously
1095    /// pushed forms in the `Form` stack.
1096    #[inline(always)]
1097    pub fn make_style(&self) -> ContentStyle {
1098        let mut style = ContentStyle::new();
1099
1100        for &(form, _) in &self.forms {
1101            style.foreground_color = form.fg().or(style.foreground_color);
1102            style.background_color = form.bg().or(style.background_color);
1103            style.underline_color = form.ul().or(style.underline_color);
1104            style.attributes = if form.attr().has(Attribute::Reset) {
1105                form.attr()
1106            } else {
1107                form.attr() | style.attributes
1108            }
1109        }
1110
1111        style
1112    }
1113
1114    #[inline(always)]
1115    pub fn apply_main_cursor(&mut self) -> ContentStyle {
1116        let style = self.apply(M_CUR_ID);
1117        self.final_form_start -= 1;
1118        style
1119    }
1120
1121    #[inline(always)]
1122    pub fn remove_main_cursor(&mut self) -> ContentStyle {
1123        let style = self.remove(M_CUR_ID);
1124        self.final_form_start += 1;
1125        style
1126    }
1127
1128    #[inline(always)]
1129    pub fn apply_extra_cursor(&mut self) -> ContentStyle {
1130        let style = self.apply(E_CUR_ID);
1131        self.final_form_start -= 1;
1132        style
1133    }
1134
1135    #[inline(always)]
1136    pub fn remove_extra_cursor(&mut self) -> ContentStyle {
1137        let style = self.remove(E_CUR_ID);
1138        self.final_form_start += 1;
1139        style
1140    }
1141
1142    /// Tells the [`Painter`] that it has printed a character
1143    pub fn confirm_printing(&mut self) {
1144        self.still_on_same_byte = false;
1145        self.reset_is_needed = false;
1146    }
1147
1148    /// The [`Form`] "ExtraCursor", and its shape.
1149    pub fn main_cursor(&self) -> Option<CursorShape> {
1150        self.inner.main_cursor
1151    }
1152
1153    /// The [`Form`] "ExtraCursor", and its shape.
1154    pub fn extra_cursor(&self) -> Option<CursorShape> {
1155        self.inner.extra_cursor
1156    }
1157
1158    /// The `"Default"` form's [`Form`]
1159    pub fn get_default(&self) -> Form {
1160        self.forms[0].0
1161    }
1162}
1163
1164pub(crate) fn set_sender(sender: Sender) {
1165    SENDER
1166        .set(sender)
1167        .unwrap_or_else(|_| panic!("Sender set more than once"));
1168}
1169
1170/// An enum that helps in the modification of forms
1171#[derive(Debug, Clone)]
1172enum FormType {
1173    Normal,
1174    Ref(FormId),
1175    Weakest,
1176}
1177
1178/// The position of each form that eventually references the `n`th
1179fn refs_of(inner: &RwLockWriteGuard<InnerPalette>, refed: usize, top_ref: usize) -> Vec<usize> {
1180    let mut refs = Vec::new();
1181    for (i, (.., f_ty)) in inner.forms.iter().enumerate() {
1182        if let FormType::Ref(ref_id) = f_ty
1183            && ref_id.0 as usize == refed
1184            && ref_id.0 as usize != top_ref
1185        {
1186            refs.push(i);
1187            refs.extend(refs_of(inner, i, top_ref));
1188        }
1189    }
1190    refs
1191}
1192
1193/// If form references would eventually lead to a loop
1194fn would_be_circular(inner: &RwLockWriteGuard<InnerPalette>, referee: usize, refed: usize) -> bool {
1195    if let (.., FormType::Ref(refed_ref)) = inner.forms[refed] {
1196        match refed_ref.0 as usize == referee {
1197            true => true,
1198            false => would_be_circular(inner, referee, refed_ref.0 as usize),
1199        }
1200    } else {
1201        false
1202    }
1203}
1204
1205/// Returns an absolute [`Form`]
1206fn absolute_style(list: &[(Form, FormId)]) -> ContentStyle {
1207    let mut style = ContentStyle::new();
1208
1209    for &(form, _) in list {
1210        style.foreground_color = form.fg().or(style.foreground_color);
1211        style.background_color = form.bg().or(style.background_color);
1212        style.underline_color = form.ul().or(style.underline_color);
1213        style.attributes = if form.attr().has(Attribute::Reset) {
1214            form.attr()
1215        } else {
1216            form.attr() | style.attributes
1217        }
1218    }
1219
1220    style
1221}
1222
1223/// Converts a string to a color, supporst hex, RGB and HSL
1224const fn str_to_color(str: &str) -> std::result::Result<Color, &'static str> {
1225    use core::str::from_utf8_unchecked as utf8_str;
1226    const fn strip_prefix<'a>(prefix: &str, str: &'a str) -> Option<&'a str> {
1227        let prefix = prefix.as_bytes();
1228        let str = str.as_bytes();
1229
1230        let mut i = 0;
1231        while i < prefix.len() {
1232            if str[i] != prefix[i] {
1233                return None;
1234            }
1235            i += 1;
1236        }
1237
1238        let (_, str) = str.split_at(prefix.len());
1239        Some(unsafe { utf8_str(str) })
1240    }
1241    const fn strip_suffix<'a>(suffix: &str, str: &'a str) -> Option<&'a str> {
1242        let prefix = suffix.as_bytes();
1243        let str = str.as_bytes();
1244
1245        let mut i = str.len() - 1;
1246        while i >= str.len() - prefix.len() {
1247            if str[i] != prefix[i - (str.len() - prefix.len())] {
1248                return None;
1249            }
1250            i += 1;
1251        }
1252
1253        let (str, _) = str.split_at(str.len() - suffix.len());
1254        Some(unsafe { utf8_str(str) })
1255    }
1256    const fn split_space(str: &str) -> Option<(&str, &str)> {
1257        if str.is_empty() {
1258            return None;
1259        }
1260        let str = str.as_bytes();
1261
1262        let mut i = 0;
1263        while i < str.len() {
1264            if str[i] == b' ' {
1265                break;
1266            }
1267            i += 1;
1268        }
1269
1270        let (cut, rest) = str.split_at(i);
1271        let (_, rest) = rest.split_at(if rest.is_empty() { 0 } else { 1 });
1272        Some(unsafe { (utf8_str(cut), utf8_str(rest)) })
1273    }
1274    const fn hue_to_rgb(p: f32, q: f32, mut t: f32) -> f32 {
1275        t = if t < 0.0 { t + 1.0 } else { t };
1276        t = if t > 1.0 { t - 1.0 } else { t };
1277        if t < 1.0 / 6.0 {
1278            p + (q - p) * 6.0 * t
1279        } else if t < 1.0 / 2.0 {
1280            q
1281        } else if t < 2.0 / 3.0 {
1282            p + (q - p) * (2.0 / 3.0 - t) * 6.0
1283        } else {
1284            p
1285        }
1286    }
1287
1288    // Expects "#{red:x}{green:x}{blue:x}"
1289    if let Some(hex) = strip_prefix("#", str) {
1290        let total = match u32::from_str_radix(hex, 16) {
1291            Ok(total) if hex.len() == 6 => total,
1292            _ => return Err("Hexcode does not contain 6 hex values"),
1293        };
1294        let r = (total >> 16) as u8;
1295        let g = (total >> 8) as u8;
1296        let b = total as u8;
1297
1298        Ok(Color::Rgb { r, g, b })
1299        // Expects "rgb {red} {green} {blue}"
1300    } else if let Some(mut rgb) = strip_prefix("rgb ", str) {
1301        let mut values = [0, 0, 0];
1302        let mut i = 0;
1303        while i < values.len() {
1304            if let Some((cut, rest)) = split_space(rgb) {
1305                rgb = rest;
1306                values[i] = match u8::from_str_radix(cut, 10) {
1307                    Ok(value) => value,
1308                    Err(_) => return Err("Rgb format value could not be parsed"),
1309                }
1310            } else {
1311                return Err("Missing value in rgb format");
1312            }
1313            i += 1;
1314        }
1315
1316        let [r, g, b] = values;
1317        Ok(Color::Rgb { r, g, b })
1318        // Expects "hsl {hue%?} {saturation%?} {lightness%?}"
1319    } else if let Some(mut hsl) = strip_prefix("hsl ", str) {
1320        let mut values = [0.0, 0.0, 0.0];
1321        let mut i = 0;
1322        while i < values.len() {
1323            if let Some((cut, rest)) = split_space(hsl) {
1324                hsl = rest;
1325                let (num, div) = match strip_suffix("%", cut) {
1326                    Some(perc) => (perc, 100),
1327                    None => (cut, 255),
1328                };
1329                values[i] = match u8::from_str_radix(num, 10) {
1330                    Ok(value) if value <= div => value as f32 / div as f32,
1331                    _ => return Err("Hsl format property could not be parsed"),
1332                }
1333            } else {
1334                return Err("Missing value in hsl format");
1335            }
1336            i += 1;
1337        }
1338        let [hue, sat, lit] = values;
1339
1340        let (r, g, b) = if sat == 0.0 {
1341            (lit, lit, lit)
1342        } else {
1343            let q = if lit < 0.5 {
1344                lit * (1.0 + sat)
1345            } else {
1346                lit + sat - lit * sat
1347            };
1348            let p = 2.0 * lit - q;
1349            let r = hue_to_rgb(p, q, hue + 1.0 / 3.0);
1350            let g = hue_to_rgb(p, q, hue);
1351            let b = hue_to_rgb(p, q, hue - 1.0 / 3.0);
1352            (r, g, b)
1353        };
1354
1355        // + 0.5 because `as` rounds floats down.
1356        let r = (0.5 + r * 255.0) as u8;
1357        let g = (0.5 + g * 255.0) as u8;
1358        let b = (0.5 + b * 255.0) as u8;
1359        Ok(Color::Rgb { r, g, b })
1360    } else {
1361        return Err("Color format was not recognized");
1362    }
1363}
1364
1365/// Mimics [`ContentStyle`] methods for the [`Form`] type
1366macro mimic_method_new {
1367    (#[$attr:meta] $method:ident $attrib:expr) => {
1368        /// New [`Form`] with the
1369        #[$attr]
1370        /// attribute
1371        pub const fn $method() -> BuiltForm {
1372            let mut built = Form::new();
1373            built.0.style.attributes = built.0.style.attributes.with($attrib);
1374            built
1375        }
1376    },
1377
1378    (#[$attr:meta] $fg:ident $bg:ident $ul:ident $color:expr) => {
1379        /// New [`Form`] with a
1380        #[$attr]
1381        /// foreground
1382        pub const fn $fg() -> BuiltForm {
1383            let mut built = Form::new();
1384            built.0.style.foreground_color = Some($color);
1385            built
1386        }
1387
1388        /// New [`Form`] with a
1389        #[$attr]
1390        /// background
1391        pub const fn $bg() -> BuiltForm {
1392            let mut built = Form::new();
1393            built.0.style.background_color = Some($color);
1394            built
1395        }
1396
1397        /// New [`Form`] with a
1398        #[$attr]
1399        /// underlining
1400        ///
1401        /// Do note that this feature may not be supported in all [`Ui`]s.
1402        ///
1403        /// [`Ui`]: crate::ui::Ui
1404        pub const fn $ul() -> BuiltForm {
1405            let mut built = Form::new();
1406            built.0.style.underline_color = Some($color);
1407            built
1408        }
1409    }
1410}
1411
1412macro mimic_method_mod {
1413    (#[$attr:meta] $method:ident $attrib:expr) => {
1414        /// Applies the
1415        #[$attr]
1416        /// attribute to this [`Form`]
1417        pub const fn $method(mut self) -> Self {
1418            self.0.style.attributes = self.0.style.attributes.with($attrib);
1419            self
1420        }
1421    },
1422
1423    (#[$attr:meta] $fg:ident $bg:ident $ul:ident $color:expr) => {
1424        /// Turns the foreground of this [`Form`]
1425        #[$attr]
1426        pub const fn $fg(mut self) -> Self {
1427            self.0.style.foreground_color = Some($color);
1428            self
1429        }
1430
1431		/// Turns the background of this [`Form`]
1432        #[$attr]
1433        pub const fn $bg(mut self) -> Self {
1434            self.0.style.background_color = Some($color);
1435            self
1436        }
1437
1438		/// Turns the underlining of this [`Form`]
1439        #[$attr]
1440        ///
1441        /// Do note that this feature may not be supported in all [`Ui`]s.
1442        ///
1443        /// [`Ui`]: crate::ui::Ui
1444        pub const fn $ul(mut self) -> Self {
1445            self.0.style.underline_color = Some($color);
1446            self
1447        }
1448    }
1449}