use std::{
collections::HashMap,
hash::BuildHasher,
rc::Rc,
sync::{Arc, Mutex},
};
use anyhow::Result;
use config::Value;
use derive_builder::Builder;
use derive_debug::Dbg;
use futures::task::AtomicWaker;
use pangocairo::functions::show_layout;
use crate::{
actions::Actions,
attrs::Attrs,
bar::{Dependence, PanelDrawInfo},
image::Image,
remove_array_from_config, remove_bool_from_config,
remove_string_from_config, Highlight, PanelHideFn, PanelShowFn, Ramp,
};
#[derive(Dbg)]
pub enum ShowHide {
Default(Arc<Mutex<bool>>, Arc<AtomicWaker>),
#[dbg(skip)]
Custom(Option<PanelShowFn>, Option<PanelHideFn>),
None,
}
pub fn draw_common(
cr: &Rc<cairo::Context>,
text: &str,
attrs: &Attrs,
dependence: Dependence,
highlight: Option<Highlight>,
images: Vec<Image>,
height: i32,
show_hide: ShowHide,
) -> Result<PanelDrawInfo> {
let layout = pangocairo::functions::create_layout(cr);
layout.set_markup(text);
attrs.apply_font(&layout);
let dims = layout.pixel_size();
let attrs = attrs.clone();
let bg = attrs.bg.clone().unwrap_or_default();
let (show, hide): (Option<PanelShowFn>, Option<PanelHideFn>) =
match show_hide {
ShowHide::Default(paused, waker) => {
let paused_ = paused.clone();
(
Some(Box::new(move || {
*paused.lock().unwrap() = false;
waker.wake();
Ok(())
})),
Some(Box::new(move || {
*paused_.lock().unwrap() = true;
Ok(())
})),
)
}
ShowHide::Custom(show, hide) => (show, hide),
ShowHide::None => (None, None),
};
Ok(PanelDrawInfo::new(
bg.adjust_dims(dims, height),
dependence,
Box::new(move |cr, _| {
let offset =
bg.draw(cr, dims.0 as f64, dims.1 as f64, height as f64)?;
for image in &images {
image.draw(cr)?;
}
cr.save()?;
cr.translate(offset, 0.0);
if let Some(ref highlight) = highlight {
highlight.draw(cr, height as f64, dims.0 as f64)?;
}
cr.translate(0.0, (height - dims.1) as f64 / 2.0);
attrs.apply_fg(cr);
show_layout(cr, &layout);
cr.restore()?;
Ok(())
}),
show,
hide,
None,
))
}
#[derive(Debug, Clone, Builder)]
#[builder_struct_attr(allow(missing_docs))]
#[builder_impl_attr(allow(missing_docs))]
pub struct PanelCommon {
pub dependence: Dependence,
pub actions: Actions,
pub images: Vec<Image>,
pub visible: bool,
}
impl PanelCommon {
pub fn parse_format<S: BuildHasher>(
table: &mut HashMap<String, Value, S>,
suffix: &'static str,
default: &'static str,
) -> String {
let format = remove_string_from_config(
format!("format{suffix}").as_str(),
table,
)
.unwrap_or_else(|| (*default).to_string());
log::debug!("got format: {:?}", format);
format
}
pub fn parse_formats<S: BuildHasher, const N: usize>(
table: &mut HashMap<String, Value, S>,
suffixes: &[&'static str; N],
defaults: &[&'static str; N],
) -> [String; N] {
let mut formats = [const { String::new() }; N];
let mut config = suffixes.iter().zip(defaults);
for format in &mut formats {
let (suffix, default) = config.next().unwrap();
*format = remove_string_from_config(
format!("format{suffix}").as_str(),
table,
)
.unwrap_or_else(|| (*default).to_string());
}
log::debug!("got formats: {:?}", formats);
formats
}
pub fn parse_formats_variadic<S: BuildHasher>(
table: &mut HashMap<String, Value, S>,
default: &[&'static str],
) -> Vec<String> {
let formats = remove_array_from_config("formats", table).map_or_else(
|| default.iter().map(ToString::to_string).collect::<Vec<_>>(),
|arr| {
arr.into_iter()
.filter_map(|v| v.into_string().ok())
.collect::<Vec<_>>()
},
);
log::debug!("got formats: {:?}", formats);
formats
}
pub fn parse_attr<S: BuildHasher>(
table: &mut HashMap<String, Value, S>,
suffix: &'static str,
) -> Attrs {
let attr =
remove_string_from_config(format!("attrs{suffix}").as_str(), table)
.map_or_else(Attrs::default, |name| {
Attrs::parse(name).unwrap_or_default()
});
log::debug!("got attrs: {:?}", attr);
attr
}
pub fn parse_attrs<S: BuildHasher, const N: usize>(
table: &mut HashMap<String, Value, S>,
suffixes: &[&'static str; N],
) -> [Attrs; N] {
let mut attrs = [const { Attrs::empty() }; N];
let mut config = suffixes.iter();
for attr in &mut attrs {
let suffix = config.next().unwrap();
if let Some(name) = remove_string_from_config(
format!("attrs{suffix}").as_str(),
table,
) {
if let Ok(res) = Attrs::parse(name) {
*attr = res;
}
}
}
log::debug!("got attrs: {:?}", attrs);
attrs
}
pub fn parse_ramp<S: BuildHasher>(
table: &mut HashMap<String, Value, S>,
suffix: &'static str,
) -> Ramp {
let ramp =
remove_string_from_config(format!("ramp{suffix}").as_str(), table)
.and_then(Ramp::parse)
.unwrap_or_default();
log::debug!("got ramps: {:?}", ramp);
ramp
}
pub fn parse_ramps<S: BuildHasher, const N: usize>(
table: &mut HashMap<String, Value, S>,
suffixes: &[&'static str; N],
) -> [Ramp; N] {
let mut ramps = [const { Ramp::empty() }; N];
let mut config = suffixes.iter();
for ramp in &mut ramps {
let suffix = config.next().unwrap();
if let Some(name) = remove_string_from_config(
format!("ramp{suffix}").as_str(),
table,
) {
if let Some(res) = Ramp::parse(name) {
*ramp = res;
}
}
}
log::debug!("got ramps: {:?}", ramps);
ramps
}
pub fn parse_highlight<S: BuildHasher>(
table: &mut HashMap<String, Value, S>,
suffix: &'static str,
) -> Highlight {
let highlight = remove_string_from_config(
format!("highlight{suffix}").as_str(),
table,
)
.and_then(Highlight::parse)
.unwrap_or_default();
log::debug!("got highlights: {:?}", highlight);
highlight
}
pub fn parse_highlights<S: BuildHasher, const N: usize>(
table: &mut HashMap<String, Value, S>,
suffixes: &[&'static str; N],
) -> [Highlight; N] {
let mut highlights = [const { Highlight::empty() }; N];
let mut config = suffixes.iter();
for highlight in &mut highlights {
let suffix = config.next().unwrap();
if let Some(name) = remove_string_from_config(
format!("highlight{suffix}").as_str(),
table,
) {
if let Some(res) = Highlight::parse(name) {
*highlight = res;
}
}
}
log::debug!("got highlights: {:?}", highlights);
highlights
}
pub fn parse_common<S: BuildHasher>(
table: &mut HashMap<String, Value, S>,
) -> Result<Self> {
let mut builder = PanelCommonBuilder::default();
builder.dependence(
match remove_string_from_config("dependence", table)
.map(|s| s.to_lowercase())
.as_deref()
{
Some("left") => Dependence::Left,
Some("right") => Dependence::Right,
Some("both") => Dependence::Both,
_ => Dependence::None,
},
);
log::debug!("got dependence: {:?}", builder.dependence);
builder.actions(Actions::parse(table)?);
log::debug!("got actions: {:?}", builder.actions);
builder.images(remove_array_from_config("images", table).map_or_else(
Vec::new,
|a| {
a.into_iter()
.filter_map(|i| {
Image::parse(
i.into_string()
.map_err(|e| {
log::warn!("Failed to parse string: {e}");
})
.ok()?
.as_str(),
)
.map_err(|e| log::warn!("Failed to parse image: {e}"))
.ok()
})
.collect()
},
));
log::debug!("got images: {:?}", builder.images);
builder
.visible(remove_bool_from_config("visible", table).unwrap_or(true));
Ok(builder.build()?)
}
}