use serde::Deserialize;
#[derive(Debug, Clone, Deserialize)]
#[serde(default, deny_unknown_fields)]
pub struct Layout {
pub rows: Vec<LayoutRow>,
}
impl Default for Layout {
fn default() -> Self {
let row = |height: u16, panels: &[(&str, u16)]| LayoutRow {
height,
panels: panels
.iter()
.map(|(widget, width)| LayoutPanel {
widget: (*widget).into(),
width: *width,
})
.collect(),
};
Self {
rows: vec![
row(26, &[("clocks", 26), ("calendar", 34), ("weather", 40)]),
row(32, &[("todo", 34), ("agenda", 30), ("notes", 36)]),
row(24, &[("news", 55), ("watchlog", 45)]),
row(
18,
&[
("pomodoro", 24),
("stocks", 28),
("cpu", 22),
("network", 26),
],
),
],
}
}
}
impl Layout {
pub fn widgets(&self) -> Vec<&str> {
self.rows
.iter()
.flat_map(|row| row.panels.iter())
.map(|panel| panel.widget.as_str())
.collect()
}
pub fn places(&self, widget: &str) -> bool {
self.widgets().contains(&widget)
}
pub fn add_widget(&mut self, widget: &str) {
if self.places(widget) {
return;
}
let target = self
.rows
.iter()
.enumerate()
.min_by_key(|(index, row)| (row.panels.len(), usize::MAX - index))
.map(|(index, _)| index);
let Some(index) = target else {
self.rows.push(LayoutRow {
height: 100,
panels: vec![LayoutPanel {
widget: widget.to_string(),
width: 100,
}],
});
return;
};
let row = &mut self.rows[index];
let width = if row.panels.is_empty() {
100
} else {
let total: u32 = row.panels.iter().map(|p| u32::from(p.width)).sum();
u16::try_from(total / row.panels.len() as u32)
.unwrap_or(25)
.max(1)
};
row.panels.push(LayoutPanel {
widget: widget.to_string(),
width,
});
}
pub fn remove_widget(&mut self, widget: &str) -> bool {
if self.widgets().iter().all(|placed| *placed == widget) {
return false;
}
for row in &mut self.rows {
row.panels.retain(|panel| panel.widget != widget);
}
self.rows.retain(|row| !row.panels.is_empty());
true
}
}
#[derive(Debug, Clone, Deserialize)]
#[serde(default, deny_unknown_fields)]
pub struct LayoutRow {
pub height: u16,
pub panels: Vec<LayoutPanel>,
}
impl Default for LayoutRow {
fn default() -> Self {
Self {
height: 1,
panels: Vec::new(),
}
}
}
#[derive(Debug, Clone, Deserialize)]
#[serde(default, deny_unknown_fields)]
pub struct LayoutPanel {
pub widget: String,
pub width: u16,
}
impl Default for LayoutPanel {
fn default() -> Self {
Self {
widget: String::new(),
width: 1,
}
}
}