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