init: initial structure

This commit is contained in:
Strix 2024-03-05 10:34:22 +01:00
commit c83273e4da
No known key found for this signature in database
GPG key ID: 5F35B3B8537287A7
9 changed files with 4546 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/target
.idea/

4298
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

23
Cargo.toml Normal file
View file

@ -0,0 +1,23 @@
[package]
name = "dashboard"
version = "0.1.0"
edition = "2021"
rust-version = "1.72"
publish = false
[dependencies]
chrono = "0.4.34"
eframe = { version = "0.26.2", features = [
"default",
"__screenshot", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO
] }
# For image support:
egui_extras = { version = "0.26.2", features = ["default", "image"] }
env_logger = { version = "0.11.2", default-features = false, features = [
"auto-color",
"humantime",
] }
reqwest = { version = "0.11.24", features = ["blocking"] }

27
src/apps/clock.rs Normal file
View file

@ -0,0 +1,27 @@
use std::time::SystemTime;
use chrono::Local;
use eframe::{
egui::{RichText, Window},
App,
};
pub struct ClockWidget;
impl App for ClockWidget {
fn update(&mut self, ctx: &eframe::egui::Context, frame: &mut eframe::Frame) {
Window::new("Clock")
.resizable(false)
.movable(true)
.show(ctx, |ui| {
ui.label(
RichText::new(Local::now().format("%H:%M:%S").to_string())
.size(64.)
.strong(),
);
if ui.is_visible() {
ctx.request_repaint();
}
});
}
}

35
src/apps/greeting.rs Normal file
View file

@ -0,0 +1,35 @@
use chrono::Timelike;
use eframe::egui::{self, RichText};
pub struct Greeting;
impl eframe::App for Greeting {
fn update(&mut self, ctx: &eframe::egui::Context, frame: &mut eframe::Frame) {
egui::Window::new("Greeting")
.title_bar(false)
.movable(true)
.resizable(false)
.interactable(false)
.show(ctx, |ui| {
ui.label(
RichText::new(format!(
"Hello, {}!",
std::env::var("USER").unwrap_or(String::from("user"))
))
.size(32.),
);
ui.label(format!("Good {}!", {
let hour = chrono::Local::now().hour();
if hour < 6 {
"night"
} else if hour >= 6 && hour < 12 {
"morning"
} else if hour >= 12 && hour < 17 {
"afternoon"
} else {
"evening"
}
}))
});
}
}

3
src/apps/mod.rs Normal file
View file

@ -0,0 +1,3 @@
pub mod uptime;
pub mod greeting;
pub mod clock;

70
src/apps/uptime.rs Normal file
View file

@ -0,0 +1,70 @@
use eframe::egui::{Color32, Context, Label, Response, Ui, Widget};
use eframe::{egui, Frame};
use reqwest::Url;
use std::ops::Sub;
use std::str::FromStr;
use std::time::{Duration, Instant};
pub struct MonitorTarget {
url: String,
last_update: Instant,
last_status: bool,
}
impl MonitorTarget {
pub fn new<S: Into<String>>(url: S) -> MonitorTarget {
MonitorTarget {
url: url.into(),
last_status: false,
last_update: Instant::now().sub(Duration::new(30, 0)),
}
}
pub fn check(&mut self) -> bool {
if self.last_update.elapsed().as_secs() > 30 {
self.last_status = reqwest::blocking::get(self.url.as_str()).is_ok();
self.last_update = Instant::now();
}
self.last_status
}
}
#[derive(Default)]
pub struct UptimeWidget {
pub targets: Vec<MonitorTarget>,
_url_input_cache: String,
}
impl eframe::App for UptimeWidget {
fn update(&mut self, ctx: &Context, frame: &mut Frame) {
egui::Window::new("Uptime Widget").show(ctx, |ui| {
self.targets.retain_mut(|target| {
ui.horizontal(|ui| {
ui.label(target.url.clone());
ui.label({
if target.check() {
format!("OK ({}s ago)", target.last_update.elapsed().as_secs())
} else {
format!("FAIL ({}s ago)", target.last_update.elapsed().as_secs())
}
});
!ui.button("x").clicked()
})
.inner
});
ui.horizontal(|ui| {
ui.text_edit_singleline(&mut self._url_input_cache)
.on_hover_text("URL to add");
if ui.button("Add URL").clicked() {
self.targets
.push(MonitorTarget::new(self._url_input_cache.clone()));
self._url_input_cache = String::default();
}
});
if ui.is_visible() {
ctx.request_repaint();
}
});
}
}

59
src/dashboard.rs Normal file
View file

@ -0,0 +1,59 @@
use std::collections::HashMap;
use crate::apps::clock::ClockWidget;
use crate::apps::greeting::Greeting;
use crate::apps::uptime::UptimeWidget;
use eframe::egui::accesskit::Role::Window;
use eframe::egui::panel::Side;
use eframe::egui::{CentralPanel, Id, SidePanel};
use eframe::{egui, App};
#[derive(Default)]
pub struct Dashboard {
pub user_name: String,
pub apps: HashMap<String, Box<dyn App>>,
}
impl Dashboard {
pub fn has_app<S: Into<String>>(&self, name: S) -> bool {
self.apps.contains_key(&name.into())
}
pub fn add_app<S: Into<String>>(&mut self, name: S, app: Box<dyn App>) {
self.apps.insert(name.into(), app);
}
pub fn remove_app<S: Into<String>>(&mut self, name: S) {
self.apps.remove(&name.into());
}
}
macro_rules! register_app {
($dash:ident, $ui:ident, $name:literal, $app:expr) => {
let button = $ui.button($name);
if button.clicked() {
if $dash.has_app($name) {
$dash.remove_app($name);
} else {
$dash.add_app($name, Box::new($app));
}
}
if $dash.has_app($name) {
button.highlight();
}
};
}
impl eframe::App for Dashboard {
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
SidePanel::new(Side::Left, Id::new("side_panel")).show(ctx, |ui| {
ui.heading("Apps");
register_app!(self, ui, "Clock", ClockWidget);
register_app!(self, ui, "Uptime Widget", UptimeWidget::default());
});
CentralPanel::default().show(ctx, |ui| {});
self.apps.iter_mut().for_each(|(_, app)| {
app.update(ctx, frame);
})
}
}

29
src/main.rs Normal file
View file

@ -0,0 +1,29 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
use crate::apps::uptime::UptimeWidget;
use apps::greeting::Greeting;
use apps::uptime::MonitorTarget;
use dashboard::Dashboard;
use eframe::egui;
use std::ops::Sub;
use std::time::{Duration, Instant};
mod apps;
mod dashboard;
fn main() -> Result<(), eframe::Error> {
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default().with_inner_size([1920., 1080.]),
..Default::default()
};
eframe::run_native(
"Dashboard",
options,
Box::new(|_| {
let mut dashboard = Box::<Dashboard>::default();
dashboard.add_app("Greeting", Box::new(Greeting));
dashboard
}),
)
}