use crate::widgets as w; mod defaults { pub const BG_COLOR: (f64, f64, f64) = (0.1, 0.1, 0.1); pub const FG_COLOR: (f64, f64, f64) = (1.0, 1.0, 1.0); pub const FONT_FAMILY: &'static str = "Fira Mono"; pub const FONT_SIZE: &'static str = "18"; } pub struct Config { left: Vec>, right: Vec>, bg_color: (f64, f64, f64), fg_color: (f64, f64, f64), font: String, } pub fn color_from_hex(input: &str) -> Result<(f64, f64, f64), failure::Error> { let s = input.trim_start_matches("0x"); let s = s.trim_start_matches(|c| !"ABCDEFabcdef0123456789".contains(c)); match s.len() { 6 => { let r = i64::from_str_radix(&s[0..2], 16)? as f64 / 255.0; let g = i64::from_str_radix(&s[2..4], 16)? as f64 / 255.0; let b = i64::from_str_radix(&s[4..6], 16)? as f64 / 255.0; Ok((r, g, b)) } 3 => { let r = i64::from_str_radix(&s[0..1], 16)? as f64 / 255.0; let g = i64::from_str_radix(&s[1..2], 16)? as f64 / 255.0; let b = i64::from_str_radix(&s[2..3], 16)? as f64 / 255.0; Ok((r, g, b)) } _ => bail!("Unable to parse {} as a hex color literal", input), } } impl Config { pub fn from_toml(input: toml::Value) -> Result { let mut conf = Config { left: Vec::new(), right: Vec::new(), bg_color: defaults::BG_COLOR, fg_color: defaults::FG_COLOR, font: format!("{} {}", defaults::FONT_FAMILY, defaults::FONT_SIZE), }; let table = input.as_table().ok_or(format_err!("invalid config"))?; let widgets = &table["widgets"]; let mut target = &mut conf.left; for section in widgets.as_array().ok_or(format_err!("invalid config"))? { let section = section.as_table().ok_or(format_err!("invalid config"))?; match section["name"].as_str().ok_or(format_err!(""))? { "box" => target.push(Box::new(w::SmallBox)), "battery" => target.push(Box::new(w::Battery::new()?)), "sep" => target = &mut conf.right, "stdin" => target.push(Box::new(w::Stdin::new())), "time" => target.push(Box::new(w::Time::new())), _ => (), } } if let Some(color) = table.get("background") { conf.bg_color = color_from_hex(color.as_str().ok_or(format_err!("`background` not a str"))?)?; } if let Some(color) = table.get("foreground") { conf.fg_color = color_from_hex(color.as_str().ok_or(format_err!("`foreground` not a str"))?)?; } if let Some(font) = table.get("font") { conf.font = font.as_str().ok_or(format_err!("`font` not a str"))?.to_string(); } conf.right.reverse(); Ok(conf) } pub fn from_file(path: impl AsRef) -> Result { let body = std::fs::read_to_string(path)?; let val = body.parse::()?; Config::from_toml(val) } pub fn find_config() -> Result { if let Some(p) = xdg::BaseDirectories::new()?.find_config_file("knurling/knurling.toml") { return Config::from_file(p); } Err(format_err!("Unable to find `knurling.toml`")) } pub fn draw(&self, ctx: &cairo::Context, layout: &pango::Layout, stdin: &str, size: w::Size) -> Result<(), failure::Error>{ // paint the background { let (r, g, b) = self.bg_color; ctx.set_source_rgb(r, g, b); } ctx.paint(); // set the foreground color for drawing { let (r, g, b) = self.fg_color; ctx.set_source_rgb(r, g, b); } // set up a struct with everything that widgets need to draw let d = w::Drawing { ctx: ctx, lyt: &layout, size, stdin, }; let mut offset = 10; for w in self.left.iter() { offset += 10 + w.draw(&d, w::Located::FromLeft(offset)); } offset = 10; for w in self.right.iter() { offset += 10 + w.draw(&d, w::Located::FromRight(offset)); } Ok(()) } pub fn font(&self) -> &str { &self.font } }