diff --git a/Cargo.toml b/Cargo.toml index 928ffaa..6af0439 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,3 +8,5 @@ eframe = "0.28" egui = "0.28" rand = "0.8.5" serde = "1.0.214" +serde_json = "1.0.132" +sysinfo = "0.32.0" diff --git a/src/configs.rs b/src/configs.rs index bc72761..92b65a8 100644 --- a/src/configs.rs +++ b/src/configs.rs @@ -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 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] + } + } +} +"#; diff --git a/src/graph.rs b/src/graph.rs new file mode 100644 index 0000000..5dace52 --- /dev/null +++ b/src/graph.rs @@ -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>, + animated_values: Vec, + last_update: Instant, + colors: Vec, +} + +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 = 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, + ); + } + } +} diff --git a/src/main.rs b/src/main.rs index c20d53e..5735b6a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,15 @@ use eframe::{egui, App}; use egui::FontFamily; use egui::Key; +use graph::CpuGraph; use input::is_locked; +use panes::{PaneConfig, PaneRenderer, SplitDirection}; use std::collections::BTreeMap; use std::collections::HashMap; use std::process::{Command, Stdio}; use std::sync::{Arc, Mutex}; use std::thread; use std::time::Duration; -use structs::{PaneConfig, PaneRenderer, SplitDirection}; // use egui::epaint::text::{FontInsert, InsertFontFamily}; @@ -22,8 +23,9 @@ use egui::Stroke; use egui::ecolor::HexColor; mod configs; +mod graph; mod input; -mod splitter; +mod panes; mod structs; mod ui; @@ -59,11 +61,10 @@ mod ui; // ctx.set_fonts(fonts); // } -use splitter::Splitter; - -#[derive(Default)] +// #[derive(Default)] struct ExampleApp { auth_state: Arc>, + cpu_graph: CpuGraph, // terminals: HashMap, } @@ -102,9 +103,10 @@ impl eframe::App for ExampleApp { let mut state = self.auth_state.lock().unwrap(); //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) { ctx.input(|i| { @@ -149,6 +151,8 @@ impl eframe::App for ExampleApp { // 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() .frame(egui::Frame::none()) .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); } + let test = [1, 2, 3]; + input::create_lock(); eframe::run_native( @@ -251,12 +267,19 @@ fn main() -> eframe::Result<()> { Box::new(|_| { Ok(Box::::new(ExampleApp { auth_state: state, + cpu_graph: CpuGraph::new(), // terminals: map, })) }), ) } +fn test_test(test: &mut [i32]) { + for i in 0..test.len() { + test[i] += 1; + } +} + fn try_sudo(password: &str) -> Result { let mut child = Command::new("sudo") .args(["-kS", "true"]) // Use -S to read password from stdin diff --git a/src/panes.rs b/src/panes.rs new file mode 100644 index 0000000..8347386 --- /dev/null +++ b/src/panes.rs @@ -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, + 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, + #[serde(default)] + pub pane_type: Option, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct SplitConfig { + pub direction: SplitDirection, + pub ratio: f32, + pub children: Vec, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct LayoutConfig { + pub root: PaneConfig, + #[serde(default)] + pub default_pane_type: Option, +} + +// Runtime representation of the layout +pub struct RuntimePane { + pub id: String, + pub split: Option, + pub pane_type: Option, +} + +pub struct RuntimeSplit { + pub direction: SplitDirection, + pub ratio: f32, + pub children: Vec, +} + +// Conversion implementations +impl From 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, +} + +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 { + 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(); + +// // }); +// } diff --git a/src/structs.rs b/src/structs.rs index a4efb6d..7864bb0 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -13,233 +13,175 @@ pub struct AuthState { pub failed_attempts: u16, } -// pub enum windowTypes { -// Temp1, -// Temp2, - -// SplitVertical, -// SplitHorisontal, +// #[derive(Debug, Clone, Copy, Serialize, Deserialize)] +// pub enum SplitDirection { +// Horizontal, +// Vertical, // } -// pub struct Pane { -// pub wintype: windowTypes, +// // #[derive(Sized)] +// pub struct PaneConfig { +// pub id: String, +// pub split: Option<(SplitDirection, f32)>, +// pub children: Option>, +// pub color: Option, // Store color in config for consistency +// pub callback: Option>, +// pub corners: [CornerTypes; 4], // } -// pub struct PaneSplit { -// pub wintype: windowTypes, -// pub sub_a: Pane, -// pub sub_b: Pane, +// // #[derive(Debug)] +// pub struct PaneRenderer { +// config: PaneConfig, // } -// impl Pane { -// pub const fn new(wintype: windowTypes) -> Self { -// Self { wintype: wintype } -// } - -// pub const fn new_hsplit(wintype: windowTypes, left: Pane, right: Pane) -> PaneSplit { -// PaneSplit { -// wintype: wintype, -// sub_a: left, -// sub_b: right, +// impl PaneConfig { +// pub fn new(id: impl Into) -> Self { +// Self { +// id: id.into(), +// split: None, +// children: None, +// color: None, +// callback: None, +// corners: [ +// CornerTypes::Ang60, +// CornerTypes::Ang30, +// CornerTypes::SQUARE, +// CornerTypes::Ang45, +// ], // } // } -// pub const fn new_rsplit(wintype: windowTypes, wintop: Pane, bottom: Pane) -> PaneSplit { -// PaneSplit { -// wintype: wintype, -// sub_a: top, -// sub_b: bottom, + +// 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) -> 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>, - pub color: Option, // Store color in config for consistency - pub callback: Option>, -} - -// #[derive(Debug)] -pub struct PaneRenderer { - config: PaneConfig, -} - -impl PaneConfig { - pub fn new(id: impl Into) -> 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) -> 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); - }); -} diff --git a/src/ui.rs b/src/ui.rs index d5a3b4a..869dfdb 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,15 +1,17 @@ use crate::configs::*; +use crate::graph::CpuGraph; +use crate::panes; use crate::structs; -use crate::structs::PaneRenderer; +use panes::PaneRenderer; // use crate::structs::cur_context; // use crate::structs::windowTypes; use eframe::egui; use egui::Color32; -use egui::Pos2; use egui::Shape; +use egui::{Pos2, Vec2}; +use panes::{PaneConfig, SplitDirection}; use std::f32::consts::PI; -use structs::{PaneConfig, SplitDirection}; use std::ops::Deref; 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) -> &mut Vec { + for i in 0..points.len() { + points[i] = points[i] + add; + } + points +} + +fn mult_Pos2(mult: f32, points: &mut Vec) -> &mut Vec { + 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) -> &mut Vec { + for i in 0..points.len() { + points[i] = Pos2 { + x: -points[i].y, + y: points[i].x, + } + } + points +} +fn rotate_180(points: &mut Vec) -> &mut Vec { + for i in 0..points.len() { + points[i] = Pos2 { + x: -points[i].x, + y: -points[i].y, + } + } + points +} +fn rotate_270(points: &mut Vec) -> &mut Vec { + 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 { + // 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 = Vec::new(); let left = rect.left() + PANE_GAP; let right = rect.right() - PANE_GAP; let bottom = rect.bottom() - 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; + // 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; - let points = [ - egui::Pos2 { x: x1, y: top }, - egui::Pos2 { x: x2, y: top }, - 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 }, - ]; + points.append(add_Pos2( + Vec2 { x: left, y: top }, + mult_Pos2(CORNER_CUT, &mut get_corner_points(corners[0]).to_vec()), + )); - let filled_polygon = Shape::convex_polygon( - points.to_vec(), - BACKGROUND_2, - egui::Stroke::new(0.5, TEXT_COLOR), - ); + points.append(add_Pos2( + Vec2 { x: right, y: top }, + mult_Pos2( + 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.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( @@ -131,7 +140,7 @@ pub fn update_password_viewer( ctx: &egui::Context, frame: &mut eframe::Frame, ui: &mut egui::Ui, - winconfig: PaneRenderer, + winconfig: &mut PaneRenderer, ) { let rect: egui::Rect = ui.available_rect_before_wrap(); // let ctx: cur_context = cur_context { @@ -153,7 +162,7 @@ pub fn update_password_viewer( ); dots(painter, ui.clip_rect()); - winconfig.render(painter, rect); + winconfig.render(painter, &rect); paint_password_circle(state, ctx, frame, ui, center, painter); } @@ -259,6 +268,8 @@ fn paint_password_circle( last_pos = pos; } } + + ctx.request_repaint(); } fn paint_windows( @@ -277,5 +288,3 @@ fn paint_windows( // } // } } - -fn paint_window(painter: &egui::Painter, width: f32, height: f32, x: f32, y: f32) {}