From f95645ea81795fb42ff674e02329e520fe077129 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Sat, 3 Jun 2023 21:35:38 +0300 Subject: [PATCH] ui: move global modal to root screen, refactor rounded box background painting, optimize center content --- src/gui/app.rs | 70 +---------------------- src/gui/colors.rs | 2 +- src/gui/screens/root.rs | 98 ++++++++++++++++++++++++++------ src/gui/views/network.rs | 9 ++- src/gui/views/network_metrics.rs | 6 +- src/gui/views/network_mining.rs | 3 +- src/gui/views/network_node.rs | 2 +- src/gui/views/views.rs | 70 ++++++++++++----------- 8 files changed, 134 insertions(+), 126 deletions(-) diff --git a/src/gui/app.rs b/src/gui/app.rs index 64fd2a2..ae4c831 100644 --- a/src/gui/app.rs +++ b/src/gui/app.rs @@ -12,15 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -use egui::{Color32, Context, RichText, Spinner, Stroke, Widget}; +use egui::{Context, Stroke}; use egui::os::OperatingSystem; -use egui::style::Margin; -use crate::gui::{Colors, Navigator}; +use crate::gui::Colors; use crate::gui::platform::PlatformCallbacks; use crate::gui::screens::Root; -use crate::gui::views::{Modal, ModalId, ModalLocation, View}; -use crate::node::Node; pub struct PlatformApp { pub(crate) app: App, @@ -30,7 +27,6 @@ pub struct PlatformApp { #[derive(Default)] pub struct App { root: Root, - show_exit_progress: bool } impl App { @@ -41,71 +37,11 @@ impl App { .. Default::default() }) .show(ctx, |ui| { - if Navigator::is_modal_open(ModalLocation::Global) { - self.show_global_modal(ui, frame, cb); - } self.root.ui(ui, frame, cb); }); } - fn show_global_modal(&mut self, - ui: &mut egui::Ui, - frame: &mut eframe::Frame, - cb: &dyn PlatformCallbacks) { - let location = ModalLocation::Global; - Navigator::modal_ui(ui, location, |ui, modal| { - match modal.id { - ModalId::Exit => { - if self.show_exit_progress { - if !Node::is_running() { - Self::exit(frame, cb); - modal.close(); - } - ui.add_space(16.0); - ui.vertical_centered(|ui| { - Spinner::new().size(42.0).color(Colors::GRAY).ui(ui); - ui.add_space(10.0); - ui.label(RichText::new(Node::get_sync_status_text()) - .size(18.0) - .color(Colors::INACTIVE_TEXT) - ); - }); - ui.add_space(12.0); - } else { - ui.add_space(8.0); - ui.vertical_centered(|ui| { - ui.label(t!("modal_exit.description")); - }); - ui.add_space(10.0); - // Setup spacing between buttons - ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0); - ui.columns(2, |columns| { - columns[0].vertical_centered_justified(|ui| { - View::button(ui, t!("modal_exit.exit"), Colors::WHITE, || { - if !Node::is_running() { - Self::exit(frame, cb); - modal.close(); - } else { - Node::stop(); - modal.disable_closing(); - self.show_exit_progress = true; - } - }); - }); - columns[1].vertical_centered_justified(|ui| { - View::button(ui, t!("modal.cancel"), Colors::WHITE, || { - modal.close(); - }); - }); - }); - ui.add_space(6.0); - } - } - } - }); - } - - fn exit(frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) { + pub fn exit(frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) { match OperatingSystem::from_target_os() { OperatingSystem::Android => { cb.exit(); diff --git a/src/gui/colors.rs b/src/gui/colors.rs index dfa0e88..3b81ee6 100644 --- a/src/gui/colors.rs +++ b/src/gui/colors.rs @@ -25,7 +25,7 @@ impl Colors { pub const GOLD: Color32 = Color32::from_rgb(255, 215, 0); pub const FILL: Color32 = Color32::from_gray(240); pub const TITLE: Color32 = Color32::from_gray(60); - pub const SUB_TITLE: Color32 = Color32::from_gray(80); + pub const TEXT: Color32 = Color32::from_gray(80); pub const BUTTON: Color32 = Color32::from_gray(70); pub const GRAY: Color32 = Color32::from_gray(120); pub const STROKE: Color32 = Color32::from_gray(190); diff --git a/src/gui/screens/root.rs b/src/gui/screens/root.rs index 7cae7d5..412feb2 100644 --- a/src/gui/screens/root.rs +++ b/src/gui/screens/root.rs @@ -14,14 +14,18 @@ use std::cmp::min; -use crate::gui::Navigator; +use egui::{RichText, Spinner, Widget}; + +use crate::gui::{App, Colors, Navigator}; use crate::gui::platform::PlatformCallbacks; use crate::gui::screens::{Account, Accounts, Screen, ScreenId}; -use crate::gui::views::{Network, View}; +use crate::gui::views::{ModalId, ModalLocation, Network, View}; +use crate::node::Node; pub struct Root { screens: Vec>, - network: Network + network: Network, + show_exit_progress: bool } impl Default for Root { @@ -33,14 +37,19 @@ impl Default for Root { Box::new(Accounts::default()), Box::new(Account::default()) ]), - network: Network::default() + network: Network::default(), + show_exit_progress: false } } } impl Root { pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) { - let (is_panel_open, panel_width) = dual_panel_state_width(frame); + if Navigator::is_modal_open(ModalLocation::Global) { + self.show_global_modal(ui, frame, cb); + } + + let (is_panel_open, panel_width) = self.dual_panel_state_width(frame); egui::SidePanel::left("network_panel") .resizable(false) .exact_width(panel_width) @@ -56,6 +65,63 @@ impl Root { }); } + fn show_global_modal(&mut self, + ui: &mut egui::Ui, + frame: &mut eframe::Frame, + cb: &dyn PlatformCallbacks) { + let location = ModalLocation::Global; + Navigator::modal_ui(ui, location, |ui, modal| { + match modal.id { + ModalId::Exit => { + if self.show_exit_progress { + if !Node::is_running() { + App::exit(frame, cb); + modal.close(); + } + ui.add_space(16.0); + ui.vertical_centered(|ui| { + Spinner::new().size(48.0).color(Colors::GRAY).ui(ui); + ui.add_space(12.0); + ui.label(RichText::new(t!("sync_status.shutdown")) + .size(17.0) + .color(Colors::TEXT) + ); + }); + ui.add_space(10.0); + } else { + ui.add_space(8.0); + ui.vertical_centered(|ui| { + ui.label(t!("modal_exit.description")); + }); + ui.add_space(10.0); + // Setup spacing between buttons + ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0); + ui.columns(2, |columns| { + columns[0].vertical_centered_justified(|ui| { + View::button(ui, t!("modal_exit.exit"), Colors::WHITE, || { + if !Node::is_running() { + App::exit(frame, cb); + modal.close(); + } else { + Node::stop(); + modal.disable_closing(); + self.show_exit_progress = true; + } + }); + }); + columns[1].vertical_centered_justified(|ui| { + View::button(ui, t!("modal.cancel"), Colors::WHITE, || { + modal.close(); + }); + }); + }); + ui.add_space(6.0); + } + } + } + }); + } + fn show_current_screen(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, @@ -68,18 +134,18 @@ impl Root { } } } -} -/// Get dual panel state and width -fn dual_panel_state_width(frame: &mut eframe::Frame) -> (bool, f32) { - let dual_panel_mode = View::is_dual_panel_mode(frame); - let is_panel_open = dual_panel_mode || Navigator::is_side_panel_open(); - let panel_width = if dual_panel_mode { - min(frame.info().window_info.size.x as i64, View::SIDE_PANEL_MIN_WIDTH) as f32 - } else { - frame.info().window_info.size.x - }; - (is_panel_open, panel_width) + /// Get dual panel state and width + fn dual_panel_state_width(&self, frame: &mut eframe::Frame) -> (bool, f32) { + let dual_panel_mode = View::is_dual_panel_mode(frame); + let is_panel_open = dual_panel_mode || Navigator::is_side_panel_open(); + let panel_width = if dual_panel_mode { + min(frame.info().window_info.size.x as i64, View::SIDE_PANEL_MIN_WIDTH) as f32 + } else { + frame.info().window_info.size.x + }; + (is_panel_open, panel_width) + } } #[allow(dead_code)] diff --git a/src/gui/views/network.rs b/src/gui/views/network.rs index 352b089..51fb30b 100644 --- a/src/gui/views/network.rs +++ b/src/gui/views/network.rs @@ -76,7 +76,6 @@ impl Network { outer_margin: Margin::same(5.0), .. Default::default() }) - .resizable(false) .show_inside(ui, |ui| { self.draw_tabs(ui); }); @@ -219,7 +218,7 @@ impl Network { }; // Draw sync text - let status_color_rgba = Rgba::from(Colors::SUB_TITLE) * color_factor; + let status_color_rgba = Rgba::from(Colors::TEXT) * color_factor; let status_color = Color32::from(status_color_rgba); View::ellipsize_text(ui, Node::get_sync_status_text(), 15.0, status_color); @@ -234,9 +233,9 @@ impl Network { }); } - pub fn server_off_content(ui: &mut egui::Ui) { - View::center_content(ui, [ui.available_width() - 48.0, 160.0], |ui| { - let text = t!("network.inactive_message","dots" => DOTS_THREE_OUTLINE_VERTICAL); + pub fn disabled_server_content(ui: &mut egui::Ui) { + View::center_content(ui, 142.0, |ui| { + let text = t!("network.disabled_server", "dots" => DOTS_THREE_OUTLINE_VERTICAL); ui.label(RichText::new(text) .size(16.0) .color(Colors::INACTIVE_TEXT) diff --git a/src/gui/views/network_metrics.rs b/src/gui/views/network_metrics.rs index 0322e1d..561cf61 100644 --- a/src/gui/views/network_metrics.rs +++ b/src/gui/views/network_metrics.rs @@ -39,14 +39,14 @@ impl NetworkTab for NetworkMetrics { let server_stats = Node::get_stats(); if server_stats.is_none() || server_stats.as_ref().unwrap().diff_stats.height == 0 { if !Node::is_running() { - Network::server_off_content(ui); + Network::disabled_server_content(ui); } else { - View::center_content(ui, [280.0, 160.0], |ui| { + View::center_content(ui, 160.0, |ui| { Spinner::new().size(104.0).color(Colors::GOLD).ui(ui); ui.add_space(18.0); ui.label(RichText::new(t!("network_metrics.loading")) .size(16.0) - .color(Colors::INACTIVE_TEXT) + .color(Colors::TEXT) ); }); } diff --git a/src/gui/views/network_mining.rs b/src/gui/views/network_mining.rs index 29e7e9a..045109f 100644 --- a/src/gui/views/network_mining.rs +++ b/src/gui/views/network_mining.rs @@ -24,6 +24,7 @@ impl NetworkTab for NetworkMining { } fn ui(&mut self, ui: &mut Ui) { - todo!() + + } } \ No newline at end of file diff --git a/src/gui/views/network_node.rs b/src/gui/views/network_node.rs index 30658fc..59996ec 100644 --- a/src/gui/views/network_node.rs +++ b/src/gui/views/network_node.rs @@ -35,7 +35,7 @@ impl NetworkTab for NetworkNode { let server_stats = Node::get_stats(); if !server_stats.is_some() { if !Node::is_running() { - Network::server_off_content(ui); + Network::disabled_server_content(ui); } else { ui.centered_and_justified(|ui| { Spinner::new().size(104.0).color(Colors::GOLD).ui(ui); diff --git a/src/gui/views/views.rs b/src/gui/views/views.rs index 51a6763..ef3ee17 100644 --- a/src/gui/views/views.rs +++ b/src/gui/views/views.rs @@ -13,7 +13,7 @@ // limitations under the License. use egui::{Button, PointerState, Response, RichText, Sense, Vec2, Widget}; -use egui::epaint::{Color32, FontId, Rounding, Stroke}; +use egui::epaint::{Color32, FontId, RectShape, Rounding, Stroke}; use egui::epaint::text::TextWrapping; use egui::text::{LayoutJob, TextFormat}; use egui_extras::{Size, StripBuilder}; @@ -56,7 +56,7 @@ impl View { /// Sub-header with uppercase characters and more lighter color. pub fn sub_header(ui: &mut egui::Ui, text: String) { - ui.label(RichText::new(text.to_uppercase()).size(16.0).color(Colors::SUB_TITLE)); + ui.label(RichText::new(text.to_uppercase()).size(16.0).color(Colors::TEXT)); } /// Temporary button click optimization for touch screens. @@ -90,7 +90,7 @@ impl View { pub fn tab_button(ui: &mut egui::Ui, icon: &str, active: bool, action: impl FnOnce()) { let text_color = match active { true => { Colors::TITLE } - false => { Colors::SUB_TITLE } + false => { Colors::TEXT } }; let wt = RichText::new(icon.to_string()).size(24.0).color(text_color); @@ -128,52 +128,58 @@ impl View { /// | label | pub fn rounded_box(ui: &mut egui::Ui, value: String, label: String, r: [bool; 4]) { let mut rect = ui.available_rect_before_wrap(); - rect.set_height(46.0); - // Draw box background - ui.painter().rect( + // Create background shape. + let mut bg_shape = RectShape { rect, - Rounding { + rounding: Rounding { nw: if r[0] { 8.0 } else { 0.0 }, ne: if r[1] { 8.0 } else { 0.0 }, sw: if r[2] { 8.0 } else { 0.0 }, se: if r[3] { 8.0 } else { 0.0 }, }, - Colors::WHITE, - Stroke { width: 1.0, color: Colors::ITEM_STROKE }, - ); + fill: Colors::WHITE, + stroke: Stroke { width: 1.0, color: Colors::ITEM_STROKE }, + }; + let bg_idx = ui.painter().add(bg_shape); - ui.vertical_centered_justified(|ui| { - // Correct vertical spacing between items - ui.style_mut().spacing.item_spacing.y = -4.0; + // Draw box content. + let content_resp = ui.allocate_ui_at_rect(rect, |ui| { + ui.vertical_centered_justified(|ui| { + // Correct vertical spacing between items. + ui.style_mut().spacing.item_spacing.y = -4.0; - // Draw box value - let mut job = LayoutJob::single_section(value, TextFormat { - font_id: FontId::proportional(18.0), - color: Colors::BLACK, - .. Default::default() + // Draw box value. + let mut job = LayoutJob::single_section(value, TextFormat { + font_id: FontId::proportional(18.0), + color: Colors::BLACK, + ..Default::default() + }); + job.wrap = TextWrapping { + max_rows: 1, + break_anywhere: false, + overflow_character: Option::from('﹍'), + ..Default::default() + }; + ui.label(job); + + // Draw box label. + ui.label(RichText::new(label).color(Colors::GRAY).size(15.0)); }); - job.wrap = TextWrapping { - max_rows: 1, - break_anywhere: false, - overflow_character: Option::from('﹍'), - ..Default::default() - }; - ui.label(job); + }).response; - // Draw box label - ui.label(RichText::new(label).color(Colors::GRAY).size(15.0)); - }); + // Setup background shape to be painted behind box content. + bg_shape.rect = content_resp.rect; + ui.painter().set(bg_idx, bg_shape); } /// Draw content in the center of current layout with specified width and height. - pub fn center_content(ui: &mut egui::Ui, w_h: [f32; 2], content: impl FnOnce(&mut egui::Ui)) { + pub fn center_content(ui: &mut egui::Ui, height: f32, content: impl FnOnce(&mut egui::Ui)) { ui.vertical_centered(|ui| { let mut rect = ui.available_rect_before_wrap(); - let side_margin = (ui.available_width() - w_h[0]) / 2.0; - rect.min += egui::emath::vec2(side_margin, ui.available_height() / 2.0 - w_h[1] / 2.0); + let side_margin = 24.0; + rect.min += egui::emath::vec2(side_margin, ui.available_height() / 2.0 - height / 2.0); rect.max -= egui::emath::vec2(side_margin, 0.0); - // rect.set_width(w_h[0]); ui.allocate_ui_at_rect(rect, |ui| { (content)(ui); });