diff --git a/Cargo.toml b/Cargo.toml index a64564d..928ffaa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,8 +6,5 @@ edition = "2021" [dependencies] eframe = "0.28" egui = "0.28" -portable-pty = "0.8.1" -termwiz = "0.22.0" -vte = "0.13.0" -egui-terminal = { git = "https://github.com/Quinntyx/egui-terminal" } -ecolor = "0.29.1" +rand = "0.8.5" +serde = "1.0.214" diff --git a/README.md b/README.md index c984902..3603f67 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,9 @@ Unfortunatly this is not the most secure desktop locker, as it involves using sw ``` # Add this to your sway config: -for_window [title="^raylock$"] sticky enable, floating enable, resize set 1920 px 1080 px, title_format "", border pixel none +for_window [title="^raylock$"] sticky enable, fullscreen mode "lock" { - bindsym $mod+Shift+q exec true + bindsym XF86MonBrightnessUp exec brightnessctl s +5% + bindsym XF86MonBrightnessDown exec brightnessctl s 5%- } ``` diff --git a/UbuntuMonoNerdFontMono-Regular.ttf b/UbuntuMonoNerdFontMono-Regular.ttf new file mode 100644 index 0000000..8396c0e Binary files /dev/null and b/UbuntuMonoNerdFontMono-Regular.ttf differ diff --git a/src/configs.rs b/src/configs.rs new file mode 100644 index 0000000..bc72761 --- /dev/null +++ b/src/configs.rs @@ -0,0 +1,42 @@ +use crate::structs; +use egui::{Color32, Stroke}; +use std::f32::consts::PI; + +// pub const windows: PaneSplit = +// Pane::new(SplitHorisontal).new_hsplit(Pane::new(Temp1), Pane::new(Temp2)); + +pub const TEXT_COLOR: Color32 = Color32::from_rgb(255, 255, 255); +pub const BACKGROUND_2: Color32 = Color32::from_rgba_premultiplied(10, 10, 10, 230); + +pub const LOGIN_CIRCLE_RADIUS: f32 = 50.; +pub const LOGIN_SUBCIRCLE_START_ANG: f32 = -PI / 4.; + +pub const LOGIN_SUBCIRCLE_RADIUS: f32 = 4.; +pub const LOGIN_SUBCIRCLE_COLOR: Color32 = Color32::TRANSPARENT; +pub const LOGIN_SUBCIRCLE_STROKE: Stroke = Stroke { + width: 2.0, + color: Color32::from_rgb(255, 255, 255), +}; +pub const LOGIN_CIRCLE_LINE_STROKE: Stroke = Stroke { + width: 2.0, + color: TEXT_COLOR, +}; +pub const LOGIN_FAIL_COLOR: Color32 = Color32::from_rgb(184, 41, 11); +pub const LOGIN_FAIL_CIRCLE_STROKE: Stroke = Stroke { + width: 5., + color: LOGIN_FAIL_COLOR, +}; +pub const LOGIN_FAIL_COUNT_CIRCLE_RADIUS: f32 = 15.; +pub const LOGIN_FAIL_COUNT_CIRCLE_COLOR: Color32 = Color32::TRANSPARENT; +pub const LOGIN_FAIL_COUNT_CIRCLE_STROKE: Stroke = Stroke { + width: 2., + color: LOGIN_FAIL_COLOR, +}; + +pub const DOTS_SPACING: f32 = 25.; +pub const DOTS_RAD: f32 = 0.7; +pub const DOTS_COLOR: Color32 = Color32::from_rgb(255, 255, 255); +// pub const DOTS_STROKE: Stroke = Stroke {0., DOTS_COLOR}; + +pub const CORNER_CUT: f32 = LOGIN_CIRCLE_RADIUS * 1.41421356237; +pub const PANE_GAP: f32 = 6.; diff --git a/src/input.rs b/src/input.rs index 0dfd56c..a31579b 100644 --- a/src/input.rs +++ b/src/input.rs @@ -1,5 +1,5 @@ use egui::Key; -use std::fs::{exists, File}; +use std::fs::File; use std::path::Path; use std::process::Command; use std::thread; @@ -9,18 +9,18 @@ const LOCK_FILEPATH: &str = "/tmp/.raylock.lock"; pub fn sway_lock_input() { thread::spawn(move || { let _ = Command::new("swaymsg").args(["mode", "lock"]).spawn(); - let _ = Command::new("swaymsg") - .args(["input", "type:touchpad", "events", "disabled"]) - .spawn(); + // let _ = Command::new("swaymsg") + // .args(["input", "type:touchpad", "events", "disabled"]) + // .spawn(); }); } pub fn sway_unlock_input() { thread::spawn(move || { let _ = Command::new("swaymsg").args(["mode", "default"]).spawn(); - let _ = Command::new("swaymsg") - .args(["input", "type:touchpad", "events", "enabled"]) - .spawn(); + // let _ = Command::new("swaymsg") + // .args(["input", "type:touchpad", "events", "enabled"]) + // .spawn(); }); } diff --git a/src/main.rs b/src/main.rs index 85a9118..c20d53e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,6 @@ use eframe::{egui, App}; use egui::FontFamily; use egui::Key; -use egui_terminal; use input::is_locked; use std::collections::BTreeMap; use std::collections::HashMap; @@ -9,6 +8,7 @@ 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}; @@ -16,54 +16,55 @@ use eframe::CreationContext; use egui::ecolor::Color32; use egui::FontId; use egui::Stroke; -use egui_terminal::prelude::*; -use egui_terminal::render::CursorType; +// use egui_terminal::prelude::*; +// use egui_terminal::render::CursorType; use egui::ecolor::HexColor; +mod configs; mod input; mod splitter; mod structs; mod ui; // Demonstrates how to add a font to the existing ones -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(); +// 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" - )), - ); +// // 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 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()); +// // 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); -} +// // Tell egui to use these fonts: +// ctx.set_fonts(fonts); +// } use splitter::Splitter; #[derive(Default)] struct ExampleApp { auth_state: Arc>, - terminals: HashMap, + // terminals: HashMap, } impl ExampleApp { @@ -101,6 +102,9 @@ 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 pane_renderer = PaneRenderer::new(config); if ctx.input(|i| i.events.len() > 0) { ctx.input(|i| { @@ -177,7 +181,7 @@ impl eframe::App for ExampleApp { // }, // ); - ui::update_password_viewer(state, ctx, _frame, ui); + ui::update_password_viewer(state, ctx, _frame, ui, pane_renderer); }); } } @@ -228,7 +232,7 @@ fn main() -> eframe::Result<()> { thread::sleep(Duration::from_millis(100)); }); - let mut map = HashMap::new(); + // 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")); @@ -247,7 +251,7 @@ fn main() -> eframe::Result<()> { Box::new(|_| { Ok(Box::::new(ExampleApp { auth_state: state, - terminals: map, + // terminals: map, })) }), ) diff --git a/src/splitter.rs b/src/splitter.rs deleted file mode 100644 index e39249a..0000000 --- a/src/splitter.rs +++ /dev/null @@ -1,190 +0,0 @@ -/// https://gist.github.com/mkalte666/f9a982be0ac0276080d3434ab9ea4655 -use std::hash::Hash; - -use egui::{CursorIcon, Id, Layout, Pos2, Rect, Rounding, Sense, Ui, Vec2}; - -/// An axis that a Splitter can use -#[derive(Copy, Clone, Debug)] -pub enum SplitterAxis { - Horizontal, - Vertical, -} - -/// The internal data used by a splitter. Stored into memory -#[derive(Debug, Clone)] -struct SplitterData { - axis: SplitterAxis, - pos: f32, - min_size: f32, -} - -/// Splits a ui in half, using a draggable separator in the middle. -/// -pub struct Splitter { - id: Id, - data: SplitterData, -} -impl Splitter { - /// Create a new Splitter - pub fn new(id_source: impl Hash, axis: SplitterAxis) -> Self { - Self { - id: Id::new(id_source), - data: SplitterData { - axis, - pos: 0.5, - min_size: 0.0, - }, - } - } - - /// Sets the minimum allowed size for the area - pub fn min_size(mut self, points: f32) -> Self { - self.data.min_size = points; - self - } - - /// Thes the default position of the splitter separator. Usually it sits in the center, this moves it around. - pub fn default_pos(mut self, pos: f32) -> Self { - self.data.pos = pos; - self - } - - /// Show the splitter and fill it with content. - /// - /// ``` - /// Splitter::new("some_plot_split", SplitterAxis::Vertical) - /// .min_size(250.0) - /// .default_pos(2.0 / 3.0) - /// .show(ui, |ui_a, ui_b| { - /// Plot::new("plot_a") - /// .legend(Legend::default()) - /// .x_axis_formatter(log_formatter) - /// .y_axis_formatter(log_formatter) - /// .x_axis_label("X Axis") - /// .y_axis_label("A Axis") - /// .link_axis("axis_link", true, false) - /// .link_cursor("cursor_link", true, false) - /// .show(ui_a, |plot_ui| { - /// for line in plot_a_lines { - /// plot_ui.line(line); - /// } - /// }); - /// - /// Plot::new("plot_b") - /// .legend(Legend::default()) - /// .x_axis_formatter(log_formatter) - /// .x_axis_label("X Axis") - /// .y_axis_label("Y Axis") - /// .link_axis("axis_link", true, false) - /// .link_cursor("cursor_link", true, false) - /// .show(ui_b, |plot_ui| { - /// for line in plot_b_lines { - /// plot_ui.line(line); - /// } - /// }); - /// }); - /// ``` - pub fn show(mut self, ui: &mut Ui, add_contents: impl FnOnce(&mut Ui, &mut Ui)) { - let mut data = if let Some(d) = ui.memory(|mem| mem.data.get_temp(self.id)) { - d - } else { - self.data.clone() - }; - - let sep_size = 10.0; - let sep_stroke = 2.0; - let whole_area = ui.available_size(); - - let split_axis_size = match data.axis { - SplitterAxis::Horizontal => whole_area.x, - SplitterAxis::Vertical => whole_area.y, - }; - let split_a_size = ((split_axis_size - sep_size) * data.pos); - let split_b_size = split_axis_size - sep_size - split_a_size; - - let child_size_a = match data.axis { - SplitterAxis::Horizontal => Vec2::new(split_a_size, whole_area.y), - SplitterAxis::Vertical => Vec2::new(whole_area.x, split_a_size), - }; - - let child_size_b = match data.axis { - SplitterAxis::Horizontal => Vec2::new(split_b_size, whole_area.y), - SplitterAxis::Vertical => Vec2::new(whole_area.x, split_b_size), - }; - - let child_rect_a = Rect::from_min_size(ui.next_widget_position(), child_size_a); - let mut ui_a = ui.child_ui(child_rect_a, Layout::default(), None); - - let sep_rect = match data.axis { - SplitterAxis::Horizontal => Rect::from_min_size( - Pos2::new(child_rect_a.max.x, child_rect_a.min.y), - Vec2::new(sep_size, whole_area.y), - ), - SplitterAxis::Vertical => Rect::from_min_size( - Pos2::new(child_rect_a.min.x, child_rect_a.max.y), - Vec2::new(whole_area.x, sep_size), - ), - }; - - let resp = ui.allocate_rect(sep_rect, Sense::hover().union(Sense::click_and_drag())); - - let sep_draw_rect = match data.axis { - SplitterAxis::Horizontal => Rect::from_min_size( - Pos2::new( - sep_rect.min.x + sep_size / 2.0 - sep_stroke / 2.0, - sep_rect.min.y, - ), - Vec2::new(sep_stroke, sep_rect.height()), - ), - SplitterAxis::Vertical => Rect::from_min_size( - Pos2::new( - sep_rect.min.x, - sep_rect.min.y + sep_size / 2.0 - sep_stroke / 2.0, - ), - Vec2::new(sep_rect.width(), sep_stroke), - ), - }; - ui.painter().rect_filled( - sep_draw_rect, - Rounding::ZERO, - ui.style().visuals.noninteractive().bg_stroke.color, - ); - - let child_rect_b = match data.axis { - SplitterAxis::Horizontal => { - Rect::from_min_size(Pos2::new(sep_rect.max.x, sep_rect.min.y), child_size_b) - } - SplitterAxis::Vertical => { - Rect::from_min_size(Pos2::new(sep_rect.min.x, sep_rect.max.y), child_size_b) - } - }; - let mut ui_b = ui.child_ui(child_rect_b, Layout::default(), None); - - add_contents(&mut ui_a, &mut ui_b); - - if resp.hovered() { - match data.axis { - SplitterAxis::Horizontal => ui.ctx().set_cursor_icon(CursorIcon::ResizeVertical), - SplitterAxis::Vertical => ui.ctx().set_cursor_icon(CursorIcon::ResizeHorizontal), - } - } - - if resp.dragged() { - let delta_pos = match data.axis { - SplitterAxis::Horizontal => resp.drag_delta().x / whole_area.x, - SplitterAxis::Vertical => resp.drag_delta().y / whole_area.y, - }; - - data.pos += delta_pos; - } - - // clip pos - let min_pos = (data.min_size / split_axis_size).min(1.0); - let max_pos = (1.0 - min_pos).max(0.0); - data.pos = data.pos.max(min_pos).min(max_pos); - - ui.memory_mut(|mem| { - mem.data.insert_temp(self.id, data); - }) - } -} diff --git a/src/structs.rs b/src/structs.rs index 88846a5..a4efb6d 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -1,6 +1,245 @@ +use egui::{Color32, Painter, Pos2, Rect, Vec2}; +use rand::Rng; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::fmt; +use std::sync::MutexGuard; +// use serde:: + #[derive(Default)] pub struct AuthState { pub password: String, pub to_be_submitted: bool, pub failed_attempts: u16, } + +// pub enum windowTypes { +// Temp1, +// Temp2, + +// SplitVertical, +// SplitHorisontal, +// } + +// pub struct Pane { +// pub wintype: windowTypes, +// } + +// pub struct PaneSplit { +// pub wintype: windowTypes, +// pub sub_a: Pane, +// pub sub_b: Pane, +// } + +// 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, +// } +// } +// pub const fn new_rsplit(wintype: windowTypes, wintop: Pane, bottom: Pane) -> PaneSplit { +// PaneSplit { +// wintype: wintype, +// sub_a: top, +// sub_b: bottom, +// } +// } +// } + +// 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 b2f1a7a..d5a3b4a 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,38 +1,19 @@ +use crate::configs::*; use crate::structs; +use crate::structs::PaneRenderer; +// use crate::structs::cur_context; +// use crate::structs::windowTypes; + use eframe::egui; use egui::Color32; use egui::Pos2; -use egui::Stroke; +use egui::Shape; use std::f32::consts::PI; +use structs::{PaneConfig, SplitDirection}; +use std::ops::Deref; use std::sync::MutexGuard; -const TEXT_COLOR: Color32 = Color32::from_rgb(255, 255, 255); -const LOGIN_CIRCLE_RADIUS: f32 = 50.; -const LOGIN_SUBCIRCLE_START_ANG: f32 = -PI / 4.; - -const LOGIN_SUBCIRCLE_RADIUS: f32 = 4.; -const LOGIN_SUBCIRCLE_COLOR: Color32 = Color32::TRANSPARENT; -const LOGIN_SUBCIRCLE_STROKE: Stroke = Stroke { - width: 2.0, - color: Color32::from_rgb(255, 255, 255), -}; -const LOGIN_CIRCLE_LINE_STROKE: Stroke = Stroke { - width: 2.0, - color: TEXT_COLOR, -}; -const LOGIN_FAIL_COLOR: Color32 = Color32::from_rgb(184, 41, 11); -const LOGIN_FAIL_CIRCLE_STROKE: Stroke = Stroke { - width: 5., - color: LOGIN_FAIL_COLOR, -}; -const LOGIN_FAIL_COUNT_CIRCLE_RADIUS: f32 = 15.; -const LOGIN_FAIL_COUNT_CIRCLE_COLOR: Color32 = Color32::TRANSPARENT; -const LOGIN_FAIL_COUNT_CIRCLE_STROKE: Stroke = Stroke { - width: 2., - color: LOGIN_FAIL_COLOR, -}; - fn rot_circle(i: i16, center: Pos2, rad: f32, offset_ang: f32, ang_per_num: f32) -> Pos2 { center + (egui::Vec2 { @@ -41,22 +22,170 @@ 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) { + 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 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 }, + ]; + + let filled_polygon = Shape::convex_polygon( + points.to_vec(), + 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( wstate: MutexGuard<'_, structs::AuthState>, ctx: &egui::Context, frame: &mut eframe::Frame, ui: &mut egui::Ui, + winconfig: PaneRenderer, ) { - let mut state = wstate; - let rect = ui.clip_rect(); - let center = Pos2 { + let rect: egui::Rect = ui.available_rect_before_wrap(); + // let ctx: cur_context = cur_context { + let state: &structs::AuthState = wstate.deref(); + + let center: Pos2 = Pos2 { x: rect.width() / 2., y: rect.height() / 2., }; + let painter: &egui::Painter = ui.painter(); - let painter = ui.painter(); + paint_windows( + painter, + rect.width() as f32, + rect.height() as f32, + 0., + 0., + // windows, + ); - // Login Circle + dots(painter, ui.clip_rect()); + winconfig.render(painter, rect); + paint_password_circle(state, ctx, frame, ui, center, painter); +} + +fn dots(painter: &egui::Painter, win_rect: egui::Rect) { + let dots_x = win_rect.right() / DOTS_SPACING; + let dots_y = win_rect.bottom() / DOTS_SPACING; + for x in (-dots_x as i32 / 2)..((dots_x as i32 / 2) + 1) { + for y in (-dots_y as i32 / 2)..((dots_y as i32 / 2) + 1) { + painter.circle( + Pos2 { + x: win_rect.right() / 2. + (x as f32 * DOTS_SPACING), + y: win_rect.bottom() / 2. + (y as f32 * DOTS_SPACING), + }, + DOTS_RAD, + DOTS_COLOR, + egui::Stroke::NONE, + ); + } + } +} + +fn paint_password_circle( + state: &structs::AuthState, + + ctx: &egui::Context, + frame: &eframe::Frame, + ui: &egui::Ui, + + center: egui::Pos2, + painter: &egui::Painter, +) { + let len = state.password.len(); if state.failed_attempts > 0 { painter.circle( @@ -131,3 +260,22 @@ pub fn update_password_viewer( } } } + +fn paint_windows( + painter: &egui::Painter, + + width: f32, + height: f32, + x: f32, + y: f32, + // pane: structs::PaneSplit, +) { + // match pane.wintype { + // windowTypes::SplitHorisontal => { + // paint_windows(painter, width, height / 2., x, y, pane.sub_a); + // paint_windows(painter, width, height / 2., x, y + height / 2., pane.sub_b); + // } + // } +} + +fn paint_window(painter: &egui::Painter, width: f32, height: f32, x: f32, y: f32) {}