config.rs 5.4 KB

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