config.rs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  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. "caesura" => target.push(Box::new(w::Caesura)),
  56. "sep" => target = &mut conf.right,
  57. "stdin" => target.push(Box::new(w::Stdin::new())),
  58. "time" => target.push(Box::new(w::Time::new())),
  59. _ => (),
  60. }
  61. }
  62. if let Some(color) = table.get("background") {
  63. conf.bg_color = color_from_hex(color.as_str().ok_or(format_err!("`background` not a str"))?)?;
  64. }
  65. if let Some(color) = table.get("foreground") {
  66. conf.fg_color = color_from_hex(color.as_str().ok_or(format_err!("`foreground` not a str"))?)?;
  67. }
  68. if let Some(font) = table.get("font") {
  69. conf.font = font.as_str().ok_or(format_err!("`font` not a str"))?.to_string();
  70. }
  71. conf.right.reverse();
  72. let text_height = conf.calc_text_height();
  73. let buffer = text_height / 4;
  74. conf.height = conf.calc_text_height() + buffer * 2;
  75. conf.buffer = buffer;
  76. Ok(conf)
  77. }
  78. pub fn from_file(path: impl AsRef<std::path::Path>) -> Result<Config, failure::Error> {
  79. let body = std::fs::read_to_string(path)?;
  80. let val = body.parse::<toml::Value>()?;
  81. Config::from_toml(val)
  82. }
  83. pub fn find_config() -> Result<Config, failure::Error> {
  84. if let Some(p) = xdg::BaseDirectories::new()?.find_config_file("knurling/knurling.toml") {
  85. return Config::from_file(p);
  86. }
  87. Err(format_err!("Unable to find `knurling.toml`"))
  88. }
  89. pub fn draw(&self, ctx: &cairo::Context, layout: &pango::Layout, stdin: &str, size: w::Size) -> Result<(), failure::Error>{
  90. // paint the background
  91. {
  92. let (r, g, b) = self.bg_color;
  93. ctx.set_source_rgb(r, g, b);
  94. }
  95. ctx.paint();
  96. // set the foreground color for drawing
  97. {
  98. let (r, g, b) = self.fg_color;
  99. ctx.set_source_rgb(r, g, b);
  100. }
  101. // set up a struct with everything that widgets need to draw
  102. let d = w::Drawing {
  103. ctx: ctx,
  104. lyt: &layout,
  105. size,
  106. stdin,
  107. buffer: self.buffer as f64,
  108. };
  109. let mut offset = 10;
  110. for w in self.left.iter() {
  111. offset += 10 + w.draw(&d, w::Located::FromLeft(offset));
  112. }
  113. offset = 10;
  114. for w in self.right.iter() {
  115. offset += 10 + w.draw(&d, w::Located::FromRight(offset));
  116. }
  117. Ok(())
  118. }
  119. pub fn font(&self) -> &str {
  120. &self.font
  121. }
  122. pub fn get_height(&self) -> i32{
  123. self.height
  124. }
  125. fn calc_text_height(&self) -> i32 {
  126. use pango::LayoutExt;
  127. // we get the height here by making a fake surface, rendering
  128. // some text using our chosen font to it, and seeing how big it ends up being
  129. let surf = cairo::ImageSurface::create(
  130. cairo::Format::Rgb24, 0, 0).unwrap();
  131. let ctx = cairo::Context::new(&surf);
  132. let layout = pangocairo::functions::create_layout(&ctx).unwrap();
  133. layout.set_width(800 * pango::SCALE);
  134. let mut font = pango::FontDescription::from_string(self.font());
  135. font.set_weight(pango::Weight::Bold);
  136. layout.set_font_description(&font);
  137. layout.set_text("lj");
  138. let (_, h) = layout.get_size();
  139. (h / pango::SCALE)
  140. }
  141. }