config.rs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. use crate::widgets as w;
  2. mod defaults {
  3. pub const BG_COLOR: (f64, f64, f64) = (0.1, 0.1, 0.1);
  4. pub const FG_COLOR: (f64, f64, f64) = (1.0, 1.0, 1.0);
  5. pub const FONT_FAMILY: &'static str = "Fira Mono";
  6. pub const FONT_SIZE: &'static str = "18";
  7. }
  8. pub struct Config {
  9. left: Vec<Box<w::Widget>>,
  10. right: Vec<Box<w::Widget>>,
  11. bg_color: (f64, f64, f64),
  12. fg_color: (f64, f64, f64),
  13. font: String,
  14. }
  15. pub fn color_from_hex(input: &str) -> Result<(f64, f64, f64), failure::Error> {
  16. let s = input.trim_start_matches("0x");
  17. let s = s.trim_start_matches(|c| !"ABCDEFabcdef0123456789".contains(c));
  18. match s.len() {
  19. 6 => {
  20. let r = i64::from_str_radix(&s[0..2], 16)? as f64 / 255.0;
  21. let g = i64::from_str_radix(&s[2..4], 16)? as f64 / 255.0;
  22. let b = i64::from_str_radix(&s[4..6], 16)? as f64 / 255.0;
  23. Ok((r, g, b))
  24. }
  25. 3 => {
  26. let r = i64::from_str_radix(&s[0..1], 16)? as f64 / 255.0;
  27. let g = i64::from_str_radix(&s[1..2], 16)? as f64 / 255.0;
  28. let b = i64::from_str_radix(&s[2..3], 16)? as f64 / 255.0;
  29. Ok((r, g, b))
  30. }
  31. _ => bail!("Unable to parse {} as a hex color literal", input),
  32. }
  33. }
  34. impl Config {
  35. pub fn from_toml(input: toml::Value) -> Result<Config, failure::Error> {
  36. let mut conf = Config {
  37. left: Vec::new(),
  38. right: Vec::new(),
  39. bg_color: defaults::BG_COLOR,
  40. fg_color: defaults::FG_COLOR,
  41. font: format!("{} {}", defaults::FONT_FAMILY, defaults::FONT_SIZE),
  42. };
  43. let table = input.as_table().ok_or(format_err!("invalid config"))?;
  44. let widgets = &table["widgets"];
  45. let mut target = &mut conf.left;
  46. for section in widgets.as_array().ok_or(format_err!("invalid config"))? {
  47. let section = section.as_table().ok_or(format_err!("invalid config"))?;
  48. match section["name"].as_str().ok_or(format_err!(""))? {
  49. "box" => target.push(Box::new(w::SmallBox)),
  50. "battery" => target.push(Box::new(w::Battery::new()?)),
  51. "sep" => target = &mut conf.right,
  52. "stdin" => target.push(Box::new(w::Stdin::new())),
  53. "time" => target.push(Box::new(w::Time::new())),
  54. _ => (),
  55. }
  56. }
  57. if let Some(color) = table.get("background") {
  58. conf.bg_color = color_from_hex(color.as_str().ok_or(format_err!("`background` not a str"))?)?;
  59. }
  60. if let Some(color) = table.get("foreground") {
  61. conf.fg_color = color_from_hex(color.as_str().ok_or(format_err!("`foreground` not a str"))?)?;
  62. }
  63. if let Some(font) = table.get("font") {
  64. conf.font = font.as_str().ok_or(format_err!("`font` not a str"))?.to_string();
  65. }
  66. conf.right.reverse();
  67. Ok(conf)
  68. }
  69. pub fn from_file(path: impl AsRef<std::path::Path>) -> Result<Config, failure::Error> {
  70. let body = std::fs::read_to_string(path)?;
  71. let val = body.parse::<toml::Value>()?;
  72. Config::from_toml(val)
  73. }
  74. pub fn find_config() -> Result<Config, failure::Error> {
  75. if let Some(p) = xdg::BaseDirectories::new()?.find_config_file("knurling/knurling.toml") {
  76. return Config::from_file(p);
  77. }
  78. Err(format_err!("Unable to find `knurling.toml`"))
  79. }
  80. pub fn draw(&self, ctx: &cairo::Context, layout: &pango::Layout, stdin: &str, size: w::Size) -> Result<(), failure::Error>{
  81. // paint the background
  82. {
  83. let (r, g, b) = self.bg_color;
  84. ctx.set_source_rgb(r, g, b);
  85. }
  86. ctx.paint();
  87. // set the foreground color for drawing
  88. {
  89. let (r, g, b) = self.fg_color;
  90. ctx.set_source_rgb(r, g, b);
  91. }
  92. // set up a struct with everything that widgets need to draw
  93. let d = w::Drawing {
  94. ctx: ctx,
  95. lyt: &layout,
  96. size,
  97. stdin,
  98. };
  99. let mut offset = 10;
  100. for w in self.left.iter() {
  101. offset += 10 + w.draw(&d, w::Located::FromLeft(offset));
  102. }
  103. offset = 10;
  104. for w in self.right.iter() {
  105. offset += 10 + w.draw(&d, w::Located::FromRight(offset));
  106. }
  107. Ok(())
  108. }
  109. pub fn font(&self) -> &str {
  110. &self.font
  111. }
  112. pub fn get_height(&self) -> i32 {
  113. use pango::LayoutExt;
  114. // we get the height here by making a fake surface, rendering
  115. // some text using our chosen font to it, and seeing how big it ends up being
  116. let surf = cairo::ImageSurface::create(
  117. cairo::Format::Rgb24, 0, 0).unwrap();
  118. let ctx = cairo::Context::new(&surf);
  119. let layout = pangocairo::functions::create_layout(&ctx).unwrap();
  120. layout.set_width(800 * pango::SCALE);
  121. let mut font = pango::FontDescription::from_string(self.font());
  122. font.set_weight(pango::Weight::Bold);
  123. layout.set_font_description(&font);
  124. layout.set_text("lj");
  125. let (_, h) = layout.get_size();
  126. (h / pango::SCALE) + 8
  127. }
  128. }