pub mod containers;
pub mod battery;
pub mod clock;
pub mod cpu;
pub mod icon_text;
pub mod keyboard;
pub mod text;
use std::{
cell::{Ref, RefMut},
fmt::Display,
ops::{Add, AddAssign},
rc::Rc,
};
use anyhow::Result;
use serde::Deserialize;
use thiserror::Error;
use crate::{
root::Environment,
services::{ProcessSettings, ServiceList, ServiceNew},
util::Color,
};
use {battery::BatterySettings, clock::ClockSettings, cpu::CPUSettings, text::TextSettings};
pub trait Widget {
fn name(&self) -> WidgetList;
fn bind(&mut self, env: Rc<Environment>) -> Result<(), WidgetError>;
fn env(&self) -> Option<Rc<Environment>>;
fn prepare(&self) -> Result<(), WidgetError> {
todo!()
}
fn draw(&self) -> Result<(), WidgetError>;
fn init(&self) -> Result<(), WidgetError>;
fn data(&self) -> Ref<'_, WidgetData>;
fn data_mut(&self) -> RefMut<'_, WidgetData>;
fn try_data(&self) -> Ref<'_, WidgetData> {
todo!()
}
fn try_data_mut(&self) -> RefMut<'_, WidgetData> {
todo!()
}
fn as_styled(&self) -> Option<&dyn WidgetStyled> {
None
}
}
pub trait WidgetNew: Widget {
type Settings;
fn new(env: Option<Rc<Environment>>, settings: Self::Settings) -> Result<Self, WidgetError>
where
Self: Sized;
}
#[derive(Debug, Error)]
pub enum WidgetError {
#[error("Invalid widget bounds")]
InvalidBounds,
#[error("Trying to draw a widget \"{0}\" not bound to any environment")]
DrawWithNoEnv(WidgetList),
#[error("Trying to initialise a widget \"{0}\" not bound to any environment")]
InitWithNoEnv(WidgetList),
#[error(
"When initialising widget \"{0}\" no coresponding signal was found.
Maybe service \"{1}\" was not created?"
)]
NoCorespondingSignal(WidgetList, ServiceList),
#[error(
"Style initialisation is invalid in widget \"{0}\". Data can not be borrowed mutably."
)]
StyleInitDataBorrowed(WidgetList),
#[error(transparent)]
Custom(#[from] anyhow::Error),
}
#[derive(Default, Debug, Clone, Copy, Deserialize)]
pub struct Position(pub usize, pub usize);
impl AddAssign for Position {
fn add_assign(&mut self, rhs: Self) {
self.0 += rhs.0;
self.1 += rhs.1;
}
}
impl AddAssign<(usize, usize)> for Position {
fn add_assign(&mut self, rhs: (usize, usize)) {
self.0 += rhs.0;
self.1 += rhs.1;
}
}
impl Add for Position {
type Output = Position;
fn add(self, rhs: Self) -> Self::Output {
Position(self.0 + rhs.0, self.1 + rhs.1)
}
}
impl Add<(usize, usize)> for Position {
type Output = Position;
fn add(self, rhs: (usize, usize)) -> Self::Output {
Position(self.0 + rhs.0, self.1 + rhs.1)
}
}
#[derive(Default, Debug, Clone, Copy, Deserialize)]
pub struct WidgetData {
#[serde(default)]
pub position: Position,
#[serde(default)]
pub width: usize,
#[serde(default)]
pub height: usize,
}
impl WidgetData {
pub const fn default() -> Self {
Self {
position: Position(0, 0),
width: 0,
height: 0,
}
}
}
#[derive(Default, Debug, Clone, Copy, Deserialize)]
pub struct Margin {
pub left: usize,
pub right: usize,
pub up: usize,
pub down: usize,
}
impl Margin {
pub const fn default() -> Self {
Self {
left: 0,
right: 0,
up: 0,
down: 0,
}
}
}
#[derive(Default, Debug, Clone, Copy, Deserialize)]
pub struct Style {
pub background: Option<Color>,
pub border: Option<(usize, Color)>,
#[serde(default)]
pub margin: Margin,
}
impl Style {
pub const fn default() -> Self {
Self {
background: None,
border: None,
margin: Margin::default(),
}
}
}
pub trait WidgetStyled: Widget {
fn style(&self) -> &Style;
fn apply_style(&self) -> Result<(), WidgetError> {
let mut data = self.data_mut();
let style = self.style();
let border = match style.border {
Some(a) => (a.0, Some(a.1)),
None => (0, None),
};
data.height += border.0 * 2;
data.width += style.margin.left + style.margin.right;
data.height += style.margin.up + style.margin.down;
Ok(())
}
fn draw_style(&self) -> Result<(), WidgetError> {
if self.env().is_none() {
return Err(WidgetError::DrawWithNoEnv(self.name()));
}
let env = self.env().unwrap();
let style = self.style();
let border = style.border.unwrap_or((0, Color::NONE));
let mut data = self.data_mut();
data.position.0 += style.margin.left;
data.position.1 += style.margin.up;
let mut drawer = env.as_ref().drawer.borrow_mut();
if let Some(color) = style.background {
for x in border.0..data.width - border.0 {
for y in border.0..data.height - border.0 {
drawer.draw_pixel(&data, (x, y), color);
}
}
}
if border.1 == Color::NONE {
return Ok(());
}
for x in 0..border.0 {
for y in 0..data.height {
drawer.draw_pixel(&data, (x, y), border.1);
drawer.draw_pixel(&data, (data.width - 1 - x, y), border.1);
}
}
for x in 0..data.width {
for y in 0..border.0 {
drawer.draw_pixel(&data, (x, y), border.1);
drawer.draw_pixel(&data, (x, data.height - 1 - y), border.1);
}
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub enum WidgetList {
Text,
IconText,
Clock,
Battery,
CPU,
Keyboard,
Row,
Bar,
Custom(String),
}
impl Display for WidgetList {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Text => write!(f, "Text"),
Self::IconText => write!(f, "Text"),
Self::Clock => write!(f, "Clock"),
Self::Battery => write!(f, "Battery"),
Self::CPU => write!(f, "Cpu"),
Self::Keyboard => write!(f, "Keyboard"),
Self::Row => write!(f, "Row"),
Self::Bar => write!(f, "Bar"),
Self::Custom(name) => write!(f, "{name}"),
}
}
}
#[derive(Deserialize, Debug, Clone)]
#[serde(tag = "widget", content = "settings", rename_all = "snake_case")]
pub enum WidgetsSettingsList {
Text(TextSettings),
Clock(ClockSettings),
Battery(BatterySettings),
#[serde(rename = "cpu")]
CPU(CPUSettings),
Keyboard(keyboard::KeyboardSettings, ProcessSettings),
Custom(String),
}
impl WidgetsSettingsList {
pub fn create_in_container(
&self,
container: &mut impl containers::ContainerSingle,
) -> Result<(), WidgetError> {
match self {
WidgetsSettingsList::Text(settings) => {
container.create_widget(text::Text::new, settings.clone())
}
WidgetsSettingsList::Clock(settings) => {
container.create_widget(clock::Clock::new, settings.clone())
}
WidgetsSettingsList::Battery(settings) => {
container.create_widget(battery::Battery::new, settings.clone())
}
WidgetsSettingsList::CPU(settings) => {
container.create_widget(cpu::CPU::new, settings.clone())
}
WidgetsSettingsList::Keyboard(wsettings, psettings) => {
container.create_service(crate::services::clients::Keyboard::new, *psettings)?;
container.create_widget(keyboard::Keyboard::new, wsettings.clone())
}
WidgetsSettingsList::Custom(_) => {
todo!()
}
}
}
}