From 5d0bd0e0c060dabf4ca0edc192f089a4cee7cf00 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 24 Jun 2024 11:52:24 +0300 Subject: [PATCH] ui: external frame for dragging window, dynamic window title, default modal size and tabs paddings fix, transport status order, dual title panel id fix --- src/gui/app.rs | 350 +++++++++++----------- src/gui/views/modal.rs | 12 +- src/gui/views/network/content.rs | 60 ++-- src/gui/views/root.rs | 31 +- src/gui/views/title_panel.rs | 6 +- src/gui/views/views.rs | 10 +- src/gui/views/wallets/content.rs | 77 ++--- src/gui/views/wallets/wallet/content.rs | 18 +- src/gui/views/wallets/wallet/transport.rs | 34 ++- src/settings/config.rs | 4 +- 10 files changed, 300 insertions(+), 302 deletions(-) diff --git a/src/gui/app.rs b/src/gui/app.rs index 24c22da..6b0c22b 100644 --- a/src/gui/app.rs +++ b/src/gui/app.rs @@ -13,17 +13,15 @@ // limitations under the License. use std::sync::atomic::{AtomicBool, Ordering}; - -use egui::{Align, Context, Layout, Modifiers, Rect, Rounding, Stroke}; -use egui::epaint::RectShape; -use egui::os::OperatingSystem; use lazy_static::lazy_static; +use egui::{Align, Context, Layout, Margin, Modifiers, Rect, Rounding, Stroke}; +use egui::epaint::{RectShape, Shadow}; -use crate::AppConfig; +use crate::{AppConfig, built_info}; use crate::gui::Colors; use crate::gui::icons::{ARROWS_IN, ARROWS_OUT, CARET_DOWN, MOON, SUN, X}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Root, TitlePanel, View}; +use crate::gui::views::{Root, View}; lazy_static! { /// State to check if platform Back button was pressed. @@ -74,12 +72,8 @@ impl App { } // Show main content with custom frame on desktop. - let os = OperatingSystem::from_target_os(); - let custom_window = os != OperatingSystem::Android; - if custom_window { - custom_window_frame(ctx, |ui| { - self.root.ui(ui, &self.platform); - }); + if View::is_desktop() { + self.window_frame_ui(ctx); } else { egui::CentralPanel::default() .frame(egui::Frame { @@ -91,6 +85,172 @@ impl App { }); } } + + /// Draw custom resizeable window frame for desktop. + fn window_frame_ui(&mut self, ctx: &Context) { + egui::CentralPanel::default().frame(egui::Frame { + inner_margin: Margin::same(Root::WINDOW_FRAME_MARGIN), + ..Default::default() + }).show(ctx, |ui| { + self.custom_window_frame(ui); + }); + } + + /// Draw custom window frame for desktop. + fn custom_window_frame(&mut self, ui: &mut egui::Ui) { + let is_fullscreen = ui.ctx().input(|i| { + i.viewport().fullscreen.unwrap_or(false) + }); + let panel_frame = if is_fullscreen { + egui::Frame::default() + } else { + egui::Frame { + shadow: Shadow { + offset: Default::default(), + blur: Root::WINDOW_FRAME_MARGIN, + spread: 0.5, + color: egui::Color32::from_black_alpha(25), + }, + rounding: Rounding { + nw: 8.0, + ne: 8.0, + sw: 0.0, + se: 0.0, + }, + ..Default::default() + } + }; + egui::CentralPanel::default().frame(panel_frame).show_inside(ui, |ui| { + let app_rect = ui.max_rect(); + + let window_title_height = Root::WINDOW_TITLE_HEIGHT; + let window_title_rect = { + let mut rect = app_rect; + rect.max.y = rect.min.y + window_title_height; + rect + }; + + let window_title_bg = RectShape { + rect: window_title_rect, + rounding: if is_fullscreen { + Rounding::ZERO + } else { + Rounding { + nw: 8.0, + ne: 8.0, + sw: 0.0, + se: 0.0, + } + }, + fill: Colors::yellow_dark(), + stroke: Stroke::NONE, + fill_texture_id: Default::default(), + uv: Rect::ZERO + }; + ui.painter().add(window_title_bg); + + // Draw window title. + self.window_title_ui(ui, window_title_rect); + + let content_rect = { + let mut rect = app_rect; + rect.min.y = window_title_rect.max.y; + rect + }; + // Draw main content. + let mut content_ui = ui.child_ui(content_rect, *ui.layout()); + self.root.ui(&mut content_ui, &self.platform); + }); + } + + /// Draw custom window title content. + fn window_title_ui(&self, ui: &mut egui::Ui, title_bar_rect: Rect) { + let is_fullscreen = ui.ctx().input(|i| { + i.viewport().fullscreen.unwrap_or(false) + }); + + let painter = ui.painter(); + + let title_bar_response = ui.interact( + title_bar_rect, + egui::Id::new("title_bar"), + egui::Sense::click_and_drag(), + ); + + // Paint the title. + let dual_wallets_panel = + ui.available_width() >= (Root::SIDE_PANEL_WIDTH * 3.0) + View::get_right_inset(); + let wallet_panel_opened = self.root.wallets.wallet_panel_opened(); + let hide_app_name = if dual_wallets_panel { + !wallet_panel_opened || (AppConfig::show_wallets_at_dual_panel() && + self.root.wallets.showing_wallet() && !self.root.wallets.creating_wallet()) + } else if Root::is_dual_panel_mode(ui) { + !wallet_panel_opened + } else { + !Root::is_network_panel_open() && !wallet_panel_opened + }; + let title_text = if hide_app_name { + "ツ".to_string() + } else { + format!("Grim {}", built_info::PKG_VERSION) + }; + painter.text( + title_bar_rect.center(), + egui::Align2::CENTER_CENTER, + title_text, + egui::FontId::proportional(15.0), + egui::Color32::from_gray(60), + ); + + // Interact with the window title (drag to move window): + if title_bar_response.double_clicked() { + ui.ctx().send_viewport_cmd(egui::ViewportCommand::Fullscreen(!is_fullscreen)); + } + + if title_bar_response.drag_started_by(egui::PointerButton::Primary) { + ui.ctx().send_viewport_cmd(egui::ViewportCommand::StartDrag); + } + + ui.allocate_ui_at_rect(title_bar_rect, |ui| { + ui.with_layout(Layout::right_to_left(Align::Center), |ui| { + // Draw button to close window. + View::title_button_small(ui, X, |_| { + Root::show_exit_modal(); + }); + + // Draw fullscreen button. + let fullscreen_icon = if is_fullscreen { + ARROWS_IN + } else { + ARROWS_OUT + }; + View::title_button_small(ui, fullscreen_icon, |ui| { + ui.ctx().send_viewport_cmd(egui::ViewportCommand::Fullscreen(!is_fullscreen)); + }); + + // Draw button to minimize window. + View::title_button_small(ui, CARET_DOWN, |ui| { + ui.ctx().send_viewport_cmd(egui::ViewportCommand::Minimized(true)); + }); + + // Draw application icon. + let layout_size = ui.available_size(); + ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| { + // Draw button to minimize window. + let use_dark = AppConfig::dark_theme().unwrap_or(false); + let theme_icon = if use_dark { + SUN + } else { + MOON + }; + View::title_button_small(ui, theme_icon, |ui| { + AppConfig::set_dark_theme(!use_dark); + crate::setup_visuals(ui.ctx()); + }); + }); + }); + }); + } } /// To draw with egui`s eframe (for wgpu, glow backends and wasm target). @@ -104,172 +264,6 @@ impl eframe::App for App { } } -/// Draw custom window frame for desktop. -fn custom_window_frame(ctx: &Context, add_contents: impl FnOnce(&mut egui::Ui)) { - let is_fullscreen = ctx.input(|i| { - i.viewport().fullscreen.unwrap_or(false) - }); - let panel_frame = egui::Frame { - fill: Colors::fill(), - rounding: if is_fullscreen { - Rounding::ZERO - } else { - Rounding { - nw: 8.0, - ne: 8.0, - sw: 8.0, - se: 8.0, - } - }, - ..Default::default() - }; - - egui::CentralPanel::default().frame(panel_frame).show(ctx, |ui| { - let app_rect = ui.max_rect(); - - let window_title_height = 38.0; - let window_title_rect = { - let mut rect = app_rect; - rect.max.y = rect.min.y + window_title_height; - rect - }; - - let window_title_bg = RectShape { - rect: window_title_rect, - rounding: if is_fullscreen { - Rounding::ZERO - } else { - Rounding { - nw: 8.0, - ne: 8.0, - sw: 0.0, - se: 0.0, - } - }, - fill: Colors::yellow_dark(), - stroke: Stroke::NONE, - fill_texture_id: Default::default(), - uv: Rect::ZERO - }; - let bg_idx = ui.painter().add(window_title_bg); - - // Draw window title. - window_title_ui(ui, window_title_rect); - - // Setup window title background. - ui.painter().set(bg_idx, window_title_bg); - - let mut title_bar_rect = window_title_rect.clone(); - title_bar_rect.min += egui::emath::vec2(0.0, window_title_height); - title_bar_rect.max += egui::emath::vec2(0.0, TitlePanel::DEFAULT_HEIGHT - 0.5); - let title_bar_bg = RectShape { - rect: title_bar_rect, - rounding: Rounding::ZERO, - fill: Colors::yellow(), - stroke: Stroke::NONE, - fill_texture_id: Default::default(), - uv: Rect::ZERO - }; - let bg_idx = ui.painter().add(title_bar_bg); - - // Draw line to support title panel. - ui.painter().line_segment( - [ - title_bar_rect.left_bottom() + egui::vec2(0.0, 0.5), - title_bar_rect.right_bottom() + egui::vec2(0.0, 0.5), - ], - View::item_stroke(), - ); - - // Draw main content. - let mut content_rect = { - let mut rect = app_rect; - rect.min.y = window_title_rect.max.y; - rect - }; - content_rect.min += egui::emath::vec2(4.0, 0.0); - content_rect.max -= egui::emath::vec2(4.0, 2.0); - let mut content_ui = ui.child_ui(content_rect, *ui.layout()); - add_contents(&mut content_ui); - - // Setup title panel background. - ui.painter().set(bg_idx, title_bar_bg); - }); -} - -/// Draw custom window title content. -fn window_title_ui(ui: &mut egui::Ui, title_bar_rect: Rect) { - let is_fullscreen = ui.ctx().input(|i| { - i.viewport().fullscreen.unwrap_or(false) - }); - - let painter = ui.painter(); - - let title_bar_response = ui.interact( - title_bar_rect, - egui::Id::new("title_bar"), - egui::Sense::click_and_drag(), - ); - - // Paint the title. - painter.text( - title_bar_rect.center(), - egui::Align2::CENTER_CENTER, - "Grim 0.1.0", - egui::FontId::proportional(15.0), - egui::Color32::from_gray(60), - ); - - // Interact with the window title (drag to move window): - if title_bar_response.double_clicked() { - ui.ctx().send_viewport_cmd(egui::ViewportCommand::Fullscreen(!is_fullscreen)); - } - - if title_bar_response.drag_started_by(egui::PointerButton::Primary) { - ui.ctx().send_viewport_cmd(egui::ViewportCommand::StartDrag); - } - - ui.allocate_ui_at_rect(title_bar_rect, |ui| { - ui.with_layout(Layout::right_to_left(Align::Center), |ui| { - // Draw button to close window. - View::title_button_small(ui, X, |_| { - Root::show_exit_modal(); - }); - - // Draw fullscreen button. - let fullscreen_icon = if is_fullscreen { - ARROWS_IN - } else { - ARROWS_OUT - }; - View::title_button_small(ui, fullscreen_icon, |ui| { - ui.ctx().send_viewport_cmd(egui::ViewportCommand::Fullscreen(!is_fullscreen)); - }); - - // Draw button to minimize window. - View::title_button_small(ui, CARET_DOWN, |ui| { - ui.ctx().send_viewport_cmd(egui::ViewportCommand::Minimized(true)); - }); - - // Draw application icon. - let layout_size = ui.available_size(); - ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| { - // Draw button to minimize window. - let use_dark = AppConfig::dark_theme().unwrap_or(false); - let theme_icon = if use_dark { - SUN - } else { - MOON - }; - View::title_button_small(ui, theme_icon, |ui| { - AppConfig::set_dark_theme(!use_dark); - crate::setup_visuals(ui.ctx()); - }); - }); - }); - }); -} - #[allow(dead_code)] #[cfg(target_os = "android")] #[allow(non_snake_case)] diff --git a/src/gui/views/modal.rs b/src/gui/views/modal.rs index 159ef05..a149324 100644 --- a/src/gui/views/modal.rs +++ b/src/gui/views/modal.rs @@ -160,7 +160,11 @@ impl Modal { /// Draw [`egui::Window`] with provided content. fn window_ui(&self, ctx: &egui::Context, add_content: impl FnOnce(&mut egui::Ui, &Modal)) { - let rect = ctx.screen_rect(); + let mut rect = ctx.screen_rect(); + if View::is_desktop() { + rect = rect.shrink(7.5); + rect.min += egui::vec2(0.0, Root::WINDOW_TITLE_HEIGHT + 0.5); + } egui::Window::new("modal_bg_window") .title_bar(false) .resizable(false) @@ -218,7 +222,11 @@ impl Modal { ModalPosition::Center => Align2::CENTER_CENTER }; let x_align = View::get_left_inset() - View::get_right_inset(); - let y_align = View::get_top_inset() + Self::DEFAULT_MARGIN; + let y_align = View::get_top_inset() + Self::DEFAULT_MARGIN + if View::is_desktop() { + Root::WINDOW_TITLE_HEIGHT + 8.0 + } else { + 0.0 + }; let offset = match self.position { ModalPosition::CenterTop => Vec2::new(x_align, y_align), ModalPosition::Center => Vec2::new(x_align, 0.0) diff --git a/src/gui/views/network/content.rs b/src/gui/views/network/content.rs index d0bb517..4c00f62 100644 --- a/src/gui/views/network/content.rs +++ b/src/gui/views/network/content.rs @@ -13,7 +13,6 @@ // limitations under the License. use egui::{Margin, RichText, ScrollArea, Stroke}; -use egui::os::OperatingSystem; use egui::scroll_area::ScrollBarVisibility; use crate::AppConfig; @@ -45,45 +44,39 @@ impl Default for NetworkContent { impl NetworkContent { pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { - // Flag to show connections or integrated node content. let show_connections = AppConfig::show_connections_network_panel(); // Show title panel. self.title_ui(ui, show_connections); // Show integrated node tabs content. - egui::TopBottomPanel::bottom("node_tabs_panel") - .resizable(false) - .frame(egui::Frame { - stroke: Stroke::NONE, - fill: Colors::fill(), - inner_margin: Margin { - left: View::get_left_inset() + 4.0, - right: View::far_right_inset_margin(ui) + 4.0, - top: if OperatingSystem::Android == OperatingSystem::from_target_os() { - 4.0 - } else { - 6.0 + if !show_connections { + egui::TopBottomPanel::bottom("node_tabs_panel") + .resizable(false) + .frame(egui::Frame { + fill: Colors::fill(), + inner_margin: Margin { + left: View::get_left_inset() + 4.0, + right: View::far_right_inset_margin(ui) + 4.0, + top: 6.0, + bottom: View::get_bottom_inset() + 5.0, }, - bottom: View::get_bottom_inset() + 4.0, - }, - ..Default::default() - }) - .show_animated_inside(ui, !show_connections, |ui| { - ui.vertical_centered(|ui| { - View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { - // Show tabs content. - self.tabs_ui(ui); + ..Default::default() + }) + .show_inside(ui, |ui| { + ui.vertical_centered(|ui| { + View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { + self.tabs_ui(ui); + }); }); }); - }); + } // Show current node tab content. egui::SidePanel::right("node_tab_content_panel") .resizable(false) .exact_width(ui.available_width()) .frame(egui::Frame { - stroke: Stroke::NONE, ..Default::default() }) .show_animated_inside(ui, !show_connections, |ui| { @@ -100,7 +93,6 @@ impl NetworkContent { ..Default::default() }) .show_inside(ui, |ui| { - // Draw node tab content. self.node_tab_content.ui(ui, cb); }); }); @@ -125,15 +117,16 @@ impl NetworkContent { 0.0 }, top: 3.0, - bottom: View::get_bottom_inset() + 4.0, + bottom: if View::is_desktop() && show_connections { + 6.0 + } else { + 4.0 + }, }, fill: Colors::button(), ..Default::default() }) .show_inside(ui, |ui| { - if !show_connections { - return; - } ScrollArea::vertical() .id_source("connections_content") .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) @@ -163,12 +156,7 @@ impl NetworkContent { fn tabs_ui(&mut self, ui: &mut egui::Ui) { ui.vertical_centered(|ui| { // Setup spacing between tabs. - let between = if OperatingSystem::Android == OperatingSystem::from_target_os() { - 4.0 - } else { - 6.0 - }; - ui.style_mut().spacing.item_spacing = egui::vec2(between, 0.0); + ui.style_mut().spacing.item_spacing = egui::vec2(6.0, 0.0); // Setup vertical padding inside tab button. ui.style_mut().spacing.button_padding = egui::vec2(0.0, 4.0); diff --git a/src/gui/views/root.rs b/src/gui/views/root.rs index 8b65454..c8e3ba6 100644 --- a/src/gui/views/root.rs +++ b/src/gui/views/root.rs @@ -35,14 +35,14 @@ pub struct Root { /// Side panel [`NetworkContent`] content. network: NetworkContent, /// Central panel [`WalletsContent`] content. - wallets: WalletsContent, + pub wallets: WalletsContent, /// Check if app exit is allowed on close event of [`eframe::App`] implementation. pub(crate) exit_allowed: bool, /// Flag to show exit progress at [`Modal`]. show_exit_progress: bool, - /// Flag to check if first it's first draw of content. + /// Flag to check it's first draw of content. first_draw: bool, /// List of allowed [`Modal`] ids for this [`ModalContainer`]. @@ -98,19 +98,23 @@ impl Root { /// Default width of side panel at application UI. pub const SIDE_PANEL_WIDTH: f32 = 400.0; + /// Desktop window title height. + pub const WINDOW_TITLE_HEIGHT: f32 = 38.0; + /// Margin of window frame at desktop. + pub const WINDOW_FRAME_MARGIN: f32 = 6.0; pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { // Draw modal content for current ui container. self.current_modal_ui(ui, cb); - let (is_panel_open, panel_width) = Self::network_panel_state_width(ui); + let dual_panel = Self::is_dual_panel_mode(ui); + let (is_panel_open, panel_width) = Self::network_panel_state_width(ui, dual_panel); + // Show network content. egui::SidePanel::left("network_panel") .resizable(false) .exact_width(panel_width) .frame(egui::Frame { - stroke: egui::Stroke::NONE, - fill: Colors::white_or_black(false), ..Default::default() }) .show_animated_inside(ui, is_panel_open, |ui| { @@ -120,8 +124,6 @@ impl Root { // Show wallets content. egui::CentralPanel::default() .frame(egui::Frame { - stroke: egui::Stroke::NONE, - fill: Colors::fill_deep(), ..Default::default() }) .show_inside(ui, |ui| { @@ -143,19 +145,22 @@ impl Root { } /// Get [`NetworkContent`] panel state and width. - fn network_panel_state_width(ui: &mut egui::Ui) -> (bool, f32) { - let dual_panel_mode = Self::is_dual_panel_mode(ui); - let is_panel_open = dual_panel_mode || Self::is_network_panel_open(); - let panel_width = if dual_panel_mode { + fn network_panel_state_width(ui: &mut egui::Ui, dual_panel: bool) -> (bool, f32) { + let is_panel_open = dual_panel || Self::is_network_panel_open(); + let panel_width = if dual_panel { Self::SIDE_PANEL_WIDTH + View::get_left_inset() } else { - View::window_size(ui).0 + View::window_size(ui).0 - if View::is_desktop() { + Root::WINDOW_FRAME_MARGIN * 2.0 + } else { + 0.0 + } }; (is_panel_open, panel_width) } /// Check if ui can show [`NetworkContent`] and [`WalletsContent`] at same time. - pub fn is_dual_panel_mode(ui: &mut egui::Ui) -> bool { + pub fn is_dual_panel_mode(ui: &egui::Ui) -> bool { let (w, h) = View::window_size(ui); // Screen is wide if width is greater than height or just 20% smaller. let is_wide_screen = w > h || w + (w * 0.2) >= h; diff --git a/src/gui/views/title_panel.rs b/src/gui/views/title_panel.rs index 881ef75..c98a4be 100644 --- a/src/gui/views/title_panel.rs +++ b/src/gui/views/title_panel.rs @@ -44,11 +44,7 @@ impl TitlePanel { TitleContentType::Title(text) => text, TitleContentType::WithSubTitle(text, _, _) => text }; - let second_text = match first { - TitleContentType::Title(text) => text, - TitleContentType::WithSubTitle(text, _, _) => text - }; - let id = Id::from(first_text.to_owned()).with(second_text); + let id = Id::from(first_text.to_owned()).with("_dual"); (id, true) }, }; diff --git a/src/gui/views/views.rs b/src/gui/views/views.rs index 01758c5..176a4b5 100644 --- a/src/gui/views/views.rs +++ b/src/gui/views/views.rs @@ -33,6 +33,12 @@ use crate::gui::views::types::TextEditOptions; pub struct View; impl View { + /// Check if current platform is desktop + pub fn is_desktop() -> bool { + let os = OperatingSystem::from_target_os(); + os != OperatingSystem::Android && os != OperatingSystem::IOS + } + /// Format timestamp in seconds with local UTC offset. pub fn format_time(ts: i64) -> String { let utc_offset = chrono::Local::now().offset().local_minus_utc(); @@ -76,7 +82,7 @@ impl View { } /// Get width and height of app window. - pub fn window_size(ui: &mut egui::Ui) -> (f32, f32) { + pub fn window_size(ui: &egui::Ui) -> (f32, f32) { ui.ctx().input(|i| { return match i.viewport().inner_rect { None => { @@ -185,7 +191,7 @@ impl View { /// Draw small size title button. pub fn title_button_small(ui: &mut egui::Ui, icon: &str, action: impl FnOnce(&mut egui::Ui)) { - Self::title_button(ui, 17.0, icon, action); + Self::title_button(ui, 16.0, icon, action); } /// Draw title button with transparent background color, contains only icon. diff --git a/src/gui/views/wallets/content.rs b/src/gui/views/wallets/content.rs index 8deef7f..72cd959 100644 --- a/src/gui/views/wallets/content.rs +++ b/src/gui/views/wallets/content.rs @@ -14,7 +14,6 @@ use std::time::Duration; use egui::{Align, Id, Layout, Margin, RichText, Rounding, ScrollArea}; -use egui::os::OperatingSystem; use egui::scroll_area::ScrollBarVisibility; use crate::AppConfig; @@ -101,12 +100,13 @@ impl WalletsContent { // Setup panels parameters. let dual_panel = is_dual_panel_mode(ui); - let open_wallet_panel = show_wallet || create_wallet || empty_list; let wallet_panel_width = self.wallet_panel_width(ui, empty_list, dual_panel, show_wallet); let content_width = ui.available_width(); // Show title panel. - self.title_ui(ui, dual_panel, create_wallet, show_wallet); + View::max_width_ui(ui, ui.available_width(), |ui| { + self.title_ui(ui, dual_panel, create_wallet, show_wallet); + }); // Show wallet panel content. egui::SidePanel::right("wallet_panel") @@ -125,7 +125,7 @@ impl WalletsContent { }, ..Default::default() }) - .show_animated_inside(ui, open_wallet_panel, |ui| { + .show_animated_inside(ui, self.wallet_panel_opened(), |ui| { // Do not draw content on zero width. if content_width == 0.0 { return; @@ -172,40 +172,34 @@ impl WalletsContent { ((!show_wallet && !dual_panel && !Root::is_network_panel_open()) || dual_panel); // Show wallets bottom panel. - egui::TopBottomPanel::bottom("wallets_bottom_panel") - .frame(egui::Frame { - fill: Colors::fill(), - inner_margin: Margin { - left: View::get_left_inset() + 4.0, - right: View::far_right_inset_margin(ui) + 4.0, - top: if OperatingSystem::Android == OperatingSystem::from_target_os() { - 4.0 - } else { - 6.0 + if show_bottom_panel { + egui::TopBottomPanel::bottom("wallets_bottom_panel") + .frame(egui::Frame { + fill: Colors::fill(), + inner_margin: Margin { + left: View::get_left_inset() + 4.0, + right: View::far_right_inset_margin(ui) + 4.0, + top: 6.0, + bottom: View::get_bottom_inset() + 5.0, }, - bottom: View::get_bottom_inset() + 4.0, - }, - ..Default::default() - }) - .show_animated_inside(ui, show_bottom_panel, |ui| { - // Setup spacing between tabs. - let between = if OperatingSystem::Android == OperatingSystem::from_target_os() { - 4.0 - } else { - 6.0 - }; - ui.style_mut().spacing.item_spacing = egui::vec2(between, 0.0); - // Setup vertical padding inside buttons. - ui.style_mut().spacing.button_padding = egui::vec2(10.0, 4.0); + ..Default::default() + }) + .show_inside(ui, |ui| { + // Setup spacing between tabs. + ui.style_mut().spacing.item_spacing = egui::vec2(6.0, 0.0); + // Setup vertical padding inside buttons. + ui.style_mut().spacing.button_padding = egui::vec2(10.0, 4.0); - ui.vertical_centered(|ui| { - let pressed = Modal::opened() == Some(WalletCreation::NAME_PASS_MODAL); - View::tab_button(ui, PLUS, pressed, || { - self.creation_content.show_name_pass_modal(cb); + ui.vertical_centered(|ui| { + let pressed = Modal::opened() == Some(WalletCreation::NAME_PASS_MODAL); + View::tab_button(ui, PLUS, pressed, || { + self.creation_content.show_name_pass_modal(cb); + }); }); }); - }); + } + // Show wallet list. egui::CentralPanel::default() .frame(if list_hidden { egui::Frame::default() @@ -227,11 +221,26 @@ impl WalletsContent { if !dual_panel { ui.ctx().request_repaint_after(Duration::from_millis(1000)); } - // Show list of wallets. self.wallet_list_ui(ui, cb); }); } + /// Check if wallet panel is showing. + pub fn wallet_panel_opened(&self) -> bool { + let empty_list = self.wallets.is_current_list_empty(); + empty_list || self.creating_wallet() || self.showing_wallet() + } + + /// Check if opened wallet is showing. + pub fn showing_wallet(&self) -> bool { + self.wallets.is_selected_open() + } + + /// Check if wallet is creating. + pub fn creating_wallet(&self) -> bool { + self.creation_content.can_go_back() + } + /// Draw [`TitlePanel`] content. fn title_ui(&mut self, ui: &mut egui::Ui, diff --git a/src/gui/views/wallets/wallet/content.rs b/src/gui/views/wallets/wallet/content.rs index d9b692b..3f5da69 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -14,7 +14,6 @@ use std::time::Duration; use egui::{Align, Id, Layout, Margin, RichText, ScrollArea}; -use egui::os::OperatingSystem; use egui::scroll_area::ScrollBarVisibility; use grin_chain::SyncStatus; use grin_core::core::amount_to_hr_string; @@ -89,7 +88,7 @@ impl WalletContent { // Show wallet balance panel not on Settings tab with selected non-repairing // wallet, when there is no error and data is not empty. let show_balance = self.current_tab.get_type() != WalletTabType::Settings && !data_empty - && !wallet.sync_error() && !wallet.is_repairing(); + && !wallet.sync_error() && !wallet.is_repairing() && !wallet.is_closing(); egui::TopBottomPanel::top(Id::from("wallet_balance").with(wallet.identifier())) .frame(egui::Frame { fill: Colors::fill(), @@ -131,12 +130,8 @@ impl WalletContent { inner_margin: Margin { left: View::far_left_inset_margin(ui) + 4.0, right: View::get_right_inset() + 4.0, - top: if OperatingSystem::Android == OperatingSystem::from_target_os() { - 4.0 - } else { - 6.0 - }, - bottom: View::get_bottom_inset() + 4.0, + top: 6.0, + bottom: View::get_bottom_inset() + 5.0, }, ..Default::default() }) @@ -517,12 +512,7 @@ impl WalletContent { fn tabs_ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet) { ui.scope(|ui| { // Setup spacing between tabs. - let between = if OperatingSystem::Android == OperatingSystem::from_target_os() { - 4.0 - } else { - 6.0 - }; - ui.style_mut().spacing.item_spacing = egui::vec2(between, 0.0); + ui.style_mut().spacing.item_spacing = egui::vec2(6.0, 0.0); // Setup vertical padding inside tab button. ui.style_mut().spacing.button_padding = egui::vec2(0.0, 4.0); diff --git a/src/gui/views/wallets/wallet/transport.rs b/src/gui/views/wallets/wallet/transport.rs index 1d1ebb9..ec5a9c8 100644 --- a/src/gui/views/wallets/wallet/transport.rs +++ b/src/gui/views/wallets/wallet/transport.rs @@ -260,21 +260,6 @@ impl WalletTransport { .size(18.0) .color(Colors::title(false))); }); - // Setup bridges status text. - let bridge = TorConfig::get_bridge(); - let bridges_text = match &bridge { - None => { - format!("{} {}", SHIELD_SLASH, t!("transport.bridges_disabled")) - } - Some(b) => { - let name = b.protocol_name().to_uppercase(); - format!("{} {}", - SHIELD_CHECKERED, - t!("transport.bridge_name", "b" = name)) - } - }; - ui.label(RichText::new(bridges_text).size(15.0).color(Colors::text(false))); - ui.add_space(1.0); // Setup Tor status text. let is_running = Tor::is_service_running(service_id); @@ -292,7 +277,24 @@ impl WalletTransport { (X_CIRCLE, t!("transport.disconnected")) }; let status_text = format!("{} {}", icon, text); - ui.label(RichText::new(status_text).size(15.0).color(Colors::gray())); + ui.label(RichText::new(status_text).size(15.0).color(Colors::text(false))); + ui.add_space(1.0); + + // Setup bridges status text. + let bridge = TorConfig::get_bridge(); + let bridges_text = match &bridge { + None => { + format!("{} {}", SHIELD_SLASH, t!("transport.bridges_disabled")) + } + Some(b) => { + let name = b.protocol_name().to_uppercase(); + format!("{} {}", + SHIELD_CHECKERED, + t!("transport.bridge_name", "b" = name)) + } + }; + + ui.label(RichText::new(bridges_text).size(15.0).color(Colors::gray())); }); }); }); diff --git a/src/settings/config.rs b/src/settings/config.rs index fee82ce..825ff62 100644 --- a/src/settings/config.rs +++ b/src/settings/config.rs @@ -70,9 +70,9 @@ impl Default for AppConfig { impl AppConfig { /// Default window width. - pub const DEFAULT_WIDTH: f32 = 1280.0; + pub const DEFAULT_WIDTH: f32 = 1269.0; /// Default window height. - pub const DEFAULT_HEIGHT: f32 = 745.0; + pub const DEFAULT_HEIGHT: f32 = 789.0; /// Application configuration file name. pub const FILE_NAME: &'static str = "app.toml";