Work on adding configurable panes

This commit is contained in:
Astatin3
2024-11-01 12:15:00 -06:00
parent 0aa59fc94d
commit 68ccc7164b
7 changed files with 934 additions and 328 deletions
+2
View File
@@ -8,3 +8,5 @@ eframe = "0.28"
egui = "0.28" egui = "0.28"
rand = "0.8.5" rand = "0.8.5"
serde = "1.0.214" serde = "1.0.214"
serde_json = "1.0.132"
sysinfo = "0.32.0"
+62
View File
@@ -40,3 +40,65 @@ 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]
}
}
}
"#;
+170
View File
@@ -0,0 +1,170 @@
use egui::{Color32, Pos2, Rect, Stroke, Vec2};
use std::collections::VecDeque;
use std::time::Instant;
use sysinfo::System;
const HISTORY_SIZE: usize = 100;
const ANIMATION_DURATION: f32 = 0.2; // seconds
const UPDATES_PER_SECOND: f32 = 2.0;
#[derive(Clone)]
struct DataPoint {
value: f32,
timestamp: Instant,
}
#[derive(Clone)]
struct AnimatedValue {
current: f32,
target: f32,
last_update: Instant,
}
impl AnimatedValue {
fn new(initial: f32) -> Self {
Self {
current: initial,
target: initial,
last_update: Instant::now(),
}
}
fn update(&mut self, new_target: f32) {
self.target = new_target;
self.last_update = Instant::now();
}
fn get_current_value(&mut self) -> f32 {
let elapsed = self.last_update.elapsed().as_secs_f32();
let progress = (elapsed / ANIMATION_DURATION).min(1.0);
self.current = self.current + (self.target - self.current) * progress;
self.current
}
}
// #[derive(Clone)]
//
// #[derive(Debug)]
pub struct CpuGraph {
sys: System,
history: Vec<VecDeque<DataPoint>>,
animated_values: Vec<AnimatedValue>,
last_update: Instant,
colors: Vec<Color32>,
}
impl CpuGraph {
pub fn new() -> 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 {
sys,
history,
animated_values,
last_update: Instant::now(),
colors,
}
}
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();
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 {
self.history[i].pop_front();
}
}
self.last_update = now;
}
pub fn render(&mut self, painter: &egui::Painter, rect: Rect) {
let stroke_width = 2.0;
// Draw background
painter.rect_filled(rect, 0.0, Color32::from_gray(20));
// Draw grid lines
for i in 0..=4 {
let y = rect.min.y + rect.height() * (i as f32 / 4.0);
painter.line_segment(
[Pos2::new(rect.min.x, y), Pos2::new(rect.max.x, y)],
Stroke::new(1.0, Color32::from_gray(40)),
);
}
// Draw CPU usage lines
for (core_idx, (history, color)) in self.history.iter().zip(self.colors.iter()).enumerate()
{
if history.is_empty() {
continue;
}
let points: Vec<Pos2> = history
.iter()
.enumerate()
.map(|(i, point)| {
let x = rect.min.x + rect.width() * (i as f32 / (HISTORY_SIZE - 1) as f32);
let y = rect.max.y - rect.height() * point.value;
Pos2::new(x, y)
})
.collect();
// Get the current animated value for the last point
let current_value = self.animated_values[core_idx].get_current_value();
let mut animated_points = points;
if let Some(last) = animated_points.last_mut() {
last.y = rect.max.y - rect.height() * current_value;
}
// Draw the line
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,
);
}
}
}
+31 -8
View File
@@ -1,14 +1,15 @@
use eframe::{egui, App}; use eframe::{egui, App};
use egui::FontFamily; use egui::FontFamily;
use egui::Key; use egui::Key;
use graph::CpuGraph;
use input::is_locked; use input::is_locked;
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 structs::{PaneConfig, PaneRenderer, SplitDirection};
// use egui::epaint::text::{FontInsert, InsertFontFamily}; // use egui::epaint::text::{FontInsert, InsertFontFamily};
@@ -22,8 +23,9 @@ use egui::Stroke;
use egui::ecolor::HexColor; use egui::ecolor::HexColor;
mod configs; mod configs;
mod graph;
mod input; mod input;
mod splitter; mod panes;
mod structs; mod structs;
mod ui; mod ui;
@@ -59,11 +61,10 @@ mod ui;
// ctx.set_fonts(fonts); // ctx.set_fonts(fonts);
// } // }
use splitter::Splitter; // #[derive(Default)]
#[derive(Default)]
struct ExampleApp { struct ExampleApp {
auth_state: Arc<Mutex<structs::AuthState>>, auth_state: Arc<Mutex<structs::AuthState>>,
cpu_graph: CpuGraph,
// terminals: HashMap<String, TermHandler>, // terminals: HashMap<String, TermHandler>,
} }
@@ -102,9 +103,10 @@ impl eframe::App for ExampleApp {
let mut state = self.auth_state.lock().unwrap(); let mut state = self.auth_state.lock().unwrap();
//ctx.set_pixels_per_point(1.5); //ctx.set_pixels_per_point(1.5);
let config = ui::winconfig(); // let config = ui::winconfig();
let pane_renderer = PaneRenderer::new(config); 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| {
@@ -149,6 +151,8 @@ impl eframe::App for ExampleApp {
// let sine_points2 = PlotPoints::from_explicit_callback(|x| x.sin(), .., 5000); // let sine_points2 = PlotPoints::from_explicit_callback(|x| x.sin(), .., 5000);
// let plot_b_lines = [Line::new(sine_points2).name("Sine")]; // 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| {
@@ -181,7 +185,17 @@ impl eframe::App for ExampleApp {
// }, // },
// ); // );
ui::update_password_viewer(state, ctx, _frame, ui, pane_renderer); 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();
}); });
} }
} }
@@ -243,6 +257,8 @@ fn main() -> eframe::Result<()> {
std::process::exit(1); std::process::exit(1);
} }
let test = [1, 2, 3];
input::create_lock(); input::create_lock();
eframe::run_native( eframe::run_native(
@@ -251,12 +267,19 @@ fn main() -> eframe::Result<()> {
Box::new(|_| { Box::new(|_| {
Ok(Box::<ExampleApp>::new(ExampleApp { Ok(Box::<ExampleApp>::new(ExampleApp {
auth_state: state, auth_state: state,
cpu_graph: CpuGraph::new(),
// terminals: map, // terminals: map,
})) }))
}), }),
) )
} }
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
+398
View File
@@ -0,0 +1,398 @@
use egui::{Color32, Painter, Pos2, Rect};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use crate::{graph::CpuGraph, ui};
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum CornerTypes {
SQUARE,
Ang45,
Ang30,
Ang60,
}
// Serializable configuration types
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum SplitDirection {
Horizontal,
Vertical,
}
#[derive(Clone, Serialize, Deserialize)]
#[serde(tag = "type", content = "config")]
pub enum PaneTypeConfig {
Solid(SolidPaneConfig),
Text(TextPaneConfig),
Gradient(GradientPaneConfig),
}
// Runtime-only pane type that includes non-serializable data
// #[derive(Clone)]
pub enum PaneType {
Solid(SolidPane),
Text(TextPane),
Gradient(GradientPane),
}
// Serializable configs
#[derive(Clone, Serialize, Deserialize)]
pub struct SolidPaneConfig {
pub color: [u8; 3],
}
#[derive(Clone, Serialize, Deserialize)]
pub struct TextPaneConfig {
pub text: String,
pub font_size: f32,
pub color: [u8; 3],
pub background_color: Option<[u8; 3]>,
}
#[derive(Clone, Serialize, Deserialize)]
pub struct GradientPaneConfig {
pub start_color: [u8; 3],
pub end_color: [u8; 3],
pub horizontal: bool,
}
// Runtime types with additional non-serializable data
#[derive(Clone)]
pub struct SolidPane {
config: SolidPaneConfig,
// Runtime-only fields
cached_color: Color32,
}
#[derive(Clone)]
pub struct TextPane {
config: TextPaneConfig,
// Runtime-only fields
cached_color: Color32,
cached_bg_color: Option<Color32>,
cached_font: egui::FontId,
}
// #[derive(Clone)]
pub struct GradientPane {
config: GradientPaneConfig,
// Runtime-only fields
cached_start_color: Color32,
cached_end_color: Color32,
}
#[derive(Clone, Serialize, Deserialize)]
pub struct PaneConfig {
pub id: String,
#[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 {
fn render(&self, painter: &Painter, rect: Rect) {
ui::background_render(
painter,
rect,
[
CornerTypes::Ang30,
CornerTypes::Ang60,
CornerTypes::Ang30,
CornerTypes::Ang60,
],
);
match self {
PaneType::Solid(pane) => {
// 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(
// rect.center(),
// egui::Align2::CENTER_CENTER,
// &pane.config.text,
// pane.cached_font.clone(),
// pane.cached_color,
// );
}
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 {
runtime_config: RuntimePane,
default_pane_type: Option<PaneType>,
}
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:
// const EXAMPLE_CONFIG: &str = r#"
// {
// "root": {
// "id": "root",
// "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 example_usage(ctx: &egui::Context) {
// // Parse configuration
// let config: LayoutConfig = serde_json::from_str(EXAMPLE_CONFIG).unwrap();
// let pane_renderer = PaneRenderer::new(config);
// // egui::CentralPanel::default().show(ctx, |ui| {
// // let painter = ui.painter();
// // });
// }
+161 -219
View File
@@ -13,233 +13,175 @@ pub struct AuthState {
pub failed_attempts: u16, pub failed_attempts: u16,
} }
// pub enum windowTypes { // #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
// Temp1, // pub enum SplitDirection {
// Temp2, // Horizontal,
// Vertical,
// SplitVertical,
// SplitHorisontal,
// } // }
// pub struct Pane { // // #[derive(Sized)]
// pub wintype: windowTypes, // pub struct PaneConfig {
// pub id: String,
// pub split: Option<(SplitDirection, f32)>,
// pub children: Option<Vec<PaneConfig>>,
// pub color: Option<Color32>, // Store color in config for consistency
// pub callback: Option<Box<dyn Fn(&Painter, Rect, [CornerTypes; 4])>>,
// pub corners: [CornerTypes; 4],
// } // }
// pub struct PaneSplit { // // #[derive(Debug)]
// pub wintype: windowTypes, // pub struct PaneRenderer {
// pub sub_a: Pane, // config: PaneConfig,
// pub sub_b: Pane,
// } // }
// impl Pane { // impl PaneConfig {
// pub const fn new(wintype: windowTypes) -> Self { // pub fn new(id: impl Into<String>) -> Self {
// Self { wintype: wintype } // Self {
// } // id: id.into(),
// split: None,
// pub const fn new_hsplit(wintype: windowTypes, left: Pane, right: Pane) -> PaneSplit { // children: None,
// PaneSplit { // color: None,
// wintype: wintype, // callback: None,
// sub_a: left, // corners: [
// sub_b: right, // CornerTypes::Ang60,
// CornerTypes::Ang30,
// CornerTypes::SQUARE,
// CornerTypes::Ang45,
// ],
// } // }
// } // }
// pub const fn new_rsplit(wintype: windowTypes, wintop: Pane, bottom: Pane) -> PaneSplit {
// PaneSplit { // pub fn with_split(mut self, direction: SplitDirection, ratio: f32) -> Self {
// wintype: wintype, // self.split = Some((direction, ratio.clamp(0.0, 1.0)));
// sub_a: top, // self
// sub_b: bottom, // }
// pub fn with_children(mut self, children: Vec<PaneConfig>) -> Self {
// self.children = Some(children);
// self
// }
// pub fn with_callback(
// mut self,
// callback: impl Fn(&Painter, Rect, [CornerTypes; 4]) + 'static,
// ) -> Self {
// self.callback = Some(Box::new(callback));
// self
// }
// pub fn with_corners(mut self, corners: [CornerTypes; 4]) -> Self {
// self.corners = corners;
// self
// }
// pub fn with_corner_tl(mut self, corner: CornerTypes) -> Self {
// self.corners[0] = corner;
// self
// }
// pub fn with_corner_tr(mut self, corner: CornerTypes) -> Self {
// self.corners[1] = corner;
// self
// }
// pub fn with_corner_br(mut self, corner: CornerTypes) -> Self {
// self.corners[2] = corner;
// self
// }
// pub fn with_corner_bl(mut self, corner: CornerTypes) -> Self {
// self.corners[3] = corner;
// self
// }
// pub fn with_random_color(mut self) -> Self {
// let mut rng = rand::thread_rng();
// self.color = Some(Color32::from_rgb(
// rng.gen_range(0..255),
// rng.gen_range(0..255),
// rng.gen_range(0..255),
// ));
// self
// }
// }
// impl PaneRenderer {
// pub fn new(config: PaneConfig) -> Self {
// Self { config }
// }
// pub fn render(&self, painter: &Painter, rect: Rect) {
// self.render_pane(painter, rect, &self.config);
// }
// fn render_pane(&self, painter: &Painter, rect: Rect, pane: &PaneConfig) {
// if let Some((direction, ratio)) = &pane.split {
// if let Some(children) = &pane.children {
// if children.len() >= 2 {
// let (first_rect, second_rect) = self.split_rect(rect, *direction, *ratio);
// // Render first child
// self.render_pane(painter, first_rect, &children[0]);
// // Render second child
// self.render_pane(painter, second_rect, &children[1]);
// // Render any additional children (will be stacked after the split)
// let remaining_rect = match direction {
// SplitDirection::Horizontal => Rect::from_min_size(
// Pos2::new(second_rect.max.x, rect.min.y),
// Vec2::new(rect.max.x - second_rect.max.x, rect.height()),
// ),
// SplitDirection::Vertical => Rect::from_min_size(
// Pos2::new(rect.min.x, second_rect.max.y),
// Vec2::new(rect.width(), rect.max.y - second_rect.max.y),
// ),
// };
// for child in children.iter().skip(2) {
// self.render_pane(painter, remaining_rect, child);
// }
// // Draw split line
// let split_line_color = Color32::from_gray(128);
// match direction {
// SplitDirection::Horizontal => {
// let x = first_rect.max.x;
// // painter.line_segment(
// // [Pos2::new(x, rect.min.y), Pos2::new(x, rect.max.y)],
// // (1.0, split_line_color),
// // );
// }
// SplitDirection::Vertical => {
// let y = first_rect.max.y;
// // painter.line_segment(
// // [Pos2::new(rect.min.x, y), Pos2::new(rect.max.x, y)],
// // (1.0, split_line_color),
// // );
// }
// }
// }
// }
// } else {
// if !pane.callback.is_none() {
// // (&self.config).corners[0] = CornerTypes::Ang30;
// pane.callback.as_ref().unwrap()(painter, rect, self.config.corners);
// return;
// }
// }
// }
// fn split_rect(&self, rect: Rect, direction: SplitDirection, ratio: f32) -> (Rect, Rect) {
// match direction {
// SplitDirection::Horizontal => {
// let split_x = rect.min.x + rect.width() * ratio;
// let first = Rect::from_min_max(rect.min, Pos2::new(split_x, rect.max.y));
// let second = Rect::from_min_max(Pos2::new(split_x, rect.min.y), rect.max);
// (first, second)
// }
// SplitDirection::Vertical => {
// let split_y = rect.min.y + rect.height() * ratio;
// let first = Rect::from_min_max(rect.min, Pos2::new(rect.max.x, split_y));
// let second = Rect::from_min_max(Pos2::new(rect.min.x, split_y), rect.max);
// (first, second)
// }
// } // }
// } // }
// } // }
// pub struct cur_context {
// pub state: AuthState,
// pub ctx: egui::Context,
// pub frame: eframe::Frame,
// pub ui: egui::Ui,
// pub center: egui::Pos2,
// pub painter: egui::Painter,
// }
// #[derive(Debug)]
// type PaintCallback = dyn Fn() -> String;
// impl fmt::Debug for PaintCallback {
// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// write!("Test")
// }
// }
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum SplitDirection {
Horizontal,
Vertical,
}
// #[derive(Sized)]
pub struct PaneConfig {
pub id: String,
pub split: Option<(SplitDirection, f32)>,
pub children: Option<Vec<PaneConfig>>,
pub color: Option<Color32>, // Store color in config for consistency
pub callback: Option<Box<dyn Fn(&Painter, Rect)>>,
}
// #[derive(Debug)]
pub struct PaneRenderer {
config: PaneConfig,
}
impl PaneConfig {
pub fn new(id: impl Into<String>) -> Self {
Self {
id: id.into(),
split: None,
children: None,
color: None,
callback: None,
}
}
pub fn with_split(mut self, direction: SplitDirection, ratio: f32) -> Self {
self.split = Some((direction, ratio.clamp(0.0, 1.0)));
self
}
pub fn with_children(mut self, children: Vec<PaneConfig>) -> Self {
self.children = Some(children);
self
}
pub fn with_callback(mut self, callback: impl Fn(&Painter, Rect) + 'static) -> Self {
self.callback = Some(Box::new(callback));
self
}
pub fn with_random_color(mut self) -> Self {
let mut rng = rand::thread_rng();
self.color = Some(Color32::from_rgb(
rng.gen_range(0..255),
rng.gen_range(0..255),
rng.gen_range(0..255),
));
self
}
}
impl PaneRenderer {
pub fn new(config: PaneConfig) -> Self {
Self { config }
}
pub fn render(&self, painter: &Painter, rect: Rect) {
self.render_pane(painter, rect, &self.config);
}
fn render_pane(&self, painter: &Painter, rect: Rect, pane: &PaneConfig) {
if let Some((direction, ratio)) = &pane.split {
if let Some(children) = &pane.children {
if children.len() >= 2 {
let (first_rect, second_rect) = self.split_rect(rect, *direction, *ratio);
// Render first child
self.render_pane(painter, first_rect, &children[0]);
// Render second child
self.render_pane(painter, second_rect, &children[1]);
// Render any additional children (will be stacked after the split)
let remaining_rect = match direction {
SplitDirection::Horizontal => Rect::from_min_size(
Pos2::new(second_rect.max.x, rect.min.y),
Vec2::new(rect.max.x - second_rect.max.x, rect.height()),
),
SplitDirection::Vertical => Rect::from_min_size(
Pos2::new(rect.min.x, second_rect.max.y),
Vec2::new(rect.width(), rect.max.y - second_rect.max.y),
),
};
for child in children.iter().skip(2) {
self.render_pane(painter, remaining_rect, child);
}
// Draw split line
let split_line_color = Color32::from_gray(128);
match direction {
SplitDirection::Horizontal => {
let x = first_rect.max.x;
// painter.line_segment(
// [Pos2::new(x, rect.min.y), Pos2::new(x, rect.max.y)],
// (1.0, split_line_color),
// );
}
SplitDirection::Vertical => {
let y = first_rect.max.y;
// painter.line_segment(
// [Pos2::new(rect.min.x, y), Pos2::new(rect.max.x, y)],
// (1.0, split_line_color),
// );
}
}
}
}
} else {
if !pane.callback.is_none() {
pane.callback.as_ref().unwrap()(painter, rect);
return;
}
}
}
fn split_rect(&self, rect: Rect, direction: SplitDirection, ratio: f32) -> (Rect, Rect) {
match direction {
SplitDirection::Horizontal => {
let split_x = rect.min.x + rect.width() * ratio;
let first = Rect::from_min_max(rect.min, Pos2::new(split_x, rect.max.y));
let second = Rect::from_min_max(Pos2::new(split_x, rect.min.y), rect.max);
(first, second)
}
SplitDirection::Vertical => {
let split_y = rect.min.y + rect.height() * ratio;
let first = Rect::from_min_max(rect.min, Pos2::new(rect.max.x, split_y));
let second = Rect::from_min_max(Pos2::new(rect.min.x, split_y), rect.max);
(first, second)
}
}
}
}
// Example usage in an app
pub fn example_usage(ctx: &egui::Context) {
// Create a complex pane configuration
let config = PaneConfig::new("root")
.with_split(SplitDirection::Horizontal, 0.3)
.with_children(vec![
PaneConfig::new("left").with_random_color(),
PaneConfig::new("right")
.with_split(SplitDirection::Vertical, 0.6)
.with_children(vec![
PaneConfig::new("right_top").with_random_color(),
PaneConfig::new("right_bottom")
.with_split(SplitDirection::Horizontal, 0.5)
.with_children(vec![
PaneConfig::new("bottom_left").with_random_color(),
PaneConfig::new("bottom_right").with_random_color(),
]),
]),
]);
let pane_renderer = PaneRenderer::new(config);
egui::CentralPanel::default().show(ctx, |ui| {
// Get the painter and available rect from the UI
let painter = ui.painter();
let rect = ui.available_rect_before_wrap();
// Render the panes
pane_renderer.render(&painter, rect);
});
}
+110 -101
View File
@@ -1,15 +1,17 @@
use crate::configs::*; use crate::configs::*;
use crate::graph::CpuGraph;
use crate::panes;
use crate::structs; use crate::structs;
use crate::structs::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::Pos2;
use egui::Shape; use egui::Shape;
use egui::{Pos2, Vec2};
use panes::{PaneConfig, SplitDirection};
use std::f32::consts::PI; use std::f32::consts::PI;
use structs::{PaneConfig, SplitDirection};
use std::ops::Deref; use std::ops::Deref;
use std::sync::MutexGuard; use std::sync::MutexGuard;
@@ -22,108 +24,115 @@ fn rot_circle(i: i16, center: Pos2, rad: f32, offset_ang: f32, ang_per_num: f32)
}) })
} }
pub fn background_render(painter: &egui::Painter, rect: egui::Rect) { fn add_Pos2(add: Vec2, points: &mut Vec<Pos2>) -> &mut Vec<Pos2> {
for i in 0..points.len() {
points[i] = points[i] + add;
}
points
}
fn mult_Pos2(mult: f32, points: &mut Vec<Pos2>) -> &mut Vec<Pos2> {
let vec = Vec2 { x: mult, y: mult };
for i in 0..points.len() {
points[i] = points[i] * mult;
}
points
}
fn rotate_90(points: &mut Vec<Pos2>) -> &mut Vec<Pos2> {
for i in 0..points.len() {
points[i] = Pos2 {
x: -points[i].y,
y: points[i].x,
}
}
points
}
fn rotate_180(points: &mut Vec<Pos2>) -> &mut Vec<Pos2> {
for i in 0..points.len() {
points[i] = Pos2 {
x: -points[i].x,
y: -points[i].y,
}
}
points
}
fn rotate_270(points: &mut Vec<Pos2>) -> &mut Vec<Pos2> {
for i in 0..points.len() {
points[i] = Pos2 {
x: points[i].y,
y: -points[i].x,
}
}
points
}
const CORNER_SQUARE: [Pos2; 1] = [Pos2 { x: 0., y: 0. }];
const CORNER_45: [Pos2; 2] = [Pos2 { x: 0., y: 1. }, Pos2 { x: 1., y: 0. }];
const CORNER_30: [Pos2; 2] = [Pos2 { x: 0., y: 0.5 }, Pos2 { x: 1., y: 0. }];
const CORNER_60: [Pos2; 2] = [Pos2 { x: 0., y: 1. }, Pos2 { x: 0.5, y: 0. }];
fn get_corner_points(ctype: panes::CornerTypes) -> Vec<Pos2> {
// CORNER_30.to_vec()
match ctype {
panes::CornerTypes::SQUARE => CORNER_SQUARE.to_vec(),
panes::CornerTypes::Ang30 => CORNER_30.to_vec(),
panes::CornerTypes::Ang45 => CORNER_45.to_vec(),
panes::CornerTypes::Ang60 => CORNER_60.to_vec(),
}
}
pub fn background_render(
painter: &egui::Painter,
rect: egui::Rect,
corners: [panes::CornerTypes; 4],
) {
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 x1 = left + CORNER_CUT - PANE_GAP;
let x2 = right - CORNER_CUT + PANE_GAP; // let x2 = right - CORNER_CUT + PANE_GAP;
let y1 = top + CORNER_CUT - PANE_GAP; // let y1 = top + CORNER_CUT - PANE_GAP;
let y2 = bottom - CORNER_CUT + PANE_GAP; // let y2 = bottom - CORNER_CUT + PANE_GAP;
let points = [ points.append(add_Pos2(
egui::Pos2 { x: x1, y: top }, Vec2 { x: left, y: top },
egui::Pos2 { x: x2, y: top }, mult_Pos2(CORNER_CUT, &mut get_corner_points(corners[0]).to_vec()),
egui::Pos2 { x: right, y: y1 }, ));
egui::Pos2 { x: right, y: y2 },
egui::Pos2 { x: x2, y: bottom },
egui::Pos2 { x: x1, y: bottom },
egui::Pos2 { x: left, y: y2 },
egui::Pos2 { x: left, y: y1 },
];
let filled_polygon = Shape::convex_polygon( points.append(add_Pos2(
points.to_vec(), Vec2 { x: right, y: top },
BACKGROUND_2, mult_Pos2(
egui::Stroke::new(0.5, TEXT_COLOR), CORNER_CUT,
); rotate_90(&mut get_corner_points(corners[1]).to_vec()),
),
));
points.append(add_Pos2(
Vec2 {
x: right,
y: bottom,
},
mult_Pos2(
CORNER_CUT,
rotate_180(&mut get_corner_points(corners[2]).to_vec()),
),
));
points.append(add_Pos2(
Vec2 { x: left, y: bottom },
mult_Pos2(
CORNER_CUT,
rotate_270(&mut get_corner_points(corners[3]).to_vec()),
),
));
let filled_polygon =
Shape::convex_polygon(points, BACKGROUND_2, egui::Stroke::new(0.5, TEXT_COLOR));
painter.add(filled_polygon); painter.add(filled_polygon);
// painter.rect_filled(rect, 0.0, Color32::RED);
// painter.rect_stroke(rect, 0.0, (1.0, Color32::BLACK));
}
// fn draw_polygon(ui: &mut egui::Ui, points: &[Pos2], fill_color: egui::Color32, stroke: ) {
// if points.len() < 3 {
// return; // Need at least 3 points for a polygon
// }
// // Create the filled polygon
// let filled_polygon = Shape::convex_polygon(
// points.to_vec(),
// fill_color,
// Stroke::new(0.0, fill_color), // No stroke for fill
// );
// // Create the outline
// let outline = Shape::line(
// points.iter().chain(std::iter::once(&points[0])).cloned().collect(),
// Stroke::new(stroke_width, outline_color),
// );
// // Get the painter and draw both shapes
// let painter = ui.painter();
// painter.add(filled_polygon);
// painter.add(outline);
// }
pub fn winconfig() -> PaneConfig {
PaneConfig::new("root")
.with_split(SplitDirection::Horizontal, 0.5)
.with_callback(|painter: &egui::Painter, rect: egui::Rect| background_render(painter, rect))
.with_children(vec![
PaneConfig::new("left")
.with_split(SplitDirection::Vertical, 0.5)
.with_children(vec![
PaneConfig::new("left_top").with_callback(
|painter: &egui::Painter, rect: egui::Rect| {
background_render(painter, rect)
},
),
PaneConfig::new("left_bottom")
// .with_split(SplitDirection::Horizontal, 0.5)
.with_callback(|painter: &egui::Painter, rect: egui::Rect| {
background_render(painter, rect)
}),
]),
PaneConfig::new("right")
.with_split(SplitDirection::Vertical, 0.5)
.with_callback(|painter: &egui::Painter, rect: egui::Rect| {
background_render(painter, rect)
})
.with_children(vec![
PaneConfig::new("right_top")
.with_split(SplitDirection::Horizontal, 0.5)
.with_children(vec![
PaneConfig::new("right_top_1").with_callback(
|painter: &egui::Painter, rect: egui::Rect| {
background_render(painter, rect)
},
),
PaneConfig::new("right_top_2").with_callback(
|painter: &egui::Painter, rect: egui::Rect| {
background_render(painter, rect)
},
),
]),
PaneConfig::new("right_bottom").with_callback(
|painter: &egui::Painter, rect: egui::Rect| {
background_render(painter, rect)
},
),
]),
])
} }
pub fn update_password_viewer( pub fn update_password_viewer(
@@ -131,7 +140,7 @@ pub fn update_password_viewer(
ctx: &egui::Context, ctx: &egui::Context,
frame: &mut eframe::Frame, frame: &mut eframe::Frame,
ui: &mut egui::Ui, ui: &mut egui::Ui,
winconfig: PaneRenderer, winconfig: &mut PaneRenderer,
) { ) {
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 {
@@ -153,7 +162,7 @@ pub fn update_password_viewer(
); );
dots(painter, ui.clip_rect()); dots(painter, ui.clip_rect());
winconfig.render(painter, rect); winconfig.render(painter, &rect);
paint_password_circle(state, ctx, frame, ui, center, painter); paint_password_circle(state, ctx, frame, ui, center, painter);
} }
@@ -259,6 +268,8 @@ fn paint_password_circle(
last_pos = pos; last_pos = pos;
} }
} }
ctx.request_repaint();
} }
fn paint_windows( fn paint_windows(
@@ -277,5 +288,3 @@ fn paint_windows(
// } // }
// } // }
} }
fn paint_window(painter: &egui::Painter, width: f32, height: f32, x: f32, y: f32) {}