Factor out how widgets are organized
Getty Ritter
5 years ago
| 61 | 61 | let section = section |
| 62 | 62 | .as_table() |
| 63 | 63 | .ok_or_else(|| format_err!("invalid config"))?; |
| 64 | match section["name"] | |
| 65 | .as_str() | |
| 66 | .ok_or_else(|| format_err!("invalid config"))? | |
| 67 | { | |
| 68 | "box" => target.push(Box::new(w::SmallBox)), | |
| 69 | "battery" => target.push(Box::new(w::Battery::new()?)), | |
| 70 | "caesura" => target.push(Box::new(w::Caesura)), | |
| 71 | "sep" => target = &mut conf.right, | |
| 72 | "stdin" => target.push(Box::new(w::Stdin::new())), | |
| 73 | "time" => target.push(Box::new(w::Time::new())), | |
| 74 |
|
|
| 64 | let name = section["name"].as_str().ok_or_else(|| format_err!("invalid config"))?; | |
| 65 | if name == "sep" { | |
| 66 | target = &mut conf.right; | |
| 67 | } else { | |
| 68 | target.push(w::mk_widget(name, section)?); | |
| 75 | 69 | } |
| 76 | 70 | } |
| 77 | 71 |
| 1 | use crate::widgets::widget::{Widget,Drawing,Located}; | |
| 2 | ||
| 3 | pub struct Battery { | |
| 4 | file_list: Vec<std::path::PathBuf>, | |
| 5 | charging: Option<std::path::PathBuf>, | |
| 6 | } | |
| 7 | ||
| 8 | impl Battery { | |
| 9 | pub fn new() -> Result<Battery, failure::Error> { | |
| 10 | use std::fs; | |
| 11 | ||
| 12 | let mut batteries = Vec::new(); | |
| 13 | for entry in fs::read_dir("/sys/class/power_supply")? { | |
| 14 | let e = entry?; | |
| 15 | if e.file_name().to_string_lossy().starts_with("BAT") { | |
| 16 | let mut path = e.path(); | |
| 17 | path.push("capacity"); | |
| 18 | batteries.push(path); | |
| 19 | } | |
| 20 | } | |
| 21 | let ac_path = std::path::Path::new("/sys/class/power_supply/AC/online"); | |
| 22 | ||
| 23 | Ok(Battery { | |
| 24 | file_list: batteries, | |
| 25 | charging: if ac_path.exists() { | |
| 26 | Some(ac_path.to_path_buf()) | |
| 27 | } else { | |
| 28 | None | |
| 29 | }, | |
| 30 | }) | |
| 31 | } | |
| 32 | ||
| 33 | fn is_charging(&self) -> Result<bool, failure::Error> { | |
| 34 | if let Some(path) = &self.charging { | |
| 35 | let is_connected: i32 = std::fs::read_to_string(path)?.trim().parse()?; | |
| 36 | Ok(is_connected != 0) | |
| 37 | } else { | |
| 38 | Ok(false) | |
| 39 | } | |
| 40 | } | |
| 41 | ||
| 42 | fn read_status(&self) -> Result<f64, failure::Error> { | |
| 43 | let charges: Result<Vec<i32>, failure::Error> = self | |
| 44 | .file_list | |
| 45 | .iter() | |
| 46 | .map(|path| Ok(std::fs::read_to_string(path)?.trim().parse()?)) | |
| 47 | .collect(); | |
| 48 | let charges = charges?; | |
| 49 | ||
| 50 | let len = charges.len() as f64; | |
| 51 | let sum: i32 = charges.into_iter().sum(); | |
| 52 | Ok(sum as f64 / len / 100.0) | |
| 53 | } | |
| 54 | } | |
| 55 | ||
| 56 | impl Widget for Battery { | |
| 57 | fn draw(&self, d: &Drawing, loc: Located) -> i32 { | |
| 58 | let amt = self.read_status(); | |
| 59 | let sz = d.size.ht - (d.buffer as i32 * 2); | |
| 60 | let x = loc.target_x(d, sz); | |
| 61 | match amt { | |
| 62 | _ if self.is_charging().unwrap_or(false) => d.ctx.set_source_rgb(0.5, 0.5, 1.0), | |
| 63 | Ok(x) if x < 0.1 => d.ctx.set_source_rgb(1.0, 0.0, 0.0), | |
| 64 | Ok(x) if x < 0.5 => d.ctx.set_source_rgb(1.0, 1.0, 0.0), | |
| 65 | Ok(_) => d.ctx.set_source_rgb(0.0, 1.0, 0.5), | |
| 66 | Err(_) => d.ctx.set_source_rgb(0.0, 0.0, 0.0), | |
| 67 | } | |
| 68 | ||
| 69 | d.ctx.rectangle( | |
| 70 | x, | |
| 71 | d.buffer * 2.0, | |
| 72 | sz as f64 * amt.unwrap_or(1.0), | |
| 73 | sz as f64 - d.buffer * 2.0, | |
| 74 | ); | |
| 75 | d.ctx.fill(); | |
| 76 | ||
| 77 | d.ctx.set_source_rgb(1.0, 1.0, 1.0); | |
| 78 | d.ctx | |
| 79 | .rectangle(x, d.buffer * 2.0, sz as f64, sz as f64 - (d.buffer * 2.0)); | |
| 80 | d.ctx.stroke(); | |
| 81 | ||
| 82 | sz | |
| 83 | } | |
| 84 | } |
| 1 | pub mod battery; | |
| 2 | pub mod standard; | |
| 3 | pub mod widget; | |
| 4 | ||
| 5 | pub use crate::widgets::widget::{Located,Drawing,Size,Widget}; | |
| 6 | ||
| 7 | const ALL_WIDGETS: [(&str, &dyn Fn(&toml::map::Map<String, toml::Value>) -> Result<Box<dyn Widget>, failure::Error>); 5] = [ | |
| 8 | ("box", &|_| Ok(Box::new(standard::Time::new()))), | |
| 9 | ("battery", &|_| Ok(Box::new(battery::Battery::new()?))), | |
| 10 | ("caesura", &|_| Ok(Box::new(standard::Caesura))), | |
| 11 | ("stdin", &|_| Ok(Box::new(standard::Stdin::new()))), | |
| 12 | ("time", &|_| Ok(Box::new(standard::Time::new()))), | |
| 13 | ]; | |
| 14 | ||
| 15 | pub fn mk_widget(name: &str, section: &toml::map::Map<String, toml::Value>) -> Result<Box<dyn Widget>, failure::Error> { | |
| 16 | for (n, f) in ALL_WIDGETS.iter() { | |
| 17 | if n == &name { | |
| 18 | return f(section); | |
| 19 | } | |
| 20 | } | |
| 21 | Err(format_err!("No widget type named {}", name)) | |
| 22 | } |
| 1 | pub use crate::widgets::widget::{Located,Drawing,Size,Widget}; | |
| 2 | ||
| 3 | #[derive(Debug)] | |
| 4 | pub struct Time { | |
| 5 | fmt: String, | |
| 6 | } | |
| 7 | ||
| 8 | impl Time { | |
| 9 | pub fn new() -> Time { | |
| 10 | Time { | |
| 11 | fmt: "%a %b %d %H:%M".to_string(), | |
| 12 | } | |
| 13 | } | |
| 14 | } | |
| 15 | ||
| 16 | impl Widget for Time { | |
| 17 | fn draw(&self, d: &Drawing, loc: Located) -> i32 { | |
| 18 | let now = chrono::Local::now(); | |
| 19 | loc.draw_text(d, &format!("{}", &now.format(&self.fmt))) | |
| 20 | } | |
| 21 | } | |
| 22 | ||
| 23 | #[derive(Debug)] | |
| 24 | pub struct Stdin; | |
| 25 | ||
| 26 | impl Stdin { | |
| 27 | pub fn new() -> Stdin { | |
| 28 | Stdin | |
| 29 | } | |
| 30 | } | |
| 31 | ||
| 32 | impl Widget for Stdin { | |
| 33 | fn draw(&self, d: &Drawing, loc: Located) -> i32 { | |
| 34 | loc.draw_text(d, &d.stdin) | |
| 35 | } | |
| 36 | } | |
| 37 | ||
| 38 | pub struct SmallBox; | |
| 39 | ||
| 40 | impl Widget for SmallBox { | |
| 41 | fn draw(&self, d: &Drawing, loc: Located) -> i32 { | |
| 42 | let sz = d.size.ht - (d.buffer as i32 * 2); | |
| 43 | let x = loc.target_x(d, sz); | |
| 44 | d.ctx.rectangle(x, d.buffer, sz as f64, sz as f64); | |
| 45 | d.ctx.fill(); | |
| 46 | sz | |
| 47 | } | |
| 48 | } | |
| 49 | ||
| 50 | pub struct Caesura; | |
| 51 | ||
| 52 | impl Widget for Caesura { | |
| 53 | fn draw(&self, d: &Drawing, loc: Located) -> i32 { | |
| 54 | let x = loc.target_x(d, 1); | |
| 55 | d.ctx.move_to(x, d.buffer); | |
| 56 | d.ctx.line_to(x, d.size.ht as f64 - d.buffer); | |
| 57 | d.ctx.stroke(); | |
| 58 | 2 | |
| 59 | } | |
| 60 | } |
| 1 | #[derive(Debug, Clone, Copy)] | |
| 2 | pub struct Size { | |
| 3 | pub wd: i32, | |
| 4 | pub ht: i32, | |
| 5 | pub xo: i32, | |
| 6 | pub yo: i32, | |
| 7 | } | |
| 8 | ||
| 9 | #[derive(Debug, Clone, Copy)] | |
| 10 | pub enum Located { | |
| 11 | FromLeft(i32), | |
| 12 | FromRight(i32), | |
| 13 | } | |
| 14 | ||
| 15 | impl Located { | |
| 16 | pub fn draw_text(self, d: &Drawing, msg: &str) -> i32 { | |
| 17 | use pango::LayoutExt; | |
| 18 | d.lyt.set_text(msg); | |
| 19 | let (w, _) = d.lyt.get_size(); | |
| 20 | d.ctx.move_to(self.target_x(d, w / pango::SCALE), d.buffer); | |
| 21 | pangocairo::functions::show_layout(d.ctx, d.lyt); | |
| 22 | w / pango::SCALE | |
| 23 | } | |
| 24 | ||
| 25 | pub fn target_x(self, d: &Drawing, w: i32) -> f64 { | |
| 26 | match self { | |
| 27 | Located::FromLeft(x) => x as f64, | |
| 28 | Located::FromRight(x) => (d.size.wd - (x + w)) as f64, | |
| 29 | } | |
| 30 | } | |
| 31 | } | |
| 32 | ||
| 33 | pub struct Drawing<'t> { | |
| 34 | pub ctx: &'t cairo::Context, | |
| 35 | pub lyt: &'t pango::Layout, | |
| 36 | pub size: Size, | |
| 37 | pub stdin: &'t str, | |
| 38 | pub buffer: f64, | |
| 39 | } | |
| 40 | ||
| 41 | pub trait Widget { | |
| 42 | fn draw(&self, d: &Drawing, loc: Located) -> i32; | |
| 43 | } |
| 1 | use pango::LayoutExt; | |
| 2 | ||
| 3 | #[derive(Debug, Clone, Copy)] | |
| 4 | pub struct Size { | |
| 5 | pub wd: i32, | |
| 6 | pub ht: i32, | |
| 7 | pub xo: i32, | |
| 8 | pub yo: i32, | |
| 9 | } | |
| 10 | ||
| 11 | #[derive(Debug, Clone, Copy)] | |
| 12 | pub enum Located { | |
| 13 | FromLeft(i32), | |
| 14 | FromRight(i32), | |
| 15 | } | |
| 16 | ||
| 17 | impl Located { | |
| 18 | fn draw_text(self, d: &Drawing, msg: &str) -> i32 { | |
| 19 | d.lyt.set_text(msg); | |
| 20 | let (w, _) = d.lyt.get_size(); | |
| 21 | d.ctx.move_to(self.target_x(d, w / pango::SCALE), d.buffer); | |
| 22 | pangocairo::functions::show_layout(d.ctx, d.lyt); | |
| 23 | w / pango::SCALE | |
| 24 | } | |
| 25 | ||
| 26 | fn target_x(self, d: &Drawing, w: i32) -> f64 { | |
| 27 | match self { | |
| 28 | Located::FromLeft(x) => x as f64, | |
| 29 | Located::FromRight(x) => (d.size.wd - (x + w)) as f64, | |
| 30 | } | |
| 31 | } | |
| 32 | } | |
| 33 | ||
| 34 | pub struct Drawing<'t> { | |
| 35 | pub ctx: &'t cairo::Context, | |
| 36 | pub lyt: &'t pango::Layout, | |
| 37 | pub size: Size, | |
| 38 | pub stdin: &'t str, | |
| 39 | pub buffer: f64, | |
| 40 | } | |
| 41 | ||
| 42 | pub trait Widget { | |
| 43 | fn draw(&self, d: &Drawing, loc: Located) -> i32; | |
| 44 | } | |
| 45 | ||
| 46 | #[derive(Debug)] | |
| 47 | pub struct Time { | |
| 48 | fmt: String, | |
| 49 | } | |
| 50 | ||
| 51 | impl Time { | |
| 52 | pub fn new() -> Time { | |
| 53 | Time { | |
| 54 | fmt: "%a %b %d %H:%M".to_string(), | |
| 55 | } | |
| 56 | } | |
| 57 | } | |
| 58 | ||
| 59 | impl Widget for Time { | |
| 60 | fn draw(&self, d: &Drawing, loc: Located) -> i32 { | |
| 61 | let now = chrono::Local::now(); | |
| 62 | loc.draw_text(d, &format!("{}", &now.format(&self.fmt))) | |
| 63 | } | |
| 64 | } | |
| 65 | ||
| 66 | #[derive(Debug)] | |
| 67 | pub struct Stdin; | |
| 68 | ||
| 69 | impl Stdin { | |
| 70 | pub fn new() -> Stdin { | |
| 71 | Stdin | |
| 72 | } | |
| 73 | } | |
| 74 | ||
| 75 | impl Widget for Stdin { | |
| 76 | fn draw(&self, d: &Drawing, loc: Located) -> i32 { | |
| 77 | loc.draw_text(d, &d.stdin) | |
| 78 | } | |
| 79 | } | |
| 80 | ||
| 81 | pub struct SmallBox; | |
| 82 | ||
| 83 | impl Widget for SmallBox { | |
| 84 | fn draw(&self, d: &Drawing, loc: Located) -> i32 { | |
| 85 | let sz = d.size.ht - (d.buffer as i32 * 2); | |
| 86 | let x = loc.target_x(d, sz); | |
| 87 | d.ctx.rectangle(x, d.buffer, sz as f64, sz as f64); | |
| 88 | d.ctx.fill(); | |
| 89 | sz | |
| 90 | } | |
| 91 | } | |
| 92 | ||
| 93 | pub struct Caesura; | |
| 94 | ||
| 95 | impl Widget for Caesura { | |
| 96 | fn draw(&self, d: &Drawing, loc: Located) -> i32 { | |
| 97 | let x = loc.target_x(d, 1); | |
| 98 | d.ctx.move_to(x, d.buffer); | |
| 99 | d.ctx.line_to(x, d.size.ht as f64 - d.buffer); | |
| 100 | d.ctx.stroke(); | |
| 101 | 2 | |
| 102 | } | |
| 103 | } | |
| 104 | ||
| 105 | pub struct Battery { | |
| 106 | file_list: Vec<std::path::PathBuf>, | |
| 107 | charging: Option<std::path::PathBuf>, | |
| 108 | } | |
| 109 | ||
| 110 | impl Battery { | |
| 111 | pub fn new() -> Result<Battery, failure::Error> { | |
| 112 | use std::fs; | |
| 113 | ||
| 114 | let mut batteries = Vec::new(); | |
| 115 | for entry in fs::read_dir("/sys/class/power_supply")? { | |
| 116 | let e = entry?; | |
| 117 | if e.file_name().to_string_lossy().starts_with("BAT") { | |
| 118 | let mut path = e.path(); | |
| 119 | path.push("capacity"); | |
| 120 | batteries.push(path); | |
| 121 | } | |
| 122 | } | |
| 123 | let ac_path = std::path::Path::new("/sys/class/power_supply/AC/online"); | |
| 124 | ||
| 125 | Ok(Battery { | |
| 126 | file_list: batteries, | |
| 127 | charging: if ac_path.exists() { | |
| 128 | Some(ac_path.to_path_buf()) | |
| 129 | } else { | |
| 130 | None | |
| 131 | }, | |
| 132 | }) | |
| 133 | } | |
| 134 | ||
| 135 | fn is_charging(&self) -> Result<bool, failure::Error> { | |
| 136 | if let Some(path) = &self.charging { | |
| 137 | let is_connected: i32 = std::fs::read_to_string(path)?.trim().parse()?; | |
| 138 | Ok(is_connected != 0) | |
| 139 | } else { | |
| 140 | Ok(false) | |
| 141 | } | |
| 142 | } | |
| 143 | ||
| 144 | fn read_status(&self) -> Result<f64, failure::Error> { | |
| 145 | let charges: Result<Vec<i32>, failure::Error> = self | |
| 146 | .file_list | |
| 147 | .iter() | |
| 148 | .map(|path| Ok(std::fs::read_to_string(path)?.trim().parse()?)) | |
| 149 | .collect(); | |
| 150 | let charges = charges?; | |
| 151 | ||
| 152 | let len = charges.len() as f64; | |
| 153 | let sum: i32 = charges.into_iter().sum(); | |
| 154 | Ok(sum as f64 / len / 100.0) | |
| 155 | } | |
| 156 | } | |
| 157 | ||
| 158 | impl Widget for Battery { | |
| 159 | fn draw(&self, d: &Drawing, loc: Located) -> i32 { | |
| 160 | let amt = self.read_status(); | |
| 161 | let sz = d.size.ht - (d.buffer as i32 * 2); | |
| 162 | let x = loc.target_x(d, sz); | |
| 163 | match amt { | |
| 164 | _ if self.is_charging().unwrap_or(false) => d.ctx.set_source_rgb(0.5, 0.5, 1.0), | |
| 165 | Ok(x) if x < 0.1 => d.ctx.set_source_rgb(1.0, 0.0, 0.0), | |
| 166 | Ok(x) if x < 0.5 => d.ctx.set_source_rgb(1.0, 1.0, 0.0), | |
| 167 | Ok(_) => d.ctx.set_source_rgb(0.0, 1.0, 0.5), | |
| 168 | Err(_) => d.ctx.set_source_rgb(0.0, 0.0, 0.0), | |
| 169 | } | |
| 170 | ||
| 171 | d.ctx.rectangle( | |
| 172 | x, | |
| 173 | d.buffer * 2.0, | |
| 174 | sz as f64 * amt.unwrap_or(1.0), | |
| 175 | sz as f64 - d.buffer * 2.0, | |
| 176 | ); | |
| 177 | d.ctx.fill(); | |
| 178 | ||
| 179 | d.ctx.set_source_rgb(1.0, 1.0, 1.0); | |
| 180 | d.ctx | |
| 181 | .rectangle(x, d.buffer * 2.0, sz as f64, sz as f64 - (d.buffer * 2.0)); | |
| 182 | d.ctx.stroke(); | |
| 183 | ||
| 184 | sz | |
| 185 | } | |
| 186 | } |