266 lines
9 KiB
Rust
266 lines
9 KiB
Rust
use std::cmp;
|
|
|
|
use chrono::prelude::*;
|
|
use hyprland::data::Workspaces;
|
|
use hyprland::event_listener;
|
|
use hyprland::shared::{HyprData, WorkspaceType};
|
|
use iced::font::Weight;
|
|
use iced::widget::{container, horizontal_space, row, text, Text};
|
|
use iced::{
|
|
time, Background, Border, Color, Element, Event, Font, Length, Shadow, Subscription, Task,
|
|
Theme, Vector,
|
|
};
|
|
|
|
use iced_layershell::build_pattern::{application, MainSettings};
|
|
use iced_layershell::reexport::{Anchor, KeyboardInteractivity};
|
|
use iced_layershell::settings::LayerShellSettings;
|
|
use iced_layershell::to_layer_message;
|
|
use iced_runtime::futures::event;
|
|
use sysinfo::{Disks, System};
|
|
use tracing::level_filters::LevelFilter;
|
|
use tracing::{debug, error};
|
|
use tracing_subscriber::EnvFilter;
|
|
|
|
fn main() -> iced_layershell::Result {
|
|
let timer =
|
|
tracing_subscriber::fmt::time::ChronoLocal::new("%Y-%m-%d_%I:%M:%S%.6f %P".to_owned());
|
|
let filter = EnvFilter::builder()
|
|
.with_default_directive(LevelFilter::WARN.into())
|
|
.parse_lossy("shelly=debug");
|
|
tracing_subscriber::FmtSubscriber::builder()
|
|
.pretty()
|
|
.with_line_number(true)
|
|
.with_level(true)
|
|
.with_target(true)
|
|
.with_env_filter(filter)
|
|
.with_target(true)
|
|
.with_timer(timer)
|
|
.init();
|
|
|
|
debug!("Starting shelly");
|
|
|
|
let mut font = Font::with_name("VictorMono Nerd Font");
|
|
font.weight = Weight::Bold;
|
|
let settings = MainSettings {
|
|
layer_settings: LayerShellSettings {
|
|
size: Some((1400, 60)),
|
|
anchor: Anchor::Bottom | Anchor::Left | Anchor::Right,
|
|
margin: (0, 0, 0, 0),
|
|
exclusive_zone: 60,
|
|
keyboard_interactivity: KeyboardInteractivity::None,
|
|
..Default::default()
|
|
},
|
|
default_font: font,
|
|
// antialiasing: true,
|
|
..Default::default()
|
|
};
|
|
|
|
application(Panel::namespace, Panel::update, Panel::view)
|
|
.subscription(Panel::subscription)
|
|
.subscription(|_| {
|
|
time::every(time::Duration::from_millis(1000)).map(|_| Message::Time(Local::now()))
|
|
})
|
|
.settings(settings)
|
|
.style(Panel::style)
|
|
.run_with(Panel::new)
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct Panel {
|
|
time: String,
|
|
cpu: String,
|
|
memory: String,
|
|
battery: String,
|
|
disk: String,
|
|
system: System,
|
|
disks: Disks,
|
|
workspaces: Workspaces,
|
|
active_workspace: i32,
|
|
apps: Vec<String>,
|
|
}
|
|
|
|
#[to_layer_message]
|
|
#[derive(Debug, Clone)]
|
|
enum Message {
|
|
Launch(usize),
|
|
Time(DateTime<Local>),
|
|
IcedEvent(Event),
|
|
WorkspaceChange(WorkspaceType),
|
|
}
|
|
|
|
impl Panel {
|
|
fn new() -> (Self, Task<Message>) {
|
|
let workspaces = Workspaces::get().map_err(|e| {
|
|
error!("Couldn't get hyprland info: {}", e);
|
|
e
|
|
});
|
|
(
|
|
Self {
|
|
system: System::new_all(),
|
|
disks: Disks::new_with_refreshed_list(),
|
|
workspaces: workspaces.unwrap(),
|
|
active_workspace: 1,
|
|
time: String::new(),
|
|
cpu: String::new(),
|
|
memory: String::new(),
|
|
battery: String::new(),
|
|
disk: String::new(),
|
|
apps: vec![String::new()],
|
|
},
|
|
Task::none(),
|
|
)
|
|
}
|
|
fn namespace(&self) -> String {
|
|
String::from("panel")
|
|
}
|
|
|
|
fn update(&mut self, message: Message) -> Task<Message> {
|
|
match message {
|
|
Message::Launch(index) => {}
|
|
Message::Time(instant) => {
|
|
self.system.refresh_memory();
|
|
self.system.refresh_cpu_usage();
|
|
// disk.refresh();
|
|
// let used_space = disk.available_space() - disk.total_space();
|
|
let disk = self.disks.first_mut();
|
|
if let Some(disk) = disk {
|
|
self.disk = format!(
|
|
" {}",
|
|
convert((disk.total_space() - disk.available_space()) as f64)
|
|
);
|
|
};
|
|
// let used_space = 393;
|
|
// self.disk = format!(" {}", used_space);
|
|
self.cpu = format!(" {}%", self.system.global_cpu_usage().round());
|
|
let memory = ((self.system.used_memory() as f64
|
|
/ self.system.total_memory() as f64)
|
|
.fract()
|
|
* 100.0)
|
|
.trunc();
|
|
self.memory = format!(" {}%", memory);
|
|
self.time = instant.format("%a %b %d, %I:%M %p").to_string();
|
|
}
|
|
Message::AnchorChange(anchor) => todo!(),
|
|
Message::AnchorSizeChange(anchor, _) => todo!(),
|
|
Message::LayerChange(layer) => todo!(),
|
|
Message::MarginChange(_) => todo!(),
|
|
Message::SizeChange(_) => todo!(),
|
|
Message::VirtualKeyboardPressed { time, key } => todo!(),
|
|
Message::IcedEvent(event) => {
|
|
debug!(?event)
|
|
}
|
|
_ => unreachable!(),
|
|
Message::WorkspaceChange(workspace_type) => todo!(),
|
|
}
|
|
Task::none()
|
|
}
|
|
|
|
fn subscription(&self) -> Subscription<Message> {
|
|
event::listen().map(Message::IcedEvent)
|
|
}
|
|
|
|
// fn hyprland_subscription(&self) -> Subscription<Message> {
|
|
// let mut event_listener = event_listener::EventListener::new();
|
|
// event_listener.add_workspace_changed_handler(|x| Message::WorkspaceChange(x));
|
|
// Subscription::run(|| event_listener.start_listener())
|
|
// }
|
|
|
|
fn view(&self) -> Element<Message> {
|
|
let workspaces: Vec<Element<'_, Message>> = self
|
|
.workspaces
|
|
.iter()
|
|
.map(|w| {
|
|
text!("{}", {
|
|
match w.id {
|
|
1 => "".to_owned(),
|
|
2 => "".to_owned(),
|
|
3 => "".to_owned(),
|
|
4 => "".to_owned(),
|
|
5 => "".to_owned(),
|
|
6 => "".to_owned(),
|
|
7 => "".to_owned(),
|
|
8 => "".to_owned(),
|
|
9 => "".to_owned(),
|
|
_ => w.name.clone(),
|
|
}
|
|
})
|
|
.color({
|
|
if w.id == self.active_workspace {
|
|
Color::parse("#ff6ac1").unwrap()
|
|
} else {
|
|
Color::parse("#57c7ff").unwrap()
|
|
}
|
|
})
|
|
.into()
|
|
})
|
|
.collect();
|
|
let workspaces = row(workspaces).spacing(20).padding([0, 5]);
|
|
let disk = text!("{}", self.disk).color(Color::parse("#ff9f43").unwrap());
|
|
let mem = text!("{}", self.memory).color(Color::parse("#f3f99d").unwrap());
|
|
let cpu = text!("{}", self.cpu).color(Color::parse("#57c7ff").unwrap());
|
|
let clock = text!("{}", self.time);
|
|
let clock = clock.color(Color::parse("#5af78e").unwrap());
|
|
|
|
let row = row!(
|
|
container(workspaces),
|
|
container(horizontal_space()),
|
|
container(clock),
|
|
container(row!(horizontal_space(), disk, cpu, mem).spacing(5))
|
|
)
|
|
.spacing(5)
|
|
.padding([0, 20]);
|
|
let mut shadow = Shadow::default();
|
|
shadow.color = Color::BLACK;
|
|
shadow.offset = Vector::new(4.5, 4.5);
|
|
shadow.blur_radius = 18.5;
|
|
container(
|
|
container(row)
|
|
.style(move |t: &iced::Theme| {
|
|
container::rounded_box(t)
|
|
.border(Border::default().rounded(20))
|
|
.background(
|
|
Background::Color(Color::parse("#282a36").unwrap()).scale_alpha(0.95),
|
|
)
|
|
.shadow(shadow)
|
|
})
|
|
.center(Length::Fill),
|
|
)
|
|
.padding([15, 20])
|
|
.center(Length::Fill)
|
|
.style(move |t: &iced::Theme| {
|
|
container::rounded_box(t)
|
|
.border(Border::default().rounded(20))
|
|
.background(Background::Color(Color::TRANSPARENT))
|
|
})
|
|
.into()
|
|
}
|
|
|
|
fn style(&self, theme: &iced::Theme) -> iced_layershell::Appearance {
|
|
use iced_layershell::Appearance;
|
|
Appearance {
|
|
background_color: Color::TRANSPARENT,
|
|
text_color: theme.palette().text,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn convert(num: f64) -> String {
|
|
let negative = if num.is_sign_positive() { "" } else { "-" };
|
|
let num = num.abs();
|
|
let units = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
|
if num < 1_f64 {
|
|
return format!("{}{} {}", negative, num, "B");
|
|
}
|
|
let delimiter = 1000_f64;
|
|
let exponent = cmp::min(
|
|
(num.ln() / delimiter.ln()).floor() as i32,
|
|
(units.len() - 1) as i32,
|
|
);
|
|
let pretty_bytes = format!("{:.2}", num / delimiter.powi(exponent))
|
|
.parse::<f64>()
|
|
.unwrap()
|
|
* 1_f64;
|
|
let unit = units[exponent as usize];
|
|
format!("{}{} {}", negative, pretty_bytes, unit)
|
|
}
|