diff --git a/Cargo.lock b/Cargo.lock index 61ea56a..e626fad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1968,6 +1968,7 @@ dependencies = [ "hyper-tls", "image", "is_elevated", + "lazy_static", "log", "num-format", "num_cpus", diff --git a/Cargo.toml b/Cargo.toml index 255c565..f3be14c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ documentation = "https://github.com/hinto-janai/gupax" edition = "2021" [profile.release] +panic = "abort" debug = false strip = "symbols" codegen-units = 1 @@ -45,6 +46,7 @@ figment = { version = "0.10.8", features = ["toml"] } hyper = "0.14.20" hyper-tls = "0.5.0" image = { version = "0.24.4", features = ["png"] } +lazy_static = "1.4.0" log = "0.4.17" num_cpus = "1.13.1" num-format = { version = "0.4.3", default-features = false } diff --git a/images/banner.png b/images/banner.png index 7fb2d6f..aa8870e 100644 Binary files a/images/banner.png and b/images/banner.png differ diff --git a/src/constants.rs b/src/constants.rs index c4f0bd2..5de7112 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -113,7 +113,7 @@ pub const WHITE: egui::Color32 = egui::Color32::WHITE; pub const GRAY: egui::Color32 = egui::Color32::GRAY; pub const LIGHT_GRAY: egui::Color32 = egui::Color32::LIGHT_GRAY; pub const BLACK: egui::Color32 = egui::Color32::BLACK; -pub const DARK_GRAY: egui::Color32 = egui::Color32::from_rgb(18, 18, 18); +pub const DARK_GRAY: egui::Color32 = egui::Color32::from_gray(13); // [Duration] constants pub const SECOND: std::time::Duration = std::time::Duration::from_secs(1); @@ -248,15 +248,15 @@ pub const GUPAX_TAB_XMRIG: &str = "Set the tab Gupax starts on to: XMRig" pub const GUPAX_SIMPLE: &str = r#"Use simple Gupax settings: - - Update button - - Basic toggles"#; + - Update button + - Basic toggles"#; pub const GUPAX_ADVANCED: &str = r#"Use advanced Gupax settings: - - Update button - - Basic toggles - - P2Pool/XMRig binary path selector - - Gupax resolution sliders - - Gupax start-up tab selector"#; + - Update button + - Basic toggles + - P2Pool/XMRig binary path selector + - Gupax resolution sliders + - Gupax start-up tab selector"#; pub const GUPAX_SELECT: &str = "Open a file explorer to select a file"; pub const GUPAX_PATH_P2POOL: &str = "The location of the P2Pool binary: Both absolute and relative paths are accepted; A red [X] will appear if there is no file found at the given path"; pub const GUPAX_PATH_XMRIG: &str = "The location of the XMRig binary: Both absolute and relative paths are accepted; A red [X] will appear if there is no file found at the given path"; @@ -293,16 +293,16 @@ r#"WARNING: Use [--no-color] and make sure to set [--data-api ] & [--local Start P2Pool with these arguments and override all below settings"#; pub const P2POOL_SIMPLE: &str = r#"Use simple P2Pool settings: - - Remote remote Monero node - - Default P2Pool settings + Mini"#; + - Remote remote Monero node + - Default P2Pool settings + Mini"#; pub const P2POOL_ADVANCED: &str = r#"Use advanced P2Pool settings: - - Terminal input - - Overriding command arguments - - Manual node list - - P2Pool Main/Mini selection - - Out/In peer setting - - Log level setting"#; + - Terminal input + - Overriding command arguments + - Manual node list + - P2Pool Main/Mini selection + - Out/In peer setting + - Log level setting"#; pub const P2POOL_NAME: &str = "Add a unique name to identify this node; Only [A-Za-z0-9-_.] and spaces allowed; Max length = 30 characters"; pub const P2POOL_NODE_IP: &str = "Specify the Monero Node IP to connect to with P2Pool; It must be a valid IPv4 address or a valid domain name; Max length = 255 characters"; pub const P2POOL_RPC_PORT: &str = "Specify the RPC port of the Monero node; [1-65535]"; @@ -321,19 +321,19 @@ pub const LIST_CLEAR: &str = "Clear all current values"; // XMRig pub const XMRIG_SIMPLE: &str = r#"Use simple XMRig settings: - - Mine to local P2Pool (localhost:3333) - - CPU thread slider - - HTTP API @ localhost:18088"#; + - Mine to local P2Pool (localhost:3333) + - CPU thread slider + - HTTP API @ localhost:18088"#; pub const XMRIG_ADVANCED: &str = r#"Use advanced XMRig settings: - - Terminal input - - Overriding command arguments - - Custom payout address - - CPU thread slider - - Manual pool list - - Custom HTTP API IP/Port - - TLS setting - - Keepalive setting"#; + - Terminal input + - Overriding command arguments + - Custom payout address + - CPU thread slider + - Manual pool list + - Custom HTTP API IP/Port + - TLS setting + - Keepalive setting"#; pub const XMRIG_INPUT: &str = "Send a command to XMRig"; pub const XMRIG_ARGUMENTS: &str = r#"WARNING: Use [--no-color] and make sure to set [--http-host ] & [--http-port ] so that the [Status] tab can work! @@ -381,6 +381,98 @@ r#"Gupax is licensed under GPLv3. For more information, see link below: "#; +//---------------------------------------------------------------------------------------------------- Visuals +use egui::epaint::{ + Rounding, + Shadow, + Stroke +}; + +use egui::{ + Color32, + Visuals, + style::Spacing, +}; + +use egui::style::{ + Selection, + Widgets, + WidgetVisuals, +}; + +pub const ACCENT_COLOR: Color32 = Color32::from_rgb(200, 100, 100); +pub const BG: Color32 = Color32::from_gray(20); + +lazy_static::lazy_static! { + /// This is based off [`Visuals::dark()`]. + pub static ref VISUALS: Visuals = { + let selection = Selection { + bg_fill: ACCENT_COLOR, + stroke: Stroke::new(1.0, Color32::from_gray(255)), + }; + + let widgets = Widgets { + noninteractive: WidgetVisuals { + bg_fill: BG, + bg_stroke: Stroke::new(1.0, Color32::from_gray(60)), // separators, indentation lines + fg_stroke: Stroke::new(1.0, Color32::from_gray(140)), // normal text color + rounding: Rounding::same(10.0), + expansion: 0.0, + }, + inactive: WidgetVisuals { + bg_fill: Color32::from_gray(50), + bg_stroke: Default::default(), + fg_stroke: Stroke::new(1.0, Color32::from_gray(180)), // button text + rounding: Rounding::same(10.0), + expansion: 0.0, + }, + hovered: WidgetVisuals { + bg_fill: Color32::from_gray(80), + bg_stroke: Stroke::new(1.0, Color32::from_gray(150)), // e.g. hover over window edge or button + fg_stroke: Stroke::new(1.5, Color32::from_gray(240)), + rounding: Rounding::same(10.0), + expansion: 1.0, + }, + active: WidgetVisuals { + bg_fill: Color32::from_gray(55), + bg_stroke: Stroke::new(1.0, Color32::WHITE), + fg_stroke: Stroke::new(2.0, Color32::WHITE), + rounding: Rounding::same(10.0), + expansion: 1.0, + }, + open: WidgetVisuals { + bg_fill: Color32::from_gray(27), + bg_stroke: Stroke::new(1.0, Color32::from_gray(60)), + fg_stroke: Stroke::new(1.0, Color32::from_gray(210)), + rounding: Rounding::same(10.0), + expansion: 0.0, + }, + }; + + Visuals { + dark_mode: true, + override_text_color: None, + widgets, + selection, + hyperlink_color: Color32::from_rgb(90, 170, 255), + faint_bg_color: Color32::from_additive_luminance(5), // visible, but barely so + extreme_bg_color: Color32::from_gray(10), // e.g. TextEdit background + code_bg_color: Color32::from_gray(64), + warn_fg_color: Color32::from_rgb(255, 143, 0), // orange + error_fg_color: Color32::from_rgb(255, 0, 0), // red + window_rounding: Rounding::same(6.0), + window_shadow: Shadow::big_dark(), + popup_shadow: Shadow::small_dark(), + resize_corner_size: 12.0, + text_cursor_width: 2.0, + text_cursor_preview: false, + clip_rect_margin: 3.0, // should be at least half the size of the widest frame stroke + max WidgetVisuals::expansion + button_frame: true, + collapsing_header_frame: false, + } + }; +} + //---------------------------------------------------------------------------------------------------- TESTS #[cfg(test)] mod test { diff --git a/src/gupax.rs b/src/gupax.rs index c243e41..421d2fb 100644 --- a/src/gupax.rs +++ b/src/gupax.rs @@ -109,7 +109,7 @@ impl crate::disk::Gupax { ui.set_enabled(updating); let prog = *lock2!(update,prog); let msg = format!("{}\n{}{}", *lock2!(update,msg), prog, "%"); - ui.add_sized([width, height*1.4], Label::new(RichText::text_style(RichText::new(msg), Monospace))); + ui.add_sized([width, height*1.4], Label::new(RichText::new(msg))); let height = height/2.0; if updating { ui.add_sized([width, height], Spinner::new().size(height)); @@ -144,11 +144,10 @@ impl crate::disk::Gupax { debug!("Gupax Tab | Rendering P2Pool/XMRig path selection"); // P2Pool/XMRig binary path selection - ui.style_mut().override_text_style = Some(Monospace); let height = height/28.0; let text_edit = (ui.available_width()/10.0)-SPACE; ui.group(|ui| { - ui.add_sized([ui.available_width(), height/2.0], Label::new(RichText::new("P2Pool/XMRig PATHs").underline().color(LIGHT_GRAY).text_style(TextStyle::Monospace))).on_hover_text("Gupax is online"); + ui.add_sized([ui.available_width(), height/2.0], Label::new(RichText::new("P2Pool/XMRig PATHs").underline().color(LIGHT_GRAY))).on_hover_text("Gupax is online"); ui.separator(); ui.horizontal(|ui| { if self.p2pool_path.is_empty() { @@ -196,7 +195,7 @@ impl crate::disk::Gupax { debug!("Gupax Tab | Rendering [Tab] selector"); ui.group(|ui| { let width = (width/5.0)-(SPACE*1.93); - ui.add_sized([ui.available_width(), height/2.0], Label::new(RichText::new("Default Tab").underline().color(LIGHT_GRAY).text_style(TextStyle::Monospace))).on_hover_text(GUPAX_TAB); + ui.add_sized([ui.available_width(), height/2.0], Label::new(RichText::new("Default Tab").underline().color(LIGHT_GRAY))).on_hover_text(GUPAX_TAB); ui.separator(); ui.horizontal(|ui| { if ui.add_sized([width, height], SelectableLabel::new(self.tab == Tab::About, "About")).on_hover_text(GUPAX_TAB_ABOUT).clicked() { self.tab = Tab::About; } @@ -213,7 +212,7 @@ impl crate::disk::Gupax { // Gupax App resolution sliders debug!("Gupax Tab | Rendering resolution sliders"); ui.group(|ui| { - ui.add_sized([ui.available_width(), height/2.0], Label::new(RichText::new("Width/Height Adjust").underline().color(LIGHT_GRAY).text_style(TextStyle::Monospace))).on_hover_text(GUPAX_ADJUST); + ui.add_sized([ui.available_width(), height/2.0], Label::new(RichText::new("Width/Height Adjust").underline().color(LIGHT_GRAY))).on_hover_text(GUPAX_ADJUST); ui.separator(); ui.vertical(|ui| { let width = width/10.0; diff --git a/src/main.rs b/src/main.rs index f842cf3..da731ed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -165,6 +165,7 @@ impl App { fn cc(cc: &eframe::CreationContext<'_>, app: Self) -> Self { let resolution = cc.integration_info.window_info.size; init_text_styles(&cc.egui_ctx, resolution[0]); + cc.egui_ctx.set_visuals(VISUALS.clone()); Self { resolution, ..app @@ -734,13 +735,13 @@ fn init_text_styles(ctx: &egui::Context, width: f32) { let scale = width / 30.0; let mut style = (*ctx.style()).clone(); style.text_styles = [ - (Small, FontId::new(scale/3.0, Proportional)), - (Body, FontId::new(scale/2.0, Proportional)), - (Button, FontId::new(scale/2.0, Proportional)), + (Small, FontId::new(scale/3.0, egui::FontFamily::Monospace)), + (Body, FontId::new(scale/2.0, egui::FontFamily::Monospace)), + (Button, FontId::new(scale/2.0, egui::FontFamily::Monospace)), (Monospace, FontId::new(scale/2.0, egui::FontFamily::Monospace)), - (Heading, FontId::new(scale/1.5, Proportional)), - (Name("Tab".into()), FontId::new(scale*1.2, Proportional)), - (Name("Bottom".into()), FontId::new(scale/2.0, Proportional)), + (Heading, FontId::new(scale/1.5, egui::FontFamily::Monospace)), + (Name("Tab".into()), FontId::new(scale*1.2, egui::FontFamily::Monospace)), + (Name("Bottom".into()), FontId::new(scale/2.0, egui::FontFamily::Monospace)), (Name("MonospaceSmall".into()), FontId::new(scale/2.5, egui::FontFamily::Monospace)), (Name("MonospaceLarge".into()), FontId::new(scale/1.5, egui::FontFamily::Monospace)), ].into(); @@ -1327,7 +1328,6 @@ impl eframe::App for App { ui.add_sized([width, height], Hyperlink::from_label_and_url("Click here for more info.", "https://xmrig.com/docs/miner/randomx-optimization-guide")) }, Debug => { - ui.style_mut().override_text_style = Some(Monospace); egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| { let width = ui.available_width(); let height = ui.available_height(); @@ -1418,7 +1418,6 @@ impl eframe::App for App { let height = ui.available_height()/4.0; let mut sudo = lock!(self.sudo); let hide = sudo.hide; - ui.style_mut().override_text_style = Some(Monospace); if sudo.testing { ui.add_sized([width, height], Spinner::new().size(height)); ui.set_enabled(false); @@ -1480,11 +1479,7 @@ impl eframe::App for App { let height = self.height/15.0; ui.add_space(4.0); ui.horizontal(|ui| { - let style = ui.style_mut(); - style.override_text_style = Some(Name("Tab".into())); - style.visuals.widgets.inactive.fg_stroke.color = Color32::from_rgb(100, 100, 100); - style.visuals.selection.bg_fill = Color32::from_rgb(255, 120, 120); - style.visuals.selection.stroke = Stroke { width: 5.0, color: Color32::from_rgb(255, 255, 255) }; + ui.style_mut().override_text_style = Some(Name("Tab".into())); if ui.add_sized([width, height], SelectableLabel::new(self.tab == Tab::About, "About")).clicked() { self.tab = Tab::About; } ui.separator(); if ui.add_sized([width, height], SelectableLabel::new(self.tab == Tab::Status, "Status")).clicked() { self.tab = Tab::Status; } @@ -1852,24 +1847,21 @@ path_xmr: {:#?}\n ui.vertical_centered(|ui| { ui.set_max_height(max_height); // Display [Gupax] banner - let link_width = width/15.0; + let link_width = width/14.0; self.img.banner.show_max_size(ui, Vec2::new(width, height*3.0)); - ui.add_sized([width, height], Label::new("Gupax is a GUI for mining")); + ui.add_sized([width, height], Label::new("is a GUI for mining")); ui.add_sized([link_width, height], Hyperlink::from_label_and_url("[Monero]", "https://www.github.com/monero-project/monero")); ui.add_sized([width, height], Label::new("on")); ui.add_sized([link_width, height], Hyperlink::from_label_and_url("[P2Pool]", "https://www.github.com/SChernykh/p2pool")); ui.add_sized([width, height], Label::new("using")); ui.add_sized([link_width, height], Hyperlink::from_label_and_url("[XMRig]", "https://www.github.com/xmrig/xmrig")); - ui.add_space(SPACE*3.0); - ui.style_mut().override_text_style = Some(Monospace); + ui.add_space(SPACE*2.0); ui.add_sized([width, height], Label::new(KEYBOARD_SHORTCUTS)); - ui.style_mut().override_text_style = Some(Body); - ui.add_space(SPACE*3.0); + ui.add_space(SPACE*2.0); - ui.add_sized([width, height], Label::new("egui is licensed under MIT & Apache-2.0")); - ui.add_sized([width, height], Label::new("Gupax, P2Pool, and XMRig are licensed under GPLv3")); if cfg!(debug_assertions) { ui.label(format!("Gupax is running in debug mode - {}", self.now.elapsed().as_secs_f64())); } + ui.label(format!("Gupax has been running for: {}", lock!(self.pub_sys).gupax_uptime)); }); } Tab::Status => { diff --git a/src/p2pool.rs b/src/p2pool.rs index f7631c4..1af9507 100644 --- a/src/p2pool.rs +++ b/src/p2pool.rs @@ -42,7 +42,6 @@ impl crate::disk::P2pool { if self.simple { let height = height / 2.4; let width = width - SPACE; - ui.style_mut().override_text_style = Some(Monospace); egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| { ui.style_mut().override_text_style = Some(Name("MonospaceSmall".into())); egui::ScrollArea::vertical().stick_to_bottom(true).max_width(width).max_height(height).auto_shrink([false; 2]).show_viewport(ui, |ui, _| { @@ -53,7 +52,6 @@ impl crate::disk::P2pool { } else { let height = height / 2.8; let width = width - SPACE; - ui.style_mut().override_text_style = Some(Monospace); egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| { ui.style_mut().override_text_style = Some(Name("MonospaceSmall".into())); egui::ScrollArea::vertical().stick_to_bottom(true).max_width(width).max_height(height).auto_shrink([false; 2]).show_viewport(ui, |ui, _| { @@ -77,7 +75,6 @@ impl crate::disk::P2pool { debug!("P2Pool Tab | Rendering [Arguments]"); ui.group(|ui| { ui.horizontal(|ui| { let width = (width/10.0) - SPACE; - ui.style_mut().override_text_style = Some(Monospace); ui.add_sized([width, text_edit], Label::new("Command arguments:")); ui.add_sized([ui.available_width(), text_edit], TextEdit::hint_text(TextEdit::singleline(&mut self.arguments), r#"--wallet <...> --host <...>"#)).on_hover_text(P2POOL_ARGUMENTS); self.arguments.truncate(1024); @@ -90,7 +87,6 @@ impl crate::disk::P2pool { ui.group(|ui| { let width = width - SPACE; ui.spacing_mut().text_edit_width = (width)-(SPACE*3.0); - ui.style_mut().override_text_style = Some(Monospace); let text; let color; let len = format!("{:02}", self.address.len()); @@ -152,11 +148,11 @@ impl crate::disk::P2pool { debug!("P2Pool Tab | Rendering [ComboBox] of Remote Nodes"); let ip_location = crate::node::format_ip_location(&self.node, false); let text = RichText::new(format!(" ⏺ {}ms | {}", ms, ip_location)).color(color); - ComboBox::from_id_source("remote_nodes").selected_text(RichText::text_style(text, Monospace)).show_ui(ui, |ui| { + ComboBox::from_id_source("remote_nodes").selected_text(text).show_ui(ui, |ui| { for data in lock!(ping).nodes.iter() { let ms = crate::node::format_ms(data.ms); let ip_location = crate::node::format_ip_location(data.ip, true); - let text = RichText::text_style(RichText::new(format!(" ⏺ {} | {}", ms, ip_location)).color(data.color), Monospace); + let text = RichText::new(format!(" ⏺ {} | {}", ms, ip_location)).color(data.color); ui.selectable_value(&mut self.node, data.ip.to_string(), text); } }); @@ -206,7 +202,7 @@ impl crate::disk::P2pool { let pinging = lock!(ping).pinging; ui.set_enabled(pinging); let prog = lock!(ping).prog.round(); - let msg = RichText::text_style(RichText::new(format!("{} ... {}%", lock!(ping).msg, prog)), Monospace); + let msg = RichText::new(format!("{} ... {}%", lock!(ping).msg, prog)); let height = height / 1.25; ui.add_space(5.0); ui.add_sized([width, height], Label::new(msg)); @@ -243,7 +239,6 @@ impl crate::disk::P2pool { ui.group(|ui| { let width = width/10.0; ui.vertical(|ui| { - ui.style_mut().override_text_style = Some(Monospace); ui.spacing_mut().text_edit_width = width*3.32; ui.horizontal(|ui| { let text; @@ -336,10 +331,10 @@ impl crate::disk::P2pool { // [Ping List] debug!("P2Pool Tab | Rendering [Node List]"); let text = RichText::new(format!("{}. {}", self.selected_index+1, self.selected_name)); - ComboBox::from_id_source("manual_nodes").selected_text(RichText::text_style(text, Monospace)).show_ui(ui, |ui| { + ComboBox::from_id_source("manual_nodes").selected_text(text).show_ui(ui, |ui| { let mut n = 0; for (name, node) in node_vec.iter() { - let text = RichText::text_style(RichText::new(format!("{}. {}\n IP: {}\n RPC: {}\n ZMQ: {}", n+1, name, node.ip, node.rpc, node.zmq)), Monospace); + let text = RichText::new(format!("{}. {}\n IP: {}\n RPC: {}\n ZMQ: {}", n+1, name, node.ip, node.rpc, node.zmq)); if ui.add(SelectableLabel::new(self.selected_name == *name, text)).clicked() { self.selected_index = n; let node = node.clone(); diff --git a/src/status.rs b/src/status.rs index e424fc5..c7d7787 100644 --- a/src/status.rs +++ b/src/status.rs @@ -55,7 +55,6 @@ pub fn show(&mut self, sys: &Arc>, p2pool_api: &Arc>, p2pool_api: &Arc>, p2pool_api: &Arc>, p2pool_api: &Arc>, p2pool_api: &Arc>, p2pool_api: &Arc --user <...> --config <...>"#)).on_hover_text(XMRIG_ARGUMENTS); self.arguments.truncate(1024); @@ -89,7 +86,6 @@ impl crate::disk::Xmrig { ui.group(|ui| { let width = width - SPACE; ui.spacing_mut().text_edit_width = (width)-(SPACE*3.0); - ui.style_mut().override_text_style = Some(Monospace); let text; let color; let len = format!("{:02}", self.address.len()); @@ -142,7 +138,6 @@ impl crate::disk::Xmrig { ui.group(|ui| { let width = width/10.0; ui.vertical(|ui| { - ui.style_mut().override_text_style = Some(Monospace); ui.spacing_mut().text_edit_width = width*3.32; ui.horizontal(|ui| { let text; @@ -234,10 +229,10 @@ impl crate::disk::Xmrig { // [Node List] debug!("XMRig Tab | Rendering [Node List] ComboBox"); let text = RichText::new(format!("{}. {}", self.selected_index+1, self.selected_name)); - ComboBox::from_id_source("manual_pool").selected_text(RichText::text_style(text, Monospace)).show_ui(ui, |ui| { + ComboBox::from_id_source("manual_pool").selected_text(text).show_ui(ui, |ui| { let mut n = 0; for (name, pool) in pool_vec.iter() { - let text = RichText::text_style(RichText::new(format!("{}. {}\n IP: {}\n Port: {}\n Rig: {}", n+1, name, pool.ip, pool.port, pool.rig)), Monospace); + let text = format!("{}. {}\n IP: {}\n Port: {}\n Rig: {}", n+1, name, pool.ip, pool.port, pool.rig); if ui.add(SelectableLabel::new(self.selected_name == *name, text)).clicked() { self.selected_index = n; let pool = pool.clone(); @@ -356,7 +351,6 @@ impl crate::disk::Xmrig { ui.group(|ui| { ui.horizontal(|ui| { ui.vertical(|ui| { let width = width/10.0; - ui.style_mut().override_text_style = Some(Monospace); ui.spacing_mut().text_edit_width = width*2.39; // HTTP API ui.horizontal(|ui| {