Dynamic widget configuration and layout

1. Implement new layout API, replacing previous hard-coded layout.
2. Make the widgets (previously modules) configurable.
3. Split some widgets into smaller modules.
4. Configuration file (reluctantly JSON at time of writing).
5. More correct damage handling, copying damage to next buffer.
This commit is contained in:
Kenny Levinsen
2019-09-06 21:54:04 +02:00
parent 4053bb8973
commit b53a909e1c
23 changed files with 1937 additions and 1157 deletions
+202
View File
@@ -0,0 +1,202 @@
use crate::cmd::Cmd;
use crate::widget;
use crate::widgets;
use std::sync::mpsc::Sender;
use serde::{Deserialize, Serialize};
use std::default::Default;
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub enum Widget {
Margin {
margins: (u32, u32, u32, u32),
widget: Box<Widget>,
},
Fixed {
width: u32,
height: u32,
widget: Box<Widget>,
},
HorizontalLayout(Vec<Box<Widget>>),
VerticalLayout(Vec<Box<Widget>>),
Clock {
font_size: f32,
},
Date {
font_size: f32,
},
Calendar {
font_size: f32,
sections: u32,
},
Launcher {
font_size: f32,
length: u32,
#[serde(default)]
app_opener: String,
#[serde(default)]
term_opener: String,
#[serde(default)]
url_opener: String,
},
Battery {
font_size: f32,
length: u32,
},
Backlight {
font_size: f32,
length: u32,
},
PulseAudio {
font_size: f32,
length: u32,
},
}
impl Widget {
pub fn construct(self, tx: Sender<Cmd>) -> Option<Box<dyn widget::Widget + Send>> {
match self {
Widget::Margin {
margins,
widget,
} => match widget.construct(tx.clone()) {
Some(w) => Some(widget::Margin::new(margins, w)),
None => None,
},
Widget::Fixed {
width,
height,
widget,
} => match widget.construct(tx.clone()) {
Some(w) => Some(widget::Fixed::new((width, height), w)),
None => None,
},
Widget::HorizontalLayout(widgets) => Some(widget::HorizontalLayout::new(
widgets
.into_iter()
.map(|x| x.construct(tx.clone()))
.filter(|x| x.is_some())
.map(|x| x.unwrap())
.collect(),
)),
Widget::VerticalLayout(widgets) => Some(widget::VerticalLayout::new(
widgets
.into_iter()
.map(|x| x.construct(tx.clone()))
.filter(|x| x.is_some())
.map(|x| x.unwrap())
.collect(),
)),
Widget::Clock { font_size } => Some(widgets::clock::Clock::new(font_size, tx.clone())),
Widget::Date { font_size } => Some(widgets::date::Date::new(font_size)),
Widget::Calendar {
font_size,
sections,
} => Some(widgets::calendar::Calendar::new(font_size, sections)),
Widget::Launcher { font_size, length, app_opener, term_opener, url_opener } => Some(widgets::launcher::Launcher::new(
font_size,
length,
tx.clone(),
app_opener,
term_opener,
if url_opener.len() == 0 { "xdg_open ".to_string() } else { url_opener },
)),
Widget::Battery { font_size, length } => {
match widgets::battery::UpowerBattery::new(font_size, length, tx.clone()) {
Ok(w) => Some(w),
Err(_) => None,
}
}
Widget::Backlight { font_size, length } => {
match widgets::backlight::Backlight::new(font_size, length) {
Ok(w) => Some(w),
Err(_) => None,
}
}
Widget::PulseAudio { font_size, length } => {
match widgets::sound::PulseAudio::new(font_size, length, tx.clone()) {
Ok(w) => Some(w),
Err(_) => None,
}
}
}
}
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub enum OutputMode {
All,
Active,
}
impl Default for OutputMode {
fn default() -> Self {
OutputMode::Active
}
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Config {
pub widget: Widget,
pub output_mode: OutputMode,
pub scale: u32,
}
impl Default for Config {
fn default() -> Self {
Config{
widget: Widget::Margin {
margins: (20, 20, 20, 20),
widget: Box::new(Widget::VerticalLayout(vec![
Box::new(Widget::HorizontalLayout(vec![
Box::new(Widget::Margin {
margins: (0, 88, 0, 32),
widget: Box::new(Widget::VerticalLayout(vec![
Box::new(Widget::Date { font_size: 64.0 }),
Box::new(Widget::Clock { font_size: 256.0 }),
])),
}),
Box::new(Widget::VerticalLayout(vec![
Box::new(Widget::Margin {
margins: (0, 0, 0, 8),
widget: Box::new(Widget::Battery {
font_size: 24.0,
length: 600,
}),
}),
Box::new(Widget::Margin {
margins: (0, 0, 0, 8),
widget: Box::new(Widget::Backlight {
font_size: 24.0,
length: 600,
}),
}),
Box::new(Widget::Margin {
margins: (0, 0, 0, 8),
widget: Box::new(Widget::PulseAudio {
font_size: 24.0,
length: 600,
}),
}),
])),
])),
Box::new(Widget::Calendar {
font_size: 16.0,
sections: 3,
}),
Box::new(Widget::Launcher {
font_size: 32.0,
length: 1200,
app_opener: "".to_string(),
term_opener: "".to_string(),
url_opener: "".to_string(),
}),
])),
},
output_mode: Default::default(),
scale: 1,
}
}
}