1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use anyhow::{Context, Result};
use csscolorparser::Color;
use derive_builder::Builder;
use pango::FontDescription;

use crate::{
    background::Bg, parser, remove_color_from_config, remove_string_from_config,
};

/// Attributes of a panel, or the defaults for the bar.
#[derive(Builder, Clone, Default, Debug, PartialEq, PartialOrd)]
#[builder_struct_attr(allow(missing_docs))]
#[builder_impl_attr(allow(missing_docs))]
pub struct Attrs {
    #[builder(default, setter(strip_option))]
    font: Option<FontDescription>,
    #[builder(default, setter(strip_option))]
    fg: Option<Color>,
    #[builder(default, setter(strip_option))]
    pub(crate) bg: Option<Bg>,
}

impl Attrs {
    /// Creates an empty instance (all fields set to [`None`]).
    ///
    /// This creates the same [`Attrs`] as [`Attrs::default`], but this is a
    /// const function.
    #[must_use]
    pub const fn empty() -> Self {
        Self {
            font: None,
            fg: None,
            bg: None,
        }
    }

    /// Parses an instance of this type from a subset of the global
    /// [`Config`][config::Config].
    ///
    /// This function first looks for a top-level table called `attrs` and then
    /// a subtable of the given name. These options are contained within the
    /// subtable.
    ///
    /// Configuration options:
    ///
    /// - `fg: String`: Specify the foreground (usually text) color. All parsing
    ///   methods from [csscolorparser] are available.
    ///
    /// - `bg`: See [`Bg::parse`].
    ///
    /// - `font: String`: Specify the font to be used. This will be turned into
    ///   a [`pango::FontDescription`], so it's very configurable. Font family,
    ///   weight, size, and more can be specified.
    pub fn parse(name: impl AsRef<str>) -> Result<Self> {
        let attrs_table = parser::ATTRS.get().unwrap();
        let name = name.as_ref();
        log::debug!("parsing {name} attrs");
        let mut attr_table = attrs_table
            .get(name)
            .with_context(|| {
                format!("couldn't find attrs table with name {name}")
            })?
            .clone()
            .into_table()?;
        log::trace!("got attr table");
        let mut builder = AttrsBuilder::default();
        if let Some(fg) = remove_color_from_config("fg", &mut attr_table) {
            log::debug!("got fg: {fg}");
            builder.fg(fg);
        }
        if let Some(bg) = remove_string_from_config("bg", &mut attr_table) {
            if let Some(bg) = Bg::parse(bg.as_str()) {
                log::debug!("got bg: {bg:?}");
                builder.bg(bg);
            }
        }
        if let Some(font) = remove_string_from_config("font", &mut attr_table) {
            log::debug!("got font: {font}");
            builder.font(FontDescription::from_string(font.as_str()));
        }

        Ok(builder.build()?)
    }

    /// Parses an instance of this type from a subset of the global
    /// [`Config`][config::Config], enforcing default colors. This ensures that
    /// the foreground and background colors always exist. No default font
    /// is set because [pango] will choose a reasonable font from those that
    /// exist on the host system.
    pub fn parse_global(name: impl AsRef<str>) -> Self {
        Self::parse(name).unwrap_or_default()
    }

    /// Sets the font of a [`pango::Layout`].
    pub fn apply_font(&self, layout: &pango::Layout) {
        if let Some(font) = &self.font {
            layout.set_font_description(Some(font));
        }
    }

    /// Sets the foreground (usually text) color of a [`cairo::Context`].
    pub fn apply_fg(&self, cr: &cairo::Context) {
        if let Some(fg) = &self.fg {
            cr.set_source_rgba(
                fg.r.into(),
                fg.g.into(),
                fg.b.into(),
                fg.a.into(),
            );
        }
    }

    // /// Sets the background color of a [`cairo::Context`].
    // pub fn apply_bg(&self, cr: &cairo::Context) {
    //     if let Some(bg) = &self.bg {
    //         cr.set_source_rgba(bg.r, bg.g, bg.b, bg.a);
    //     }
    // }

    /// Combines two [`Attrs`] instances into one, choosing options from `self`
    /// as long as they are [`Some`], otherwise choosing them from `new`.
    pub fn apply_to(&mut self, new: &Self) {
        if self.font.is_none() {
            self.font.clone_from(&new.font);
        }
        if self.fg.is_none() {
            self.fg.clone_from(&new.fg);
        }
        if self.bg.is_none() {
            self.bg.clone_from(&new.bg);
        }
    }
}