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);
}
}
}