init: initial structure
This commit is contained in:
commit
c83273e4da
9 changed files with 4546 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
.idea/
|
4298
Cargo.lock
generated
Normal file
4298
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
23
Cargo.toml
Normal file
23
Cargo.toml
Normal 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
27
src/apps/clock.rs
Normal 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
35
src/apps/greeting.rs
Normal 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
3
src/apps/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub mod uptime;
|
||||
pub mod greeting;
|
||||
pub mod clock;
|
70
src/apps/uptime.rs
Normal file
70
src/apps/uptime.rs
Normal 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
59
src/dashboard.rs
Normal 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
29
src/main.rs
Normal 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
|
||||
}),
|
||||
)
|
||||
}
|
Loading…
Reference in a new issue