lazybar_core/
attrs.rs

1use anyhow::{Context, Result};
2use csscolorparser::Color;
3use derive_builder::Builder;
4use pango::FontDescription;
5
6use crate::{
7    background::Bg, parser, remove_color_from_config, remove_string_from_config,
8};
9
10/// Attributes of a panel, or the defaults for the bar.
11#[derive(Builder, Clone, Default, Debug, PartialEq, PartialOrd)]
12#[builder_struct_attr(allow(missing_docs))]
13#[builder_impl_attr(allow(missing_docs))]
14pub struct Attrs {
15    #[builder(default, setter(strip_option))]
16    font: Option<FontDescription>,
17    #[builder(default, setter(strip_option))]
18    fg: Option<Color>,
19    #[builder(default, setter(strip_option))]
20    pub(crate) bg: Option<Bg>,
21}
22
23impl Attrs {
24    /// Creates an empty instance (all fields set to [`None`]).
25    ///
26    /// This creates the same [`Attrs`] as [`Attrs::default`], but this is a
27    /// const function.
28    #[must_use]
29    pub const fn empty() -> Self {
30        Self {
31            font: None,
32            fg: None,
33            bg: None,
34        }
35    }
36
37    /// Parses an instance of this type from a subset of the global
38    /// [`Config`][config::Config].
39    ///
40    /// This function first looks for a top-level table called `attrs` and then
41    /// a subtable of the given name. These options are contained within the
42    /// subtable.
43    ///
44    /// Configuration options:
45    ///
46    /// - `fg: String`: Specify the foreground (usually text) color. All parsing
47    ///   methods from [csscolorparser] are available.
48    /// - `bg`: See [`Bg::parse`].
49    /// - `font: String`: Specify the font to be used. This will be turned into
50    ///   a [`pango::FontDescription`], so it's very configurable. Font family,
51    ///   weight, size, and more can be specified.
52    pub fn parse(name: impl AsRef<str>) -> Result<Self> {
53        let attrs_table = parser::ATTRS.get().unwrap();
54        let name = name.as_ref();
55        log::debug!("parsing {name} attrs");
56        let mut attr_table = attrs_table
57            .get(name)
58            .with_context(|| {
59                format!("couldn't find attrs table with name {name}")
60            })?
61            .clone()
62            .into_table()?;
63        log::trace!("got attr table");
64        let mut builder = AttrsBuilder::default();
65        if let Some(fg) = remove_color_from_config("fg", &mut attr_table) {
66            log::debug!("got fg: {fg}");
67            builder.fg(fg);
68        }
69        if let Some(bg) = remove_string_from_config("bg", &mut attr_table) {
70            if let Some(bg) = Bg::parse(bg.as_str()) {
71                log::debug!("got bg: {bg:?}");
72                builder.bg(bg);
73            }
74        }
75        if let Some(font) = remove_string_from_config("font", &mut attr_table) {
76            log::debug!("got font: {font}");
77            builder.font(FontDescription::from_string(font.as_str()));
78        }
79
80        Ok(builder.build()?)
81    }
82
83    /// Parses an instance of this type from a subset of the global
84    /// [`Config`][config::Config], enforcing default colors. This ensures that
85    /// the foreground and background colors always exist. No default font
86    /// is set because [pango] will choose a reasonable font from those that
87    /// exist on the host system.
88    pub fn parse_global(name: impl AsRef<str>) -> Self {
89        Self::parse(name).unwrap_or_default()
90    }
91
92    /// Sets the font of a [`pango::Layout`].
93    pub fn apply_font(&self, layout: &pango::Layout) {
94        if let Some(font) = &self.font {
95            layout.set_font_description(Some(font));
96        }
97    }
98
99    /// Sets the foreground (usually text) color of a [`cairo::Context`].
100    pub fn apply_fg(&self, cr: &cairo::Context) {
101        if let Some(fg) = &self.fg {
102            cr.set_source_rgba(
103                fg.r.into(),
104                fg.g.into(),
105                fg.b.into(),
106                fg.a.into(),
107            );
108        }
109    }
110
111    /// Combines two [`Attrs`] instances into one, choosing options from `self`
112    /// as long as they are [`Some`], otherwise choosing them from `new`.
113    pub fn apply_to(&mut self, new: &Self) {
114        if self.font.is_none() {
115            self.font.clone_from(&new.font);
116        }
117        if self.fg.is_none() {
118            self.fg.clone_from(&new.fg);
119        }
120        if self.bg.is_none() {
121            self.bg.clone_from(&new.bg);
122        }
123    }
124}