mirror of
https://github.com/Astatin3/raylock.git
synced 2026-06-09 00:28:00 -06:00
Add info pane
This commit is contained in:
@@ -4,8 +4,11 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
battery = "0.7.8"
|
||||||
|
chrono = "0.4.38"
|
||||||
eframe = "0.28"
|
eframe = "0.28"
|
||||||
egui = "0.28"
|
egui = "0.28"
|
||||||
|
os_info = "3.8.2"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
serde = "1.0.214"
|
serde = "1.0.214"
|
||||||
serde_json = "1.0.132"
|
serde_json = "1.0.132"
|
||||||
|
|||||||
+23
-63
@@ -1,11 +1,33 @@
|
|||||||
use crate::structs;
|
use crate::structs;
|
||||||
use egui::{Color32, Stroke};
|
use egui::{Color32, FontId, Stroke};
|
||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
// pub const windows: PaneSplit =
|
// pub const windows: PaneSplit =
|
||||||
// Pane::new(SplitHorisontal).new_hsplit(Pane::new(Temp1), Pane::new(Temp2));
|
// Pane::new(SplitHorisontal).new_hsplit(Pane::new(Temp1), Pane::new(Temp2));
|
||||||
|
|
||||||
|
pub const SCREEN_WIDTH: f32 = 1920.;
|
||||||
|
pub const SCREEN_HEIGHT: f32 = 1080.;
|
||||||
|
|
||||||
|
pub const TITLE_FONT: FontId = FontId::monospace(25.);
|
||||||
|
pub const GRAPH_STROKE: Stroke = Stroke {
|
||||||
|
width: 0.5,
|
||||||
|
color: Color32::from_rgba_premultiplied(255, 255, 255, 1),
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const TEXT_FONT: FontId = FontId::monospace(16.);
|
||||||
|
|
||||||
|
pub const UP_GRAPH_STROKE: Stroke = Stroke {
|
||||||
|
width: 1.5,
|
||||||
|
color: Color32::from_rgba_premultiplied(0, 255, 255, 1),
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const DOWN_GRAPH_STROKE: Stroke = Stroke {
|
||||||
|
width: 1.5,
|
||||||
|
color: Color32::from_rgba_premultiplied(255, 64, 4, 1),
|
||||||
|
};
|
||||||
|
|
||||||
pub const TEXT_COLOR: Color32 = Color32::from_rgb(255, 255, 255);
|
pub const TEXT_COLOR: Color32 = Color32::from_rgb(255, 255, 255);
|
||||||
|
pub const BACKGROUND: Color32 = Color32::BLACK;
|
||||||
pub const BACKGROUND_2: Color32 = Color32::from_rgba_premultiplied(10, 10, 10, 230);
|
pub const BACKGROUND_2: Color32 = Color32::from_rgba_premultiplied(10, 10, 10, 230);
|
||||||
|
|
||||||
pub const LOGIN_CIRCLE_RADIUS: f32 = 50.;
|
pub const LOGIN_CIRCLE_RADIUS: f32 = 50.;
|
||||||
@@ -40,65 +62,3 @@ pub const DOTS_COLOR: Color32 = Color32::from_rgb(255, 255, 255);
|
|||||||
|
|
||||||
pub const CORNER_CUT: f32 = LOGIN_CIRCLE_RADIUS * 1.41421356237;
|
pub const CORNER_CUT: f32 = LOGIN_CIRCLE_RADIUS * 1.41421356237;
|
||||||
pub const PANE_GAP: f32 = 6.;
|
pub const PANE_GAP: f32 = 6.;
|
||||||
|
|
||||||
// Example JSON configuration:
|
|
||||||
pub const EXAMPLE_CONFIG: &str = r#"
|
|
||||||
{
|
|
||||||
"root": {
|
|
||||||
"id": "root",
|
|
||||||
"split": {
|
|
||||||
"direction": "Horizontal",
|
|
||||||
"ratio": 0.9,
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"id": "left",
|
|
||||||
"pane_type": {
|
|
||||||
"type": "Solid",
|
|
||||||
"config": {
|
|
||||||
"color": [255, 255, 255]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "right",
|
|
||||||
"split": {
|
|
||||||
"direction": "Vertical",
|
|
||||||
"ratio": 0.6,
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"id": "right_top",
|
|
||||||
"pane_type": {
|
|
||||||
"type": "Text",
|
|
||||||
"config": {
|
|
||||||
"text": "Hello World",
|
|
||||||
"font_size": 24.0,
|
|
||||||
"color": [255, 255, 255],
|
|
||||||
"background_color": [50, 50, 150]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "right_bottom",
|
|
||||||
"pane_type": {
|
|
||||||
"type": "Gradient",
|
|
||||||
"config": {
|
|
||||||
"start_color": [200, 200, 200],
|
|
||||||
"end_color": [100, 200, 100],
|
|
||||||
"horizontal": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"default_pane_type": {
|
|
||||||
"type": "Solid",
|
|
||||||
"config": {
|
|
||||||
"color": [200, 200, 200]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"#;
|
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
use egui::{Color32, Pos2, Rect, Stroke};
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::time::Instant;
|
||||||
|
use sysinfo::System;
|
||||||
|
|
||||||
|
use crate::configs;
|
||||||
|
use crate::graph::*;
|
||||||
|
|
||||||
|
pub struct CpuGraph {
|
||||||
|
sys: System,
|
||||||
|
cpu_graph: ResourceGraph,
|
||||||
|
last_update: Instant,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CpuGraph {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut sys = System::new();
|
||||||
|
sys.refresh_cpu_all();
|
||||||
|
|
||||||
|
let mut cpu_graph =
|
||||||
|
ResourceGraph::new("CPU Usage".to_string(), "%".to_string(), 0.0, 100.0);
|
||||||
|
|
||||||
|
let cpu_count = sys.cpus().len();
|
||||||
|
for i in 0..cpu_count {
|
||||||
|
let hue = (i as f32 / cpu_count as f32) * 360.0;
|
||||||
|
cpu_graph.add_line(format!("CPU {}", i), configs::GRAPH_STROKE);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
sys,
|
||||||
|
cpu_graph,
|
||||||
|
last_update: Instant::now(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self) {
|
||||||
|
let now = Instant::now();
|
||||||
|
if now.duration_since(self.last_update).as_secs_f32() < 1.0 / UPDATES_PER_SECOND {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.sys.refresh_cpu_all();
|
||||||
|
// self.sys.refresh_memory();
|
||||||
|
|
||||||
|
// Update CPU usage
|
||||||
|
for (i, cpu) in self.sys.cpus().iter().enumerate() {
|
||||||
|
self.cpu_graph.update_line(i, cpu.cpu_usage());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.last_update = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(&mut self, painter: &egui::Painter, rect: Rect) {
|
||||||
|
self.cpu_graph.render(painter, rect);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
use egui::Rect;
|
||||||
|
use std::time::Instant;
|
||||||
|
use sysinfo::ProcessRefreshKind;
|
||||||
|
use sysinfo::RefreshKind;
|
||||||
|
use sysinfo::System;
|
||||||
|
|
||||||
|
use crate::configs;
|
||||||
|
use crate::graph::*;
|
||||||
|
use crate::table::ProcessTable;
|
||||||
|
|
||||||
|
pub struct DiskGraph {
|
||||||
|
sys: sysinfo::System,
|
||||||
|
mem_graph: ResourceGraph,
|
||||||
|
last_update: Instant,
|
||||||
|
|
||||||
|
proc_refresh: ProcessRefreshKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiskGraph {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let proc_refresh = ProcessRefreshKind::new().with_disk_usage();
|
||||||
|
|
||||||
|
let mut sys =
|
||||||
|
sysinfo::System::new_with_specifics(RefreshKind::new().with_processes(proc_refresh));
|
||||||
|
|
||||||
|
let mut memory_graph =
|
||||||
|
ResourceGraph::new("Net Usage".to_string(), " MB/s".to_string(), 0.0, 10.0);
|
||||||
|
|
||||||
|
// Add memory lines
|
||||||
|
memory_graph.add_line("U".to_string(), configs::UP_GRAPH_STROKE);
|
||||||
|
memory_graph.add_line("D".to_string(), configs::DOWN_GRAPH_STROKE);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
sys,
|
||||||
|
mem_graph: memory_graph,
|
||||||
|
last_update: Instant::now(),
|
||||||
|
proc_refresh,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self) {
|
||||||
|
let now = Instant::now();
|
||||||
|
if now.duration_since(self.last_update).as_secs_f32() < 1.0 / UPDATES_PER_SECOND {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// self.mem_graph.
|
||||||
|
|
||||||
|
// self.networks.refresh_cpu_all();
|
||||||
|
|
||||||
|
let mut transmitted: f32 = 0.;
|
||||||
|
let mut recieved: f32 = 0.;
|
||||||
|
|
||||||
|
for (_, proc) in self.sys.processes() {
|
||||||
|
// println!("in: {} B", network.received());
|
||||||
|
let disk = proc.disk_usage();
|
||||||
|
recieved += (disk.read_bytes as f32 / 1024. / 1024.) * UPDATES_PER_SECOND;
|
||||||
|
|
||||||
|
transmitted += (disk.written_bytes as f32 / 1024. / 1024.) * UPDATES_PER_SECOND;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.sys.refresh_processes_specifics(
|
||||||
|
sysinfo::ProcessesToUpdate::All,
|
||||||
|
false,
|
||||||
|
self.proc_refresh,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.mem_graph.update_line(0, transmitted);
|
||||||
|
self.mem_graph.update_line(1, recieved);
|
||||||
|
|
||||||
|
self.mem_graph.redo_max();
|
||||||
|
|
||||||
|
self.last_update = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(&mut self, painter: &egui::Painter, rect: Rect) {
|
||||||
|
// self.cpu_graph.render(painter, cpu_rect);
|
||||||
|
self.mem_graph.render(painter, rect);
|
||||||
|
}
|
||||||
|
}
|
||||||
+122
-91
@@ -1,11 +1,14 @@
|
|||||||
use egui::{Color32, Pos2, Rect, Stroke, Vec2};
|
use egui::{Color32, Pos2, Rect, Stroke};
|
||||||
|
use std::borrow::BorrowMut;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use sysinfo::System;
|
use sysinfo::System;
|
||||||
|
|
||||||
const HISTORY_SIZE: usize = 100;
|
use crate::configs;
|
||||||
const ANIMATION_DURATION: f32 = 0.2; // seconds
|
|
||||||
const UPDATES_PER_SECOND: f32 = 2.0;
|
pub const HISTORY_SIZE: usize = 25;
|
||||||
|
pub const ANIMATION_DURATION: f32 = 0.2; // seconds
|
||||||
|
pub const UPDATES_PER_SECOND: f32 = 1.5;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct DataPoint {
|
struct DataPoint {
|
||||||
@@ -41,130 +44,158 @@ impl AnimatedValue {
|
|||||||
self.current
|
self.current
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// #[derive(Clone)]
|
|
||||||
//
|
#[derive(Clone)]
|
||||||
// #[derive(Debug)]
|
pub struct GraphLine {
|
||||||
pub struct CpuGraph {
|
label: String,
|
||||||
sys: System,
|
stroke: Stroke,
|
||||||
history: Vec<VecDeque<DataPoint>>,
|
history: VecDeque<DataPoint>,
|
||||||
animated_values: Vec<AnimatedValue>,
|
animated_value: AnimatedValue,
|
||||||
last_update: Instant,
|
|
||||||
colors: Vec<Color32>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CpuGraph {
|
impl GraphLine {
|
||||||
pub fn new() -> Self {
|
fn new(label: String, stroke: Stroke) -> Self {
|
||||||
let mut sys = System::new();
|
|
||||||
sys.refresh_cpu_all();
|
|
||||||
|
|
||||||
let cpu_count = sys.cpus().len();
|
|
||||||
let history = vec![VecDeque::with_capacity(HISTORY_SIZE); cpu_count];
|
|
||||||
let animated_values = vec![AnimatedValue::new(0.0); cpu_count];
|
|
||||||
|
|
||||||
// Generate distinct colors for each CPU core
|
|
||||||
let colors = (0..cpu_count)
|
|
||||||
.map(|i| {
|
|
||||||
let hue = (i as f32 / cpu_count as f32) * 360.0;
|
|
||||||
Color32::from_rgb(
|
|
||||||
((1.0 + (hue.sin())) * 127.5) as u8,
|
|
||||||
((1.0 + ((hue + 120.0).sin())) * 127.5) as u8,
|
|
||||||
((1.0 + ((hue + 240.0).sin())) * 127.5) as u8,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
sys,
|
label,
|
||||||
history,
|
stroke,
|
||||||
animated_values,
|
history: VecDeque::with_capacity(HISTORY_SIZE),
|
||||||
last_update: Instant::now(),
|
animated_value: AnimatedValue::new(0.0),
|
||||||
colors,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self) {
|
fn update(&mut self, value: f32) {
|
||||||
let now = Instant::now();
|
self.animated_value.update(value);
|
||||||
if now.duration_since(self.last_update).as_secs_f32() < 1.0 / UPDATES_PER_SECOND {
|
self.history.push_back(DataPoint {
|
||||||
return;
|
value,
|
||||||
}
|
timestamp: Instant::now(),
|
||||||
|
|
||||||
self.sys.refresh_cpu_all();
|
|
||||||
|
|
||||||
for (i, cpu) in self.sys.cpus().iter().enumerate() {
|
|
||||||
let usage = cpu.cpu_usage() / 100.0;
|
|
||||||
|
|
||||||
self.animated_values[i].update(usage);
|
|
||||||
|
|
||||||
self.history[i].push_back(DataPoint {
|
|
||||||
value: usage,
|
|
||||||
timestamp: now,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
while self.history[i].len() > HISTORY_SIZE {
|
while self.history.len() > HISTORY_SIZE {
|
||||||
self.history[i].pop_front();
|
self.history.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ResourceGraph {
|
||||||
|
lines: Vec<GraphLine>,
|
||||||
|
// last_update: Instant,
|
||||||
|
title: String,
|
||||||
|
y_axis_label: String,
|
||||||
|
min_value: f32,
|
||||||
|
max_value: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResourceGraph {
|
||||||
|
pub fn new(title: String, y_axis_label: String, min_value: f32, max_value: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
lines: Vec::new(),
|
||||||
|
// last_update: Instant::now(),
|
||||||
|
title,
|
||||||
|
y_axis_label,
|
||||||
|
min_value,
|
||||||
|
max_value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.last_update = now;
|
pub fn redo_max(&mut self) {
|
||||||
|
let mut max = 0.5;
|
||||||
|
for i in 0..self.lines.len() {
|
||||||
|
let history = self.lines.get_mut(i).unwrap();
|
||||||
|
|
||||||
|
for j in 0..history.history.len() {
|
||||||
|
let val = history.history.get(j).unwrap();
|
||||||
|
if val.value > max {
|
||||||
|
max = val.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.max_value = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_line(&mut self, label: String, stroke: Stroke) {
|
||||||
|
self.lines.push(GraphLine::new(label, stroke));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_line(&mut self, index: usize, value: f32) {
|
||||||
|
if let Some(line) = self.lines.get_mut(index) {
|
||||||
|
line.update(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(&mut self, painter: &egui::Painter, rect: Rect) {
|
pub fn render(&mut self, painter: &egui::Painter, rect: Rect) {
|
||||||
let stroke_width = 2.0;
|
// let now = Instant::now();
|
||||||
|
|
||||||
// Draw background
|
// Draw background
|
||||||
painter.rect_filled(rect, 0.0, Color32::from_gray(20));
|
// painter.rect_filled(rect, 0.0, Color32::from_gray(20));
|
||||||
|
|
||||||
// Draw grid lines
|
// Draw title
|
||||||
|
// painter.text(
|
||||||
|
// Pos2::new(rect.min.x + 5.0, rect.min.y + 5.0),
|
||||||
|
// egui::Align2::LEFT_TOP,
|
||||||
|
// &self.title,
|
||||||
|
// egui::FontId::proportional(16.0),
|
||||||
|
// Color32::WHITE,
|
||||||
|
// );
|
||||||
|
|
||||||
|
// Calculate graph area (leaving space for labels)
|
||||||
|
let label_width = 65.0;
|
||||||
|
let graph_rect = Rect::from_min_max(
|
||||||
|
Pos2::new(rect.min.x + label_width, rect.min.y + 30.0),
|
||||||
|
rect.max,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Draw grid lines and labels
|
||||||
for i in 0..=4 {
|
for i in 0..=4 {
|
||||||
let y = rect.min.y + rect.height() * (i as f32 / 4.0);
|
let y = graph_rect.min.y + graph_rect.height() * (i as f32 / 4.0);
|
||||||
painter.line_segment(
|
painter.line_segment(
|
||||||
[Pos2::new(rect.min.x, y), Pos2::new(rect.max.x, y)],
|
[
|
||||||
|
Pos2::new(graph_rect.min.x, y),
|
||||||
|
Pos2::new(graph_rect.max.x, y),
|
||||||
|
],
|
||||||
Stroke::new(1.0, Color32::from_gray(40)),
|
Stroke::new(1.0, Color32::from_gray(40)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let value = self.max_value - (i as f32 / 4.0) * (self.max_value - self.min_value);
|
||||||
|
painter.text(
|
||||||
|
Pos2::new(rect.min.x + 5.0, y - 8.0),
|
||||||
|
egui::Align2::LEFT_CENTER,
|
||||||
|
format!("{:.1}{}", value, self.y_axis_label),
|
||||||
|
egui::FontId::proportional(12.0),
|
||||||
|
Color32::LIGHT_GRAY,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw CPU usage lines
|
// Draw lines
|
||||||
for (core_idx, (history, color)) in self.history.iter().zip(self.colors.iter()).enumerate()
|
for (_, line) in self.lines.iter_mut().enumerate() {
|
||||||
{
|
if line.history.is_empty() {
|
||||||
if history.is_empty() {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let points: Vec<Pos2> = history
|
let points: Vec<Pos2> = line
|
||||||
|
.history
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, point)| {
|
.map(|(i, point)| {
|
||||||
let x = rect.min.x + rect.width() * (i as f32 / (HISTORY_SIZE - 1) as f32);
|
let x = graph_rect.min.x
|
||||||
let y = rect.max.y - rect.height() * point.value;
|
+ graph_rect.width() * (i as f32 / (HISTORY_SIZE - 1) as f32);
|
||||||
|
|
||||||
|
let normalized_value =
|
||||||
|
(point.value - self.min_value) / (self.max_value - self.min_value);
|
||||||
|
let y = graph_rect.max.y - graph_rect.height() * normalized_value;
|
||||||
Pos2::new(x, y)
|
Pos2::new(x, y)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Get the current animated value for the last point
|
// Get the current animated value for the last point
|
||||||
let current_value = self.animated_values[core_idx].get_current_value();
|
let current_value = line.animated_value.get_current_value();
|
||||||
let mut animated_points = points;
|
let mut animated_points = points;
|
||||||
if let Some(last) = animated_points.last_mut() {
|
if let Some(last) = animated_points.last_mut() {
|
||||||
last.y = rect.max.y - rect.height() * current_value;
|
let normalized_value =
|
||||||
|
(current_value - self.min_value) / (self.max_value - self.min_value);
|
||||||
|
last.y = graph_rect.max.y - graph_rect.height() * normalized_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the line
|
painter.add(egui::Shape::line(animated_points, line.stroke));
|
||||||
painter.add(egui::Shape::line(
|
|
||||||
animated_points,
|
|
||||||
Stroke::new(stroke_width, *color),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw labels
|
|
||||||
for (i, color) in self.colors.iter().enumerate() {
|
|
||||||
let text = format!("CPU {}", i);
|
|
||||||
let pos = Pos2::new(rect.min.x + 5.0, rect.min.y + 15.0 + (i as f32 * 20.0));
|
|
||||||
painter.text(
|
|
||||||
pos,
|
|
||||||
egui::Align2::LEFT_TOP,
|
|
||||||
text,
|
|
||||||
egui::FontId::proportional(14.0),
|
|
||||||
*color,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
// self.last_update = now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+157
@@ -0,0 +1,157 @@
|
|||||||
|
use battery::units::electric_potential::volt;
|
||||||
|
use battery::units::energy::joule;
|
||||||
|
use battery::units::power::watt;
|
||||||
|
use battery::units::time::minute;
|
||||||
|
use battery::units::time::second;
|
||||||
|
use battery::Manager;
|
||||||
|
use chrono::Local;
|
||||||
|
use egui::Align2;
|
||||||
|
use egui::Galley;
|
||||||
|
use egui::Pos2;
|
||||||
|
use egui::Rect;
|
||||||
|
use std::env;
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use crate::configs::*;
|
||||||
|
use crate::graph::*;
|
||||||
|
|
||||||
|
pub struct InfoPane {
|
||||||
|
manager: Manager,
|
||||||
|
unamea: String,
|
||||||
|
state: BatteryState,
|
||||||
|
last_update: Instant,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BatteryState {
|
||||||
|
percent: f32,
|
||||||
|
rate: f32,
|
||||||
|
time: f32,
|
||||||
|
discharging: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InfoPane {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
manager: Manager::new().unwrap(),
|
||||||
|
unamea: os_info::get().to_string(),
|
||||||
|
state: BatteryState {
|
||||||
|
percent: 0.,
|
||||||
|
rate: 0.,
|
||||||
|
time: 0.,
|
||||||
|
discharging: false,
|
||||||
|
},
|
||||||
|
last_update: Instant::now(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn get_unamea() ->
|
||||||
|
|
||||||
|
fn get_percent(&mut self) -> Result<BatteryState, battery::errors::Error> {
|
||||||
|
// let mut percent = 1.;
|
||||||
|
// let mut rate = 0.;
|
||||||
|
// let mut min_time;
|
||||||
|
|
||||||
|
for (idx, unsafe_battery) in self.manager.batteries()?.enumerate() {
|
||||||
|
let battery = unsafe_battery.unwrap();
|
||||||
|
let percent = battery.energy().get::<joule>() / battery.energy_full().get::<joule>();
|
||||||
|
let rate = battery.energy_rate().get::<watt>();
|
||||||
|
|
||||||
|
let time_to_empty = battery.time_to_empty();
|
||||||
|
|
||||||
|
let time: f32 = if !time_to_empty.is_none() {
|
||||||
|
time_to_empty.unwrap().get::<minute>()
|
||||||
|
} else if !battery.time_to_full().is_none() {
|
||||||
|
battery.time_to_full().unwrap().get::<minute>()
|
||||||
|
} else {
|
||||||
|
0.
|
||||||
|
};
|
||||||
|
|
||||||
|
return Ok((BatteryState {
|
||||||
|
percent: percent * 100.0,
|
||||||
|
rate: rate,
|
||||||
|
time: time,
|
||||||
|
discharging: !time_to_empty.is_none(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// percent *= battery
|
||||||
|
// .unwrap()
|
||||||
|
// .state_of_charge()
|
||||||
|
// .get::<battery::units::ratio::percent>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((BatteryState {
|
||||||
|
percent: 0.,
|
||||||
|
rate: 0.,
|
||||||
|
time: 0.,
|
||||||
|
discharging: false,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self) {
|
||||||
|
let now = Instant::now();
|
||||||
|
if now.duration_since(self.last_update).as_secs_f32() < 1.0 / UPDATES_PER_SECOND {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.state = self.get_percent().unwrap();
|
||||||
|
self.last_update = Instant::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(&mut self, painter: &egui::Painter, rect: Rect) {
|
||||||
|
let now = Local::now();
|
||||||
|
|
||||||
|
let mut y = 0.;
|
||||||
|
|
||||||
|
painter.text(
|
||||||
|
Pos2 {
|
||||||
|
x: rect.min.x,
|
||||||
|
y: rect.min.y,
|
||||||
|
},
|
||||||
|
Align2::LEFT_TOP,
|
||||||
|
format!("TIME: {}", now.format("UTC%Z %H:%M:%S%.f (%Y-%d-%m)")),
|
||||||
|
TEXT_FONT,
|
||||||
|
TEXT_COLOR,
|
||||||
|
);
|
||||||
|
|
||||||
|
y += TEXT_FONT.size;
|
||||||
|
|
||||||
|
// let galley = painter.layout_no_wrap(format!("{}%", self.percent), TITLE_FONT, TEXT_COLOR);
|
||||||
|
//
|
||||||
|
|
||||||
|
painter.text(
|
||||||
|
Pos2 {
|
||||||
|
x: rect.min.x,
|
||||||
|
y: rect.min.y + y,
|
||||||
|
},
|
||||||
|
Align2::LEFT_TOP,
|
||||||
|
format!(
|
||||||
|
"BAT: {}% {} ({} min) ({} W)",
|
||||||
|
self.state.percent,
|
||||||
|
{
|
||||||
|
if self.state.discharging {
|
||||||
|
"discharging"
|
||||||
|
} else {
|
||||||
|
"charging"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
self.state.time,
|
||||||
|
self.state.rate,
|
||||||
|
),
|
||||||
|
TEXT_FONT,
|
||||||
|
TEXT_COLOR,
|
||||||
|
);
|
||||||
|
|
||||||
|
y += TEXT_FONT.size;
|
||||||
|
|
||||||
|
painter.text(
|
||||||
|
Pos2 {
|
||||||
|
x: rect.min.x,
|
||||||
|
y: rect.min.y + y,
|
||||||
|
},
|
||||||
|
Align2::LEFT_TOP,
|
||||||
|
format!("OS: {}", self.unamea,),
|
||||||
|
TEXT_FONT,
|
||||||
|
TEXT_COLOR,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+41
-137
@@ -1,16 +1,20 @@
|
|||||||
|
use configs::SCREEN_HEIGHT;
|
||||||
|
use eframe::Error;
|
||||||
use eframe::{egui, App};
|
use eframe::{egui, App};
|
||||||
use egui::FontFamily;
|
use egui::FontFamily;
|
||||||
use egui::Key;
|
use egui::Key;
|
||||||
use graph::CpuGraph;
|
// use graph::CpuGraph;
|
||||||
use input::is_locked;
|
use input::is_locked;
|
||||||
use panes::{PaneConfig, PaneRenderer, SplitDirection};
|
use panes::Pane;
|
||||||
|
// use serde::de::Error;
|
||||||
|
// use rand::Error;
|
||||||
|
// use panes::{PaneConfig, PaneRenderer, SplitDirection};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
// use egui::epaint::text::{FontInsert, InsertFontFamily};
|
// use egui::epaint::text::{FontInsert, InsertFontFamily};
|
||||||
|
|
||||||
use eframe::CreationContext;
|
use eframe::CreationContext;
|
||||||
@@ -23,49 +27,27 @@ use egui::Stroke;
|
|||||||
use egui::ecolor::HexColor;
|
use egui::ecolor::HexColor;
|
||||||
|
|
||||||
mod configs;
|
mod configs;
|
||||||
|
|
||||||
|
mod cpugraph;
|
||||||
|
mod diskgraph;
|
||||||
mod graph;
|
mod graph;
|
||||||
|
mod memgraph;
|
||||||
|
mod netgraph;
|
||||||
|
|
||||||
|
mod table;
|
||||||
|
|
||||||
|
mod infopane;
|
||||||
|
|
||||||
mod input;
|
mod input;
|
||||||
mod panes;
|
mod panes;
|
||||||
mod structs;
|
mod structs;
|
||||||
mod ui;
|
mod ui;
|
||||||
|
|
||||||
// Demonstrates how to add a font to the existing ones
|
use panes::{load_pane_config, EXAMPLE_CONFIG};
|
||||||
// fn add_font(ctx: &egui::Context) {
|
|
||||||
// // Start with the default fonts (we will be adding to them rather than replacing them).
|
|
||||||
// let mut fonts = egui::FontDefinitions::default();
|
|
||||||
|
|
||||||
// // Install my own font (maybe supporting non-latin characters).
|
|
||||||
// // .ttf and .otf files supported.
|
|
||||||
// fonts.font_data.insert(
|
|
||||||
// "my_font".to_owned(),
|
|
||||||
// egui::FontData::from_static(include_bytes!(
|
|
||||||
// "/home/astatin3/.fonts/UbuntuMono/UbuntuMonoNerdFontMono-Regular.ttf"
|
|
||||||
// )),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// // Put my font first (highest priority) for proportional text:
|
|
||||||
// // fonts
|
|
||||||
// // .families
|
|
||||||
// // .entry(egui::FontFamily::Proportional)
|
|
||||||
// // .or_default()
|
|
||||||
// // .insert(0, "my_font".to_owned());
|
|
||||||
|
|
||||||
// // Put my font as last fallback for monospace:
|
|
||||||
// fonts
|
|
||||||
// .families
|
|
||||||
// .entry(egui::FontFamily::Monospace)
|
|
||||||
// .or_default()
|
|
||||||
// .push("my_font".to_owned());
|
|
||||||
|
|
||||||
// // Tell egui to use these fonts:
|
|
||||||
// ctx.set_fonts(fonts);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[derive(Default)]
|
|
||||||
struct ExampleApp {
|
struct ExampleApp {
|
||||||
auth_state: Arc<Mutex<structs::AuthState>>,
|
auth_state: Arc<Mutex<structs::AuthState>>,
|
||||||
cpu_graph: CpuGraph,
|
root_pane: panes::PaneInstance,
|
||||||
// terminals: HashMap<String, TermHandler>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExampleApp {
|
impl ExampleApp {
|
||||||
@@ -80,34 +62,7 @@ impl eframe::App for ExampleApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||||
// let s1 = self.terminals.get_mut("0").unwrap();
|
|
||||||
// s1.style()
|
|
||||||
// s1.cursor_trail = false;
|
|
||||||
// s1.cursor_trail_color = Some(HexColor::Hex8(Color32::LIGHT_BLUE.gamma_multiply(0.5)));
|
|
||||||
|
|
||||||
// s1.default_focus_cursor = CursorType::OpenBlock(HexColor::Hex8(Color32::RED));
|
|
||||||
// s1.default_unfocus_cursor = CursorType::None;
|
|
||||||
// for str in egui::FontDefinitions::builtin_font_names() {
|
|
||||||
// println!("{}", str);
|
|
||||||
// }
|
|
||||||
// s1.cursor_stroke = Stroke::new(1., Color32::WHITE);
|
|
||||||
|
|
||||||
// add_font(ctx);
|
|
||||||
|
|
||||||
// let font = FontId {
|
|
||||||
// size: 9.,
|
|
||||||
// family: FontFamily::Name("my_font".into()),
|
|
||||||
// };
|
|
||||||
|
|
||||||
// s1.font = font;
|
|
||||||
|
|
||||||
let mut state = self.auth_state.lock().unwrap();
|
let mut state = self.auth_state.lock().unwrap();
|
||||||
//ctx.set_pixels_per_point(1.5);
|
|
||||||
// let config = ui::winconfig();
|
|
||||||
|
|
||||||
let config: panes::LayoutConfig = serde_json::from_str(configs::EXAMPLE_CONFIG).unwrap();
|
|
||||||
let mut pane_renderer = PaneRenderer::new(config);
|
|
||||||
|
|
||||||
if ctx.input(|i| i.events.len() > 0) {
|
if ctx.input(|i| i.events.len() > 0) {
|
||||||
ctx.input(|i| {
|
ctx.input(|i| {
|
||||||
for event in &i.events {
|
for event in &i.events {
|
||||||
@@ -134,67 +89,18 @@ impl eframe::App for ExampleApp {
|
|||||||
state.password += str;
|
state.password += str;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//let mod_str = if modifiers.is_empty() {
|
|
||||||
// String::new()
|
|
||||||
//} else {
|
|
||||||
// format!(" + {:?}", modifiers)
|
|
||||||
//};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// let sine_points = PlotPoints::from_explicit_callback(|x| x.sin(), .., 5000);
|
// let mut pane = ;
|
||||||
// let plot_a_lines = [Line::new(sine_points).name("Sine")];
|
|
||||||
|
|
||||||
// let sine_points2 = PlotPoints::from_explicit_callback(|x| x.sin(), .., 5000);
|
|
||||||
// let plot_b_lines = [Line::new(sine_points2).name("Sine")];
|
|
||||||
|
|
||||||
// self.cpu_graph.update();
|
|
||||||
|
|
||||||
egui::CentralPanel::default()
|
egui::CentralPanel::default()
|
||||||
.frame(egui::Frame::none())
|
.frame(egui::Frame::none())
|
||||||
.show(ctx, |ui| {
|
.show(ctx, |ui| {
|
||||||
// let ht = ui.available_height();
|
std::thread::sleep(Duration::from_millis(50));
|
||||||
// for (_idx, (_id, term)) in self.terminals.iter_mut().enumerate() {
|
ui::update(state, ctx, _frame, ui, &mut self.root_pane);
|
||||||
// ui.terminal_sized(term, egui::vec2(ui.available_width(), ht));
|
|
||||||
// }
|
|
||||||
// ui.add_space(200.0);
|
|
||||||
// ui.heading("*".repeat(state.password.clone().len()));
|
|
||||||
// ui.add_space(20.0);
|
|
||||||
//
|
|
||||||
|
|
||||||
// ui.terminal_sized(
|
|
||||||
// self.terminals.get_mut("0").unwrap(),
|
|
||||||
// egui::vec2(ui.available_width(), ui.available_height()),
|
|
||||||
// );
|
|
||||||
// Splitter::new("some_unique_id", splitter::SplitterAxis::Horizontal).show(
|
|
||||||
// ui,
|
|
||||||
// |ui_a, ui_b| {
|
|
||||||
// ui_a.terminal_sized(
|
|
||||||
// self.terminals.get_mut("0").unwrap(),
|
|
||||||
// egui::vec2(ui_a.available_width(), ui_a.available_height()),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// ui_b.terminal_sized(
|
|
||||||
// self.terminals.get_mut("1h-1").unwrap(),
|
|
||||||
// egui::vec2(ui_a.available_width(), ui_a.available_height()),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// },
|
|
||||||
// );
|
|
||||||
|
|
||||||
ui::update_password_viewer(state, ctx, _frame, ui, &mut pane_renderer);
|
|
||||||
|
|
||||||
// self.cpu_graph.render(
|
|
||||||
// ui.painter(),
|
|
||||||
// egui::Rect {
|
|
||||||
// min: egui::Pos2 { x: 0., y: 0. },
|
|
||||||
// max: egui::Pos2 { x: 500., y: 500. },
|
|
||||||
// },
|
|
||||||
// );
|
|
||||||
|
|
||||||
ctx.request_repaint();
|
ctx.request_repaint();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -206,10 +112,6 @@ fn main() -> eframe::Result<()> {
|
|||||||
..eframe::NativeOptions::default()
|
..eframe::NativeOptions::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
// let system_shell = std::env::var("SHELL")
|
|
||||||
// .expect("SHELL variable is not defined")
|
|
||||||
// .to_string();
|
|
||||||
|
|
||||||
let state = Arc::new(Mutex::new(structs::AuthState {
|
let state = Arc::new(Mutex::new(structs::AuthState {
|
||||||
password: String::new(),
|
password: String::new(),
|
||||||
to_be_submitted: false,
|
to_be_submitted: false,
|
||||||
@@ -218,7 +120,6 @@ fn main() -> eframe::Result<()> {
|
|||||||
|
|
||||||
let auth_state_clone = state.clone();
|
let auth_state_clone = state.clone();
|
||||||
|
|
||||||
// Spawn authentication thread
|
|
||||||
thread::spawn(move || loop {
|
thread::spawn(move || loop {
|
||||||
let mut state = auth_state_clone.lock().unwrap();
|
let mut state = auth_state_clone.lock().unwrap();
|
||||||
|
|
||||||
@@ -246,40 +147,43 @@ fn main() -> eframe::Result<()> {
|
|||||||
thread::sleep(Duration::from_millis(100));
|
thread::sleep(Duration::from_millis(100));
|
||||||
});
|
});
|
||||||
|
|
||||||
// let mut map = HashMap::new();
|
|
||||||
// map.insert(String::from("0"), TermHandler::new_from_str("btop"));
|
|
||||||
// map.insert(String::from("1h-0"), TermHandler::new_from_str("nvitop"));
|
|
||||||
// map.insert(String::from("1h-1"), TermHandler::new_from_str("neofetch"));
|
|
||||||
input::sway_lock_input();
|
|
||||||
|
|
||||||
if input::is_locked() {
|
if input::is_locked() {
|
||||||
println!("Raylock is already running!");
|
println!("Raylock is already running!");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let test = [1, 2, 3];
|
input::sway_lock_input();
|
||||||
|
|
||||||
input::create_lock();
|
input::create_lock();
|
||||||
|
|
||||||
|
let default_panic = std::panic::take_hook();
|
||||||
|
std::panic::set_hook(Box::new(move |info| {
|
||||||
|
input::sway_unlock_input();
|
||||||
|
input::remove_lock();
|
||||||
|
default_panic(info);
|
||||||
|
}));
|
||||||
|
|
||||||
|
let mut pane_config = load_pane_config(EXAMPLE_CONFIG).unwrap();
|
||||||
|
pane_config.precalc(egui::Rect {
|
||||||
|
min: egui::Pos2 { x: 0., y: 0. },
|
||||||
|
max: egui::Pos2 {
|
||||||
|
x: configs::SCREEN_WIDTH,
|
||||||
|
y: configs::SCREEN_HEIGHT,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
eframe::run_native(
|
eframe::run_native(
|
||||||
ExampleApp::name(),
|
ExampleApp::name(),
|
||||||
native_options,
|
native_options,
|
||||||
Box::new(|_| {
|
Box::new(|_| {
|
||||||
Ok(Box::<ExampleApp>::new(ExampleApp {
|
Ok(Box::<ExampleApp>::new(ExampleApp {
|
||||||
auth_state: state,
|
auth_state: state,
|
||||||
cpu_graph: CpuGraph::new(),
|
root_pane: pane_config,
|
||||||
// terminals: map,
|
// cpu_graph: CpuGraph::new(),
|
||||||
}))
|
}))
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_test(test: &mut [i32]) {
|
|
||||||
for i in 0..test.len() {
|
|
||||||
test[i] += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_sudo(password: &str) -> Result<bool, std::io::Error> {
|
fn try_sudo(password: &str) -> Result<bool, std::io::Error> {
|
||||||
let mut child = Command::new("sudo")
|
let mut child = Command::new("sudo")
|
||||||
.args(["-kS", "true"]) // Use -S to read password from stdin
|
.args(["-kS", "true"]) // Use -S to read password from stdin
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
use egui::Rect;
|
||||||
|
use std::time::Instant;
|
||||||
|
use sysinfo::System;
|
||||||
|
|
||||||
|
use crate::configs;
|
||||||
|
use crate::graph::*;
|
||||||
|
|
||||||
|
pub struct MemGraph {
|
||||||
|
sys: System,
|
||||||
|
mem_graph: ResourceGraph,
|
||||||
|
last_update: Instant,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemGraph {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut sys = System::new();
|
||||||
|
sys.refresh_memory();
|
||||||
|
|
||||||
|
let mut memory_graph = ResourceGraph::new(
|
||||||
|
"Memory Usage".to_string(),
|
||||||
|
"GB".to_string(),
|
||||||
|
0.0,
|
||||||
|
sys.total_memory() as f32 / 1024.0 / 1024.0 / 1024.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add memory lines
|
||||||
|
memory_graph.add_line("Used".to_string(), configs::GRAPH_STROKE);
|
||||||
|
memory_graph.add_line("Cached".to_string(), configs::GRAPH_STROKE);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
sys,
|
||||||
|
mem_graph: memory_graph,
|
||||||
|
last_update: Instant::now(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self) {
|
||||||
|
let now = Instant::now();
|
||||||
|
if now.duration_since(self.last_update).as_secs_f32() < 1.0 / UPDATES_PER_SECOND {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// self.sys.refresh_cpu_all();
|
||||||
|
self.sys.refresh_memory();
|
||||||
|
|
||||||
|
// Update memory usage
|
||||||
|
let total_gb = self.sys.total_memory() as f32 / 1024.0 / 1024.0 / 1024.0;
|
||||||
|
let used_gb = self.sys.used_memory() as f32 / 1024.0 / 1024.0 / 1024.0;
|
||||||
|
let cached_gb = (self.sys.total_memory() - self.sys.available_memory()) as f32
|
||||||
|
/ 1024.0
|
||||||
|
/ 1024.0
|
||||||
|
/ 1024.0;
|
||||||
|
|
||||||
|
self.mem_graph.update_line(0, used_gb);
|
||||||
|
self.mem_graph.update_line(1, cached_gb);
|
||||||
|
|
||||||
|
self.last_update = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(&mut self, painter: &egui::Painter, rect: Rect) {
|
||||||
|
// self.cpu_graph.render(painter, cpu_rect);
|
||||||
|
self.mem_graph.render(painter, rect);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
use egui::Rect;
|
||||||
|
use std::time::Instant;
|
||||||
|
use sysinfo::Process;
|
||||||
|
use sysinfo::ProcessRefreshKind;
|
||||||
|
use sysinfo::RefreshKind;
|
||||||
|
use sysinfo::System;
|
||||||
|
|
||||||
|
use crate::configs;
|
||||||
|
use crate::graph::*;
|
||||||
|
|
||||||
|
pub struct NetGraph {
|
||||||
|
net: sysinfo::Networks,
|
||||||
|
|
||||||
|
mem_graph: ResourceGraph,
|
||||||
|
last_update: Instant,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetGraph {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut net = sysinfo::Networks::new_with_refreshed_list();
|
||||||
|
|
||||||
|
let mut memory_graph =
|
||||||
|
ResourceGraph::new("Disk Usage".to_string(), " KB/s".to_string(), 0.0, 10.0);
|
||||||
|
|
||||||
|
// Add memory lines
|
||||||
|
memory_graph.add_line("U".to_string(), configs::UP_GRAPH_STROKE);
|
||||||
|
memory_graph.add_line("D".to_string(), configs::DOWN_GRAPH_STROKE);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
net,
|
||||||
|
mem_graph: memory_graph,
|
||||||
|
last_update: Instant::now(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self) {
|
||||||
|
let now = Instant::now();
|
||||||
|
if now.duration_since(self.last_update).as_secs_f32() < 1.0 / UPDATES_PER_SECOND {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// self.mem_graph.
|
||||||
|
|
||||||
|
// self.networks.refresh_cpu_all();
|
||||||
|
|
||||||
|
let mut transmitted: f32 = 0.;
|
||||||
|
let mut recieved: f32 = 0.;
|
||||||
|
|
||||||
|
for (_, network) in &self.net {
|
||||||
|
// println!("in: {} B", network.received());
|
||||||
|
recieved += (network.received() as f32 / 1024.);
|
||||||
|
transmitted += (network.transmitted() as f32 / 1024.);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.net.refresh();
|
||||||
|
|
||||||
|
self.mem_graph
|
||||||
|
.update_line(0, transmitted * UPDATES_PER_SECOND);
|
||||||
|
self.mem_graph.update_line(1, recieved * UPDATES_PER_SECOND);
|
||||||
|
|
||||||
|
self.mem_graph.redo_max();
|
||||||
|
|
||||||
|
self.last_update = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(&mut self, painter: &egui::Painter, rect: Rect) {
|
||||||
|
// self.cpu_graph.render(painter, cpu_rect);
|
||||||
|
self.mem_graph.render(painter, rect);
|
||||||
|
}
|
||||||
|
}
|
||||||
+457
-354
@@ -1,8 +1,24 @@
|
|||||||
use egui::{Color32, Painter, Pos2, Rect};
|
use egui::ecolor::linear_u8_from_linear_f32;
|
||||||
|
use egui::emath::Rot2;
|
||||||
|
use egui::epaint::TextShape;
|
||||||
|
use egui::{Color32, FontId, Galley, Image, Painter, Pos2, Rect, Rounding};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::Value;
|
||||||
|
use std::borrow::BorrowMut;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::default::Default;
|
||||||
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
use crate::{graph::CpuGraph, ui};
|
use crate::cpugraph::CpuGraph;
|
||||||
|
use crate::diskgraph::DiskGraph;
|
||||||
|
use crate::infopane::InfoPane;
|
||||||
|
use crate::memgraph::MemGraph;
|
||||||
|
use crate::netgraph::NetGraph;
|
||||||
|
use crate::table::{ProcessTable, BAR_HEIGHT, ROW_HEIGHT};
|
||||||
|
use crate::ui::get_corners;
|
||||||
|
use crate::{configs::*, ui};
|
||||||
|
|
||||||
|
// use crate::{graph::CpuGraph, ui};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||||
pub enum CornerTypes {
|
pub enum CornerTypes {
|
||||||
@@ -12,387 +28,474 @@ pub enum CornerTypes {
|
|||||||
Ang60,
|
Ang60,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serializable configuration types
|
#[derive(Clone)]
|
||||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
pub enum TitleFormats {
|
||||||
pub enum SplitDirection {
|
TOP { text: Option<String> },
|
||||||
Horizontal,
|
SIDE { text: Option<String> },
|
||||||
Vertical,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
impl Default for TitleFormats {
|
||||||
#[serde(tag = "type", content = "config")]
|
fn default() -> Self {
|
||||||
pub enum PaneTypeConfig {
|
TitleFormats::TOP { text: None }
|
||||||
Solid(SolidPaneConfig),
|
}
|
||||||
Text(TextPaneConfig),
|
|
||||||
Gradient(GradientPaneConfig),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Runtime-only pane type that includes non-serializable data
|
const DEFAULT_CORNERS: [CornerTypes; 4] = [
|
||||||
// #[derive(Clone)]
|
CornerTypes::Ang30,
|
||||||
|
CornerTypes::Ang60,
|
||||||
|
CornerTypes::Ang60,
|
||||||
|
CornerTypes::Ang30,
|
||||||
|
];
|
||||||
|
|
||||||
|
// Enum to represent different types of panes
|
||||||
|
#[derive(Deserialize, Serialize, Clone)]
|
||||||
|
// #[serde(tag = "type")]
|
||||||
pub enum PaneType {
|
pub enum PaneType {
|
||||||
Solid(SolidPane),
|
Info,
|
||||||
Text(TextPane),
|
CpuGraph,
|
||||||
Gradient(GradientPane),
|
MemGraph,
|
||||||
|
NetGraph,
|
||||||
|
DiskGraph,
|
||||||
|
ProcTable,
|
||||||
|
No,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serializable configs
|
#[derive(Deserialize, Serialize, Clone, PartialEq)]
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
pub enum SplitType {
|
||||||
pub struct SolidPaneConfig {
|
H,
|
||||||
pub color: [u8; 3],
|
V,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
// Structure to store runtime data for different pane types
|
||||||
pub struct TextPaneConfig {
|
pub enum PaneData {
|
||||||
pub text: String,
|
Info { info_man: InfoPane },
|
||||||
pub font_size: f32,
|
CpuGraph { cpu_graph: CpuGraph },
|
||||||
pub color: [u8; 3],
|
MemGraph { mem_graph: MemGraph },
|
||||||
pub background_color: Option<[u8; 3]>,
|
NetGraph { net_graph: NetGraph },
|
||||||
|
DiskGraph { disk_graph: DiskGraph },
|
||||||
|
ProcTable { proc_table: ProcessTable },
|
||||||
|
No {},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
impl Default for PaneData {
|
||||||
pub struct GradientPaneConfig {
|
fn default() -> Self {
|
||||||
pub start_color: [u8; 3],
|
PaneData::No {}
|
||||||
pub end_color: [u8; 3],
|
}
|
||||||
pub horizontal: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Runtime types with additional non-serializable data
|
impl PaneData {
|
||||||
#[derive(Clone)]
|
pub fn new(pane: &Pane) -> Self {
|
||||||
pub struct SolidPane {
|
match pane {
|
||||||
config: SolidPaneConfig,
|
Pane::Split { .. } => PaneData::No {},
|
||||||
// Runtime-only fields
|
|
||||||
cached_color: Color32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
Pane::Leaf { pane_type, .. } => match pane_type {
|
||||||
pub struct TextPane {
|
PaneType::Info => PaneData::Info {
|
||||||
config: TextPaneConfig,
|
info_man: InfoPane::new(),
|
||||||
// Runtime-only fields
|
},
|
||||||
cached_color: Color32,
|
PaneType::CpuGraph => PaneData::CpuGraph {
|
||||||
cached_bg_color: Option<Color32>,
|
cpu_graph: CpuGraph::new(),
|
||||||
cached_font: egui::FontId,
|
},
|
||||||
}
|
PaneType::MemGraph => PaneData::MemGraph {
|
||||||
|
mem_graph: MemGraph::new(),
|
||||||
// #[derive(Clone)]
|
},
|
||||||
pub struct GradientPane {
|
PaneType::NetGraph => PaneData::NetGraph {
|
||||||
config: GradientPaneConfig,
|
net_graph: NetGraph::new(),
|
||||||
// Runtime-only fields
|
},
|
||||||
cached_start_color: Color32,
|
PaneType::DiskGraph => PaneData::DiskGraph {
|
||||||
cached_end_color: Color32,
|
disk_graph: DiskGraph::new(),
|
||||||
}
|
},
|
||||||
|
PaneType::ProcTable => PaneData::ProcTable {
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
proc_table: ProcessTable::new(30, 50, 0),
|
||||||
pub struct PaneConfig {
|
},
|
||||||
pub id: String,
|
PaneType::No {} => PaneData::No {},
|
||||||
#[serde(default)]
|
},
|
||||||
pub split: Option<SplitConfig>,
|
|
||||||
#[serde(default)]
|
|
||||||
pub pane_type: Option<PaneTypeConfig>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
|
||||||
pub struct SplitConfig {
|
|
||||||
pub direction: SplitDirection,
|
|
||||||
pub ratio: f32,
|
|
||||||
pub children: Vec<PaneConfig>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
|
||||||
pub struct LayoutConfig {
|
|
||||||
pub root: PaneConfig,
|
|
||||||
#[serde(default)]
|
|
||||||
pub default_pane_type: Option<PaneTypeConfig>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Runtime representation of the layout
|
|
||||||
pub struct RuntimePane {
|
|
||||||
pub id: String,
|
|
||||||
pub split: Option<RuntimeSplit>,
|
|
||||||
pub pane_type: Option<PaneType>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct RuntimeSplit {
|
|
||||||
pub direction: SplitDirection,
|
|
||||||
pub ratio: f32,
|
|
||||||
pub children: Vec<RuntimePane>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Conversion implementations
|
|
||||||
impl From<PaneTypeConfig> for PaneType {
|
|
||||||
fn from(config: PaneTypeConfig) -> Self {
|
|
||||||
match config {
|
|
||||||
PaneTypeConfig::Solid(config) => PaneType::Solid(SolidPane {
|
|
||||||
cached_color: Color32::from_rgb(config.color[0], config.color[1], config.color[2]),
|
|
||||||
config,
|
|
||||||
}),
|
|
||||||
PaneTypeConfig::Text(config) => PaneType::Text(TextPane {
|
|
||||||
cached_color: Color32::from_rgb(config.color[0], config.color[1], config.color[2]),
|
|
||||||
cached_bg_color: config
|
|
||||||
.background_color
|
|
||||||
.map(|c| Color32::from_rgb(c[0], c[1], c[2])),
|
|
||||||
cached_font: egui::FontId::proportional(config.font_size),
|
|
||||||
config,
|
|
||||||
}),
|
|
||||||
PaneTypeConfig::Gradient(config) => PaneType::Gradient(GradientPane {
|
|
||||||
cached_start_color: Color32::from_rgb(
|
|
||||||
config.start_color[0],
|
|
||||||
config.start_color[1],
|
|
||||||
config.start_color[2],
|
|
||||||
),
|
|
||||||
cached_end_color: Color32::from_rgb(
|
|
||||||
config.end_color[0],
|
|
||||||
config.end_color[1],
|
|
||||||
config.end_color[2],
|
|
||||||
),
|
|
||||||
|
|
||||||
config,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PaneType {
|
// Main pane structure that represents either a split or leaf node
|
||||||
fn render(&self, painter: &Painter, rect: Rect) {
|
#[derive(Deserialize, Serialize)]
|
||||||
ui::background_render(
|
// #[default]
|
||||||
painter,
|
#[serde(tag = "kind")]
|
||||||
|
pub enum Pane {
|
||||||
|
Leaf {
|
||||||
|
pane_type: PaneType,
|
||||||
|
corners: [CornerTypes; 4],
|
||||||
|
#[serde(skip, default = "get_default_rect")]
|
||||||
|
rect: Rect,
|
||||||
|
#[serde(skip, default = "get_default_rect")]
|
||||||
|
inner_rect: Rect,
|
||||||
|
#[serde(skip)]
|
||||||
|
container_points: Vec<Pos2>,
|
||||||
|
#[serde(skip)]
|
||||||
|
title_type: TitleFormats,
|
||||||
|
},
|
||||||
|
Split {
|
||||||
|
direction: SplitType,
|
||||||
|
bias: f32,
|
||||||
|
#[serde(skip)]
|
||||||
|
first: Box<PaneInstance>,
|
||||||
|
#[serde(skip)]
|
||||||
|
second: Box<PaneInstance>,
|
||||||
|
a: Box<Pane>,
|
||||||
|
b: Box<Pane>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_RECT: Rect = Rect {
|
||||||
|
min: Pos2 { x: 0., y: 0. },
|
||||||
|
max: Pos2 { x: 0., y: 0. },
|
||||||
|
};
|
||||||
|
const DEFAULT_POINTS: Vec<Pos2> = Vec::new();
|
||||||
|
|
||||||
|
fn get_default_rect() -> Rect {
|
||||||
|
DEFAULT_RECT
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Pane {
|
||||||
|
fn default() -> Self {
|
||||||
|
Pane::Leaf {
|
||||||
|
pane_type: PaneType::No {},
|
||||||
|
corners: DEFAULT_CORNERS,
|
||||||
|
rect: DEFAULT_RECT,
|
||||||
|
inner_rect: DEFAULT_RECT,
|
||||||
|
container_points: DEFAULT_POINTS,
|
||||||
|
title_type: TitleFormats::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Default for PaneInstance {
|
||||||
|
fn default() -> Self {
|
||||||
|
PaneInstance {
|
||||||
|
config: Pane::default(),
|
||||||
|
runtime_data: PaneData::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl Pane {
|
||||||
|
// fn
|
||||||
|
// }
|
||||||
|
// Runtime instance of a pane that includes both config and runtime data
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct PaneInstance {
|
||||||
|
config: Pane,
|
||||||
|
#[serde(skip)]
|
||||||
|
runtime_data: PaneData,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaneInstance {
|
||||||
|
// Create a new instance from configuration
|
||||||
|
fn from_config(config: Pane) -> Self {
|
||||||
|
PaneInstance {
|
||||||
|
runtime_data: PaneData::new(&config),
|
||||||
|
config: config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn precalc(&mut self, rect: Rect) {
|
||||||
|
match &mut self.config {
|
||||||
|
Pane::Split {
|
||||||
|
direction,
|
||||||
|
bias,
|
||||||
|
first,
|
||||||
|
second,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let (first_rect, second_rect) = if *direction == SplitType::V {
|
||||||
|
let split_x = rect.min.x + rect.width() * bias.clone();
|
||||||
|
(
|
||||||
|
Rect::from_min_max(rect.min, egui::pos2(split_x, rect.max.y)),
|
||||||
|
Rect::from_min_max(egui::pos2(split_x, rect.min.y), rect.max),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let mut split_y = rect.min.y + rect.height() * bias.clone();
|
||||||
|
(
|
||||||
|
Rect::from_min_max(rect.min, egui::pos2(rect.max.x, split_y)),
|
||||||
|
Rect::from_min_max(egui::pos2(rect.min.x, split_y), rect.max),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
first.precalc(first_rect);
|
||||||
|
second.precalc(second_rect);
|
||||||
|
}
|
||||||
|
Pane::Leaf {
|
||||||
|
pane_type,
|
||||||
|
corners,
|
||||||
|
rect: rect2,
|
||||||
|
inner_rect,
|
||||||
|
container_points,
|
||||||
|
title_type: title_type,
|
||||||
|
} => {
|
||||||
|
container_points.clone_from(&get_corners(rect, corners.to_owned()));
|
||||||
|
rect2.clone_from(&rect);
|
||||||
|
inner_rect.clone_from(&ui::find_largest_rectangle(container_points).unwrap());
|
||||||
|
|
||||||
|
let text = Some(
|
||||||
|
match pane_type {
|
||||||
|
PaneType::Info => "INFO",
|
||||||
|
PaneType::CpuGraph => "CPU",
|
||||||
|
PaneType::MemGraph => "MEM",
|
||||||
|
PaneType::ProcTable => "PROC",
|
||||||
|
PaneType::NetGraph => "NET",
|
||||||
|
PaneType::DiskGraph => "DISK",
|
||||||
|
_ => "ERR",
|
||||||
|
}
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
|
||||||
|
title_type.clone_from(&if ((inner_rect.left() - rect2.left())
|
||||||
|
+ (rect2.right() - inner_rect.right()))
|
||||||
|
> ((inner_rect.top() - rect2.top()) + (rect2.bottom() - inner_rect.bottom()))
|
||||||
|
{
|
||||||
|
TitleFormats::SIDE { text: text }
|
||||||
|
} else {
|
||||||
|
TitleFormats::TOP { text: text }
|
||||||
|
});
|
||||||
|
|
||||||
|
match pane_type {
|
||||||
|
PaneType::ProcTable { .. } => {
|
||||||
|
if let PaneData::ProcTable { proc_table } = self.runtime_data.borrow_mut() {
|
||||||
|
proc_table.row_count =
|
||||||
|
((inner_rect.height() - BAR_HEIGHT) / ROW_HEIGHT).floor() as usize;
|
||||||
|
// cpu_proc_table.row_count = 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
// let adj_rect = background_render(painter, rect, *corners);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render the pane and its children
|
||||||
|
pub fn render(&mut self, painter: &Painter) {
|
||||||
|
match &mut self.config {
|
||||||
|
Pane::Split { first, second, .. } => {
|
||||||
|
first.render(painter);
|
||||||
|
second.render(painter);
|
||||||
|
}
|
||||||
|
Pane::Leaf {
|
||||||
|
pane_type,
|
||||||
rect,
|
rect,
|
||||||
[
|
inner_rect,
|
||||||
CornerTypes::Ang30,
|
container_points,
|
||||||
CornerTypes::Ang60,
|
title_type,
|
||||||
CornerTypes::Ang30,
|
..
|
||||||
CornerTypes::Ang60,
|
} => {
|
||||||
],
|
painter.add(egui::Shape::convex_polygon(
|
||||||
);
|
container_points.to_owned(),
|
||||||
match self {
|
BACKGROUND_2,
|
||||||
PaneType::Solid(pane) => {
|
egui::Stroke::new(0.5, TEXT_COLOR),
|
||||||
// painter.rect_filled(rect, 0.0, pane.cached_color);
|
));
|
||||||
// painter.rect_stroke(rect, 0.0, (1.0, Color32::BLACK));
|
|
||||||
}
|
|
||||||
PaneType::Text(pane) => {
|
|
||||||
// if let Some(bg_color) = pane.cached_bg_color {
|
|
||||||
// painter.rect_filled(rect, 0.0, bg_color);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// painter.text(
|
match title_type {
|
||||||
// rect.center(),
|
TitleFormats::SIDE { text } => {
|
||||||
// egui::Align2::CENTER_CENTER,
|
let galley = painter.layout_no_wrap(
|
||||||
// &pane.config.text,
|
text.as_ref().unwrap().to_owned(),
|
||||||
// pane.cached_font.clone(),
|
TITLE_FONT,
|
||||||
// pane.cached_color,
|
TEXT_COLOR,
|
||||||
// );
|
);
|
||||||
|
let size = galley.size();
|
||||||
|
let mid_x = rect.min.x + (f32::abs(rect.min.x - inner_rect.min.x) / 2.)
|
||||||
|
- (size.y / 2.)
|
||||||
|
+ PANE_GAP;
|
||||||
|
let mid_y = (inner_rect.min.y
|
||||||
|
+ f32::abs(inner_rect.min.y - inner_rect.max.y) / 2.)
|
||||||
|
+ size.x / 2.;
|
||||||
|
painter.add(
|
||||||
|
TextShape::new(Pos2 { x: mid_x, y: mid_y }, galley, Color32::WHITE)
|
||||||
|
.with_angle(-PI / 2.),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
TitleFormats::TOP { text } => {
|
||||||
|
let galley = painter.layout_no_wrap(
|
||||||
|
text.as_ref().unwrap().to_owned(),
|
||||||
|
TITLE_FONT,
|
||||||
|
TEXT_COLOR,
|
||||||
|
);
|
||||||
|
let size = galley.size();
|
||||||
|
let mid_x = rect.min.x
|
||||||
|
+ (f32::abs(inner_rect.min.x - inner_rect.max.x) / 2.)
|
||||||
|
- (size.x / 2.);
|
||||||
|
let mid_y = (rect.min.y + f32::abs(rect.min.y - inner_rect.min.y) / 2.)
|
||||||
|
- (size.y / 2.)
|
||||||
|
+ PANE_GAP;
|
||||||
|
painter.add(TextShape::new(
|
||||||
|
Pos2 { x: mid_x, y: mid_y },
|
||||||
|
galley,
|
||||||
|
Color32::WHITE,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
painter.rect_stroke(
|
||||||
|
inner_rect.to_owned(),
|
||||||
|
0.0,
|
||||||
|
egui::Stroke::new(0.25, TEXT_COLOR),
|
||||||
|
);
|
||||||
|
|
||||||
|
match pane_type {
|
||||||
|
PaneType::Info => {
|
||||||
|
render_info(painter, inner_rect.to_owned(), &mut self.runtime_data);
|
||||||
|
}
|
||||||
|
PaneType::CpuGraph => {
|
||||||
|
render_cpu_graph(painter, inner_rect.to_owned(), &mut self.runtime_data);
|
||||||
|
}
|
||||||
|
PaneType::MemGraph => {
|
||||||
|
render_mem_graph(painter, inner_rect.to_owned(), &mut self.runtime_data);
|
||||||
|
}
|
||||||
|
PaneType::NetGraph => {
|
||||||
|
render_net_graph(painter, inner_rect.to_owned(), &mut self.runtime_data);
|
||||||
|
}
|
||||||
|
PaneType::DiskGraph => {
|
||||||
|
render_disk_graph(painter, inner_rect.to_owned(), &mut self.runtime_data);
|
||||||
|
}
|
||||||
|
PaneType::ProcTable => {
|
||||||
|
render_proc_table(painter, inner_rect.to_owned(), &mut self.runtime_data);
|
||||||
|
}
|
||||||
|
PaneType::No => {}
|
||||||
}
|
}
|
||||||
PaneType::Gradient(pane) => {
|
|
||||||
let mut pane2 = pane;
|
|
||||||
// pane.cpu_graph.update();
|
|
||||||
// pane.cpu_graph.render(painter, rect);
|
|
||||||
// if pane.config.horizontal {
|
|
||||||
// painter.rect_filled(rect, 0.0, pane.cached_start_color); // Simplified
|
|
||||||
// } else {
|
|
||||||
// painter.rect_filled(rect, 0.0, pane.cached_end_color); // Simplified
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PaneRenderer {
|
pub fn render_info(painter: &Painter, rect: Rect, data: &mut PaneData) {
|
||||||
runtime_config: RuntimePane,
|
if let PaneData::Info { info_man } = data {
|
||||||
default_pane_type: Option<PaneType>,
|
info_man.update();
|
||||||
}
|
info_man.render(painter, rect);
|
||||||
|
|
||||||
impl PaneRenderer {
|
|
||||||
pub fn new(config: LayoutConfig) -> Self {
|
|
||||||
Self {
|
|
||||||
runtime_config: Self::convert_config(&config.root),
|
|
||||||
default_pane_type: config.default_pane_type.map(Into::into),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert_config(config: &PaneConfig) -> RuntimePane {
|
|
||||||
RuntimePane {
|
|
||||||
id: config.id.clone(),
|
|
||||||
split: config.split.as_ref().map(|split| RuntimeSplit {
|
|
||||||
direction: split.direction,
|
|
||||||
ratio: split.ratio,
|
|
||||||
children: split.children.iter().map(Self::convert_config).collect(),
|
|
||||||
}),
|
|
||||||
pane_type: config.pane_type.clone().map(Into::into),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render(&self, painter: &Painter, rect: &Rect) {
|
|
||||||
self.render_pane(painter, &rect, &self.runtime_config);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_pane(&self, painter: &Painter, rect: &Rect, pane: &RuntimePane) {
|
|
||||||
if let Some(split) = &pane.split {
|
|
||||||
if !split.children.is_empty() {
|
|
||||||
let rects =
|
|
||||||
self.split_rect(rect, split.direction, split.ratio, split.children.len());
|
|
||||||
|
|
||||||
for (child, child_rect) in split.children.iter().zip(&rects) {
|
|
||||||
self.render_pane(painter, child_rect, child);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw split lines
|
|
||||||
let split_line_color = Color32::from_gray(128);
|
|
||||||
match split.direction {
|
|
||||||
SplitDirection::Horizontal => {
|
|
||||||
for rect in rects.windows(2) {
|
|
||||||
let x = rect[0].max.x;
|
|
||||||
painter.line_segment(
|
|
||||||
[Pos2::new(x, rect[0].min.y), Pos2::new(x, rect[0].max.y)],
|
|
||||||
(1.0, split_line_color),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SplitDirection::Vertical => {
|
|
||||||
for rect in rects.windows(2) {
|
|
||||||
let y = rect[0].max.y;
|
|
||||||
painter.line_segment(
|
|
||||||
[Pos2::new(rect[0].min.x, y), Pos2::new(rect[0].max.x, y)],
|
|
||||||
(1.0, split_line_color),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Render leaf pane
|
|
||||||
let pane_type = &mut pane.pane_type.as_ref();
|
|
||||||
|
|
||||||
if let Some(pane_type) = pane_type {
|
|
||||||
pane_type.render(painter, *rect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn split_rect(
|
|
||||||
&self,
|
|
||||||
rect: &Rect,
|
|
||||||
direction: SplitDirection,
|
|
||||||
ratio: f32,
|
|
||||||
count: usize,
|
|
||||||
) -> Vec<Rect> {
|
|
||||||
let mut rects = Vec::with_capacity(count);
|
|
||||||
let size = match direction {
|
|
||||||
SplitDirection::Horizontal => rect.width(),
|
|
||||||
SplitDirection::Vertical => rect.height(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let first_size = size * ratio;
|
|
||||||
let remaining_size = size - first_size;
|
|
||||||
let size_per_remaining = if count > 1 {
|
|
||||||
remaining_size / (count as f32 - 1.0)
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
};
|
|
||||||
|
|
||||||
for i in 0..count {
|
|
||||||
let (start, end) = match direction {
|
|
||||||
SplitDirection::Horizontal => {
|
|
||||||
let start = if i == 0 {
|
|
||||||
rect.min.x
|
|
||||||
} else {
|
|
||||||
rect.min.x + first_size + size_per_remaining * (i as f32 - 1.0)
|
|
||||||
};
|
|
||||||
let end = if i == 0 {
|
|
||||||
rect.min.x + first_size
|
|
||||||
} else {
|
|
||||||
start + size_per_remaining
|
|
||||||
};
|
|
||||||
(Pos2::new(start, rect.min.y), Pos2::new(end, rect.max.y))
|
|
||||||
}
|
|
||||||
SplitDirection::Vertical => {
|
|
||||||
let start = if i == 0 {
|
|
||||||
rect.min.y
|
|
||||||
} else {
|
|
||||||
rect.min.y + first_size + size_per_remaining * (i as f32 - 1.0)
|
|
||||||
};
|
|
||||||
let end = if i == 0 {
|
|
||||||
rect.min.y + first_size
|
|
||||||
} else {
|
|
||||||
start + size_per_remaining
|
|
||||||
};
|
|
||||||
(Pos2::new(rect.min.x, start), Pos2::new(rect.max.x, end))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
rects.push(Rect::from_min_max(start, end));
|
|
||||||
}
|
|
||||||
rects
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// // Example JSON configuration:
|
// Example rendering functions for different pane types
|
||||||
// const EXAMPLE_CONFIG: &str = r#"
|
pub fn render_cpu_graph(painter: &Painter, rect: Rect, data: &mut PaneData) {
|
||||||
// {
|
if let PaneData::CpuGraph { cpu_graph } = data {
|
||||||
// "root": {
|
cpu_graph.update();
|
||||||
// "id": "root",
|
cpu_graph.render(painter, rect);
|
||||||
// "split": {
|
}
|
||||||
// "direction": "Horizontal",
|
}
|
||||||
// "ratio": 0.3,
|
|
||||||
// "children": [
|
|
||||||
// {
|
|
||||||
// "id": "left",
|
|
||||||
// "pane_type": {
|
|
||||||
// "type": "Solid",
|
|
||||||
// "config": {
|
|
||||||
// "color": [100, 150, 200]
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// "id": "right",
|
|
||||||
// "split": {
|
|
||||||
// "direction": "Vertical",
|
|
||||||
// "ratio": 0.6,
|
|
||||||
// "children": [
|
|
||||||
// {
|
|
||||||
// "id": "right_top",
|
|
||||||
// "pane_type": {
|
|
||||||
// "type": "Text",
|
|
||||||
// "config": {
|
|
||||||
// "text": "Hello World",
|
|
||||||
// "font_size": 24.0,
|
|
||||||
// "color": [255, 255, 255],
|
|
||||||
// "background_color": [50, 50, 150]
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// "id": "right_bottom",
|
|
||||||
// "pane_type": {
|
|
||||||
// "type": "Gradient",
|
|
||||||
// "config": {
|
|
||||||
// "start_color": [200, 100, 100],
|
|
||||||
// "end_color": [100, 200, 100],
|
|
||||||
// "horizontal": true
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// ]
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// ]
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// "default_pane_type": {
|
|
||||||
// "type": "Solid",
|
|
||||||
// "config": {
|
|
||||||
// "color": [200, 200, 200]
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// "#;
|
|
||||||
|
|
||||||
// // Example usage
|
pub fn render_mem_graph(painter: &Painter, rect: Rect, data: &mut PaneData) {
|
||||||
// pub fn example_usage(ctx: &egui::Context) {
|
if let PaneData::MemGraph { mem_graph } = data {
|
||||||
// // Parse configuration
|
mem_graph.update();
|
||||||
// let config: LayoutConfig = serde_json::from_str(EXAMPLE_CONFIG).unwrap();
|
mem_graph.render(painter, rect);
|
||||||
// let pane_renderer = PaneRenderer::new(config);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// // egui::CentralPanel::default().show(ctx, |ui| {
|
pub fn render_net_graph(painter: &Painter, rect: Rect, data: &mut PaneData) {
|
||||||
// // let painter = ui.painter();
|
if let PaneData::NetGraph { net_graph } = data {
|
||||||
|
net_graph.update();
|
||||||
|
net_graph.render(painter, rect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// // });
|
pub fn render_disk_graph(painter: &Painter, rect: Rect, data: &mut PaneData) {
|
||||||
// }
|
if let PaneData::DiskGraph { disk_graph } = data {
|
||||||
|
disk_graph.update();
|
||||||
|
disk_graph.render(painter, rect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_proc_table(painter: &Painter, rect: Rect, data: &mut PaneData) {
|
||||||
|
if let PaneData::ProcTable { proc_table } = data {
|
||||||
|
proc_table.update();
|
||||||
|
proc_table.render(painter, rect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to load pane configuration from JSON
|
||||||
|
pub fn load_pane_config(json: &str) -> Result<PaneInstance, serde_json::Error> {
|
||||||
|
let config: Pane = serde_json::from_str(json)?;
|
||||||
|
|
||||||
|
// Create the pane hierarchy with runtime data
|
||||||
|
Ok(create_pane_instance(config))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to create the pane hierarchy
|
||||||
|
pub fn create_pane_instance(config: Pane) -> PaneInstance {
|
||||||
|
match config {
|
||||||
|
Pane::Split {
|
||||||
|
direction,
|
||||||
|
bias,
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let first = Box::new(create_pane_instance(*a));
|
||||||
|
let second = Box::new(create_pane_instance(*b));
|
||||||
|
|
||||||
|
PaneInstance {
|
||||||
|
config: Pane::Split {
|
||||||
|
direction,
|
||||||
|
bias,
|
||||||
|
first,
|
||||||
|
second,
|
||||||
|
a: Box::new(Pane::default()),
|
||||||
|
b: Box::new(Pane::default()),
|
||||||
|
},
|
||||||
|
runtime_data: PaneData::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
leaf => PaneInstance::from_config(leaf),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example JSON configuration
|
||||||
|
pub const EXAMPLE_CONFIG: &str = r#"
|
||||||
|
{
|
||||||
|
"kind": "Split",
|
||||||
|
"direction": "V",
|
||||||
|
"bias": 0.5,
|
||||||
|
"a": {
|
||||||
|
"kind": "Split",
|
||||||
|
"direction": "H",
|
||||||
|
"bias": 0.2,
|
||||||
|
"a": {
|
||||||
|
"kind": "Leaf",
|
||||||
|
"corners": ["Ang60", "Ang30", "Ang60", "Ang30"],
|
||||||
|
"pane_type": "Info"
|
||||||
|
},
|
||||||
|
"b": {
|
||||||
|
"kind": "Leaf",
|
||||||
|
"corners": ["Ang30", "Ang60", "SQUARE", "SQUARE"],
|
||||||
|
"pane_type": "ProcTable"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"b": {
|
||||||
|
"kind": "Split",
|
||||||
|
"direction": "H",
|
||||||
|
"bias": 0.5,
|
||||||
|
"a": {
|
||||||
|
"kind": "Split",
|
||||||
|
"direction": "H",
|
||||||
|
"bias": 0.5,
|
||||||
|
"a": {
|
||||||
|
"kind": "Leaf",
|
||||||
|
"corners": ["Ang60", "SQUARE", "SQUARE", "Ang30"],
|
||||||
|
"pane_type": "CpuGraph"
|
||||||
|
},
|
||||||
|
"b": {
|
||||||
|
"kind": "Leaf",
|
||||||
|
"corners": ["Ang60", "SQUARE", "SQUARE", "Ang30"],
|
||||||
|
"pane_type": "MemGraph"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"b": {
|
||||||
|
"kind": "Split",
|
||||||
|
"direction": "H",
|
||||||
|
"bias": 0.5,
|
||||||
|
"a": {
|
||||||
|
"kind": "Leaf",
|
||||||
|
"corners": ["Ang60", "SQUARE", "SQUARE", "Ang30"],
|
||||||
|
"pane_type": "NetGraph"
|
||||||
|
},
|
||||||
|
"b": {
|
||||||
|
"kind": "Leaf",
|
||||||
|
"corners": ["Ang60", "SQUARE", "SQUARE", "Ang30"],
|
||||||
|
"pane_type": "DiskGraph"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|||||||
+297
@@ -0,0 +1,297 @@
|
|||||||
|
use egui::{Color32, Pos2, Rect, Response, Stroke, Vec2};
|
||||||
|
use std::time::Instant;
|
||||||
|
use sysinfo::{Pid, System};
|
||||||
|
|
||||||
|
const UPDATE_INTERVAL: f32 = 0.5; // seconds
|
||||||
|
pub const BAR_HEIGHT: f32 = 16.0;
|
||||||
|
pub const ROW_HEIGHT: f32 = 24.0;
|
||||||
|
const COLUMN_PADDING: f32 = 10.0;
|
||||||
|
const SWAP_INTERVAL: f32 = 5.; // seconds
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum SortColumn {
|
||||||
|
Cpu,
|
||||||
|
Memory,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ProcessInfo {
|
||||||
|
pid: Pid,
|
||||||
|
name: String,
|
||||||
|
command: String,
|
||||||
|
cpu_usage: f32,
|
||||||
|
memory_bytes: u64,
|
||||||
|
memory_percent: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ProcessTable {
|
||||||
|
sys: System,
|
||||||
|
last_update: Instant,
|
||||||
|
last_swap: Instant,
|
||||||
|
processes: Vec<ProcessInfo>,
|
||||||
|
sort_by: SortColumn,
|
||||||
|
name_width: usize,
|
||||||
|
command_width: usize,
|
||||||
|
pub row_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rem_first_and_last(value: String, num: usize) -> String {
|
||||||
|
let mut chars = value.chars();
|
||||||
|
chars.next();
|
||||||
|
chars.next_back();
|
||||||
|
chars.take(num).collect::<String>()
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProcessTable {
|
||||||
|
pub fn new(name_width: usize, command_width: usize, row_count: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
sys: System::new_all(),
|
||||||
|
last_update: Instant::now(),
|
||||||
|
last_swap: Instant::now(),
|
||||||
|
processes: Vec::new(),
|
||||||
|
sort_by: SortColumn::Cpu,
|
||||||
|
name_width,
|
||||||
|
command_width,
|
||||||
|
row_count,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_sort(&mut self, sort_by: SortColumn) {
|
||||||
|
self.sort_by = sort_by;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self) {
|
||||||
|
let now = Instant::now();
|
||||||
|
if now.duration_since(self.last_update).as_secs_f32() < UPDATE_INTERVAL {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if now.duration_since(self.last_swap).as_secs_f32() > SWAP_INTERVAL {
|
||||||
|
self.sort_by = match self.sort_by {
|
||||||
|
SortColumn::Cpu => SortColumn::Memory,
|
||||||
|
SortColumn::Memory => SortColumn::Cpu,
|
||||||
|
};
|
||||||
|
self.last_swap = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.sys.refresh_all();
|
||||||
|
|
||||||
|
let total_memory = self.sys.total_memory() as f64;
|
||||||
|
|
||||||
|
// Collect process information
|
||||||
|
self.processes = self
|
||||||
|
.sys
|
||||||
|
.processes()
|
||||||
|
.iter()
|
||||||
|
.map(|(pid, proc)| ProcessInfo {
|
||||||
|
pid: *pid,
|
||||||
|
name: proc.name().to_str().unwrap().to_string(),
|
||||||
|
command: format!("{:?}", proc.cmd()),
|
||||||
|
cpu_usage: proc.cpu_usage(),
|
||||||
|
memory_bytes: proc.memory(),
|
||||||
|
memory_percent: (proc.memory() as f64 / total_memory * 100.0) as f32,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Sort processes based on selected criterion
|
||||||
|
match self.sort_by {
|
||||||
|
SortColumn::Cpu => {
|
||||||
|
self.processes
|
||||||
|
.sort_by(|a, b| b.cpu_usage.partial_cmp(&a.cpu_usage).unwrap());
|
||||||
|
}
|
||||||
|
SortColumn::Memory => {
|
||||||
|
self.processes
|
||||||
|
.sort_by_key(|p| std::cmp::Reverse(p.memory_bytes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep only top N processes
|
||||||
|
self.processes.truncate(self.row_count);
|
||||||
|
|
||||||
|
self.last_update = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_bar(&self, painter: &egui::Painter, rect: Rect, percentage: f32, color: Color32) {
|
||||||
|
// Background
|
||||||
|
painter.rect_filled(rect, 0.0, Color32::from_gray(40));
|
||||||
|
|
||||||
|
// Foreground bar
|
||||||
|
let bar_width = rect.width() * (percentage / 100.0).min(1.0);
|
||||||
|
let bar_rect = Rect::from_min_max(rect.min, Pos2::new(rect.min.x + bar_width, rect.max.y));
|
||||||
|
painter.rect_filled(bar_rect, 0.0, color);
|
||||||
|
|
||||||
|
// Percentage text
|
||||||
|
let text = format!("{:.1}%", percentage);
|
||||||
|
painter.text(
|
||||||
|
rect.center(),
|
||||||
|
egui::Align2::CENTER_CENTER,
|
||||||
|
text,
|
||||||
|
egui::FontId::proportional(12.0),
|
||||||
|
Color32::WHITE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_header(&self, painter: &egui::Painter, rect: Rect) -> f32 {
|
||||||
|
let text_color = Color32::LIGHT_GRAY;
|
||||||
|
let header_height = 24.0;
|
||||||
|
let header_rect =
|
||||||
|
Rect::from_min_max(rect.min, Pos2::new(rect.max.x, rect.min.y + header_height));
|
||||||
|
|
||||||
|
// Background
|
||||||
|
painter.rect_filled(header_rect, 0.0, Color32::from_gray(30));
|
||||||
|
|
||||||
|
let mut x = rect.min.x + COLUMN_PADDING;
|
||||||
|
|
||||||
|
// PID header
|
||||||
|
painter.text(
|
||||||
|
Pos2::new(x, header_rect.center().y),
|
||||||
|
egui::Align2::LEFT_CENTER,
|
||||||
|
"PID",
|
||||||
|
egui::FontId::proportional(14.0),
|
||||||
|
text_color,
|
||||||
|
);
|
||||||
|
x += rect.width() * 0.1;
|
||||||
|
|
||||||
|
// Name header
|
||||||
|
painter.text(
|
||||||
|
Pos2::new(x, header_rect.center().y),
|
||||||
|
egui::Align2::LEFT_CENTER,
|
||||||
|
"Name",
|
||||||
|
egui::FontId::proportional(14.0),
|
||||||
|
text_color,
|
||||||
|
);
|
||||||
|
x += rect.width() * 0.2;
|
||||||
|
|
||||||
|
// Command header
|
||||||
|
painter.text(
|
||||||
|
Pos2::new(x, header_rect.center().y),
|
||||||
|
egui::Align2::LEFT_CENTER,
|
||||||
|
"Command",
|
||||||
|
egui::FontId::proportional(14.0),
|
||||||
|
text_color,
|
||||||
|
);
|
||||||
|
x += rect.width() * 0.5;
|
||||||
|
|
||||||
|
// CPU header
|
||||||
|
painter.text(
|
||||||
|
Pos2::new(x, header_rect.center().y),
|
||||||
|
egui::Align2::LEFT_CENTER,
|
||||||
|
"CPU %",
|
||||||
|
egui::FontId::proportional(14.0),
|
||||||
|
text_color,
|
||||||
|
);
|
||||||
|
x += rect.width() * 0.1;
|
||||||
|
|
||||||
|
// Memory header
|
||||||
|
painter.text(
|
||||||
|
Pos2::new(x, header_rect.center().y),
|
||||||
|
egui::Align2::LEFT_CENTER,
|
||||||
|
"Memory %",
|
||||||
|
egui::FontId::proportional(14.0),
|
||||||
|
text_color,
|
||||||
|
);
|
||||||
|
|
||||||
|
header_height
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(&mut self, painter: &egui::Painter, rect: Rect) {
|
||||||
|
let header_height = self.draw_header(painter, rect);
|
||||||
|
let content_rect =
|
||||||
|
Rect::from_min_max(Pos2::new(rect.min.x, rect.min.y + header_height), rect.max);
|
||||||
|
|
||||||
|
for (i, process) in self.processes.iter().enumerate() {
|
||||||
|
let row_min_y = content_rect.min.y + (i as f32 * ROW_HEIGHT);
|
||||||
|
let row_rect = Rect::from_min_max(
|
||||||
|
Pos2::new(content_rect.min.x, row_min_y),
|
||||||
|
Pos2::new(content_rect.max.x, row_min_y + ROW_HEIGHT),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Background (alternating)
|
||||||
|
painter.rect_filled(
|
||||||
|
row_rect,
|
||||||
|
0.0,
|
||||||
|
if i % 2 == 0 {
|
||||||
|
Color32::from_gray(25)
|
||||||
|
} else {
|
||||||
|
Color32::from_gray(20)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut x = row_rect.min.x + COLUMN_PADDING;
|
||||||
|
let text_y = row_rect.center().y;
|
||||||
|
|
||||||
|
// PID
|
||||||
|
painter.text(
|
||||||
|
Pos2::new(x, text_y),
|
||||||
|
egui::Align2::LEFT_CENTER,
|
||||||
|
process.pid.to_string(),
|
||||||
|
egui::FontId::proportional(14.0),
|
||||||
|
Color32::WHITE,
|
||||||
|
);
|
||||||
|
x += rect.width() * 0.1;
|
||||||
|
|
||||||
|
// Name (truncated)
|
||||||
|
painter.text(
|
||||||
|
Pos2::new(x, text_y),
|
||||||
|
egui::Align2::LEFT_CENTER,
|
||||||
|
process
|
||||||
|
.name
|
||||||
|
.chars()
|
||||||
|
.take(self.name_width)
|
||||||
|
.collect::<String>(),
|
||||||
|
egui::FontId::proportional(14.0),
|
||||||
|
Color32::WHITE,
|
||||||
|
);
|
||||||
|
x += rect.width() * 0.2;
|
||||||
|
|
||||||
|
// let cmd = rem_first_and_last(
|
||||||
|
// process.command.split("\", \"").collect(),
|
||||||
|
// self.command_width,
|
||||||
|
// );
|
||||||
|
|
||||||
|
// Command (truncated)
|
||||||
|
painter.text(
|
||||||
|
Pos2::new(x, text_y),
|
||||||
|
egui::Align2::LEFT_CENTER,
|
||||||
|
process
|
||||||
|
.command
|
||||||
|
.chars()
|
||||||
|
.take(self.row_count)
|
||||||
|
.collect::<String>(),
|
||||||
|
// .chars()
|
||||||
|
// .take(self.command_width).collect::<String>(),
|
||||||
|
egui::FontId::proportional(14.0),
|
||||||
|
Color32::WHITE,
|
||||||
|
);
|
||||||
|
x += rect.width() * 0.5;
|
||||||
|
|
||||||
|
let cpu_bar_rect = Rect::from_min_max(
|
||||||
|
Pos2::new(x, row_rect.min.y + (ROW_HEIGHT - BAR_HEIGHT) / 2.0),
|
||||||
|
Pos2::new(
|
||||||
|
x + rect.width() * 0.08,
|
||||||
|
row_rect.min.y + (ROW_HEIGHT + BAR_HEIGHT) / 2.0,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
self.draw_bar(
|
||||||
|
painter,
|
||||||
|
cpu_bar_rect,
|
||||||
|
process.cpu_usage,
|
||||||
|
Color32::from_rgb(46, 194, 126),
|
||||||
|
);
|
||||||
|
x += rect.width() * 0.1;
|
||||||
|
|
||||||
|
// Memory bar
|
||||||
|
let mem_bar_rect = Rect::from_min_max(
|
||||||
|
Pos2::new(x, row_rect.min.y + (ROW_HEIGHT - BAR_HEIGHT) / 2.0),
|
||||||
|
Pos2::new(
|
||||||
|
x + rect.width() * 0.08,
|
||||||
|
row_rect.min.y + (ROW_HEIGHT + BAR_HEIGHT) / 2.0,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
self.draw_bar(
|
||||||
|
painter,
|
||||||
|
mem_bar_rect,
|
||||||
|
process.memory_percent,
|
||||||
|
Color32::from_rgb(194, 137, 46),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
use crate::configs::*;
|
use crate::configs::*;
|
||||||
use crate::graph::CpuGraph;
|
|
||||||
use crate::panes;
|
use crate::panes;
|
||||||
|
use crate::panes::PaneInstance;
|
||||||
use crate::structs;
|
use crate::structs;
|
||||||
use panes::PaneRenderer;
|
// use panes::PaneRenderer;
|
||||||
// use crate::structs::cur_context;
|
// use crate::structs::cur_context;
|
||||||
// use crate::structs::windowTypes;
|
// use crate::structs::windowTypes;
|
||||||
|
|
||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
use egui::Color32;
|
use egui::Color32;
|
||||||
use egui::Shape;
|
use egui::Shape;
|
||||||
use egui::{Pos2, Vec2};
|
use egui::{Pos2, Rect, Vec2};
|
||||||
use panes::{PaneConfig, SplitDirection};
|
use panes::Pane;
|
||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
@@ -24,14 +24,16 @@ fn rot_circle(i: i16, center: Pos2, rad: f32, offset_ang: f32, ang_per_num: f32)
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_Pos2(add: Vec2, points: &mut Vec<Pos2>) -> &mut Vec<Pos2> {
|
fn add_Pos2(add: Vec2, points: Vec<Pos2>) -> Vec<Pos2> {
|
||||||
|
let mut points = points;
|
||||||
for i in 0..points.len() {
|
for i in 0..points.len() {
|
||||||
points[i] = points[i] + add;
|
points[i] = points[i] + add;
|
||||||
}
|
}
|
||||||
points
|
points
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mult_Pos2(mult: f32, points: &mut Vec<Pos2>) -> &mut Vec<Pos2> {
|
fn mult_Pos2(mult: f32, points: Vec<Pos2>) -> Vec<Pos2> {
|
||||||
|
let mut points = points;
|
||||||
let vec = Vec2 { x: mult, y: mult };
|
let vec = Vec2 { x: mult, y: mult };
|
||||||
for i in 0..points.len() {
|
for i in 0..points.len() {
|
||||||
points[i] = points[i] * mult;
|
points[i] = points[i] * mult;
|
||||||
@@ -39,7 +41,8 @@ fn mult_Pos2(mult: f32, points: &mut Vec<Pos2>) -> &mut Vec<Pos2> {
|
|||||||
points
|
points
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rotate_90(points: &mut Vec<Pos2>) -> &mut Vec<Pos2> {
|
fn rotate_90(points: Vec<Pos2>) -> Vec<Pos2> {
|
||||||
|
let mut points = points;
|
||||||
for i in 0..points.len() {
|
for i in 0..points.len() {
|
||||||
points[i] = Pos2 {
|
points[i] = Pos2 {
|
||||||
x: -points[i].y,
|
x: -points[i].y,
|
||||||
@@ -48,7 +51,8 @@ fn rotate_90(points: &mut Vec<Pos2>) -> &mut Vec<Pos2> {
|
|||||||
}
|
}
|
||||||
points
|
points
|
||||||
}
|
}
|
||||||
fn rotate_180(points: &mut Vec<Pos2>) -> &mut Vec<Pos2> {
|
fn rotate_180(points: Vec<Pos2>) -> Vec<Pos2> {
|
||||||
|
let mut points = points;
|
||||||
for i in 0..points.len() {
|
for i in 0..points.len() {
|
||||||
points[i] = Pos2 {
|
points[i] = Pos2 {
|
||||||
x: -points[i].x,
|
x: -points[i].x,
|
||||||
@@ -57,7 +61,8 @@ fn rotate_180(points: &mut Vec<Pos2>) -> &mut Vec<Pos2> {
|
|||||||
}
|
}
|
||||||
points
|
points
|
||||||
}
|
}
|
||||||
fn rotate_270(points: &mut Vec<Pos2>) -> &mut Vec<Pos2> {
|
fn rotate_270(points: Vec<Pos2>) -> Vec<Pos2> {
|
||||||
|
let mut points = points;
|
||||||
for i in 0..points.len() {
|
for i in 0..points.len() {
|
||||||
points[i] = Pos2 {
|
points[i] = Pos2 {
|
||||||
x: points[i].y,
|
x: points[i].y,
|
||||||
@@ -82,65 +87,66 @@ fn get_corner_points(ctype: panes::CornerTypes) -> Vec<Pos2> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn background_render(
|
pub fn get_corners(rect: egui::Rect, corners: [panes::CornerTypes; 4]) -> Vec<Pos2> {
|
||||||
painter: &egui::Painter,
|
|
||||||
rect: egui::Rect,
|
|
||||||
corners: [panes::CornerTypes; 4],
|
|
||||||
) {
|
|
||||||
let mut points: Vec<Pos2> = Vec::new();
|
let mut points: Vec<Pos2> = Vec::new();
|
||||||
let left = rect.left() + PANE_GAP;
|
let left = rect.left() + PANE_GAP;
|
||||||
let right = rect.right() - PANE_GAP;
|
let right = rect.right() - PANE_GAP;
|
||||||
let bottom = rect.bottom() - PANE_GAP;
|
let bottom = rect.bottom() - PANE_GAP;
|
||||||
let top = rect.top() + PANE_GAP;
|
let top = rect.top() + PANE_GAP;
|
||||||
// let x1 = left + CORNER_CUT - PANE_GAP;
|
|
||||||
// let x2 = right - CORNER_CUT + PANE_GAP;
|
|
||||||
// let y1 = top + CORNER_CUT - PANE_GAP;
|
|
||||||
// let y2 = bottom - CORNER_CUT + PANE_GAP;
|
|
||||||
|
|
||||||
points.append(add_Pos2(
|
let rot_point_tl = get_corner_points(corners[0]).to_vec();
|
||||||
|
let rot_point_tr = rotate_90(get_corner_points(corners[1]).to_vec());
|
||||||
|
let rot_point_bl = rotate_180(get_corner_points(corners[2]).to_vec());
|
||||||
|
let rot_point_br = rotate_270(get_corner_points(corners[3]).to_vec());
|
||||||
|
|
||||||
|
points.append(
|
||||||
|
add_Pos2(
|
||||||
Vec2 { x: left, y: top },
|
Vec2 { x: left, y: top },
|
||||||
mult_Pos2(CORNER_CUT, &mut get_corner_points(corners[0]).to_vec()),
|
mult_Pos2(CORNER_CUT, rot_point_tl),
|
||||||
));
|
)
|
||||||
|
.as_mut(),
|
||||||
|
);
|
||||||
|
|
||||||
points.append(add_Pos2(
|
points.append(
|
||||||
|
add_Pos2(
|
||||||
Vec2 { x: right, y: top },
|
Vec2 { x: right, y: top },
|
||||||
mult_Pos2(
|
mult_Pos2(CORNER_CUT, rot_point_tr),
|
||||||
CORNER_CUT,
|
)
|
||||||
rotate_90(&mut get_corner_points(corners[1]).to_vec()),
|
.as_mut(),
|
||||||
),
|
);
|
||||||
));
|
|
||||||
|
|
||||||
points.append(add_Pos2(
|
points.append(
|
||||||
|
add_Pos2(
|
||||||
Vec2 {
|
Vec2 {
|
||||||
x: right,
|
x: right,
|
||||||
y: bottom,
|
y: bottom,
|
||||||
},
|
},
|
||||||
mult_Pos2(
|
mult_Pos2(CORNER_CUT, rot_point_bl),
|
||||||
CORNER_CUT,
|
)
|
||||||
rotate_180(&mut get_corner_points(corners[2]).to_vec()),
|
.as_mut(),
|
||||||
),
|
);
|
||||||
));
|
|
||||||
|
|
||||||
points.append(add_Pos2(
|
points.append(
|
||||||
|
add_Pos2(
|
||||||
Vec2 { x: left, y: bottom },
|
Vec2 { x: left, y: bottom },
|
||||||
mult_Pos2(
|
mult_Pos2(CORNER_CUT, rot_point_br),
|
||||||
CORNER_CUT,
|
)
|
||||||
rotate_270(&mut get_corner_points(corners[3]).to_vec()),
|
.as_mut(),
|
||||||
),
|
);
|
||||||
));
|
|
||||||
|
|
||||||
let filled_polygon =
|
// let largest_rect = find_largest_rectangle(&points).unwrap();
|
||||||
Shape::convex_polygon(points, BACKGROUND_2, egui::Stroke::new(0.5, TEXT_COLOR));
|
|
||||||
|
|
||||||
painter.add(filled_polygon);
|
// let filled_polygon =
|
||||||
|
|
||||||
|
return points;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_password_viewer(
|
pub fn update(
|
||||||
wstate: MutexGuard<'_, structs::AuthState>,
|
wstate: MutexGuard<'_, structs::AuthState>,
|
||||||
ctx: &egui::Context,
|
ctx: &egui::Context,
|
||||||
frame: &mut eframe::Frame,
|
frame: &mut eframe::Frame,
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
winconfig: &mut PaneRenderer,
|
root_pane: &mut PaneInstance,
|
||||||
) {
|
) {
|
||||||
let rect: egui::Rect = ui.available_rect_before_wrap();
|
let rect: egui::Rect = ui.available_rect_before_wrap();
|
||||||
// let ctx: cur_context = cur_context {
|
// let ctx: cur_context = cur_context {
|
||||||
@@ -152,17 +158,17 @@ pub fn update_password_viewer(
|
|||||||
};
|
};
|
||||||
let painter: &egui::Painter = ui.painter();
|
let painter: &egui::Painter = ui.painter();
|
||||||
|
|
||||||
paint_windows(
|
// paint_windows(
|
||||||
painter,
|
// painter,
|
||||||
rect.width() as f32,
|
// rect.width() as f32,
|
||||||
rect.height() as f32,
|
// rect.height() as f32,
|
||||||
0.,
|
// 0.,
|
||||||
0.,
|
// 0.,
|
||||||
// windows,
|
// // windows,
|
||||||
);
|
// );
|
||||||
|
|
||||||
dots(painter, ui.clip_rect());
|
// dots(painter, ui.clip_rect());
|
||||||
winconfig.render(painter, &rect);
|
root_pane.render(ui.painter());
|
||||||
paint_password_circle(state, ctx, frame, ui, center, painter);
|
paint_password_circle(state, ctx, frame, ui, center, painter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,19 +278,144 @@ fn paint_password_circle(
|
|||||||
ctx.request_repaint();
|
ctx.request_repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint_windows(
|
pub fn find_largest_rectangle(points: &[Pos2]) -> Option<Rect> {
|
||||||
painter: &egui::Painter,
|
if points.len() < 4 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
width: f32,
|
// Helper function to calculate area of a rectangle
|
||||||
height: f32,
|
fn calculate_area(rect: &Rect) -> f32 {
|
||||||
x: f32,
|
rect.width() * rect.height()
|
||||||
y: f32,
|
}
|
||||||
// pane: structs::PaneSplit,
|
|
||||||
) {
|
// Helper function to determine if a point is inside a polygon using ray casting
|
||||||
// match pane.wintype {
|
fn is_point_in_polygon(point: &Pos2, polygon: &[Pos2]) -> bool {
|
||||||
// windowTypes::SplitHorisontal => {
|
let mut inside = false;
|
||||||
// paint_windows(painter, width, height / 2., x, y, pane.sub_a);
|
let mut j = polygon.len() - 1;
|
||||||
// paint_windows(painter, width, height / 2., x, y + height / 2., pane.sub_b);
|
|
||||||
|
for i in 0..polygon.len() {
|
||||||
|
if ((polygon[i].y >= point.y) != (polygon[j].y >= point.y))
|
||||||
|
&& (point.x
|
||||||
|
<= (polygon[j].x - polygon[i].x) * (point.y - polygon[i].y)
|
||||||
|
/ (polygon[j].y - polygon[i].y)
|
||||||
|
+ polygon[i].x)
|
||||||
|
{
|
||||||
|
inside = !inside;
|
||||||
|
}
|
||||||
|
j = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
inside
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to check if a rectangle is completely inside the polygon
|
||||||
|
fn is_rect_in_polygon(rect: &Rect, polygon: &[Pos2]) -> bool {
|
||||||
|
// Check all four corners of the rectangle
|
||||||
|
let corners = [
|
||||||
|
rect.min,
|
||||||
|
Pos2::new(rect.max.x, rect.min.y),
|
||||||
|
rect.max,
|
||||||
|
Pos2::new(rect.min.x, rect.max.y),
|
||||||
|
];
|
||||||
|
|
||||||
|
corners
|
||||||
|
.iter()
|
||||||
|
.all(|corner| is_point_in_polygon(corner, polygon))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to check if a set of points forms a valid rectangle
|
||||||
|
fn is_valid_rectangle(p1: &Pos2, p2: &Pos2, p3: &Pos2, p4: &Pos2) -> bool {
|
||||||
|
// Sort points by x coordinate
|
||||||
|
let mut pts = vec![p1, p2, p3, p4];
|
||||||
|
pts.sort_by(|a, b| a.x.partial_cmp(&b.x).unwrap());
|
||||||
|
|
||||||
|
// For a valid rectangle:
|
||||||
|
// - The first two points should have the same x-coordinate
|
||||||
|
// - The last two points should have the same x-coordinate
|
||||||
|
// - Two points should have the min y-coordinate
|
||||||
|
// - Two points should have the max y-coordinate
|
||||||
|
let x_eps = 0.;
|
||||||
|
let y_eps = 0.;
|
||||||
|
|
||||||
|
// // Check x-coordinates are properly paired
|
||||||
|
// if (pts[0].x - pts[1].x).abs() >= x_eps || (pts[2].x - pts[3].x).abs() >= x_eps {
|
||||||
|
// return false;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
// // Sort points by y coordinate
|
||||||
|
// pts.sort_by(|a, b| a.y.partial_cmp(&b.y).unwrap());
|
||||||
|
|
||||||
|
// // Check y-coordinates are properly paired
|
||||||
|
// if (pts[0].y - pts[1].y).abs() >= y_eps || (pts[2].y - pts[3].y).abs() >= y_eps {
|
||||||
|
// return false;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort points to ensure they form a proper polygon
|
||||||
|
let mut polygon_points = points.to_vec();
|
||||||
|
let center = Pos2::new(
|
||||||
|
polygon_points.iter().map(|p| p.x).sum::<f32>() / polygon_points.len() as f32,
|
||||||
|
polygon_points.iter().map(|p| p.y).sum::<f32>() / polygon_points.len() as f32,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sort points clockwise around the center
|
||||||
|
polygon_points.sort_by(|a, b| {
|
||||||
|
let a_angle = (a.y - center.y).atan2(a.x - center.x);
|
||||||
|
let b_angle = (b.y - center.y).atan2(b.x - center.x);
|
||||||
|
b_angle.partial_cmp(&a_angle).unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut largest_rect = None;
|
||||||
|
let mut max_area = 0.0;
|
||||||
|
|
||||||
|
// Try all combinations of 4 points
|
||||||
|
for i in 0..points.len() {
|
||||||
|
for j in i + 1..points.len() {
|
||||||
|
for k in j + 1..points.len() {
|
||||||
|
for l in k + 1..points.len() {
|
||||||
|
let p1 = points[i];
|
||||||
|
let p2 = points[j];
|
||||||
|
let p3 = points[k];
|
||||||
|
let p4 = points[l];
|
||||||
|
|
||||||
|
// Skip if these points don't form a valid rectangle
|
||||||
|
if !is_valid_rectangle(&p1, &p2, &p3, &p4) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create rectangle from these points
|
||||||
|
let min_x = p1.x.min(p2.x).min(p3.x).min(p4.x);
|
||||||
|
let max_x = p1.x.max(p2.x).max(p3.x).max(p4.x);
|
||||||
|
let min_y = p1.y.min(p2.y).min(p3.y).min(p4.y);
|
||||||
|
let max_y = p1.y.max(p2.y).max(p3.y).max(p4.y);
|
||||||
|
|
||||||
|
let rect = Rect {
|
||||||
|
min: Pos2 {
|
||||||
|
x: min_x + 1.,
|
||||||
|
y: min_y + 1.,
|
||||||
|
},
|
||||||
|
max: Pos2 {
|
||||||
|
x: max_x - 1.,
|
||||||
|
y: max_y - 1.,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Verify the rectangle is inside the polygon
|
||||||
|
if !is_rect_in_polygon(&rect, &polygon_points) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let area = calculate_area(&rect);
|
||||||
|
if area > max_area {
|
||||||
|
max_area = area;
|
||||||
|
largest_rect = Some(rect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
largest_rect
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user