From adcb73594d082efb6683deaeacf1dfbc1a84ad6b Mon Sep 17 00:00:00 2001 From: Andrew Golovashevich Date: Tue, 10 Mar 2026 17:10:14 +0300 Subject: [PATCH] Gui stub --- Cargo.toml | 4 +- app/Cargo.toml | 3 +- app/src/main.rs | 3 +- gui/abstract/Cargo.toml | 10 ++++ gui/abstract/src/lib.rs | 11 ++++ gui/egui/Cargo.toml | 13 +++++ gui/egui/src/lib.rs | 104 +++++++++++++++++++++++++++++++++ gui/egui/src/plot.rs | 14 +++++ gui/egui/src/subwindows.rs | 39 +++++++++++++ network/abstract/src/lib.rs | 2 +- network/windows/src/address.rs | 4 +- 11 files changed, 200 insertions(+), 7 deletions(-) create mode 100644 gui/abstract/Cargo.toml create mode 100644 gui/abstract/src/lib.rs create mode 100644 gui/egui/Cargo.toml create mode 100644 gui/egui/src/lib.rs create mode 100644 gui/egui/src/plot.rs create mode 100644 gui/egui/src/subwindows.rs diff --git a/Cargo.toml b/Cargo.toml index 3f4527d..92781aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,9 @@ members = [ "app", "network/icmp", "network/abstract", - "network/windows" + "network/windows", + "gui/abstract", + "gui/egui" ] [workspace.lints] diff --git a/app/Cargo.toml b/app/Cargo.toml index 1e5f6aa..1f35c45 100644 --- a/app/Cargo.toml +++ b/app/Cargo.toml @@ -7,9 +7,8 @@ edition = "2024" workspace = true [dependencies] -eframe = { version = "0.33.3", default-features = false, features = ["default_fonts", "glow"] } -egui_extras = { version = "0.33.3" } bgtu-networks-2-network-abstract = { path = "../network/abstract" } +bgtu-networks-2-gui-abstract = { path = "../gui/abstract" } [target.'cfg(windows)'.dependencies] bgtu-networks-2-network-windows = { path = "../network/windows" } \ No newline at end of file diff --git a/app/src/main.rs b/app/src/main.rs index 63a0703..4ddb190 100644 --- a/app/src/main.rs +++ b/app/src/main.rs @@ -1,5 +1,6 @@ use bgtu_networks_2_network_abstract::{Address, NetworkContext, NetworkScope, ServersContext}; use bgtu_networks_2_network_windows::winsocks_scope_2_2; +use bgtu_networks_2_gui_abstract::ServersStorage; // mod data; @@ -43,4 +44,4 @@ impl ServersContext for Servers2NetworkCtxTestImpl { println!("{} err", addr.to_string()) } -} +} \ No newline at end of file diff --git a/gui/abstract/Cargo.toml b/gui/abstract/Cargo.toml new file mode 100644 index 0000000..e304543 --- /dev/null +++ b/gui/abstract/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "bgtu-networks-2-gui-abstract" +version = "0.0.0" +edition = "2024" + +[lints] +workspace = true + +[dependencies] +bgtu-networks-2-network-abstract = { path = "../../network/abstract" } diff --git a/gui/abstract/src/lib.rs b/gui/abstract/src/lib.rs new file mode 100644 index 0000000..c2be72e --- /dev/null +++ b/gui/abstract/src/lib.rs @@ -0,0 +1,11 @@ +use bgtu_networks_2_network_abstract::Address; + +pub trait ServersStorage { + type A: Address; + + fn add_server(&mut self, addr: Self::A, memo: String); + fn remove_server(&mut self, addr: &Self::A); + + // type TimeIterator: Iterator>; + fn iter_servers(&self) -> impl Iterator>)>; +} diff --git a/gui/egui/Cargo.toml b/gui/egui/Cargo.toml new file mode 100644 index 0000000..febda60 --- /dev/null +++ b/gui/egui/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "bgtu-networks-2-gui-egui" +version = "0.0.0" +edition = "2024" + +[lints] +workspace = true + +[dependencies] +eframe = { version = "0.33.3", default-features = false, features = ["default_fonts", "glow"] } +egui_extras = { version = "0.33.3" } +bgtu-networks-2-gui-abstract = { path = "../abstract" } +bgtu-networks-2-network-abstract = { path = "../../network/abstract" } diff --git a/gui/egui/src/lib.rs b/gui/egui/src/lib.rs new file mode 100644 index 0000000..826fc3d --- /dev/null +++ b/gui/egui/src/lib.rs @@ -0,0 +1,104 @@ +use bgtu_networks_2_network_abstract::Address; +mod plot; +mod subwindows; + +use crate::plot::draw_plot; +use bgtu_networks_2_gui_abstract::ServersStorage; +use eframe::egui::Context; +use eframe::{App, Frame, egui}; +use egui_extras::{Column, TableBuilder}; + +pub fn run_eframe_gui(ctx: &mut Ctx) -> eframe::Result { + let options = eframe::NativeOptions { + viewport: egui::ViewportBuilder::default().with_inner_size([640.0, 400.0]), + ..Default::default() + }; + eframe::run_native( + "ICMP status monitor", + options, + Box::new(|_cc| { + Ok(Box::new(EguiApp { + ctx, + modal_windows: ModalWindows::Root, + })) + }), + ) +} + +#[derive(PartialEq, Eq)] +enum ModalWindows { + Root, + Add, + Edit, +} + +struct EguiApp<'a, Ctx> { + ctx: &'a mut Ctx, + modal_windows: ModalWindows, +} + +impl App for EguiApp<'_, Ctx> { + fn update(&mut self, ctx: &Context, frame: &mut Frame) { + egui::CentralPanel::default().show(ctx, |ui| { + ui.add_enabled_ui(self.modal_windows == ModalWindows::Root, |ui| { + if ui.button("Add server").clicked() { + self.modal_windows = ModalWindows::Add + } + }); + + TableBuilder::new(ui) + .striped(true) // Alternating row colors + .resizable(true) + .vscroll(true) + .columns(Column::remainder(), 3) + .header(20.0, |mut r| { + r.col(|ui| { + ui.label("Address"); + }); + r.col(|ui| { + ui.label("Memo"); + }); + r.col(|ui| { + ui.label("Graph"); + }); + }) + .body(|mut t| { + for (addr, memo, pings) in self.ctx.iter_servers() { + t.row(20.0, |mut r| { + r.col(|ui| { + ui.horizontal(|ui| { + ui.add_enabled_ui( + self.modal_windows == ModalWindows::Root, + |ui| { + if ui.button("-").clicked() { + self.modal_windows = ModalWindows::Add + } + }, + ); + ui.label(addr.to_string()); + }); + }); + + r.col(|ui| { + ui.horizontal(|ui| { + ui.add(egui::Label::new(memo).wrap()); + ui.add_enabled_ui( + self.modal_windows == ModalWindows::Root, + |ui| { + if ui.button("-").clicked() { + self.modal_windows = ModalWindows::Add + } + }, + ); + }); + }); + + r.col(|ui| { + draw_plot(ui, pings); + }); + }) + } + }); + }); + } +} diff --git a/gui/egui/src/plot.rs b/gui/egui/src/plot.rs new file mode 100644 index 0000000..ccf7151 --- /dev/null +++ b/gui/egui/src/plot.rs @@ -0,0 +1,14 @@ +use eframe::egui::{Color32, Rect, StrokeKind, Ui}; +use eframe::epaint::{Pos2, Stroke}; + +pub(crate) fn draw_plot(ui: &mut Ui, time: impl Iterator>) { + let rect = ui.available_size(); + let canvas = ui.painter(); + canvas.rect( + Rect::from_min_size(Pos2::new(0.0, 0.0), rect), + 0, + Color32::from_rgb(255, 255, 255), + Stroke::new(1.0, Color32::from_rgb(127, 127, 127)), + StrokeKind::Inside + ); +} diff --git a/gui/egui/src/subwindows.rs b/gui/egui/src/subwindows.rs new file mode 100644 index 0000000..0a4548f --- /dev/null +++ b/gui/egui/src/subwindows.rs @@ -0,0 +1,39 @@ +use eframe::egui; +use eframe::egui::{Frame, Ui, ViewportBuilder}; + +pub fn subwindow( + ui: &mut Ui, + id: &str, + title: &str, + extra_settings: impl FnOnce(ViewportBuilder) -> ViewportBuilder, + mut content: impl FnMut(&mut Ui), +) -> Response { + let id = egui::ViewportId::from_hash_of(id); + let vb = extra_settings(ViewportBuilder::default().with_title(title)); + + let closed = ui.ctx().show_viewport_immediate(id, vb, |ui, _| { + egui::CentralPanel::default() + .frame(Frame::default().inner_margin(0.0)) + .show(ui, |ui| content(ui)); + + return ui.input(|i| i.viewport().close_requested()); + }); + + return Response { closed }; +} + +pub struct Response { + closed: bool, +} + +impl Response { + pub fn close_requested(&self) -> bool { + return self.closed; + } + + pub fn on_close(&self, cb: impl FnOnce()) { + if self.closed { + cb() + } + } +} diff --git a/network/abstract/src/lib.rs b/network/abstract/src/lib.rs index ad8642b..66c6b32 100644 --- a/network/abstract/src/lib.rs +++ b/network/abstract/src/lib.rs @@ -1,6 +1,6 @@ pub trait Address: Sized + Clone { fn parse(raw: &str) -> Result; - fn to_string(self) -> String; + fn to_string(&self) -> String; } pub trait NetworkContext { diff --git a/network/windows/src/address.rs b/network/windows/src/address.rs index 44f59fe..85e21ff 100644 --- a/network/windows/src/address.rs +++ b/network/windows/src/address.rs @@ -45,7 +45,7 @@ impl Address for WindowsAddressAny { } } - fn to_string(self) -> String { + fn to_string(&self) -> String { let mut buffer = [0u16; 1024]; let mut buf_size = buffer.len() as u32; unsafe { @@ -123,7 +123,7 @@ impl Address for WindowsAddressKnown { } } - fn to_string(self) -> String { + fn to_string(&self) -> String { return self.any.to_string(); } }