From c7bcd64ae14214a44950b119a429c62465b9f81f Mon Sep 17 00:00:00 2001 From: ardocrat Date: Fri, 21 Jun 2024 14:22:59 +0300 Subject: [PATCH 01/28] ui: custom window title, small title button --- src/gui/app.rs | 138 +++++++++++++++++++++++++++++-- src/gui/colors.rs | 5 ++ src/gui/views/network/content.rs | 4 +- src/gui/views/root.rs | 16 +--- src/gui/views/views.rs | 19 +++-- src/gui/views/wallets/content.rs | 10 +-- src/main.rs | 11 +-- 7 files changed, 164 insertions(+), 39 deletions(-) diff --git a/src/gui/app.rs b/src/gui/app.rs index 6c85f8d..5890c86 100644 --- a/src/gui/app.rs +++ b/src/gui/app.rs @@ -14,12 +14,15 @@ use std::sync::atomic::{AtomicBool, Ordering}; -use egui::{Context, Modifiers}; +use egui::{Align, Context, Layout, Modifiers}; +use egui::os::OperatingSystem; use lazy_static::lazy_static; use crate::AppConfig; +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; +use crate::gui::views::{Root, View}; lazy_static! { /// State to check if platform Back button was pressed. @@ -69,14 +72,22 @@ impl App { } } - // Show main content. - egui::CentralPanel::default() - .frame(egui::Frame { - ..Default::default() - }) - .show(ctx, |ui| { + // 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); }); + } else { + egui::CentralPanel::default() + .frame(egui::Frame { + ..Default::default() + }) + .show(ctx, |ui| { + self.root.ui(ui, &self.platform); + }); + } } } @@ -85,6 +96,117 @@ impl eframe::App for App { fn update(&mut self, ctx: &Context, _: &mut eframe::Frame) { self.ui(ctx); } + + fn clear_color(&self, _visuals: &egui::Visuals) -> [f32; 4] { + egui::Rgba::TRANSPARENT.to_array() + } +} + +/// Draw custom window frame for desktop. +fn custom_window_frame(ctx: &Context, add_contents: impl FnOnce(&mut egui::Ui)) { + let panel_frame = egui::Frame { + fill: Colors::yellow_dark(), + rounding: egui::Rounding { + nw: 8.0, + ne: 8.0, + sw: 0.0, + se: 0.0, + }, + ..Default::default() + }; + + egui::CentralPanel::default().frame(panel_frame).show(ctx, |ui| { + let app_rect = ui.max_rect(); + + let title_bar_height = 38.0; + let title_bar_rect = { + let mut rect = app_rect; + rect.max.y = rect.min.y + title_bar_height; + rect + }; + window_title_ui(ui, title_bar_rect); + let content_rect = { + let mut rect = app_rect; + rect.min.y = title_bar_rect.max.y; + rect + }; + let mut content_ui = ui.child_ui(content_rect, *ui.layout()); + add_contents(&mut content_ui); + }); +} + +/// Draw custom window title content. +fn window_title_ui(ui: &mut egui::Ui, title_bar_rect: egui::epaint::Rect) { + 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 title bar (drag to move window): + if title_bar_response.double_clicked() { + let is_maximized = ui.input(|i| i.viewport().maximized.unwrap_or(false)); + ui.ctx() + .send_viewport_cmd(egui::ViewportCommand::Maximized(!is_maximized)); + } + + 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 is_fullscreen = ui.ctx().input(|i| { + i.viewport().fullscreen.unwrap_or(false) + }); + 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)] diff --git a/src/gui/colors.rs b/src/gui/colors.rs index a2d054d..3bfecc8 100644 --- a/src/gui/colors.rs +++ b/src/gui/colors.rs @@ -28,6 +28,7 @@ const DARK_SEMI_TRANSPARENT: Color32 = Color32::from_black_alpha(170); const GOLD: Color32 = Color32::from_rgb(255, 215, 0); const YELLOW: Color32 = Color32::from_rgb(254, 241, 2); +const YELLOW_DARK: Color32 = Color32::from_rgb(239, 229, 3); const GREEN: Color32 = Color32::from_rgb(0, 0x64, 0); @@ -118,6 +119,10 @@ impl Colors { YELLOW } + pub fn yellow_dark() -> Color32 { + YELLOW_DARK + } + pub fn green() -> Color32 { if use_dark() { GREEN.linear_multiply(1.3) diff --git a/src/gui/views/network/content.rs b/src/gui/views/network/content.rs index 9db3618..fc3781b 100644 --- a/src/gui/views/network/content.rs +++ b/src/gui/views/network/content.rs @@ -202,7 +202,7 @@ impl NetworkContent { // Draw title panel. TitlePanel::ui(TitleType::Single(title_content), |ui| { if !show_connections { - View::title_button(ui, DOTS_THREE_OUTLINE_VERTICAL, || { + View::title_button_big(ui, DOTS_THREE_OUTLINE_VERTICAL, |_| { AppConfig::toggle_show_connections_network_panel(); if AppConfig::show_connections_network_panel() { ExternalConnection::start_ext_conn_availability_check(); @@ -211,7 +211,7 @@ impl NetworkContent { } }, |ui| { if !Root::is_dual_panel_mode(ui) { - View::title_button(ui, BRIEFCASE, || { + View::title_button_big(ui, BRIEFCASE, |_| { Root::toggle_network_panel(); }); } diff --git a/src/gui/views/root.rs b/src/gui/views/root.rs index bc2c26e..87c1233 100644 --- a/src/gui/views/root.rs +++ b/src/gui/views/root.rs @@ -113,13 +113,7 @@ impl Root { ..Default::default() }) .show_animated_inside(ui, is_panel_open, |ui| { - // Set content height as window height. - let mut rect = ui.available_rect_before_wrap(); - let window_size = View::window_size(ui); - rect.set_height(window_size.1); - ui.allocate_ui_at_rect(rect, |ui| { - self.network.ui(ui, cb); - }); + self.network.ui(ui, cb); }); // Show wallets content. @@ -129,13 +123,7 @@ impl Root { ..Default::default() }) .show_inside(ui, |ui| { - // Set content height as window height. - let mut rect = ui.available_rect_before_wrap(); - let window_size = View::window_size(ui); - rect.set_height(window_size.1); - ui.allocate_ui_at_rect(rect, |ui| { - self.wallets.ui(ui, cb); - }); + self.wallets.ui(ui, cb); }); // Show integrated node warning on Android if needed. diff --git a/src/gui/views/views.rs b/src/gui/views/views.rs index 57331ba..01758c5 100644 --- a/src/gui/views/views.rs +++ b/src/gui/views/views.rs @@ -77,7 +77,6 @@ impl View { /// Get width and height of app window. pub fn window_size(ui: &mut egui::Ui) -> (f32, f32) { - ui.ctx().input(|i| { return match i.viewport().inner_rect { None => { @@ -179,8 +178,18 @@ impl View { false } - /// Title button with transparent background fill color, contains only icon. - pub fn title_button(ui: &mut egui::Ui, icon: &str, action: impl FnOnce()) { + /// Draw big size title button. + pub fn title_button_big(ui: &mut egui::Ui, icon: &str, action: impl FnOnce(&mut egui::Ui)) { + Self::title_button(ui, 22.0, icon, action); + } + + /// 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); + } + + /// Draw title button with transparent background color, contains only icon. + fn title_button(ui: &mut egui::Ui, size: f32, icon: &str, action: impl FnOnce(&mut egui::Ui)) { ui.scope(|ui| { // Disable strokes. ui.style_mut().visuals.widgets.inactive.bg_stroke = Stroke::NONE; @@ -190,7 +199,7 @@ impl View { ui.style_mut().visuals.widgets.active.expansion = 0.0; // Setup text. - let wt = RichText::new(icon.to_string()).size(22.0).color(Colors::title(true)); + let wt = RichText::new(icon.to_string()).size(size).color(Colors::title(true)); // Draw button. let br = Button::new(wt) .fill(Colors::TRANSPARENT) @@ -198,7 +207,7 @@ impl View { .on_hover_cursor(CursorIcon::PointingHand); br.surrender_focus(); if Self::touched(ui, br) { - (action)(); + (action)(ui); } }); } diff --git a/src/gui/views/wallets/content.rs b/src/gui/views/wallets/content.rs index bd5f612..818d5e5 100644 --- a/src/gui/views/wallets/content.rs +++ b/src/gui/views/wallets/content.rs @@ -263,11 +263,11 @@ impl WalletsContent { // Draw title panel. TitlePanel::ui(title_content, |ui| { if show_wallet && !dual_panel { - View::title_button(ui, ARROW_LEFT, || { + View::title_button_big(ui, ARROW_LEFT, |_| { self.wallets.select(None); }); } else if create_wallet { - View::title_button(ui, ARROW_LEFT, || { + View::title_button_big(ui, ARROW_LEFT, |_| { self.creation_content.back(); }); } else if show_wallet && dual_panel { @@ -276,17 +276,17 @@ impl WalletsContent { } else { SUITCASE }; - View::title_button(ui, list_icon, || { + View::title_button_big(ui, list_icon, |_| { self.show_wallets_at_dual_panel = !show_list; AppConfig::toggle_show_wallets_at_dual_panel(); }); } else if !Root::is_dual_panel_mode(ui) { - View::title_button(ui, GLOBE, || { + View::title_button_big(ui, GLOBE, |_| { Root::toggle_network_panel(); }); }; }, |ui| { - View::title_button(ui, GEAR, || { + View::title_button_big(ui, GEAR, |_| { // Show settings modal. Modal::new(Root::SETTINGS_MODAL) .position(ModalPosition::CenterTop) diff --git a/src/main.rs b/src/main.rs index 3d63438..b1bb043 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,9 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use grim::gui::App; -use grim::gui::platform::Desktop; - pub fn main() { #[allow(dead_code)] #[cfg(not(target_os = "android"))] @@ -54,8 +51,7 @@ fn real_main() { // Setup window size. let (width, height) = AppConfig::window_size(); - let mut viewport = egui::ViewportBuilder::default() - .with_inner_size([width, height]); + let mut viewport = egui::ViewportBuilder::default().with_inner_size([width, height]); // Setup an icon. if let Ok(icon) = from_png_bytes(include_bytes!("../img/icon.png")) { @@ -67,6 +63,11 @@ fn real_main() { viewport = viewport.with_position(pos2(x, y)); } + // Setup window decorations. + viewport = viewport + .with_transparent(true) + .with_decorations(false); + let mut options = eframe::NativeOptions { viewport, ..Default::default() -- 2.39.5 From 35a974aba965f45129fecbfe0681eb56954a01f7 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Fri, 21 Jun 2024 15:32:17 +0300 Subject: [PATCH 02/28] ui: window title frame for future resize --- src/gui/app.rs | 58 ++++++++++++++++++++----- src/gui/views/network/content.rs | 14 +++++- src/gui/views/wallets/content.rs | 14 +++++- src/gui/views/wallets/wallet/content.rs | 14 +++++- 4 files changed, 85 insertions(+), 15 deletions(-) diff --git a/src/gui/app.rs b/src/gui/app.rs index 5890c86..5220615 100644 --- a/src/gui/app.rs +++ b/src/gui/app.rs @@ -14,7 +14,8 @@ use std::sync::atomic::{AtomicBool, Ordering}; -use egui::{Align, Context, Layout, Modifiers}; +use egui::{Align, Context, Layout, Modifiers, Rect, Rounding, Stroke}; +use egui::epaint::RectShape; use egui::os::OperatingSystem; use lazy_static::lazy_static; @@ -22,7 +23,7 @@ use crate::AppConfig; 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, View}; +use crate::gui::views::{Root, TitlePanel, View}; lazy_static! { /// State to check if platform Back button was pressed. @@ -105,33 +106,70 @@ 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 panel_frame = egui::Frame { - fill: Colors::yellow_dark(), - rounding: egui::Rounding { + fill: Colors::fill(), + rounding: Rounding { nw: 8.0, ne: 8.0, sw: 0.0, se: 0.0, }, + // stroke: ctx.style().visuals.widgets.noninteractive.fg_stroke, + // outer_margin: 0.5.into(), // so the stroke is within the bounds ..Default::default() }; egui::CentralPanel::default().frame(panel_frame).show(ctx, |ui| { let app_rect = ui.max_rect(); - let title_bar_height = 38.0; - let title_bar_rect = { + let window_title_height = 38.0; + let window_title_rect = { let mut rect = app_rect; - rect.max.y = rect.min.y + title_bar_height; + rect.max.y = rect.min.y + window_title_height; rect }; - window_title_ui(ui, title_bar_rect); - let content_rect = { + + let window_title_bg = RectShape { + rect: window_title_rect, + rounding: panel_frame.rounding, + 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 main content. + let mut content_rect = { let mut rect = app_rect; - rect.min.y = title_bar_rect.max.y; + 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, 4.0); let mut content_ui = ui.child_ui(content_rect, *ui.layout()); add_contents(&mut content_ui); + + // Setup title bar background. + ui.painter().set(bg_idx, title_bar_bg); }); } diff --git a/src/gui/views/network/content.rs b/src/gui/views/network/content.rs index fc3781b..446d9a4 100644 --- a/src/gui/views/network/content.rs +++ b/src/gui/views/network/content.rs @@ -13,6 +13,7 @@ // limitations under the License. use egui::{Margin, RichText, ScrollArea, Stroke}; +use egui::os::OperatingSystem; use egui::scroll_area::ScrollBarVisibility; use crate::AppConfig; @@ -58,7 +59,11 @@ impl NetworkContent { inner_margin: Margin { left: View::get_left_inset() + 4.0, right: View::far_right_inset_margin(ui) + 4.0, - top: 4.0, + top: if OperatingSystem::Android == OperatingSystem::from_target_os() { + 4.0 + } else { + 6.0 + }, bottom: View::get_bottom_inset() + 4.0, }, ..Default::default() @@ -156,7 +161,12 @@ impl NetworkContent { fn tabs_ui(&mut self, ui: &mut egui::Ui) { ui.vertical_centered(|ui| { // Setup spacing between tabs. - ui.style_mut().spacing.item_spacing = egui::vec2(4.0, 0.0); + 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 tab button. ui.style_mut().spacing.button_padding = egui::vec2(0.0, 4.0); diff --git a/src/gui/views/wallets/content.rs b/src/gui/views/wallets/content.rs index 818d5e5..8deef7f 100644 --- a/src/gui/views/wallets/content.rs +++ b/src/gui/views/wallets/content.rs @@ -14,6 +14,7 @@ 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; @@ -177,12 +178,23 @@ impl WalletsContent { inner_margin: Margin { left: View::get_left_inset() + 4.0, right: View::far_right_inset_margin(ui) + 4.0, - top: 4.0, + top: if OperatingSystem::Android == OperatingSystem::from_target_os() { + 4.0 + } else { + 6.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); diff --git a/src/gui/views/wallets/wallet/content.rs b/src/gui/views/wallets/wallet/content.rs index 9c1fd78..d9b692b 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -14,6 +14,7 @@ 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; @@ -130,7 +131,11 @@ impl WalletContent { inner_margin: Margin { left: View::far_left_inset_margin(ui) + 4.0, right: View::get_right_inset() + 4.0, - top: 4.0, + top: if OperatingSystem::Android == OperatingSystem::from_target_os() { + 4.0 + } else { + 6.0 + }, bottom: View::get_bottom_inset() + 4.0, }, ..Default::default() @@ -512,7 +517,12 @@ impl WalletContent { fn tabs_ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet) { ui.scope(|ui| { // Setup spacing between tabs. - ui.style_mut().spacing.item_spacing = egui::vec2(4.0, 0.0); + 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 tab button. ui.style_mut().spacing.button_padding = egui::vec2(0.0, 4.0); -- 2.39.5 From 7ab64da9ee9967853a0bb73987dc61d5cc39ae4e Mon Sep 17 00:00:00 2001 From: ardocrat Date: Fri, 21 Jun 2024 16:37:27 +0300 Subject: [PATCH 03/28] ui: window stroke for title panel, bottom padding and corners --- src/gui/app.rs | 31 ++++++++++++++++++++++--------- src/gui/views/network/content.rs | 2 ++ src/gui/views/root.rs | 2 ++ 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/gui/app.rs b/src/gui/app.rs index 5220615..d727a4a 100644 --- a/src/gui/app.rs +++ b/src/gui/app.rs @@ -83,6 +83,7 @@ impl App { } else { egui::CentralPanel::default() .frame(egui::Frame { + stroke: Stroke::NONE, ..Default::default() }) .show(ctx, |ui| { @@ -110,11 +111,9 @@ fn custom_window_frame(ctx: &Context, add_contents: impl FnOnce(&mut egui::Ui)) rounding: Rounding { nw: 8.0, ne: 8.0, - sw: 0.0, - se: 0.0, + sw: 8.0, + se: 8.0, }, - // stroke: ctx.style().visuals.widgets.noninteractive.fg_stroke, - // outer_margin: 0.5.into(), // so the stroke is within the bounds ..Default::default() }; @@ -130,7 +129,12 @@ fn custom_window_frame(ctx: &Context, add_contents: impl FnOnce(&mut egui::Ui)) let window_title_bg = RectShape { rect: window_title_rect, - rounding: panel_frame.rounding, + rounding: 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(), @@ -157,6 +161,15 @@ fn custom_window_frame(ctx: &Context, add_contents: impl FnOnce(&mut egui::Ui)) }; 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; @@ -164,17 +177,17 @@ fn custom_window_frame(ctx: &Context, add_contents: impl FnOnce(&mut egui::Ui)) rect }; content_rect.min += egui::emath::vec2(4.0, 0.0); - content_rect.max -= egui::emath::vec2(4.0, 4.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 bar background. + // 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: egui::epaint::Rect) { +fn window_title_ui(ui: &mut egui::Ui, title_bar_rect: Rect) { let painter = ui.painter(); let title_bar_response = ui.interact( @@ -192,7 +205,7 @@ fn window_title_ui(ui: &mut egui::Ui, title_bar_rect: egui::epaint::Rect) { egui::Color32::from_gray(60), ); - // Interact with the title bar (drag to move window): + // Interact with the window title (drag to move window): if title_bar_response.double_clicked() { let is_maximized = ui.input(|i| i.viewport().maximized.unwrap_or(false)); ui.ctx() diff --git a/src/gui/views/network/content.rs b/src/gui/views/network/content.rs index 446d9a4..d0bb517 100644 --- a/src/gui/views/network/content.rs +++ b/src/gui/views/network/content.rs @@ -55,6 +55,7 @@ impl NetworkContent { 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, @@ -82,6 +83,7 @@ impl NetworkContent { .resizable(false) .exact_width(ui.available_width()) .frame(egui::Frame { + stroke: Stroke::NONE, ..Default::default() }) .show_animated_inside(ui, !show_connections, |ui| { diff --git a/src/gui/views/root.rs b/src/gui/views/root.rs index 87c1233..8b65454 100644 --- a/src/gui/views/root.rs +++ b/src/gui/views/root.rs @@ -109,6 +109,7 @@ impl Root { .resizable(false) .exact_width(panel_width) .frame(egui::Frame { + stroke: egui::Stroke::NONE, fill: Colors::white_or_black(false), ..Default::default() }) @@ -119,6 +120,7 @@ impl Root { // Show wallets content. egui::CentralPanel::default() .frame(egui::Frame { + stroke: egui::Stroke::NONE, fill: Colors::fill_deep(), ..Default::default() }) -- 2.39.5 From 022f9a1d2facdc217e53d8daf5f5d13b3b120beb Mon Sep 17 00:00:00 2001 From: ardocrat Date: Fri, 21 Jun 2024 21:40:29 +0300 Subject: [PATCH 04/28] ui: fullscreen on double click and corners --- src/gui/app.rs | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/src/gui/app.rs b/src/gui/app.rs index d727a4a..24c22da 100644 --- a/src/gui/app.rs +++ b/src/gui/app.rs @@ -106,13 +106,20 @@ 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: Rounding { - nw: 8.0, - ne: 8.0, - sw: 8.0, - se: 8.0, + rounding: if is_fullscreen { + Rounding::ZERO + } else { + Rounding { + nw: 8.0, + ne: 8.0, + sw: 8.0, + se: 8.0, + } }, ..Default::default() }; @@ -129,11 +136,15 @@ fn custom_window_frame(ctx: &Context, add_contents: impl FnOnce(&mut egui::Ui)) let window_title_bg = RectShape { rect: window_title_rect, - rounding: Rounding { - nw: 8.0, - ne: 8.0, - sw: 0.0, - se: 0.0, + 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, @@ -188,6 +199,10 @@ fn custom_window_frame(ctx: &Context, add_contents: impl FnOnce(&mut egui::Ui)) /// 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( @@ -207,9 +222,7 @@ fn window_title_ui(ui: &mut egui::Ui, title_bar_rect: Rect) { // Interact with the window title (drag to move window): if title_bar_response.double_clicked() { - let is_maximized = ui.input(|i| i.viewport().maximized.unwrap_or(false)); - ui.ctx() - .send_viewport_cmd(egui::ViewportCommand::Maximized(!is_maximized)); + ui.ctx().send_viewport_cmd(egui::ViewportCommand::Fullscreen(!is_fullscreen)); } if title_bar_response.drag_started_by(egui::PointerButton::Primary) { @@ -224,9 +237,6 @@ fn window_title_ui(ui: &mut egui::Ui, title_bar_rect: Rect) { }); // Draw fullscreen button. - let is_fullscreen = ui.ctx().input(|i| { - i.viewport().fullscreen.unwrap_or(false) - }); let fullscreen_icon = if is_fullscreen { ARROWS_IN } else { -- 2.39.5 From 5d0bd0e0c060dabf4ca0edc192f089a4cee7cf00 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 24 Jun 2024 11:52:24 +0300 Subject: [PATCH 05/28] 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"; -- 2.39.5 From fefbe76f9c37ae18285d38b28661f06e4b5e1be0 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 24 Jun 2024 12:07:23 +0300 Subject: [PATCH 06/28] ui: content background and stroke --- src/gui/app.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/gui/app.rs b/src/gui/app.rs index 6b0c22b..ad19a75 100644 --- a/src/gui/app.rs +++ b/src/gui/app.rs @@ -77,6 +77,7 @@ impl App { } else { egui::CentralPanel::default() .frame(egui::Frame { + fill: Colors::fill(), stroke: Stroke::NONE, ..Default::default() }) @@ -105,11 +106,13 @@ impl App { egui::Frame::default() } else { egui::Frame { + fill: Colors::fill(), + stroke: Stroke { width: 1.0, color: egui::Color32::from_gray(220) }, shadow: Shadow { offset: Default::default(), blur: Root::WINDOW_FRAME_MARGIN, spread: 0.5, - color: egui::Color32::from_black_alpha(25), + color: egui::Color32::from_black_alpha(35), }, rounding: Rounding { nw: 8.0, -- 2.39.5 From de53e867e9d3401312d8e76b3f4c07b3b8e9a41a Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 24 Jun 2024 12:56:00 +0300 Subject: [PATCH 07/28] ui: window stroke, fix tabs stroke, modal margin --- src/gui/views/modal.rs | 8 ++++---- src/gui/views/network/content.rs | 17 ++++++++++++++++- src/gui/views/wallets/content.rs | 18 ++++++++++++++++++ src/gui/views/wallets/wallet/content.rs | 14 ++++++++++++++ 4 files changed, 52 insertions(+), 5 deletions(-) diff --git a/src/gui/views/modal.rs b/src/gui/views/modal.rs index a149324..11bb792 100644 --- a/src/gui/views/modal.rs +++ b/src/gui/views/modal.rs @@ -43,7 +43,7 @@ pub struct Modal { impl Modal { /// Margin from [`Modal`] window at top/left/right. - const DEFAULT_MARGIN: f32 = 6.0; + const DEFAULT_MARGIN: f32 = 8.0; /// Maximum width of the content. const DEFAULT_WIDTH: f32 = Root::SIDE_PANEL_WIDTH - (2.0 * Self::DEFAULT_MARGIN); @@ -223,7 +223,7 @@ impl Modal { }; let x_align = View::get_left_inset() - View::get_right_inset(); let y_align = View::get_top_inset() + Self::DEFAULT_MARGIN + if View::is_desktop() { - Root::WINDOW_TITLE_HEIGHT + 8.0 + Root::WINDOW_TITLE_HEIGHT + Root::WINDOW_FRAME_MARGIN } else { 0.0 }; @@ -296,12 +296,12 @@ impl Modal { // Draw title content. let title_resp = ui.allocate_ui_at_rect(rect, |ui| { ui.vertical_centered_justified(|ui| { - ui.add_space(9.0); + ui.add_space(Self::DEFAULT_MARGIN); ui.label(RichText::new(self.title.as_ref().unwrap()) .size(19.0) .color(Colors::title(true)) ); - ui.add_space(8.0); + ui.add_space(Self::DEFAULT_MARGIN); }); }).response; diff --git a/src/gui/views/network/content.rs b/src/gui/views/network/content.rs index 4c00f62..f309c1e 100644 --- a/src/gui/views/network/content.rs +++ b/src/gui/views/network/content.rs @@ -45,6 +45,7 @@ impl Default for NetworkContent { impl NetworkContent { pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { let show_connections = AppConfig::show_connections_network_panel(); + let dual_panel = Root::is_dual_panel_mode(ui); // Show title panel. self.title_ui(ui, show_connections); @@ -61,6 +62,20 @@ impl NetworkContent { top: 6.0, bottom: View::get_bottom_inset() + 5.0, }, + outer_margin: if View::is_desktop() { + Margin { + left: -0.5, + right: if dual_panel { + 0.0 + } else { + -0.5 + }, + top: 0.0, + bottom: -0.5, + } + } else { + Margin::ZERO + }, ..Default::default() }) .show_inside(ui, |ui| { @@ -134,7 +149,7 @@ impl NetworkContent { .show(ui, |ui| { ui.add_space(1.0); ui.vertical_centered(|ui| { - let max_width = if !Root::is_dual_panel_mode(ui) { + let max_width = if !dual_panel { Root::SIDE_PANEL_WIDTH * 1.3 } else { ui.available_width() diff --git a/src/gui/views/wallets/content.rs b/src/gui/views/wallets/content.rs index 72cd959..70c4277 100644 --- a/src/gui/views/wallets/content.rs +++ b/src/gui/views/wallets/content.rs @@ -182,6 +182,24 @@ impl WalletsContent { top: 6.0, bottom: View::get_bottom_inset() + 5.0, }, + outer_margin: if View::is_desktop() { + Margin { + left: if !dual_panel { + -0.5 + } else { + 0.0 + }, + right: if !self.wallet_panel_opened() { + -0.5 + } else { + 0.0 + }, + top: 0.0, + bottom: -0.5, + } + } else { + Margin::ZERO + }, ..Default::default() }) .show_inside(ui, |ui| { diff --git a/src/gui/views/wallets/wallet/content.rs b/src/gui/views/wallets/wallet/content.rs index 3f5da69..4145869 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -133,6 +133,20 @@ impl WalletContent { top: 6.0, bottom: View::get_bottom_inset() + 5.0, }, + outer_margin: if View::is_desktop() { + Margin { + left: if dual_panel { + 0.0 + } else { + -0.5 + }, + right: -0.5, + top: 0.0, + bottom: -0.5, + } + } else { + Margin::ZERO + }, ..Default::default() }) .show_animated_inside(ui, !Self::block_navigation_on_sync(wallet), |ui| { -- 2.39.5 From 6d052576e76c04d58ccd78dfaccbe04ff219e9fd Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 24 Jun 2024 18:45:01 +0300 Subject: [PATCH 08/28] ui: window resize, tabs and creation navigation paddings, title panel hiding --- src/gui/app.rs | 121 ++++++++++++++++++--- src/gui/views/network/content.rs | 24 ++-- src/gui/views/title_panel.rs | 3 +- src/gui/views/views.rs | 3 + src/gui/views/wallets/content.rs | 32 +++--- src/gui/views/wallets/creation/creation.rs | 15 ++- src/gui/views/wallets/wallet/content.rs | 10 +- 7 files changed, 154 insertions(+), 54 deletions(-) diff --git a/src/gui/app.rs b/src/gui/app.rs index ad19a75..0edc8f6 100644 --- a/src/gui/app.rs +++ b/src/gui/app.rs @@ -14,7 +14,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use lazy_static::lazy_static; -use egui::{Align, Context, Layout, Margin, Modifiers, Rect, Rounding, Stroke}; +use egui::{Align, Context, CursorIcon, Layout, Margin, Modifiers, Rect, ResizeDirection, Rounding, Stroke, ViewportCommand}; use egui::epaint::{RectShape, Shadow}; use crate::{AppConfig, built_info}; @@ -57,7 +57,7 @@ impl App { // Handle Close event (on desktop). if ctx.input(|i| i.viewport().close_requested()) { if !self.root.exit_allowed { - ctx.send_viewport_cmd(egui::ViewportCommand::CancelClose); + ctx.send_viewport_cmd(ViewportCommand::CancelClose); Root::show_exit_modal(); } else { ctx.input(|i| { @@ -93,10 +93,93 @@ impl App { inner_margin: Margin::same(Root::WINDOW_FRAME_MARGIN), ..Default::default() }).show(ctx, |ui| { + // Draw resize areas. + Self::resize_area_ui(ui, ResizeDirection::North); + Self::resize_area_ui(ui, ResizeDirection::East); + Self::resize_area_ui(ui, ResizeDirection::South); + Self::resize_area_ui(ui, ResizeDirection::West); + Self::resize_area_ui(ui, ResizeDirection::NorthWest); + Self::resize_area_ui(ui, ResizeDirection::NorthEast); + Self::resize_area_ui(ui, ResizeDirection::SouthEast); + Self::resize_area_ui(ui, ResizeDirection::SouthWest); + // Draw window content. self.custom_window_frame(ui); }); } + /// Draw window resize area. + fn resize_area_ui(ui: &egui::Ui, direction: ResizeDirection) { + let mut rect = ui.max_rect(); + rect.min.y -= Root::WINDOW_FRAME_MARGIN; + rect.min.x -= Root::WINDOW_FRAME_MARGIN; + rect.max.y += Root::WINDOW_FRAME_MARGIN; + rect.max.x += Root::WINDOW_FRAME_MARGIN; + + // Setup area id, cursor and area rect based on direction. + let (id, cursor, rect) = match direction { + ResizeDirection::North => ("n", CursorIcon::ResizeNorth, { + rect.min.x += Root::WINDOW_FRAME_MARGIN; + rect.max.y = rect.min.y + Root::WINDOW_FRAME_MARGIN; + rect.max.x -= Root::WINDOW_FRAME_MARGIN; + rect + }), + ResizeDirection::East => ("e", CursorIcon::ResizeEast, { + rect.min.y += Root::WINDOW_FRAME_MARGIN; + rect.min.x = rect.max.x - Root::WINDOW_FRAME_MARGIN; + rect.max.y -= Root::WINDOW_FRAME_MARGIN; + rect + }), + ResizeDirection::South => ("s", CursorIcon::ResizeSouth, { + rect.min.x += Root::WINDOW_FRAME_MARGIN; + rect.min.y = rect.max.y - Root::WINDOW_FRAME_MARGIN; + rect.max.x -= Root::WINDOW_FRAME_MARGIN; + rect + }), + ResizeDirection::West => ("w", CursorIcon::ResizeWest, { + rect.min.y += Root::WINDOW_FRAME_MARGIN; + rect.max.x = rect.min.x + Root::WINDOW_FRAME_MARGIN; + rect.max.y -= Root::WINDOW_FRAME_MARGIN; + rect + }), + ResizeDirection::NorthWest => ("nw", CursorIcon::ResizeNorthWest, { + rect.max.y = rect.min.y + Root::WINDOW_FRAME_MARGIN; + rect.max.x = rect.max.y + Root::WINDOW_FRAME_MARGIN; + rect + }), + ResizeDirection::NorthEast => ("ne", CursorIcon::ResizeNorthEast, { + rect.min.y += Root::WINDOW_FRAME_MARGIN; + rect.min.x = rect.max.x - Root::WINDOW_FRAME_MARGIN; + rect.max.y = rect.min.y; + rect + }), + ResizeDirection::SouthEast => ("se", CursorIcon::ResizeSouthEast, { + rect.min.y = rect.max.y - Root::WINDOW_FRAME_MARGIN; + rect.min.x = rect.max.x - Root::WINDOW_FRAME_MARGIN; + rect + }), + ResizeDirection::SouthWest => ("sw", CursorIcon::ResizeSouthWest, { + rect.min.y = rect.max.y - Root::WINDOW_FRAME_MARGIN; + rect.max.y = rect.min.y; + rect.max.x = rect.min.x + Root::WINDOW_FRAME_MARGIN; + rect + }), + }; + + // Draw resize area. + let id = egui::Id::new("window_resize").with(id); + let sense = egui::Sense::drag(); + let area_resp = ui.interact(rect, id, sense).on_hover_cursor(cursor); + if area_resp.dragged() { + let current_pos = area_resp.interact_pointer_pos(); + if let Some(pos) = current_pos { + ui.ctx().send_viewport_cmd(ViewportCommand::BeginResize(direction)); + ui.ctx().send_viewport_cmd(ViewportCommand::InnerSize( + pos.to_vec2() + )); + } + } + } + /// Draw custom window frame for desktop. fn custom_window_frame(&mut self, ui: &mut egui::Ui) { let is_fullscreen = ui.ctx().input(|i| { @@ -107,7 +190,7 @@ impl App { } else { egui::Frame { fill: Colors::fill(), - stroke: Stroke { width: 1.0, color: egui::Color32::from_gray(220) }, + stroke: Stroke { width: 1.0, color: egui::Color32::from_gray(210) }, shadow: Shadow { offset: Default::default(), blur: Root::WINDOW_FRAME_MARGIN, @@ -126,10 +209,9 @@ impl App { 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.max.y = rect.min.y + Root::WINDOW_TITLE_HEIGHT; rect }; @@ -167,16 +249,21 @@ impl App { } /// Draw custom window title content. - fn window_title_ui(&self, ui: &mut egui::Ui, title_bar_rect: Rect) { + fn window_title_ui(&self, ui: &mut egui::Ui, title_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"), + let interact_rect = { + let mut rect = title_rect; + rect.min.y += Root::WINDOW_FRAME_MARGIN; + rect + }; + let title_resp = ui.interact( + interact_rect, + egui::Id::new("window_title"), egui::Sense::click_and_drag(), ); @@ -198,7 +285,7 @@ impl App { format!("Grim {}", built_info::PKG_VERSION) }; painter.text( - title_bar_rect.center(), + title_rect.center(), egui::Align2::CENTER_CENTER, title_text, egui::FontId::proportional(15.0), @@ -206,15 +293,15 @@ impl App { ); // 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_resp.double_clicked() { + ui.ctx().send_viewport_cmd(ViewportCommand::Fullscreen(!is_fullscreen)); } - if title_bar_response.drag_started_by(egui::PointerButton::Primary) { - ui.ctx().send_viewport_cmd(egui::ViewportCommand::StartDrag); + if title_resp.drag_started_by(egui::PointerButton::Primary) { + ui.ctx().send_viewport_cmd(ViewportCommand::StartDrag); } - ui.allocate_ui_at_rect(title_bar_rect, |ui| { + ui.allocate_ui_at_rect(title_rect, |ui| { ui.with_layout(Layout::right_to_left(Align::Center), |ui| { // Draw button to close window. View::title_button_small(ui, X, |_| { @@ -228,12 +315,12 @@ impl App { ARROWS_OUT }; View::title_button_small(ui, fullscreen_icon, |ui| { - ui.ctx().send_viewport_cmd(egui::ViewportCommand::Fullscreen(!is_fullscreen)); + ui.ctx().send_viewport_cmd(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)); + ui.ctx().send_viewport_cmd(ViewportCommand::Minimized(true)); }); // Draw application icon. diff --git a/src/gui/views/network/content.rs b/src/gui/views/network/content.rs index f309c1e..a0eda88 100644 --- a/src/gui/views/network/content.rs +++ b/src/gui/views/network/content.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use egui::{Margin, RichText, ScrollArea, Stroke}; +use egui::{Margin, RichText, ScrollArea}; use egui::scroll_area::ScrollBarVisibility; use crate::AppConfig; @@ -48,7 +48,7 @@ impl NetworkContent { let dual_panel = Root::is_dual_panel_mode(ui); // Show title panel. - self.title_ui(ui, show_connections); + self.title_ui(ui, dual_panel || Root::is_network_panel_open(), show_connections); // Show integrated node tabs content. if !show_connections { @@ -57,10 +57,10 @@ impl NetworkContent { .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, + left: View::get_left_inset() + View::TAB_ITEMS_PADDING, + right: View::far_right_inset_margin(ui) + View::TAB_ITEMS_PADDING, + top: View::TAB_ITEMS_PADDING, + bottom: View::get_bottom_inset() + View::TAB_ITEMS_PADDING, }, outer_margin: if View::is_desktop() { Margin { @@ -115,11 +115,7 @@ impl NetworkContent { // Show connections content. egui::CentralPanel::default() .frame(egui::Frame { - stroke: if show_connections { - View::item_stroke() - } else { - Stroke::NONE - }, + stroke: View::item_stroke(), inner_margin: Margin { left: if show_connections { View::get_left_inset() + 4.0 @@ -171,7 +167,7 @@ impl NetworkContent { fn tabs_ui(&mut self, ui: &mut egui::Ui) { ui.vertical_centered(|ui| { // Setup spacing between tabs. - ui.style_mut().spacing.item_spacing = egui::vec2(6.0, 0.0); + ui.style_mut().spacing.item_spacing = egui::vec2(View::TAB_ITEMS_PADDING, 0.0); // Setup vertical padding inside tab button. ui.style_mut().spacing.button_padding = egui::vec2(0.0, 4.0); @@ -203,7 +199,7 @@ impl NetworkContent { } /// Draw title content. - fn title_ui(&mut self, ui: &mut egui::Ui, show_connections: bool) { + fn title_ui(&mut self, ui: &mut egui::Ui, show_panel: bool, show_connections: bool) { // Setup values for title panel. let title_text = self.node_tab_content.get_type().title().to_uppercase(); let subtitle_text = Node::get_sync_status_text(); @@ -215,7 +211,7 @@ impl NetworkContent { }; // Draw title panel. - TitlePanel::ui(TitleType::Single(title_content), |ui| { + TitlePanel::ui(TitleType::Single(title_content), show_panel, |ui| { if !show_connections { View::title_button_big(ui, DOTS_THREE_OUTLINE_VERTICAL, |_| { AppConfig::toggle_show_connections_network_panel(); diff --git a/src/gui/views/title_panel.rs b/src/gui/views/title_panel.rs index c98a4be..31fa664 100644 --- a/src/gui/views/title_panel.rs +++ b/src/gui/views/title_panel.rs @@ -27,6 +27,7 @@ impl TitlePanel { pub const DEFAULT_HEIGHT: f32 = 54.0; pub fn ui(title: TitleType, + show: bool, mut left_content: impl FnMut(&mut egui::Ui), mut right_content: impl FnMut(&mut egui::Ui), ui: &mut egui::Ui) { @@ -57,7 +58,7 @@ impl TitlePanel { fill: Colors::yellow(), ..Default::default() }) - .show_inside(ui, |ui| { + .show_animated_inside(ui, show, |ui| { StripBuilder::new(ui) .size(Size::exact(Self::DEFAULT_HEIGHT)) .size(if dual_title { diff --git a/src/gui/views/views.rs b/src/gui/views/views.rs index 176a4b5..64c2a76 100644 --- a/src/gui/views/views.rs +++ b/src/gui/views/views.rs @@ -218,6 +218,9 @@ impl View { }); } + /// Padding for tab items. + pub const TAB_ITEMS_PADDING: f32 = 5.0; + /// Tab button with white background fill color, contains only icon. pub fn tab_button(ui: &mut egui::Ui, icon: &str, active: bool, action: impl FnOnce()) { ui.scope(|ui| { diff --git a/src/gui/views/wallets/content.rs b/src/gui/views/wallets/content.rs index 70c4277..a434632 100644 --- a/src/gui/views/wallets/content.rs +++ b/src/gui/views/wallets/content.rs @@ -103,10 +103,13 @@ impl WalletsContent { let wallet_panel_width = self.wallet_panel_width(ui, empty_list, dual_panel, show_wallet); let content_width = ui.available_width(); + // Flag to check if wallet list is hidden on the screen. + let list_hidden = content_width == 0.0 || empty_list || create_wallet + || (dual_panel && show_wallet && !self.show_wallets_at_dual_panel) + || (!dual_panel && show_wallet) || (!dual_panel && Root::is_network_panel_open()); + // Show title panel. - View::max_width_ui(ui, ui.available_width(), |ui| { - self.title_ui(ui, dual_panel, create_wallet, show_wallet); - }); + self.title_ui(ui, dual_panel, create_wallet, show_wallet); // Show wallet panel content. egui::SidePanel::right("wallet_panel") @@ -161,15 +164,9 @@ impl WalletsContent { } }); - // Flag to check if wallet list is hidden on the screen. - let list_hidden = content_width == 0.0 || empty_list || create_wallet - || (dual_panel && show_wallet && !self.show_wallets_at_dual_panel) - || (!dual_panel && show_wallet); - // Setup flag to show wallets bottom panel if wallet is not showing // at non-dual panel mode and network is no open or showing at dual panel mode. - let show_bottom_panel = !list_hidden && - ((!show_wallet && !dual_panel && !Root::is_network_panel_open()) || dual_panel); + let show_bottom_panel = !list_hidden || dual_panel; // Show wallets bottom panel. if show_bottom_panel { @@ -177,10 +174,10 @@ impl WalletsContent { .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, + left: View::get_left_inset() + View::TAB_ITEMS_PADDING, + right: View::far_right_inset_margin(ui) + View::TAB_ITEMS_PADDING, + top: View::TAB_ITEMS_PADDING, + bottom: View::get_bottom_inset() + View::TAB_ITEMS_PADDING, }, outer_margin: if View::is_desktop() { Margin { @@ -204,7 +201,7 @@ impl WalletsContent { }) .show_inside(ui, |ui| { // Setup spacing between tabs. - ui.style_mut().spacing.item_spacing = egui::vec2(6.0, 0.0); + ui.style_mut().spacing.item_spacing = egui::vec2(View::TAB_ITEMS_PADDING, 0.0); // Setup vertical padding inside buttons. ui.style_mut().spacing.button_padding = egui::vec2(10.0, 4.0); @@ -235,6 +232,9 @@ impl WalletsContent { } }) .show_inside(ui, |ui| { + if list_hidden { + return; + } // Update ui after 1 sec at single panel mode. if !dual_panel { ui.ctx().request_repaint_after(Duration::from_millis(1000)); @@ -300,7 +300,7 @@ impl WalletsContent { }; // Draw title panel. - TitlePanel::ui(title_content, |ui| { + TitlePanel::ui(title_content, dual_panel || !Root::is_network_panel_open(), |ui| { if show_wallet && !dual_panel { View::title_button_big(ui, ARROW_LEFT, |_| { self.wallets.select(None); diff --git a/src/gui/views/wallets/creation/creation.rs b/src/gui/views/wallets/creation/creation.rs index 59e14f6..91681c5 100644 --- a/src/gui/views/wallets/creation/creation.rs +++ b/src/gui/views/wallets/creation/creation.rs @@ -73,13 +73,26 @@ impl WalletCreation { egui::TopBottomPanel::bottom("wallet_creation_step_panel") .frame(egui::Frame { fill: Colors::fill(), - stroke: View::default_stroke(), inner_margin: Margin { left: View::far_left_inset_margin(ui) + 8.0, right: View::get_right_inset() + 8.0, top: 4.0, bottom: View::get_bottom_inset(), }, + outer_margin: if View::is_desktop() { + Margin { + left: if !Root::is_dual_panel_mode(ui) { + -0.5 + } else { + 0.0 + }, + right: -0.5, + top: 0.0, + bottom: -0.5, + } + } else { + Margin::ZERO + }, ..Default::default() }) .show_inside(ui, |ui| { diff --git a/src/gui/views/wallets/wallet/content.rs b/src/gui/views/wallets/wallet/content.rs index 4145869..656b8c7 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -128,10 +128,10 @@ impl WalletContent { .frame(egui::Frame { fill: Colors::fill(), inner_margin: Margin { - left: View::far_left_inset_margin(ui) + 4.0, - right: View::get_right_inset() + 4.0, - top: 6.0, - bottom: View::get_bottom_inset() + 5.0, + left: View::far_left_inset_margin(ui) + View::TAB_ITEMS_PADDING, + right: View::get_right_inset() + View::TAB_ITEMS_PADDING, + top: View::TAB_ITEMS_PADDING, + bottom: View::get_bottom_inset() + View::TAB_ITEMS_PADDING, }, outer_margin: if View::is_desktop() { Margin { @@ -526,7 +526,7 @@ impl WalletContent { fn tabs_ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet) { ui.scope(|ui| { // Setup spacing between tabs. - ui.style_mut().spacing.item_spacing = egui::vec2(6.0, 0.0); + ui.style_mut().spacing.item_spacing = egui::vec2(View::TAB_ITEMS_PADDING, 0.0); // Setup vertical padding inside tab button. ui.style_mut().spacing.button_padding = egui::vec2(0.0, 4.0); -- 2.39.5 From c3c53ac003524cc75d09ba77b9b6bace925479a2 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 24 Jun 2024 18:56:22 +0300 Subject: [PATCH 09/28] fix: wallets list hiding --- src/gui/views/wallets/content.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/gui/views/wallets/content.rs b/src/gui/views/wallets/content.rs index a434632..19a0eae 100644 --- a/src/gui/views/wallets/content.rs +++ b/src/gui/views/wallets/content.rs @@ -106,7 +106,8 @@ impl WalletsContent { // Flag to check if wallet list is hidden on the screen. let list_hidden = content_width == 0.0 || empty_list || create_wallet || (dual_panel && show_wallet && !self.show_wallets_at_dual_panel) - || (!dual_panel && show_wallet) || (!dual_panel && Root::is_network_panel_open()); + || (!dual_panel && show_wallet) || + (!Root::is_dual_panel_mode(ui) && Root::is_network_panel_open()); // Show title panel. self.title_ui(ui, dual_panel, create_wallet, show_wallet); @@ -300,7 +301,8 @@ impl WalletsContent { }; // Draw title panel. - TitlePanel::ui(title_content, dual_panel || !Root::is_network_panel_open(), |ui| { + let show_title = Root::is_dual_panel_mode(ui) || !Root::is_network_panel_open(); + TitlePanel::ui(title_content, show_title, |ui| { if show_wallet && !dual_panel { View::title_button_big(ui, ARROW_LEFT, |_| { self.wallets.select(None); -- 2.39.5 From 735e9ad94dd32b6b9a47b1ef7ac733bd40316479 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Tue, 25 Jun 2024 16:48:44 +0300 Subject: [PATCH 10/28] mnemonic: trim input word --- src/gui/views/wallets/creation/mnemonic.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/views/wallets/creation/mnemonic.rs b/src/gui/views/wallets/creation/mnemonic.rs index 7100ae1..f243a45 100644 --- a/src/gui/views/wallets/creation/mnemonic.rs +++ b/src/gui/views/wallets/creation/mnemonic.rs @@ -304,6 +304,8 @@ impl MnemonicSetup { columns[1].vertical_centered_justified(|ui| { // Callback to save the word. let mut save = || { + self.word_edit = self.word_edit.trim().to_string(); + // Check if word is valid. let word_index = self.word_num_edit - 1; if !self.mnemonic.is_valid_word(&self.word_edit, word_index) { -- 2.39.5 From 2469d3f02199e045778b618ddb1baff5fb569aba Mon Sep 17 00:00:00 2001 From: ardocrat Date: Thu, 27 Jun 2024 12:30:27 +0300 Subject: [PATCH 11/28] ui: window resize, update egui (waiting for window dragging/resizing patch), pull to refresh widget, colors optimization, min window size, root panels switch optimization --- Cargo.lock | 168 ++++------- Cargo.toml | 10 +- src/gui/app.rs | 351 +++++++++++----------- src/gui/colors.rs | 8 +- src/gui/views/mod.rs | 5 +- src/gui/views/modal.rs | 4 +- src/gui/views/network/content.rs | 57 ++-- src/gui/views/pull_to_refresh.rs | 370 ++++++++++++++++++++++++ src/gui/views/qr.rs | 4 +- src/gui/views/root.rs | 24 +- src/gui/views/title_panel.rs | 22 +- src/gui/views/views.rs | 1 + src/gui/views/wallets/content.rs | 50 ++-- src/gui/views/wallets/wallet/content.rs | 46 +-- src/gui/views/wallets/wallet/txs.rs | 3 +- src/lib.rs | 2 +- src/main.rs | 4 +- src/settings/config.rs | 15 +- 18 files changed, 755 insertions(+), 389 deletions(-) create mode 100644 src/gui/views/pull_to_refresh.rs diff --git a/Cargo.lock b/Cargo.lock index 36585ce..8342c94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,10 +23,6 @@ name = "accesskit" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74a4b14f3d99c1255dcba8f45621ab1a2e7540a0009652d33989005a4d0bfc6b" -dependencies = [ - "enumn", - "serde", -] [[package]] name = "accesskit_consumer" @@ -199,7 +195,6 @@ dependencies = [ "cfg-if 1.0.0", "getrandom 0.2.15", "once_cell", - "serde", "version_check", "zerocopy", ] @@ -1698,36 +1693,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "cocoa" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" -dependencies = [ - "bitflags 1.3.2", - "block", - "cocoa-foundation", - "core-foundation 0.9.4", - "core-graphics", - "foreign-types 0.5.0", - "libc", - "objc", -] - -[[package]] -name = "cocoa-foundation" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" -dependencies = [ - "bitflags 1.3.2", - "block", - "core-foundation 0.9.4", - "core-graphics-types", - "libc", - "objc", -] - [[package]] name = "codespan-reporting" version = "0.11.1" @@ -2107,12 +2072,12 @@ dependencies = [ [[package]] name = "d3d12" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e3d747f100290a1ca24b752186f61f6637e1deffe3bf6320de6fcb29510a307" +checksum = "b28bfe653d79bd16c77f659305b195b82bb5ce0c0eb2a4846b82ddbd77586813" dependencies = [ "bitflags 2.5.0", - "libloading 0.8.3", + "libloading 0.7.4", "winapi 0.3.9", ] @@ -2582,11 +2547,10 @@ dependencies = [ [[package]] name = "ecolor" version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20930a432bbd57a6d55e07976089708d4893f3d556cf42a0d79e9e321fa73b10" +source = "git+https://github.com/emilk/egui/?rev=10571e9da5b3658964adc8b85c7e3f8c1353e0db#10571e9da5b3658964adc8b85c7e3f8c1353e0db" dependencies = [ "bytemuck", - "serde", + "emath", ] [[package]] @@ -2653,11 +2617,10 @@ dependencies = [ [[package]] name = "eframe" version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020e2ccef6bbcec71dbc542f7eed64a5846fc3076727f5746da8fd307c91bab2" +source = "git+https://github.com/emilk/egui/?rev=10571e9da5b3658964adc8b85c7e3f8c1353e0db#10571e9da5b3658964adc8b85c7e3f8c1353e0db" dependencies = [ + "ahash 0.8.11", "bytemuck", - "cocoa", "document-features", "egui", "egui-wgpu", @@ -2666,17 +2629,18 @@ dependencies = [ "glow", "glutin", "glutin-winit", - "image 0.24.9", + "image 0.25.1", "js-sys", "log", - "objc", + "objc2 0.5.2", + "objc2-app-kit", + "objc2-foundation", "parking_lot 0.12.3", "percent-encoding", "pollster", "raw-window-handle 0.5.2", "raw-window-handle 0.6.2", "static_assertions", - "thiserror", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -2689,23 +2653,22 @@ dependencies = [ [[package]] name = "egui" version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "584c5d1bf9a67b25778a3323af222dbe1a1feb532190e103901187f92c7fe29a" +source = "git+https://github.com/emilk/egui/?rev=10571e9da5b3658964adc8b85c7e3f8c1353e0db#10571e9da5b3658964adc8b85c7e3f8c1353e0db" dependencies = [ "accesskit", "ahash 0.8.11", + "emath", "epaint", "log", "nohash-hasher", - "serde", ] [[package]] name = "egui-wgpu" version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469ff65843f88a702b731a1532b7d03b0e8e96d283e70f3a22b0e06c46cb9b37" +source = "git+https://github.com/emilk/egui/?rev=10571e9da5b3658964adc8b85c7e3f8c1353e0db#10571e9da5b3658964adc8b85c7e3f8c1353e0db" dependencies = [ + "ahash 0.8.11", "bytemuck", "document-features", "egui", @@ -2721,10 +2684,10 @@ dependencies = [ [[package]] name = "egui-winit" version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e3da0cbe020f341450c599b35b92de4af7b00abde85624fd16f09c885573609" +source = "git+https://github.com/emilk/egui/?rev=10571e9da5b3658964adc8b85c7e3f8c1353e0db#10571e9da5b3658964adc8b85c7e3f8c1353e0db" dependencies = [ "accesskit_winit", + "ahash 0.8.11", "arboard", "egui", "log", @@ -2738,24 +2701,23 @@ dependencies = [ [[package]] name = "egui_extras" version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b78779f35ded1a853786c9ce0b43fe1053e10a21ea3b23ebea411805ce41593" +source = "git+https://github.com/emilk/egui/?rev=10571e9da5b3658964adc8b85c7e3f8c1353e0db#10571e9da5b3658964adc8b85c7e3f8c1353e0db" dependencies = [ + "ahash 0.8.11", "egui", "enum-map", - "image 0.24.9", + "image 0.25.1", "log", "mime_guess2", "resvg", - "serde", ] [[package]] name = "egui_glow" version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0e5d975f3c86edc3d35b1db88bb27c15dde7c55d3b5af164968ab5ede3f44ca" +source = "git+https://github.com/emilk/egui/?rev=10571e9da5b3658964adc8b85c7e3f8c1353e0db#10571e9da5b3658964adc8b85c7e3f8c1353e0db" dependencies = [ + "ahash 0.8.11", "bytemuck", "egui", "glow", @@ -2766,15 +2728,6 @@ dependencies = [ "winit", ] -[[package]] -name = "egui_pull_to_refresh" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c649d50513fc760df6d6ad74b739189f2768e81df3ec734f1f280bcf68e33c75" -dependencies = [ - "egui", -] - [[package]] name = "either" version = "1.12.0" @@ -2803,11 +2756,9 @@ dependencies = [ [[package]] name = "emath" version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4c3a552cfca14630702449d35f41c84a0d15963273771c6059175a803620f3f" +source = "git+https://github.com/emilk/egui/?rev=10571e9da5b3658964adc8b85c7e3f8c1353e0db#10571e9da5b3658964adc8b85c7e3f8c1353e0db" dependencies = [ "bytemuck", - "serde", ] [[package]] @@ -2895,17 +2846,6 @@ dependencies = [ "syn 2.0.66", ] -[[package]] -name = "enumn" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" -dependencies = [ - "proc-macro2 1.0.85", - "quote 1.0.36", - "syn 2.0.66", -] - [[package]] name = "env_filter" version = "0.1.0" @@ -2981,8 +2921,7 @@ dependencies = [ [[package]] name = "epaint" version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b381f8b149657a4acf837095351839f32cd5c4aec1817fc4df84e18d76334176" +source = "git+https://github.com/emilk/egui/?rev=10571e9da5b3658964adc8b85c7e3f8c1353e0db#10571e9da5b3658964adc8b85c7e3f8c1353e0db" dependencies = [ "ab_glyph", "ahash 0.8.11", @@ -2992,7 +2931,6 @@ dependencies = [ "log", "nohash-hasher", "parking_lot 0.12.3", - "serde", ] [[package]] @@ -3808,9 +3746,9 @@ dependencies = [ [[package]] name = "gpu-descriptor" -version = "0.2.4" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c" +checksum = "9c08c1f623a8d0b722b8b99f821eb0ba672a1618f0d3b16ddbee1cedd2dd8557" dependencies = [ "bitflags 2.5.0", "gpu-descriptor-types", @@ -3819,9 +3757,9 @@ dependencies = [ [[package]] name = "gpu-descriptor-types" -version = "0.1.2" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bf0b36e6f090b7e1d8a4b49c0cb81c1f8376f72198c65dd3ad9ff3556b8b78c" +checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" dependencies = [ "bitflags 2.5.0", ] @@ -3844,7 +3782,6 @@ dependencies = [ "eframe", "egui", "egui_extras", - "egui_pull_to_refresh", "env_logger 0.11.3", "eye", "fs-mistrust", @@ -4909,7 +4846,6 @@ dependencies = [ "byteorder", "color_quant", "num-traits 0.2.19", - "png", ] [[package]] @@ -5613,9 +5549,9 @@ dependencies = [ [[package]] name = "metal" -version = "0.27.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43f73953f8cbe511f021b58f18c3ce1c3d1ae13fe953293e13345bf83217f25" +checksum = "5637e166ea14be6063a3f8ba5ccb9a4159df7d8f6d61c02fc3d480b1f90dcfcb" dependencies = [ "bitflags 2.5.0", "block", @@ -5775,10 +5711,11 @@ dependencies = [ [[package]] name = "naga" -version = "0.19.2" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e3524642f53d9af419ab5e8dd29d3ba155708267667c2f3f06c88c9e130843" +checksum = "e536ae46fcab0876853bd4a632ede5df4b1c2527a58f6c5a4150fe86be858231" dependencies = [ + "arrayvec 0.7.4", "bit-set", "bitflags 2.5.0", "codespan-reporting", @@ -6211,7 +6148,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" dependencies = [ "malloc_buf", - "objc_exception", ] [[package]] @@ -6366,15 +6302,6 @@ dependencies = [ "objc2-metal", ] -[[package]] -name = "objc_exception" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" -dependencies = [ - "cc", -] - [[package]] name = "objc_id" version = "0.1.1" @@ -11004,17 +10931,18 @@ dependencies = [ [[package]] name = "webbrowser" -version = "0.8.15" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db67ae75a9405634f5882791678772c94ff5f16a66535aae186e26aa0841fc8b" +checksum = "425ba64c1e13b1c6e8c5d2541c8fac10022ca584f33da781db01b5756aef1f4e" dependencies = [ + "block2 0.5.1", "core-foundation 0.9.4", "home", "jni", "log", "ndk-context", - "objc", - "raw-window-handle 0.5.2", + "objc2 0.5.2", + "objc2-foundation", "url", "web-sys", ] @@ -11056,13 +10984,14 @@ checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" [[package]] name = "wgpu" -version = "0.19.4" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbd7311dbd2abcfebaabf1841a2824ed7c8be443a0f29166e5d3c6a53a762c01" +checksum = "90e37c7b9921b75dfd26dd973fdcbce36f13dfa6e2dc82aece584e0ed48c355c" dependencies = [ "arrayvec 0.7.4", "cfg-if 1.0.0", "cfg_aliases", + "document-features", "js-sys", "log", "naga", @@ -11081,15 +11010,16 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "0.19.4" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b94525fc99ba9e5c9a9e24764f2bc29bad0911a7446c12f446a8277369bf3a" +checksum = "d59e0d5fc509601c69e4e1fa06c1eb3c4c9f12956a5e30c79b61ef1c1be7daf0" dependencies = [ "arrayvec 0.7.4", "bit-vec", "bitflags 2.5.0", "cfg_aliases", "codespan-reporting", + "document-features", "indexmap 2.2.6", "log", "naga", @@ -11107,9 +11037,9 @@ dependencies = [ [[package]] name = "wgpu-hal" -version = "0.19.4" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1a4924366df7ab41a5d8546d6534f1f33231aa5b3f72b9930e300f254e39c3" +checksum = "6aa24c3889f885a3fb9133b454c8418bfcfaadcfe4ed3be96ac80e76703b863b" dependencies = [ "android_system_properties", "arrayvec 0.7.4", @@ -11129,7 +11059,7 @@ dependencies = [ "js-sys", "khronos-egl", "libc", - "libloading 0.8.3", + "libloading 0.7.4", "log", "metal", "naga", @@ -11152,9 +11082,9 @@ dependencies = [ [[package]] name = "wgpu-types" -version = "0.19.2" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b671ff9fb03f78b46ff176494ee1ebe7d603393f42664be55b64dc8d53969805" +checksum = "1353d9a46bff7f955a680577f34c69122628cc2076e1d6f3a9be6ef00ae793ef" dependencies = [ "bitflags 2.5.0", "js-sys", diff --git a/Cargo.toml b/Cargo.toml index ac6a081..47444fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ build = "src/build/build.rs" [lib] name="grim" +crate_type=["cdylib", "rlib"] [dependencies] log = "0.4" @@ -37,7 +38,6 @@ grin_wallet_controller = "5.3.1" egui = { version = "0.27.2", default-features = false } egui_extras = { version = "0.27.2", features = ["image", "svg"] } rust-i18n = "2.3.1" -egui_pull_to_refresh = "0.4.0" ## other thiserror = "1.0.58" @@ -110,7 +110,7 @@ dark-light = "1.1.1" android_logger = "0.13.1" jni = "0.21.1" android-activity = { version = "0.6.0", features = ["game-activity"] } -wgpu = "0.19.1" +wgpu = "0.20.1" winit = { version = "0.29.15", features = ["android-game-activity"] } eframe = { version = "0.27.2", features = ["wgpu", "android-game-activity"] } @@ -120,4 +120,8 @@ eframe = { version = "0.27.2", features = ["wgpu", "android-game-activity"] } #grin_store = { path = "../grin-store" } [patch.crates-io] ### fix cross-compilation support for macos -openpnp_capture_sys = { git = "https://github.com/ardocrat/openpnp-capture-rs", branch = "cross_compilation_support" } \ No newline at end of file +openpnp_capture_sys = { git = "https://github.com/ardocrat/openpnp-capture-rs", branch = "cross_compilation_support" } +### actual version +egui = { git = "https://github.com/emilk/egui/", rev="10571e9da5b3658964adc8b85c7e3f8c1353e0db" } +egui_extras = { git = "https://github.com/emilk/egui/", rev="10571e9da5b3658964adc8b85c7e3f8c1353e0db" } +eframe = { git = "https://github.com/emilk/egui/", rev="10571e9da5b3658964adc8b85c7e3f8c1353e0db" } \ No newline at end of file diff --git a/src/gui/app.rs b/src/gui/app.rs index 0edc8f6..8fb1758 100644 --- a/src/gui/app.rs +++ b/src/gui/app.rs @@ -14,14 +14,14 @@ use std::sync::atomic::{AtomicBool, Ordering}; use lazy_static::lazy_static; -use egui::{Align, Context, CursorIcon, Layout, Margin, Modifiers, Rect, ResizeDirection, Rounding, Stroke, ViewportCommand}; -use egui::epaint::{RectShape, Shadow}; +use egui::{Align, Context, CursorIcon, Layout, Modifiers, Rect, ResizeDirection, Rounding, Stroke, ViewportCommand}; +use egui::epaint::{RectShape}; 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, View}; +use crate::gui::views::{Root, TitlePanel, View}; lazy_static! { /// State to check if platform Back button was pressed. @@ -32,13 +32,17 @@ lazy_static! { pub struct App { /// Platform specific callbacks handler. pub(crate) platform: Platform, + /// Main ui content. - root: Root + root: Root, + + /// Last window resize direction. + resize_direction: Option } impl App { pub fn new(platform: Platform) -> Self { - Self { platform, root: Root::default() } + Self { platform, root: Root::default(), resize_direction: None } } /// Draw application content. @@ -72,180 +76,130 @@ impl App { } // Show main content with custom frame on desktop. - if View::is_desktop() { - self.window_frame_ui(ctx); - } else { - egui::CentralPanel::default() - .frame(egui::Frame { - fill: Colors::fill(), - stroke: Stroke::NONE, - ..Default::default() - }) - .show(ctx, |ui| { + egui::CentralPanel::default() + .frame(egui::Frame { + ..Default::default() + }) + .show(ctx, |ui| { + if View::is_desktop() { + self.desktop_window_ui(ui); + } else { self.root.ui(ui, &self.platform); - }); - } + } + }); } - /// 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| { - // Draw resize areas. - Self::resize_area_ui(ui, ResizeDirection::North); - Self::resize_area_ui(ui, ResizeDirection::East); - Self::resize_area_ui(ui, ResizeDirection::South); - Self::resize_area_ui(ui, ResizeDirection::West); - Self::resize_area_ui(ui, ResizeDirection::NorthWest); - Self::resize_area_ui(ui, ResizeDirection::NorthEast); - Self::resize_area_ui(ui, ResizeDirection::SouthEast); - Self::resize_area_ui(ui, ResizeDirection::SouthWest); - // Draw window content. - self.custom_window_frame(ui); + /// Draw custom resizeable window content. + fn desktop_window_ui(&mut self, ui: &mut egui::Ui) { + let title_stroke_rect = { + let mut rect = ui.max_rect().shrink(Root::WINDOW_FRAME_MARGIN); + rect.max.y = Root::WINDOW_FRAME_MARGIN + Root::WINDOW_TITLE_HEIGHT + + TitlePanel::DEFAULT_HEIGHT + 0.5; + rect + }; + let title_stroke = RectShape { + rect: title_stroke_rect, + rounding: Rounding { + nw: 8.0, + ne: 8.0, + sw: 0.0, + se: 0.0, + }, + fill: Colors::TRANSPARENT, + stroke: Stroke { + width: 1.0, + color: egui::Color32::from_gray(200) + }, + blur_width: 0.0, + fill_texture_id: Default::default(), + uv: Rect::ZERO + }; + // Draw title stroke. + ui.painter().add(title_stroke); + + let content_stroke_rect = { + let mut rect = ui.max_rect().shrink(Root::WINDOW_FRAME_MARGIN); + let top = Root::WINDOW_TITLE_HEIGHT + TitlePanel::DEFAULT_HEIGHT + 0.5; + rect.min += egui::vec2(0.0, top); + rect + }; + let content_stroke = RectShape { + rect: content_stroke_rect, + rounding: Rounding::ZERO, + fill: Colors::TRANSPARENT, + stroke: Stroke { + width: 1.0, + color: Colors::stroke() + }, + blur_width: 0.0, + fill_texture_id: Default::default(), + uv: Rect::ZERO + }; + // Draw content stroke. + ui.painter().add(content_stroke); + + // Draw window content. + let content_rect = ui.max_rect().shrink(Root::WINDOW_FRAME_MARGIN); + ui.allocate_ui_at_rect(content_rect, |ui| { + self.window_content(ui); }); + + // Setup resize areas. + self.resize_area_ui(ui, ResizeDirection::North); + self.resize_area_ui(ui, ResizeDirection::East); + self.resize_area_ui(ui, ResizeDirection::South); + self.resize_area_ui(ui, ResizeDirection::West); + self.resize_area_ui(ui, ResizeDirection::NorthWest); + self.resize_area_ui(ui, ResizeDirection::NorthEast); + self.resize_area_ui(ui, ResizeDirection::SouthEast); + self.resize_area_ui(ui, ResizeDirection::SouthWest); } - /// Draw window resize area. - fn resize_area_ui(ui: &egui::Ui, direction: ResizeDirection) { - let mut rect = ui.max_rect(); - rect.min.y -= Root::WINDOW_FRAME_MARGIN; - rect.min.x -= Root::WINDOW_FRAME_MARGIN; - rect.max.y += Root::WINDOW_FRAME_MARGIN; - rect.max.x += Root::WINDOW_FRAME_MARGIN; + /// Draw window content for desktop. + fn window_content(&mut self, ui: &mut egui::Ui) { + let content_rect = ui.max_rect(); - // Setup area id, cursor and area rect based on direction. - let (id, cursor, rect) = match direction { - ResizeDirection::North => ("n", CursorIcon::ResizeNorth, { - rect.min.x += Root::WINDOW_FRAME_MARGIN; - rect.max.y = rect.min.y + Root::WINDOW_FRAME_MARGIN; - rect.max.x -= Root::WINDOW_FRAME_MARGIN; - rect - }), - ResizeDirection::East => ("e", CursorIcon::ResizeEast, { - rect.min.y += Root::WINDOW_FRAME_MARGIN; - rect.min.x = rect.max.x - Root::WINDOW_FRAME_MARGIN; - rect.max.y -= Root::WINDOW_FRAME_MARGIN; - rect - }), - ResizeDirection::South => ("s", CursorIcon::ResizeSouth, { - rect.min.x += Root::WINDOW_FRAME_MARGIN; - rect.min.y = rect.max.y - Root::WINDOW_FRAME_MARGIN; - rect.max.x -= Root::WINDOW_FRAME_MARGIN; - rect - }), - ResizeDirection::West => ("w", CursorIcon::ResizeWest, { - rect.min.y += Root::WINDOW_FRAME_MARGIN; - rect.max.x = rect.min.x + Root::WINDOW_FRAME_MARGIN; - rect.max.y -= Root::WINDOW_FRAME_MARGIN; - rect - }), - ResizeDirection::NorthWest => ("nw", CursorIcon::ResizeNorthWest, { - rect.max.y = rect.min.y + Root::WINDOW_FRAME_MARGIN; - rect.max.x = rect.max.y + Root::WINDOW_FRAME_MARGIN; - rect - }), - ResizeDirection::NorthEast => ("ne", CursorIcon::ResizeNorthEast, { - rect.min.y += Root::WINDOW_FRAME_MARGIN; - rect.min.x = rect.max.x - Root::WINDOW_FRAME_MARGIN; - rect.max.y = rect.min.y; - rect - }), - ResizeDirection::SouthEast => ("se", CursorIcon::ResizeSouthEast, { - rect.min.y = rect.max.y - Root::WINDOW_FRAME_MARGIN; - rect.min.x = rect.max.x - Root::WINDOW_FRAME_MARGIN; - rect - }), - ResizeDirection::SouthWest => ("sw", CursorIcon::ResizeSouthWest, { - rect.min.y = rect.max.y - Root::WINDOW_FRAME_MARGIN; - rect.max.y = rect.min.y; - rect.max.x = rect.min.x + Root::WINDOW_FRAME_MARGIN; - rect - }), + let window_title_rect = { + let mut rect = content_rect; + rect.max.y = rect.min.y + Root::WINDOW_TITLE_HEIGHT; + rect }; - // Draw resize area. - let id = egui::Id::new("window_resize").with(id); - let sense = egui::Sense::drag(); - let area_resp = ui.interact(rect, id, sense).on_hover_cursor(cursor); - if area_resp.dragged() { - let current_pos = area_resp.interact_pointer_pos(); - if let Some(pos) = current_pos { - ui.ctx().send_viewport_cmd(ViewportCommand::BeginResize(direction)); - ui.ctx().send_viewport_cmd(ViewportCommand::InnerSize( - pos.to_vec2() - )); - } - } - } - - /// 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 { - fill: Colors::fill(), - stroke: Stroke { width: 1.0, color: egui::Color32::from_gray(210) }, - shadow: Shadow { - offset: Default::default(), - blur: Root::WINDOW_FRAME_MARGIN, - spread: 0.5, - color: egui::Color32::from_black_alpha(35), - }, - rounding: Rounding { + + 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, - }, - ..Default::default() - } + } + }, + fill: Colors::yellow_dark(), + stroke: Stroke::NONE, + blur_width: 0.0, + fill_texture_id: Default::default(), + uv: Rect::ZERO }; - egui::CentralPanel::default().frame(panel_frame).show_inside(ui, |ui| { - let app_rect = ui.max_rect(); + ui.painter().add(window_title_bg); - let window_title_rect = { - let mut rect = app_rect; - rect.max.y = rect.min.y + Root::WINDOW_TITLE_HEIGHT; - rect - }; + // Draw window title. + self.window_title_ui(ui, window_title_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); - }); + let content_rect = { + let mut rect = content_rect; + rect.min.y = window_title_rect.max.y; + rect + }; + // Draw main content. + let mut content_ui = ui.child_ui(content_rect, *ui.layout(), None); + self.root.ui(&mut content_ui, &self.platform); } /// Draw custom window title content. @@ -289,7 +243,7 @@ impl App { egui::Align2::CENTER_CENTER, title_text, egui::FontId::proportional(15.0), - egui::Color32::from_gray(60), + Colors::title(true), ); // Interact with the window title (drag to move window): @@ -341,6 +295,81 @@ impl App { }); }); } + + /// Setup window resize area. + fn resize_area_ui(&mut self, ui: &egui::Ui, direction: ResizeDirection) { + let mut rect = ui.max_rect(); + + // Setup area id, cursor and area rect based on direction. + let (id, cursor, rect) = match direction { + ResizeDirection::North => ("n", CursorIcon::ResizeNorth, { + rect.min.x += Root::WINDOW_FRAME_MARGIN * 2.0; + rect.max.y = rect.min.y + Root::WINDOW_FRAME_MARGIN; + rect.max.x -= Root::WINDOW_FRAME_MARGIN * 2.0; + rect + }), + ResizeDirection::East => ("e", CursorIcon::ResizeEast, { + rect.min.y += Root::WINDOW_FRAME_MARGIN * 2.0; + rect.min.x = rect.max.x - Root::WINDOW_FRAME_MARGIN; + rect.max.y -= Root::WINDOW_FRAME_MARGIN * 2.0; + rect + }), + ResizeDirection::South => ("s", CursorIcon::ResizeSouth, { + rect.min.x += Root::WINDOW_FRAME_MARGIN * 2.0; + rect.min.y = rect.max.y - Root::WINDOW_FRAME_MARGIN; + rect.max.x -= Root::WINDOW_FRAME_MARGIN * 2.0; + rect + }), + ResizeDirection::West => ("w", CursorIcon::ResizeWest, { + rect.min.y += Root::WINDOW_FRAME_MARGIN * 2.0; + rect.max.x = rect.min.x + Root::WINDOW_FRAME_MARGIN; + rect.max.y -= Root::WINDOW_FRAME_MARGIN * 2.0; + rect + }), + ResizeDirection::NorthWest => ("nw", CursorIcon::ResizeNorthWest, { + rect.max.y = rect.min.y + Root::WINDOW_FRAME_MARGIN * 2.0; + rect.max.x = rect.max.y + Root::WINDOW_FRAME_MARGIN * 2.0; + rect + }), + ResizeDirection::NorthEast => ("ne", CursorIcon::ResizeNorthEast, { + rect.min.y += Root::WINDOW_FRAME_MARGIN * 2.0; + rect.min.x = rect.max.x - Root::WINDOW_FRAME_MARGIN * 2.0; + rect.max.y = rect.min.y; + rect + }), + ResizeDirection::SouthEast => ("se", CursorIcon::ResizeSouthEast, { + rect.min.y = rect.max.y - Root::WINDOW_FRAME_MARGIN * 2.0; + rect.min.x = rect.max.x - Root::WINDOW_FRAME_MARGIN * 2.0; + rect + }), + ResizeDirection::SouthWest => ("sw", CursorIcon::ResizeSouthWest, { + rect.min.y = rect.max.y - Root::WINDOW_FRAME_MARGIN * 2.0; + rect.max.y = rect.min.y; + rect.max.x = rect.min.x + Root::WINDOW_FRAME_MARGIN * 2.0; + rect + }), + }; + + // Setup resize area. + let id = egui::Id::new("window_resize").with(id); + let sense = egui::Sense::drag(); + let area_resp = ui.interact(rect, id, sense).on_hover_cursor(cursor); + if area_resp.dragged() { + let current_pos = area_resp.interact_pointer_pos(); + if let Some(pos) = current_pos { + if self.resize_direction.is_none() { + self.resize_direction = Some(direction.clone()); + ui.ctx().send_viewport_cmd(ViewportCommand::BeginResize(direction)); + } + ui.ctx().send_viewport_cmd(ViewportCommand::InnerSize( + pos.to_vec2() + egui::vec2(Root::WINDOW_FRAME_MARGIN, Root::WINDOW_FRAME_MARGIN) + )); + } + } + if area_resp.drag_stopped() { + self.resize_direction = None; + } + } } /// To draw with egui`s eframe (for wgpu, glow backends and wasm target). diff --git a/src/gui/colors.rs b/src/gui/colors.rs index 3bfecc8..109725e 100644 --- a/src/gui/colors.rs +++ b/src/gui/colors.rs @@ -109,7 +109,7 @@ impl Colors { pub fn gold() -> Color32 { if use_dark() { - GOLD.linear_multiply(0.9) + GOLD.gamma_multiply(0.9) } else { GOLD } @@ -125,7 +125,7 @@ impl Colors { pub fn green() -> Color32 { if use_dark() { - GREEN.linear_multiply(1.3) + GREEN.gamma_multiply(1.3) } else { GREEN } @@ -133,7 +133,7 @@ impl Colors { pub fn red() -> Color32 { if use_dark() { - RED.linear_multiply(1.3) + RED.gamma_multiply(1.3) } else { RED } @@ -141,7 +141,7 @@ impl Colors { pub fn blue() -> Color32 { if use_dark() { - BLUE.linear_multiply(1.3) + BLUE.gamma_multiply(1.3) } else { BLUE } diff --git a/src/gui/views/mod.rs b/src/gui/views/mod.rs index 259d6cd..e3a2f0b 100644 --- a/src/gui/views/mod.rs +++ b/src/gui/views/mod.rs @@ -39,4 +39,7 @@ mod qr; pub use qr::*; mod file; -pub use file::*; \ No newline at end of file +pub use file::*; + +mod pull_to_refresh; +pub use pull_to_refresh::*; \ No newline at end of file diff --git a/src/gui/views/modal.rs b/src/gui/views/modal.rs index 11bb792..e233029 100644 --- a/src/gui/views/modal.rs +++ b/src/gui/views/modal.rs @@ -162,7 +162,7 @@ impl Modal { fn window_ui(&self, ctx: &egui::Context, add_content: impl FnOnce(&mut egui::Ui, &Modal)) { let mut rect = ctx.screen_rect(); if View::is_desktop() { - rect = rect.shrink(7.5); + rect = rect.shrink(Root::WINDOW_FRAME_MARGIN - 0.5); rect.min += egui::vec2(0.0, Root::WINDOW_TITLE_HEIGHT + 0.5); } egui::Window::new("modal_bg_window") @@ -256,6 +256,7 @@ impl Modal { rounding, fill: Colors::fill(), stroke: Stroke::NONE, + blur_width: 0.0, fill_texture_id: Default::default(), uv: Rect::ZERO }; @@ -288,6 +289,7 @@ impl Modal { }, fill: Colors::yellow(), stroke: Stroke::NONE, + blur_width: 0.0, fill_texture_id: Default::default(), uv: Rect::ZERO }; diff --git a/src/gui/views/network/content.rs b/src/gui/views/network/content.rs index a0eda88..cd29b9b 100644 --- a/src/gui/views/network/content.rs +++ b/src/gui/views/network/content.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::time::Duration; use egui::{Margin, RichText, ScrollArea}; use egui::scroll_area::ScrollBarVisibility; @@ -30,7 +31,7 @@ pub struct NetworkContent { /// Current integrated node tab content. node_tab_content: Box, /// Connections content. - connections: ConnectionsContent + connections: ConnectionsContent, } impl Default for NetworkContent { @@ -48,34 +49,20 @@ impl NetworkContent { let dual_panel = Root::is_dual_panel_mode(ui); // Show title panel. - self.title_ui(ui, dual_panel || Root::is_network_panel_open(), show_connections); + self.title_ui(ui, show_connections); // Show integrated node tabs content. 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() + View::TAB_ITEMS_PADDING, right: View::far_right_inset_margin(ui) + View::TAB_ITEMS_PADDING, top: View::TAB_ITEMS_PADDING, bottom: View::get_bottom_inset() + View::TAB_ITEMS_PADDING, }, - outer_margin: if View::is_desktop() { - Margin { - left: -0.5, - right: if dual_panel { - 0.0 - } else { - -0.5 - }, - top: 0.0, - bottom: -0.5, - } - } else { - Margin::ZERO - }, + fill: Colors::fill(), ..Default::default() }) .show_inside(ui, |ui| { @@ -92,6 +79,20 @@ impl NetworkContent { .resizable(false) .exact_width(ui.available_width()) .frame(egui::Frame { + outer_margin: if !show_connections { + Margin { + left: -0.5, + right: if !dual_panel { + -0.5 + } else { + 0.0 + }, + top: 0.0, + bottom: 0.0, + } + } else { + Margin::ZERO + }, ..Default::default() }) .show_animated_inside(ui, !show_connections, |ui| { @@ -116,6 +117,20 @@ impl NetworkContent { egui::CentralPanel::default() .frame(egui::Frame { stroke: View::item_stroke(), + outer_margin: if show_connections { + Margin { + left: -0.5, + right: if !dual_panel { + -0.5 + } else { + 0.0 + }, + top: 0.0, + bottom: -0.5, + } + } else { + Margin::ZERO + }, inner_margin: Margin { left: if show_connections { View::get_left_inset() + 4.0 @@ -157,9 +172,11 @@ impl NetworkContent { }); }); - // Redraw after delay if node is syncing to update stats. + // Redraw after delay. if Node::is_running() { ui.ctx().request_repaint_after(Node::STATS_UPDATE_DELAY); + } else if show_connections { + ui.ctx().request_repaint_after(Duration::from_millis(1000)); } } @@ -199,7 +216,7 @@ impl NetworkContent { } /// Draw title content. - fn title_ui(&mut self, ui: &mut egui::Ui, show_panel: bool, show_connections: bool) { + fn title_ui(&mut self, ui: &mut egui::Ui, show_connections: bool) { // Setup values for title panel. let title_text = self.node_tab_content.get_type().title().to_uppercase(); let subtitle_text = Node::get_sync_status_text(); @@ -211,7 +228,7 @@ impl NetworkContent { }; // Draw title panel. - TitlePanel::ui(TitleType::Single(title_content), show_panel, |ui| { + TitlePanel::ui(TitleType::Single(title_content), |ui| { if !show_connections { View::title_button_big(ui, DOTS_THREE_OUTLINE_VERTICAL, |_| { AppConfig::toggle_show_connections_network_panel(); diff --git a/src/gui/views/pull_to_refresh.rs b/src/gui/views/pull_to_refresh.rs new file mode 100644 index 0000000..6a2c2c3 --- /dev/null +++ b/src/gui/views/pull_to_refresh.rs @@ -0,0 +1,370 @@ +// Copyright 2024 The Grim Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use egui::scroll_area::ScrollAreaOutput; +use egui::{Sense, Align2, Area, Color32, Id, Rect, Response, Widget, Vec2}; +use egui::epaint::{emath::lerp, vec2, Pos2, Shape, Stroke}; + +/// A spinner widget used to indicate loading. +/// This was taken from egui and modified slightly to allow passing a progress value +#[must_use = "You should put this widget in an ui with `ui.add(widget);`"] +#[derive(Default)] +pub struct ProgressSpinner { + /// Uses the style's `interact_size` if `None`. + size: Option, + color: Option, + progress: Option, +} + +impl ProgressSpinner { + /// Create a new spinner that uses the style's `interact_size` unless changed. + pub fn new() -> Self { + Self::default() + } + + /// Sets the spinner's size. The size sets both the height and width, as the spinner is always + /// square. If the size isn't set explicitly, the active style's `interact_size` is used. + #[allow(unused)] + pub fn size(mut self, size: f32) -> Self { + self.size = Some(size); + self + } + + /// Sets the spinner's color. + pub fn color(mut self, color: impl Into) -> Self { + self.color = Some(color.into()); + self + } + + /// Sets the spinner's progress. + /// Should be in the range `[0.0, 1.0]`. + pub fn progress(mut self, progress: impl Into>) -> Self { + self.progress = progress.into(); + self + } + + /// Paint the spinner in the given rectangle. + pub fn paint_at(&self, ui: &egui::Ui, rect: Rect) { + if ui.is_rect_visible(rect) { + ui.ctx().request_repaint(); // because it is animated + + let color = self + .color + .unwrap_or_else(|| ui.visuals().strong_text_color()); + let radius = (rect.height() / 2.0) - 2.0; + let n_points = 20; + + let (start_angle, end_angle) = if let Some(progress) = self.progress { + let start_angle = 0f64.to_radians(); + let end_angle = start_angle + 360f64.to_radians() * progress; + (start_angle, end_angle) + } else { + let time = ui.input(|i| i.time); + let start_angle = time * std::f64::consts::TAU; + let end_angle = start_angle + 240f64.to_radians() * time.sin(); + (start_angle, end_angle) + }; + + let points: Vec = (0..=n_points) + .map(|i| { + let angle = lerp(start_angle..=end_angle, i as f64 / n_points as f64); + let (sin, cos) = angle.sin_cos(); + rect.center() + radius * vec2(cos as f32, sin as f32) + }) + .collect(); + ui.painter() + .add(Shape::line(points, Stroke::new(3.0, color))); + } + } +} + +impl Widget for ProgressSpinner { + fn ui(self, ui: &mut egui::Ui) -> Response { + let size = self + .size + .unwrap_or_else(|| ui.style().spacing.interact_size.y); + let (rect, response) = ui.allocate_exact_size(vec2(size, size), Sense::hover()); + self.paint_at(ui, rect); + + response + } +} + +/// The current state of the pull to refresh widget. +#[derive(Debug, Clone)] +pub enum PullToRefreshState { + /// The widget is idle, no refresh is happening. + Idle, + /// The user is dragging. + Dragging { + /// `distance` is the distance the user dragged. + distance: f32, + /// `far_enough` is true if the user dragged far enough to trigger a refresh. + far_enough: bool, + }, + /// The user dragged far enough to trigger a refresh and released the pointer. + DoRefresh, + /// The refresh is currently happening. + Refreshing, +} + +impl PullToRefreshState { + fn progress(&self, min_distance: f32) -> Option { + match self { + PullToRefreshState::Idle => Some(0.0), + PullToRefreshState::Dragging { distance, .. } => { + Some((distance / min_distance).min(1.0).max(0.0) as f64) + } + PullToRefreshState::DoRefresh => Some(1.0), + PullToRefreshState::Refreshing => None, + } + } +} + +/// The response of the pull to refresh widget. +#[derive(Debug, Clone)] +pub struct PullToRefreshResponse { + /// Current state of the pull to refresh widget. + pub state: PullToRefreshState, + /// The inner response of the widget you wrapped in [`PullToRefresh::ui`] or [`PullToRefresh::scroll_area_ui`]. + pub inner: T, +} + +impl PullToRefreshResponse { + /// Returns true if the user dragged far enough to trigger a refresh. + pub fn should_refresh(&self) -> bool { + matches!(self.state, PullToRefreshState::DoRefresh) + } +} + +/// A widget that allows the user to pull to refresh. +pub struct PullToRefresh { + id: Id, + loading: bool, + min_refresh_distance: f32, + can_refresh: bool, +} + +impl PullToRefresh { + /// Creates a new pull to refresh widget. + /// If `loading` is true, the widget will show the loading indicator. + pub fn new(loading: bool) -> Self { + Self { + id: Id::new("pull_to_refresh"), + loading, + min_refresh_distance: 100.0, + can_refresh: true, + } + } + + /// Sets the minimum distance the user needs to drag to trigger a refresh. + pub fn min_refresh_distance(mut self, min_refresh_distance: f32) -> Self { + self.min_refresh_distance = min_refresh_distance; + self + } + + /// You need to provide a id if you use multiple pull to refresh widgets at once. + pub fn id(mut self, id: Id) -> Self { + self.id = id; + self + } + + /// If `can_refresh` is false, pulling will not trigger a refresh. + pub fn can_refresh(mut self, can_refresh: bool) -> Self { + self.can_refresh = can_refresh; + self + } + + /// Shows the pull to refresh widget. + /// Note: If you want to use the pull to refresh widget in a scroll area, use [`Self::scroll_area_ui`]. + /// You might want to disable text selection via [`egui::style::Interaction`] + /// to avoid conflicts with the drag gesture. + pub fn ui( + self, + ui: &mut egui::Ui, + content: impl FnOnce(&mut egui::Ui) -> T, + ) -> PullToRefreshResponse { + let mut child = ui.child_ui(ui.available_rect_before_wrap(), *ui.layout(), None); + + let output = content(&mut child); + + let can_refresh = self.can_refresh; + let state = self.internal_ui(ui, can_refresh, None, child.min_rect()); + + PullToRefreshResponse { + state, + inner: output, + } + } + + /// Shows the pull to refresh widget, wrapping a [egui::ScrollArea]. + /// Pass the output of the scroll area to the content function. + pub fn scroll_area_ui( + self, + ui: &mut egui::Ui, + content: impl FnOnce(&mut egui::Ui) -> ScrollAreaOutput, + ) -> PullToRefreshResponse> { + let scroll_output = content(ui); + let content_rect = scroll_output.inner_rect; + let can_refresh = scroll_output.state.offset.y == 0.0 && self.can_refresh; + // This is the id used in the Sense of the scroll area + // I hope this id is stable across egui patches... + let allow_dragged_id = scroll_output.id.with("area"); + let state = self.internal_ui(ui, can_refresh, Some(allow_dragged_id), content_rect); + PullToRefreshResponse { + state, + inner: scroll_output, + } + } + + fn internal_ui( + self, + ui: &mut egui::Ui, + can_refresh: bool, + allow_dragged_id: Option, + content_rect: Rect, + ) -> PullToRefreshState { + let last_state = ui.data_mut(|data| { + data.get_temp_mut_or(self.id, PullToRefreshState::Idle) + .clone() + }); + + let mut state = last_state; + if self.loading { + state = PullToRefreshState::Refreshing; + } + + if !self.loading && matches!(state, PullToRefreshState::Refreshing) { + state = PullToRefreshState::Idle; + } + + if can_refresh && !self.loading { + let sense = ui.interact(content_rect, self.id, Sense::hover()); + + let is_something_blocking_drag = ui.ctx().dragged_id().is_some() + && !allow_dragged_id.map_or(false, |id| ui.ctx().is_being_dragged(id)); + + if sense.contains_pointer() && !is_something_blocking_drag { + let (delta, any_released) = ui.input(|input| { + ( + if input.pointer.is_decidedly_dragging() { + Some(input.pointer.delta()) + } else { + None + }, + input.pointer.any_released(), + ) + }); + if let Some(delta) = delta { + if matches!(state, PullToRefreshState::Idle) { + state = PullToRefreshState::Dragging { + distance: 0.0, + far_enough: false, + }; + } + if let PullToRefreshState::Dragging { distance: drag, .. } = state.clone() { + let dist = drag + delta.y; + state = PullToRefreshState::Dragging { + distance: dist, + far_enough: dist > self.min_refresh_distance, + }; + } + } else { + state = PullToRefreshState::Idle; + } + if any_released { + if let PullToRefreshState::Dragging { + far_enough: enough, .. + } = state.clone() + { + if enough { + state = PullToRefreshState::DoRefresh; + } else { + state = PullToRefreshState::Idle; + } + } else { + state = PullToRefreshState::Idle; + } + } + } else { + state = PullToRefreshState::Idle; + } + } else { + state = PullToRefreshState::Idle; + } + + if self.loading { + state = PullToRefreshState::Refreshing; + } + + let spinner_size = Vec2::splat(24.0); + + let progress_for_offset = match &state { + PullToRefreshState::Idle => 0.0, + PullToRefreshState::Dragging { .. } => { + state.progress(self.min_refresh_distance).unwrap_or(1.0) + } + PullToRefreshState::DoRefresh => 1.0, + PullToRefreshState::Refreshing => 1.0, + } as f32; + + let anim_progress = ui.ctx().animate_value_with_time( + self.id.with("offset_top"), + progress_for_offset, + ui.style().animation_time, + ); + + let offset_top = -spinner_size.y + spinner_size.y * anim_progress * 2.0; + + if anim_progress > 0.0 { + Area::new(Id::new("Pull to refresh indicator")) + .fixed_pos(content_rect.center_top()) + .pivot(Align2::CENTER_TOP) + .show(ui.ctx(), |ui| { + let (rect, _) = ui.allocate_exact_size(spinner_size, Sense::hover()); + + ui.set_clip_rect(Rect::everything_below(rect.min.y)); + + let rect = rect.translate(Vec2::new(0.0, offset_top)); + + ui.painter().circle( + rect.center(), + spinner_size.x / 1.5, + ui.style().visuals.widgets.inactive.bg_fill, + ui.visuals().widgets.inactive.bg_stroke, + ); + + let mut spinner_color = ui.style().visuals.widgets.inactive.fg_stroke.color; + if anim_progress < 1.0 { + spinner_color = Color32::from_rgba_unmultiplied( + spinner_color.r(), + spinner_color.g(), + spinner_color.b(), + (spinner_color.a() as f32 * 0.7).round() as u8, + ); + } + ProgressSpinner::new() + .color(spinner_color) + .progress(state.progress(self.min_refresh_distance)) + .paint_at(ui, rect); + }); + } + + ui.data_mut(|data| { + data.insert_temp(self.id, state.clone()); + }); + + state + } +} \ No newline at end of file diff --git a/src/gui/views/qr.rs b/src/gui/views/qr.rs index bfaa9fd..1f69cd8 100644 --- a/src/gui/views/qr.rs +++ b/src/gui/views/qr.rs @@ -17,6 +17,7 @@ use std::sync::Arc; use parking_lot::RwLock; use std::thread; use egui::{SizeHint, TextureHandle, TextureOptions}; +use egui::epaint::RectShape; use egui::load::SizedTexture; use egui_extras::image::load_svg_bytes_with_size; use image::{ExtendedColorType, ImageEncoder}; @@ -235,11 +236,12 @@ impl QrCodeContent { rect.max -= egui::emath::vec2(10.0, 0.0); // Create background shape. - let mut bg_shape = egui::epaint::RectShape { + let mut bg_shape = RectShape { rect, rounding: egui::Rounding::default(), fill: egui::Color32::WHITE, stroke: egui::Stroke::NONE, + blur_width: 0.0, fill_texture_id: Default::default(), uv: egui::Rect::ZERO }; diff --git a/src/gui/views/root.rs b/src/gui/views/root.rs index c8e3ba6..e7fa303 100644 --- a/src/gui/views/root.rs +++ b/src/gui/views/root.rs @@ -111,19 +111,23 @@ impl Root { 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 { - ..Default::default() - }) - .show_animated_inside(ui, is_panel_open, |ui| { - self.network.ui(ui, cb); - }); + if is_panel_open { + egui::SidePanel::left("network_panel") + .resizable(false) + .exact_width(panel_width) + .frame(egui::Frame { + fill: Colors::fill(), + ..Default::default() + }) + .show_inside(ui, |ui| { + self.network.ui(ui, cb); + }); + } // Show wallets content. egui::CentralPanel::default() .frame(egui::Frame { + fill: Colors::fill(), ..Default::default() }) .show_inside(ui, |ui| { @@ -151,7 +155,7 @@ impl Root { Self::SIDE_PANEL_WIDTH + View::get_left_inset() } else { View::window_size(ui).0 - if View::is_desktop() { - Root::WINDOW_FRAME_MARGIN * 2.0 + Self::WINDOW_FRAME_MARGIN * 2.0 } else { 0.0 } diff --git a/src/gui/views/title_panel.rs b/src/gui/views/title_panel.rs index 31fa664..e3a7b84 100644 --- a/src/gui/views/title_panel.rs +++ b/src/gui/views/title_panel.rs @@ -27,7 +27,6 @@ impl TitlePanel { pub const DEFAULT_HEIGHT: f32 = 54.0; pub fn ui(title: TitleType, - show: bool, mut left_content: impl FnMut(&mut egui::Ui), mut right_content: impl FnMut(&mut egui::Ui), ui: &mut egui::Ui) { @@ -52,13 +51,18 @@ impl TitlePanel { // Draw title panel. egui::TopBottomPanel::top(id) .resizable(false) - .exact_height(Self::DEFAULT_HEIGHT) + .exact_height(Self::DEFAULT_HEIGHT + View::get_top_inset()) .frame(egui::Frame { - inner_margin: Self::inner_margin(ui), + inner_margin: Margin { + left: View::far_left_inset_margin(ui), + right: View::far_right_inset_margin(ui), + top: View::get_top_inset(), + bottom: 0.0, + }, fill: Colors::yellow(), ..Default::default() }) - .show_animated_inside(ui, show, |ui| { + .show_inside(ui, |ui| { StripBuilder::new(ui) .size(Size::exact(Self::DEFAULT_HEIGHT)) .size(if dual_title { @@ -126,16 +130,6 @@ impl TitlePanel { } } - /// Calculate inner margin based on display insets (cutouts). - fn inner_margin(ui: &mut egui::Ui) -> Margin { - Margin { - left: View::far_left_inset_margin(ui), - right: View::far_right_inset_margin(ui), - top: View::get_top_inset(), - bottom: 0.0, - } - } - /// Draw content for [`TitleType::WithSubTitle`] type. fn with_sub_title(builder: StripBuilder, title: String, subtitle: String, animate_sub: bool) { builder diff --git a/src/gui/views/views.rs b/src/gui/views/views.rs index 64c2a76..1a6f8d9 100644 --- a/src/gui/views/views.rs +++ b/src/gui/views/views.rs @@ -532,6 +532,7 @@ impl View { }, fill: Colors::TRANSPARENT, stroke: Self::item_stroke(), + blur_width: 0.0, fill_texture_id: Default::default(), uv: Rect::ZERO }; diff --git a/src/gui/views/wallets/content.rs b/src/gui/views/wallets/content.rs index 19a0eae..6bffb42 100644 --- a/src/gui/views/wallets/content.rs +++ b/src/gui/views/wallets/content.rs @@ -103,16 +103,19 @@ impl WalletsContent { let wallet_panel_width = self.wallet_panel_width(ui, empty_list, dual_panel, show_wallet); let content_width = ui.available_width(); + let root_dual_panel = Root::is_dual_panel_mode(ui); + // Flag to check if wallet list is hidden on the screen. let list_hidden = content_width == 0.0 || empty_list || create_wallet || (dual_panel && show_wallet && !self.show_wallets_at_dual_panel) || (!dual_panel && show_wallet) || - (!Root::is_dual_panel_mode(ui) && Root::is_network_panel_open()); + (!root_dual_panel && Root::is_network_panel_open()); // Show title panel. self.title_ui(ui, dual_panel, create_wallet, show_wallet); // Show wallet panel content. + let wallet_panel_opened = self.wallet_panel_opened(); egui::SidePanel::right("wallet_panel") .resizable(false) .exact_width(wallet_panel_width) @@ -129,11 +132,11 @@ impl WalletsContent { }, ..Default::default() }) - .show_animated_inside(ui, self.wallet_panel_opened(), |ui| { - // Do not draw content on zero width. - if content_width == 0.0 { - return; - } + .show_animated_inside(ui, wallet_panel_opened, |ui| { + // // Do not draw content on zero width. + // if content_width == 0.0 { + // return; + // } if create_wallet || !show_wallet { // Show wallet creation content. self.creation_content.ui(ui, cb, |wallet| { @@ -180,24 +183,6 @@ impl WalletsContent { top: View::TAB_ITEMS_PADDING, bottom: View::get_bottom_inset() + View::TAB_ITEMS_PADDING, }, - outer_margin: if View::is_desktop() { - Margin { - left: if !dual_panel { - -0.5 - } else { - 0.0 - }, - right: if !self.wallet_panel_opened() { - -0.5 - } else { - 0.0 - }, - top: 0.0, - bottom: -0.5, - } - } else { - Margin::ZERO - }, ..Default::default() }) .show_inside(ui, |ui| { @@ -221,6 +206,20 @@ impl WalletsContent { egui::Frame::default() } else { egui::Frame { + outer_margin: Margin { + left: if !root_dual_panel { + -0.5 + } else { + 0.0 + }, + right: if !wallet_panel_opened { + -0.5 + } else { + 0.0 + }, + top: 0.0, + bottom: 0.0, + }, stroke: View::item_stroke(), fill: Colors::fill_deep(), inner_margin: Margin { @@ -301,8 +300,7 @@ impl WalletsContent { }; // Draw title panel. - let show_title = Root::is_dual_panel_mode(ui) || !Root::is_network_panel_open(); - TitlePanel::ui(title_content, show_title, |ui| { + TitlePanel::ui(title_content, |ui| { if show_wallet && !dual_panel { View::title_button_big(ui, ARROW_LEFT, |_| { self.wallets.select(None); diff --git a/src/gui/views/wallets/wallet/content.rs b/src/gui/views/wallets/wallet/content.rs index 656b8c7..c8e6b0c 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -87,8 +87,11 @@ 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 + let mut show_balance = self.current_tab.get_type() != WalletTabType::Settings && !data_empty && !wallet.sync_error() && !wallet.is_repairing() && !wallet.is_closing(); + if wallet.get_current_ext_conn().is_none() && !Node::is_running() { + show_balance = false; + } egui::TopBottomPanel::top(Id::from("wallet_balance").with(wallet.identifier())) .frame(egui::Frame { fill: Colors::fill(), @@ -100,13 +103,17 @@ impl WalletContent { bottom: 0.0, }, outer_margin: Margin { - left: 0.0, - right: 0.0, - top: 0.0, - bottom: if !dual_panel { - 0.0 + left: if !dual_panel { + -0.5 } else { + 0.0 + }, + right: -0.5, + top: 0.0, + bottom: if dual_panel { -1.0 + } else { + -0.5 }, }, ..Default::default() @@ -124,6 +131,7 @@ impl WalletContent { }); // Show wallet tabs panel. + let show_tabs = !Self::block_navigation_on_sync(wallet); egui::TopBottomPanel::bottom("wallet_tabs") .frame(egui::Frame { fill: Colors::fill(), @@ -133,23 +141,9 @@ impl WalletContent { top: View::TAB_ITEMS_PADDING, bottom: View::get_bottom_inset() + View::TAB_ITEMS_PADDING, }, - outer_margin: if View::is_desktop() { - Margin { - left: if dual_panel { - 0.0 - } else { - -0.5 - }, - right: -0.5, - top: 0.0, - bottom: -0.5, - } - } else { - Margin::ZERO - }, ..Default::default() }) - .show_animated_inside(ui, !Self::block_navigation_on_sync(wallet), |ui| { + .show_animated_inside(ui, show_tabs, |ui| { ui.vertical_centered(|ui| { // Draw wallet tabs. View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { @@ -161,6 +155,16 @@ impl WalletContent { // Show tab content panel. egui::CentralPanel::default() .frame(egui::Frame { + outer_margin: Margin { + left: if !dual_panel { + -0.5 + } else { + 0.0 + }, + right: -0.5, + top: 0.0, + bottom: 0.0, + }, stroke: View::item_stroke(), fill: Colors::white_or_black(false), ..Default::default() diff --git a/src/gui/views/wallets/wallet/txs.rs b/src/gui/views/wallets/wallet/txs.rs index 20a54a7..17a6bec 100644 --- a/src/gui/views/wallets/wallet/txs.rs +++ b/src/gui/views/wallets/wallet/txs.rs @@ -17,7 +17,6 @@ use std::thread; use std::time::{SystemTime, UNIX_EPOCH}; use egui::{Align, Id, Layout, Margin, RichText, Rounding, ScrollArea}; use egui::scroll_area::ScrollBarVisibility; -use egui_pull_to_refresh::PullToRefresh; use grin_core::core::amount_to_hr_string; use grin_util::ToHex; use grin_wallet_libwallet::{Error, Slate, SlateState, TxLogEntryType}; @@ -26,7 +25,7 @@ use parking_lot::RwLock; use crate::gui::Colors; use crate::gui::icons::{ARROW_CIRCLE_DOWN, ARROW_CIRCLE_UP, ARROW_CLOCKWISE, BRIDGE, BROOM, CALENDAR_CHECK, CHAT_CIRCLE_TEXT, CHECK, CHECK_CIRCLE, CLIPBOARD_TEXT, COPY, DOTS_THREE_CIRCLE, FILE_ARCHIVE, FILE_TEXT, GEAR_FINE, HASH_STRAIGHT, PROHIBIT, QR_CODE, SCAN, X_CIRCLE}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{CameraContent, FilePickButton, Modal, QrCodeContent, Root, View}; +use crate::gui::views::{CameraContent, FilePickButton, Modal, PullToRefresh, QrCodeContent, Root, View}; use crate::gui::views::types::ModalPosition; use crate::gui::views::wallets::types::WalletTab; use crate::gui::views::wallets::wallet::types::{GRIN, SLATEPACK_MESSAGE_HINT, WalletTabType}; diff --git a/src/lib.rs b/src/lib.rs index 959b254..b5f9970 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,7 +112,7 @@ pub fn app_creator(app: App) -> eframe::AppCreator // Setup fonts. setup_fonts(&cc.egui_ctx); // Return app instance. - Box::new(app) + Ok(Box::new(app)) }) } diff --git a/src/main.rs b/src/main.rs index b1bb043..e25795a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -51,7 +51,9 @@ fn real_main() { // Setup window size. let (width, height) = AppConfig::window_size(); - let mut viewport = egui::ViewportBuilder::default().with_inner_size([width, height]); + let mut viewport = egui::ViewportBuilder::default() + .with_min_inner_size([AppConfig::MIN_WIDTH, AppConfig::MIN_HEIGHT]) + .with_inner_size([width, height]); // Setup an icon. if let Ok(icon) = from_png_bytes(include_bytes!("../img/icon.png")) { diff --git a/src/settings/config.rs b/src/settings/config.rs index 825ff62..8829a24 100644 --- a/src/settings/config.rs +++ b/src/settings/config.rs @@ -14,6 +14,7 @@ use grin_core::global::ChainTypes; use serde_derive::{Deserialize, Serialize}; +use crate::gui::views::Root; use crate::node::{NodeConfig, PeersConfig}; use crate::Settings; @@ -69,10 +70,16 @@ impl Default for AppConfig { } impl AppConfig { - /// Default window width. - pub const DEFAULT_WIDTH: f32 = 1269.0; - /// Default window height. - pub const DEFAULT_HEIGHT: f32 = 789.0; + /// Desktop window frame margin sum, horizontal or vertical. + const FRAME_MARGIN: f32 = Root::WINDOW_FRAME_MARGIN * 2.0; + /// Default desktop window width. + pub const DEFAULT_WIDTH: f32 = Root::SIDE_PANEL_WIDTH * 3.0 + Self::FRAME_MARGIN; + /// Default desktop window height. + pub const DEFAULT_HEIGHT: f32 = 698.0 + Root::WINDOW_TITLE_HEIGHT + Self::FRAME_MARGIN; + /// Minimal desktop window width. + pub const MIN_WIDTH: f32 = Root::SIDE_PANEL_WIDTH + Self::FRAME_MARGIN; + /// Minimal desktop window height. + pub const MIN_HEIGHT: f32 = 630.0 + Root::WINDOW_TITLE_HEIGHT + Self::FRAME_MARGIN; /// Application configuration file name. pub const FILE_NAME: &'static str = "app.toml"; -- 2.39.5 From cbb022ac503dcdfadeed632b9da7159c0c641d11 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Thu, 27 Jun 2024 12:30:51 +0300 Subject: [PATCH 12/28] build: fix android --- scripts/android.sh | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/scripts/android.sh b/scripts/android.sh index b3bd17b..5aa7579 100755 --- a/scripts/android.sh +++ b/scripts/android.sh @@ -39,21 +39,29 @@ type=$1 [[ $2 == "v8" ]] && rustup target install aarch64-linux-android # Build native code -export CPPFLAGS="-DMDB_USE_ROBUST=0" && export CFLAGS="-DMDB_USE_ROBUST=0" \ -&& cargo ndk -t ${arch} build ${release_param[@]} +mkdir -p android/app/src/main/jniLibs +cargo install cargo-ndk +cargo ndk -t ${arch} -o android/app/src/main/jniLibs build ${release_param} # Build Android application and launch at all connected devices if [ $? -eq 0 ] then - yes | mkdir -p android/app/src/main/jniLibs/${arch} && cp -f target/${platform}/${type}/libgrim.so android/app/src/main/jniLibs/${arch} cd android + + # Setup gradle argument + [[ $1 == "release" ]] && gradle_param+=(assembleRelease) + [[ $1 == "debug" ]] && gradle_param+=(build) + ./gradlew clean -# ./gradlew assembleRelease - ./gradlew build + ./gradlew ${gradle_param} + + # Setup apk path + [[ $1 == "release" ]] && apk_path+=(app/build/outputs/apk/release/app-release.apk) + [[ $1 == "debug" ]] && apk_path+=(app/build/outputs/apk/debug/app-debug.apk) + for SERIAL in $(adb devices | grep -v List | cut -f 1); do -# adb -s $SERIAL install app/build/outputs/apk/release/app-release.apk - adb -s $SERIAL install app/build/outputs/apk/debug/app-debug.apk + adb -s $SERIAL install ${apk_path} sleep 1s adb -s $SERIAL shell am start -n mw.gri.android/.MainActivity; done -- 2.39.5 From 58802c4d0bed672c88b792be9dc04cec21a76108 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Thu, 27 Jun 2024 12:55:16 +0300 Subject: [PATCH 13/28] fix: resize corners --- src/gui/app.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/gui/app.rs b/src/gui/app.rs index 8fb1758..955a86f 100644 --- a/src/gui/app.rs +++ b/src/gui/app.rs @@ -332,9 +332,8 @@ impl App { rect }), ResizeDirection::NorthEast => ("ne", CursorIcon::ResizeNorthEast, { - rect.min.y += Root::WINDOW_FRAME_MARGIN * 2.0; rect.min.x = rect.max.x - Root::WINDOW_FRAME_MARGIN * 2.0; - rect.max.y = rect.min.y; + rect.max.y = Root::WINDOW_FRAME_MARGIN * 2.0; rect }), ResizeDirection::SouthEast => ("se", CursorIcon::ResizeSouthEast, { @@ -344,7 +343,6 @@ impl App { }), ResizeDirection::SouthWest => ("sw", CursorIcon::ResizeSouthWest, { rect.min.y = rect.max.y - Root::WINDOW_FRAME_MARGIN * 2.0; - rect.max.y = rect.min.y; rect.max.x = rect.min.x + Root::WINDOW_FRAME_MARGIN * 2.0; rect }), -- 2.39.5 From 7fb19619cf73b404e00ed047c7f83c67986148ba Mon Sep 17 00:00:00 2001 From: ardocrat Date: Thu, 27 Jun 2024 13:52:28 +0300 Subject: [PATCH 14/28] fix: modal shadow padding --- src/gui/views/modal.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/views/modal.rs b/src/gui/views/modal.rs index e233029..42fc37a 100644 --- a/src/gui/views/modal.rs +++ b/src/gui/views/modal.rs @@ -164,6 +164,7 @@ impl Modal { if View::is_desktop() { rect = rect.shrink(Root::WINDOW_FRAME_MARGIN - 0.5); rect.min += egui::vec2(0.0, Root::WINDOW_TITLE_HEIGHT + 0.5); + rect.max.x += 0.5; } egui::Window::new("modal_bg_window") .title_bar(false) -- 2.39.5 From b91e90237a529c8b4b53da62aaf043e12120adb7 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Thu, 27 Jun 2024 23:22:49 +0300 Subject: [PATCH 15/28] fix: focus on text edit touch --- src/gui/views/views.rs | 1 + src/gui/views/wallets/wallet/messages.rs | 1 + src/gui/views/wallets/wallet/txs.rs | 1 + 3 files changed, 3 insertions(+) diff --git a/src/gui/views/views.rs b/src/gui/views/views.rs index 1a6f8d9..80127fc 100644 --- a/src/gui/views/views.rs +++ b/src/gui/views/views.rs @@ -431,6 +431,7 @@ impl View { .ui(ui); // Show keyboard on click. if text_edit_resp.clicked() { + text_edit_resp.request_focus(); cb.show_keyboard(); } // Setup focus on input field. diff --git a/src/gui/views/wallets/wallet/messages.rs b/src/gui/views/wallets/wallet/messages.rs index 0ad9335..7da37f7 100644 --- a/src/gui/views/wallets/wallet/messages.rs +++ b/src/gui/views/wallets/wallet/messages.rs @@ -665,6 +665,7 @@ impl WalletMessages { .response; // Show soft keyboard on click. if response_empty && resp.clicked() { + resp.request_focus(); cb.show_keyboard(); } if response_empty && resp.has_focus() { diff --git a/src/gui/views/wallets/wallet/txs.rs b/src/gui/views/wallets/wallet/txs.rs index 17a6bec..55f05ba 100644 --- a/src/gui/views/wallets/wallet/txs.rs +++ b/src/gui/views/wallets/wallet/txs.rs @@ -855,6 +855,7 @@ impl WalletTransactions { .show(ui).response; // Show soft keyboard on click. if self.tx_info_finalize && resp.clicked() { + resp.request_focus(); cb.show_keyboard(); } if self.tx_info_finalize && resp.has_focus() { -- 2.39.5 From 518b8e65d17d68c8900b1ececbe275045c7eedd5 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 1 Jul 2024 01:40:00 +0300 Subject: [PATCH 16/28] i18n: fix de format --- locales/de.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/locales/de.yml b/locales/de.yml index 99b54e9..ac8da44 100644 --- a/locales/de.yml +++ b/locales/de.yml @@ -96,7 +96,7 @@ wallets: parse_s3_slatepack_desc: 'Transaktion posten, um das Senden von %{amount} abzuschließen ツ:' resp_slatepack_err: 'Beim Erstellen der Antwort ist ein Fehler aufgetreten. Überprüfen Sie die Eingabedaten:' resp_exists_err: 'Eine solche Transaktion existiert bereits.' - resp_canceled_err: 'Eine solche Transaktion wurde schon abgebrochen. + resp_canceled_err: 'Eine solche Transaktion wurde schon abgebrochen.' create_request_desc: 'Erstellen Sie eine Anfrage zum Senden oder Empfangen der Gelder:' send_request_desc: 'Sie haben eine Anfrage zum Senden von %{amount} ツ erstellt. Senden Sie diese Nachricht an den Empfänger:' send_slatepack_err: Beim Erstellen der Anfrage zum Senden von Geldern ist ein Fehler aufgetreten. Überprüfen Sie die Eingabedaten. @@ -142,7 +142,7 @@ transport: bin_file: 'Binärdatei:' conn_line: 'Verbindungsleitung:' bridges_disabled: Brücken deaktiviert - bridge_name: 'Brücke %{b} + bridge_name: 'Brücke %{b}' network: self: Netzwerk type: 'Netzwerk Typ:' -- 2.39.5 From f2a23ec0e0e3b59bedc22c188073701a79033ec5 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 1 Jul 2024 11:50:19 +0300 Subject: [PATCH 17/28] ui: content paddings, background, glow renderer on windows to fix transparency --- src/gui/app.rs | 4 +- src/gui/views/network/content.rs | 98 ++++++++++------------ src/gui/views/root.rs | 22 ++--- src/gui/views/wallets/content.rs | 24 +++--- src/gui/views/wallets/creation/creation.rs | 14 ---- src/gui/views/wallets/wallet/content.rs | 12 +-- src/main.rs | 24 ++++-- 7 files changed, 88 insertions(+), 110 deletions(-) diff --git a/src/gui/app.rs b/src/gui/app.rs index 955a86f..581045f 100644 --- a/src/gui/app.rs +++ b/src/gui/app.rs @@ -105,7 +105,7 @@ impl App { sw: 0.0, se: 0.0, }, - fill: Colors::TRANSPARENT, + fill: Colors::yellow(), stroke: Stroke { width: 1.0, color: egui::Color32::from_gray(200) @@ -126,7 +126,7 @@ impl App { let content_stroke = RectShape { rect: content_stroke_rect, rounding: Rounding::ZERO, - fill: Colors::TRANSPARENT, + fill: Colors::fill(), stroke: Stroke { width: 1.0, color: Colors::stroke() diff --git a/src/gui/views/network/content.rs b/src/gui/views/network/content.rs index cd29b9b..54dd3ca 100644 --- a/src/gui/views/network/content.rs +++ b/src/gui/views/network/content.rs @@ -79,19 +79,11 @@ impl NetworkContent { .resizable(false) .exact_width(ui.available_width()) .frame(egui::Frame { - outer_margin: if !show_connections { - Margin { - left: -0.5, - right: if !dual_panel { - -0.5 - } else { - 0.0 - }, - top: 0.0, - bottom: 0.0, - } - } else { - Margin::ZERO + outer_margin: Margin { + left: -0.5, + right: -0.5, + top: 0.0, + bottom: 0.0, }, ..Default::default() }) @@ -116,59 +108,61 @@ impl NetworkContent { // Show connections content. egui::CentralPanel::default() .frame(egui::Frame { - stroke: View::item_stroke(), outer_margin: if show_connections { Margin { left: -0.5, - right: if !dual_panel { - -0.5 - } else { - 0.0 - }, + right: -0.5, top: 0.0, bottom: -0.5, } } else { Margin::ZERO }, - inner_margin: Margin { - left: if show_connections { - View::get_left_inset() + 4.0 - } else { - 0.0 - }, - right: if show_connections { - View::far_right_inset_margin(ui) + 4.0 - } else { - 0.0 - }, - top: 3.0, - bottom: if View::is_desktop() && show_connections { - 6.0 - } else { - 4.0 - }, - }, - fill: Colors::button(), ..Default::default() }) .show_inside(ui, |ui| { - ScrollArea::vertical() - .id_source("connections_content") - .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) - .auto_shrink([false; 2]) - .show(ui, |ui| { - ui.add_space(1.0); - ui.vertical_centered(|ui| { - let max_width = if !dual_panel { - Root::SIDE_PANEL_WIDTH * 1.3 + egui::CentralPanel::default() + .frame(egui::Frame { + stroke: View::item_stroke(), + inner_margin: Margin { + left: if show_connections { + View::get_left_inset() + 4.0 } else { - ui.available_width() - }; - View::max_width_ui(ui, max_width, |ui| { - self.connections.ui(ui, cb); + 0.0 + }, + right: if show_connections { + View::far_right_inset_margin(ui) + 4.0 + } else { + 0.0 + }, + top: 3.0, + bottom: if View::is_desktop() && show_connections { + 6.0 + } else { + 4.0 + }, + }, + fill: Colors::fill(), + ..Default::default() + }) + .show_inside(ui, |ui| { + ScrollArea::vertical() + .id_source("connections_content") + .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) + .auto_shrink([false; 2]) + .show(ui, |ui| { + ui.add_space(1.0); + ui.vertical_centered(|ui| { + let max_width = if !dual_panel { + Root::SIDE_PANEL_WIDTH * 1.3 + } else { + ui.available_width() + }; + View::max_width_ui(ui, max_width, |ui| { + self.connections.ui(ui, cb); + }); + }); }); - }); }); }); diff --git a/src/gui/views/root.rs b/src/gui/views/root.rs index e7fa303..0e7701a 100644 --- a/src/gui/views/root.rs +++ b/src/gui/views/root.rs @@ -111,23 +111,19 @@ impl Root { let (is_panel_open, panel_width) = Self::network_panel_state_width(ui, dual_panel); // Show network content. - if is_panel_open { - egui::SidePanel::left("network_panel") - .resizable(false) - .exact_width(panel_width) - .frame(egui::Frame { - fill: Colors::fill(), - ..Default::default() - }) - .show_inside(ui, |ui| { - self.network.ui(ui, cb); - }); - } + egui::SidePanel::left("network_panel") + .resizable(false) + .exact_width(panel_width) + .frame(egui::Frame { + ..Default::default() + }) + .show_animated_inside(ui, is_panel_open, |ui| { + self.network.ui(ui, cb); + }); // Show wallets content. egui::CentralPanel::default() .frame(egui::Frame { - fill: Colors::fill(), ..Default::default() }) .show_inside(ui, |ui| { diff --git a/src/gui/views/wallets/content.rs b/src/gui/views/wallets/content.rs index 6bffb42..eb7ae97 100644 --- a/src/gui/views/wallets/content.rs +++ b/src/gui/views/wallets/content.rs @@ -206,20 +206,6 @@ impl WalletsContent { egui::Frame::default() } else { egui::Frame { - outer_margin: Margin { - left: if !root_dual_panel { - -0.5 - } else { - 0.0 - }, - right: if !wallet_panel_opened { - -0.5 - } else { - 0.0 - }, - top: 0.0, - bottom: 0.0, - }, stroke: View::item_stroke(), fill: Colors::fill_deep(), inner_margin: Margin { @@ -228,6 +214,16 @@ impl WalletsContent { top: 3.0, bottom: 4.0, }, + outer_margin: Margin { + left: 0.0, + right: if !wallet_panel_opened { + -0.5 + } else { + 0.0 + }, + top: 0.0, + bottom: 0.0, + }, ..Default::default() } }) diff --git a/src/gui/views/wallets/creation/creation.rs b/src/gui/views/wallets/creation/creation.rs index 91681c5..e3651b8 100644 --- a/src/gui/views/wallets/creation/creation.rs +++ b/src/gui/views/wallets/creation/creation.rs @@ -79,20 +79,6 @@ impl WalletCreation { top: 4.0, bottom: View::get_bottom_inset(), }, - outer_margin: if View::is_desktop() { - Margin { - left: if !Root::is_dual_panel_mode(ui) { - -0.5 - } else { - 0.0 - }, - right: -0.5, - top: 0.0, - bottom: -0.5, - } - } else { - Margin::ZERO - }, ..Default::default() }) .show_inside(ui, |ui| { diff --git a/src/gui/views/wallets/wallet/content.rs b/src/gui/views/wallets/wallet/content.rs index c8e6b0c..6a90ec6 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -103,11 +103,7 @@ impl WalletContent { bottom: 0.0, }, outer_margin: Margin { - left: if !dual_panel { - -0.5 - } else { - 0.0 - }, + left: -0.5, right: -0.5, top: 0.0, bottom: if dual_panel { @@ -156,11 +152,7 @@ impl WalletContent { egui::CentralPanel::default() .frame(egui::Frame { outer_margin: Margin { - left: if !dual_panel { - -0.5 - } else { - 0.0 - }, + left: -0.5, right: -0.5, top: 0.0, bottom: 0.0, diff --git a/src/main.rs b/src/main.rs index e25795a..457ba0d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![windows_subsystem = "windows"] + +use egui::os::OperatingSystem; + pub fn main() { #[allow(dead_code)] #[cfg(not(target_os = "android"))] @@ -74,17 +78,27 @@ fn real_main() { viewport, ..Default::default() }; - options.renderer = eframe::Renderer::Wgpu; + + // Use Glow renderer for Windows. + let is_windows = OperatingSystem::from_target_os() == OperatingSystem::Windows; + options.renderer = if is_windows { + eframe::Renderer::Glow + } else { + eframe::Renderer::Wgpu + }; match grim::start(options.clone(), grim::app_creator(App::new(platform.clone()))) { Ok(_) => {} - Err(_) => { - // Start with Glow renderer on error. + Err(e) => { + if is_windows { + panic!("{}", e); + } + // Start with another renderer on error. options.renderer = eframe::Renderer::Glow; match grim::start(options, grim::app_creator(App::new(platform))) { Ok(_) => {} - Err(_) => { - panic!("Impossible to render"); + Err(e) => { + panic!("{}", e); } } } -- 2.39.5 From 262cfdfca96b96375be35e3dc4154f30380b31b5 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 1 Jul 2024 16:17:14 +0300 Subject: [PATCH 18/28] ui: fix window stroke --- src/gui/views/network/content.rs | 18 +----------------- src/gui/views/root.rs | 12 +++++++++++- src/gui/views/wallets/content.rs | 10 ---------- src/gui/views/wallets/wallet/content.rs | 16 ++++++++++++---- 4 files changed, 24 insertions(+), 32 deletions(-) diff --git a/src/gui/views/network/content.rs b/src/gui/views/network/content.rs index 54dd3ca..9e7f2aa 100644 --- a/src/gui/views/network/content.rs +++ b/src/gui/views/network/content.rs @@ -79,12 +79,6 @@ impl NetworkContent { .resizable(false) .exact_width(ui.available_width()) .frame(egui::Frame { - outer_margin: Margin { - left: -0.5, - right: -0.5, - top: 0.0, - bottom: 0.0, - }, ..Default::default() }) .show_animated_inside(ui, !show_connections, |ui| { @@ -108,16 +102,6 @@ impl NetworkContent { // Show connections content. egui::CentralPanel::default() .frame(egui::Frame { - outer_margin: if show_connections { - Margin { - left: -0.5, - right: -0.5, - top: 0.0, - bottom: -0.5, - } - } else { - Margin::ZERO - }, ..Default::default() }) .show_inside(ui, |ui| { @@ -142,7 +126,7 @@ impl NetworkContent { 4.0 }, }, - fill: Colors::fill(), + fill: Colors::button(), ..Default::default() }) .show_inside(ui, |ui| { diff --git a/src/gui/views/root.rs b/src/gui/views/root.rs index 0e7701a..cc1261e 100644 --- a/src/gui/views/root.rs +++ b/src/gui/views/root.rs @@ -14,7 +14,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use egui::os::OperatingSystem; -use egui::{Align, Layout, RichText}; +use egui::{Align, Layout, Margin, RichText}; use lazy_static::lazy_static; use crate::gui::Colors; @@ -115,6 +115,16 @@ impl Root { .resizable(false) .exact_width(panel_width) .frame(egui::Frame { + outer_margin: Margin { + left: 0.0, + right: if dual_panel { + -0.5 + } else { + 0.0 + }, + top: 0.0, + bottom: 0.0, + }, ..Default::default() }) .show_animated_inside(ui, is_panel_open, |ui| { diff --git a/src/gui/views/wallets/content.rs b/src/gui/views/wallets/content.rs index eb7ae97..f854aeb 100644 --- a/src/gui/views/wallets/content.rs +++ b/src/gui/views/wallets/content.rs @@ -214,16 +214,6 @@ impl WalletsContent { top: 3.0, bottom: 4.0, }, - outer_margin: Margin { - left: 0.0, - right: if !wallet_panel_opened { - -0.5 - } else { - 0.0 - }, - top: 0.0, - bottom: 0.0, - }, ..Default::default() } }) diff --git a/src/gui/views/wallets/wallet/content.rs b/src/gui/views/wallets/wallet/content.rs index 6a90ec6..d7d29c8 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -103,8 +103,12 @@ impl WalletContent { bottom: 0.0, }, outer_margin: Margin { - left: -0.5, - right: -0.5, + left: if dual_panel { + -0.5 + } else { + 0.0 + }, + right: 0.0, top: 0.0, bottom: if dual_panel { -1.0 @@ -152,8 +156,12 @@ impl WalletContent { egui::CentralPanel::default() .frame(egui::Frame { outer_margin: Margin { - left: -0.5, - right: -0.5, + left: if dual_panel { + -0.5 + } else { + 0.0 + }, + right: 0.0, top: 0.0, bottom: 0.0, }, -- 2.39.5 From dafd2a1db39d31a779d55d72425eee25bc687382 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 1 Jul 2024 18:58:54 +0300 Subject: [PATCH 19/28] ui: fix panels margins --- src/gui/views/network/content.rs | 33 +++++++++++++++++++++++-- src/gui/views/root.rs | 12 +-------- src/gui/views/wallets/wallet/content.rs | 2 +- 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/src/gui/views/network/content.rs b/src/gui/views/network/content.rs index 9e7f2aa..1f6848e 100644 --- a/src/gui/views/network/content.rs +++ b/src/gui/views/network/content.rs @@ -53,7 +53,8 @@ impl NetworkContent { // Show integrated node tabs content. if !show_connections { - egui::TopBottomPanel::bottom("node_tabs_panel") + egui::TopBottomPanel::bottom("node_tabs_content") + .min_height(0.5) .resizable(false) .frame(egui::Frame { inner_margin: Margin { @@ -75,10 +76,24 @@ impl NetworkContent { } // Show current node tab content. - egui::SidePanel::right("node_tab_content_panel") + egui::SidePanel::right("node_tab_content") .resizable(false) .exact_width(ui.available_width()) .frame(egui::Frame { + outer_margin: if !show_connections { + Margin { + left: 0.0, + right: if !dual_panel { + -0.5 + } else { + 0.0 + }, + top: 0.0, + bottom: 0.0, + } + } else { + Margin::ZERO + }, ..Default::default() }) .show_animated_inside(ui, !show_connections, |ui| { @@ -102,6 +117,20 @@ impl NetworkContent { // Show connections content. egui::CentralPanel::default() .frame(egui::Frame { + outer_margin: if show_connections { + Margin { + left: 0.0, + right: if !dual_panel { + -0.5 + } else { + 0.0 + }, + top: 0.0, + bottom: 0.0, + } + } else { + Margin::ZERO + }, ..Default::default() }) .show_inside(ui, |ui| { diff --git a/src/gui/views/root.rs b/src/gui/views/root.rs index cc1261e..0e7701a 100644 --- a/src/gui/views/root.rs +++ b/src/gui/views/root.rs @@ -14,7 +14,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use egui::os::OperatingSystem; -use egui::{Align, Layout, Margin, RichText}; +use egui::{Align, Layout, RichText}; use lazy_static::lazy_static; use crate::gui::Colors; @@ -115,16 +115,6 @@ impl Root { .resizable(false) .exact_width(panel_width) .frame(egui::Frame { - outer_margin: Margin { - left: 0.0, - right: if dual_panel { - -0.5 - } else { - 0.0 - }, - top: 0.0, - bottom: 0.0, - }, ..Default::default() }) .show_animated_inside(ui, is_panel_open, |ui| { diff --git a/src/gui/views/wallets/wallet/content.rs b/src/gui/views/wallets/wallet/content.rs index d7d29c8..7d66dd5 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -132,7 +132,7 @@ impl WalletContent { // Show wallet tabs panel. let show_tabs = !Self::block_navigation_on_sync(wallet); - egui::TopBottomPanel::bottom("wallet_tabs") + egui::TopBottomPanel::bottom("wallet_tabs_content") .frame(egui::Frame { fill: Colors::fill(), inner_margin: Margin { -- 2.39.5 From 8b0bb2b7717089200e701abb41ddea31539a0066 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 1 Jul 2024 22:08:45 +0300 Subject: [PATCH 20/28] ui: window resize --- src/gui/app.rs | 12 +++--------- src/gui/views/views.rs | 11 ++--------- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/src/gui/app.rs b/src/gui/app.rs index 581045f..5480eae 100644 --- a/src/gui/app.rs +++ b/src/gui/app.rs @@ -353,15 +353,9 @@ impl App { let sense = egui::Sense::drag(); let area_resp = ui.interact(rect, id, sense).on_hover_cursor(cursor); if area_resp.dragged() { - let current_pos = area_resp.interact_pointer_pos(); - if let Some(pos) = current_pos { - if self.resize_direction.is_none() { - self.resize_direction = Some(direction.clone()); - ui.ctx().send_viewport_cmd(ViewportCommand::BeginResize(direction)); - } - ui.ctx().send_viewport_cmd(ViewportCommand::InnerSize( - pos.to_vec2() + egui::vec2(Root::WINDOW_FRAME_MARGIN, Root::WINDOW_FRAME_MARGIN) - )); + if self.resize_direction.is_none() { + self.resize_direction = Some(direction.clone()); + ui.ctx().send_viewport_cmd(ViewportCommand::BeginResize(direction)); } } if area_resp.drag_stopped() { diff --git a/src/gui/views/views.rs b/src/gui/views/views.rs index 80127fc..7f3d92f 100644 --- a/src/gui/views/views.rs +++ b/src/gui/views/views.rs @@ -83,15 +83,8 @@ impl View { /// Get width and height of app window. pub fn window_size(ui: &egui::Ui) -> (f32, f32) { - ui.ctx().input(|i| { - return match i.viewport().inner_rect { - None => { - let rect = i.screen_rect; - (rect.width(), rect.height()) - }, - Some(rect) => (rect.width(), rect.height()) - }; - }) + let rect = ui.ctx().screen_rect(); + (rect.width(), rect.height()) } /// Callback on Enter key press event. -- 2.39.5 From 6ef6d48b4a53f63ea83d1f9958f4e29f541ca11f Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 1 Jul 2024 22:28:09 +0300 Subject: [PATCH 21/28] ui: fullscreen support, default window height --- src/gui/app.rs | 52 +++++++++++++++++++++++++++++------------- src/gui/views/modal.rs | 21 +++++++++++++---- src/gui/views/root.rs | 5 +++- src/settings/config.rs | 2 +- 4 files changed, 58 insertions(+), 22 deletions(-) diff --git a/src/gui/app.rs b/src/gui/app.rs index 5480eae..5edd974 100644 --- a/src/gui/app.rs +++ b/src/gui/app.rs @@ -91,10 +91,20 @@ impl App { /// Draw custom resizeable window content. fn desktop_window_ui(&mut self, ui: &mut egui::Ui) { + let is_fullscreen = ui.ctx().input(|i| { + i.viewport().fullscreen.unwrap_or(false) + }); + let title_stroke_rect = { - let mut rect = ui.max_rect().shrink(Root::WINDOW_FRAME_MARGIN); - rect.max.y = Root::WINDOW_FRAME_MARGIN + Root::WINDOW_TITLE_HEIGHT + - TitlePanel::DEFAULT_HEIGHT + 0.5; + let mut rect = ui.max_rect(); + if !is_fullscreen { + rect = rect.shrink(Root::WINDOW_FRAME_MARGIN); + } + rect.max.y = if !is_fullscreen { + Root::WINDOW_FRAME_MARGIN + } else { + 0.0 + } + Root::WINDOW_TITLE_HEIGHT + TitlePanel::DEFAULT_HEIGHT + 0.5; rect }; let title_stroke = RectShape { @@ -118,7 +128,10 @@ impl App { ui.painter().add(title_stroke); let content_stroke_rect = { - let mut rect = ui.max_rect().shrink(Root::WINDOW_FRAME_MARGIN); + let mut rect = ui.max_rect(); + if !is_fullscreen { + rect = rect.shrink(Root::WINDOW_FRAME_MARGIN); + } let top = Root::WINDOW_TITLE_HEIGHT + TitlePanel::DEFAULT_HEIGHT + 0.5; rect.min += egui::vec2(0.0, top); rect @@ -139,20 +152,25 @@ impl App { ui.painter().add(content_stroke); // Draw window content. - let content_rect = ui.max_rect().shrink(Root::WINDOW_FRAME_MARGIN); + let mut content_rect = ui.max_rect(); + if !is_fullscreen { + content_rect = content_rect.shrink(Root::WINDOW_FRAME_MARGIN); + } ui.allocate_ui_at_rect(content_rect, |ui| { self.window_content(ui); }); // Setup resize areas. - self.resize_area_ui(ui, ResizeDirection::North); - self.resize_area_ui(ui, ResizeDirection::East); - self.resize_area_ui(ui, ResizeDirection::South); - self.resize_area_ui(ui, ResizeDirection::West); - self.resize_area_ui(ui, ResizeDirection::NorthWest); - self.resize_area_ui(ui, ResizeDirection::NorthEast); - self.resize_area_ui(ui, ResizeDirection::SouthEast); - self.resize_area_ui(ui, ResizeDirection::SouthWest); + if !is_fullscreen { + self.resize_area_ui(ui, ResizeDirection::North); + self.resize_area_ui(ui, ResizeDirection::East); + self.resize_area_ui(ui, ResizeDirection::South); + self.resize_area_ui(ui, ResizeDirection::West); + self.resize_area_ui(ui, ResizeDirection::NorthWest); + self.resize_area_ui(ui, ResizeDirection::NorthEast); + self.resize_area_ui(ui, ResizeDirection::SouthEast); + self.resize_area_ui(ui, ResizeDirection::SouthWest); + } } /// Draw window content for desktop. @@ -212,7 +230,9 @@ impl App { let interact_rect = { let mut rect = title_rect; - rect.min.y += Root::WINDOW_FRAME_MARGIN; + if !is_fullscreen { + rect.min.y += Root::WINDOW_FRAME_MARGIN; + } rect }; let title_resp = ui.interact( @@ -247,11 +267,11 @@ impl App { ); // Interact with the window title (drag to move window): - if title_resp.double_clicked() { + if !is_fullscreen && title_resp.double_clicked() { ui.ctx().send_viewport_cmd(ViewportCommand::Fullscreen(!is_fullscreen)); } - if title_resp.drag_started_by(egui::PointerButton::Primary) { + if !is_fullscreen && title_resp.drag_started_by(egui::PointerButton::Primary) { ui.ctx().send_viewport_cmd(ViewportCommand::StartDrag); } diff --git a/src/gui/views/modal.rs b/src/gui/views/modal.rs index 42fc37a..3c7dd77 100644 --- a/src/gui/views/modal.rs +++ b/src/gui/views/modal.rs @@ -160,9 +160,18 @@ impl Modal { /// Draw [`egui::Window`] with provided content. fn window_ui(&self, ctx: &egui::Context, add_content: impl FnOnce(&mut egui::Ui, &Modal)) { + let is_fullscreen = ctx.input(|i| { + i.viewport().fullscreen.unwrap_or(false) + }); + let mut rect = ctx.screen_rect(); if View::is_desktop() { - rect = rect.shrink(Root::WINDOW_FRAME_MARGIN - 0.5); + let margin = if !is_fullscreen { + Root::WINDOW_FRAME_MARGIN + } else { + 0.0 + }; + rect = rect.shrink(margin - 0.5); rect.min += egui::vec2(0.0, Root::WINDOW_TITLE_HEIGHT + 0.5); rect.max.x += 0.5; } @@ -185,7 +194,7 @@ impl Modal { let width = f32::min(available_width, Self::DEFAULT_WIDTH); // Show main content Window at given position. - let (content_align, content_offset) = self.modal_position(); + let (content_align, content_offset) = self.modal_position(is_fullscreen); let layer_id = egui::Window::new(format!("modal_window_{}", self.id)) .title_bar(false) .resizable(false) @@ -217,14 +226,18 @@ impl Modal { } /// Get [`egui::Window`] position based on [`ModalPosition`]. - fn modal_position(&self) -> (Align2, Vec2) { + fn modal_position(&self, is_fullscreen: bool) -> (Align2, Vec2) { let align = match self.position { ModalPosition::CenterTop => Align2::CENTER_TOP, 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 + if View::is_desktop() { - Root::WINDOW_TITLE_HEIGHT + Root::WINDOW_FRAME_MARGIN + Root::WINDOW_TITLE_HEIGHT + if !is_fullscreen { + Root::WINDOW_FRAME_MARGIN + } else { + 0.0 + } } else { 0.0 }; diff --git a/src/gui/views/root.rs b/src/gui/views/root.rs index 0e7701a..16ac38a 100644 --- a/src/gui/views/root.rs +++ b/src/gui/views/root.rs @@ -150,7 +150,10 @@ impl Root { let panel_width = if dual_panel { Self::SIDE_PANEL_WIDTH + View::get_left_inset() } else { - View::window_size(ui).0 - if View::is_desktop() { + let is_fullscreen = ui.ctx().input(|i| { + i.viewport().fullscreen.unwrap_or(false) + }); + View::window_size(ui).0 - if View::is_desktop() && !is_fullscreen { Self::WINDOW_FRAME_MARGIN * 2.0 } else { 0.0 diff --git a/src/settings/config.rs b/src/settings/config.rs index 8829a24..0f93df4 100644 --- a/src/settings/config.rs +++ b/src/settings/config.rs @@ -75,7 +75,7 @@ impl AppConfig { /// Default desktop window width. pub const DEFAULT_WIDTH: f32 = Root::SIDE_PANEL_WIDTH * 3.0 + Self::FRAME_MARGIN; /// Default desktop window height. - pub const DEFAULT_HEIGHT: f32 = 698.0 + Root::WINDOW_TITLE_HEIGHT + Self::FRAME_MARGIN; + pub const DEFAULT_HEIGHT: f32 = 706.0; /// Minimal desktop window width. pub const MIN_WIDTH: f32 = Root::SIDE_PANEL_WIDTH + Self::FRAME_MARGIN; /// Minimal desktop window height. -- 2.39.5 From f67aac23fcfbd2ab2afd07c3b0d6369a1169c928 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 1 Jul 2024 23:21:45 +0300 Subject: [PATCH 22/28] wix: ability to update --- wix/main.wxs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/wix/main.wxs b/wix/main.wxs index 2efdd50..2735064 100644 --- a/wix/main.wxs +++ b/wix/main.wxs @@ -8,9 +8,14 @@ - + + + -- 2.39.5 From 18138c9c66b1bf7c02e5e39ad4eb9d44fa3700bd Mon Sep 17 00:00:00 2001 From: ardocrat Date: Tue, 2 Jul 2024 16:40:07 +0300 Subject: [PATCH 23/28] build: macos and android fix --- Cargo.toml | 14 +++++++++++++- README.md | 2 +- macos/build_release.sh | 29 +++++++++++++++++++---------- scripts/android.sh | 23 ++++++++++++++++++++--- src/main.rs | 3 +-- 5 files changed, 54 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 47444fe..1eadf60 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,9 +9,21 @@ keywords = [ "crypto", "grin", "mimblewimble" ] edition = "2021" build = "src/build/build.rs" +[[bin]] +name = "grim-bin" +path = "src/main.rs" + [lib] name="grim" -crate_type=["cdylib", "rlib"] +crate-type = ["rlib"] + +[profile.release-apk] +inherits = "release" +strip = true +opt-level = "z" +lto = true +codegen-units = 1 +panic = "abort" [dependencies] log = "0.4" diff --git a/README.md b/README.md index a0049f5..142a2d3 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ To build and run application go to project directory and run: ``` cargo build --release -./target/release/grim +./target/release/grim-bin ``` ### Android diff --git a/macos/build_release.sh b/macos/build_release.sh index b8a2d39..32e32c8 100755 --- a/macos/build_release.sh +++ b/macos/build_release.sh @@ -1,10 +1,11 @@ #!/bin/bash +set -e case $1 in - x86|arm) + x86|arm|all) ;; *) - echo "Usage: release_macos.sh [platform]\n - platform: 'x86', 'arm'" >&2 + echo "Usage: release_macos.sh [platform]\n - platform: 'x86', 'arm', 'all'" >&2 exit 1 esac @@ -23,23 +24,31 @@ BASEDIR=$(cd $(dirname $0) && pwd) cd ${BASEDIR} cd .. -# Setup platform argument +# Setup platform +rustup target add x86_64-apple-darwin +rustup target add aarch64-apple-darwin + +rm -rf target/x86_64-apple-darwin +rm -rf target/aarch64-apple-darwin + [[ $1 == "x86" ]] && arch+=(x86_64-apple-darwin) [[ $1 == "arm" ]] && arch+=(aarch64-apple-darwin) +[[ $1 == "all" ]] && arch+=(universal2-apple-darwin) # Start release build with zig linker for cross-compilation -cargo install cargo-zigbuild +# zig 0.12 required +cargo install cargo-zigbuild@0.18.4 cargo zigbuild --release --target ${arch} -rm .intentionally-empty-file.o -yes | cp -rf target/${arch}/release/grim macos/Grim.app/Contents/MacOS/grim-bin +rm -rf .intentionally-empty-file.o +yes | cp -rf target/${arch}/release/grim-bin macos/Grim.app/Contents/MacOS -### Sign .app before distribution: -### rcodesign generate-self-signed-certificate -### rcodesign sign --pem-file cert.pem macos/Grim.app +### Sign .app resources: +#rcodesign generate-self-signed-certificate +#rcodesign sign --pem-file cert.pem macos/Grim.app # Create release package FILE_NAME=Grim-0.1.0-macos-$1.zip -rm target/${arch}/release/${FILE_NAME} +rm -rf target/${arch}/release/${FILE_NAME} cd macos zip -r ${FILE_NAME} Grim.app mv ${FILE_NAME} ../target/${arch}/release \ No newline at end of file diff --git a/scripts/android.sh b/scripts/android.sh index 5aa7579..5f1b790 100755 --- a/scripts/android.sh +++ b/scripts/android.sh @@ -24,7 +24,7 @@ cd .. # Setup release argument type=$1 -[[ ${type} == "release" ]] && release_param+=(--release) +[[ ${type} == "release" ]] && release_param="--profile release-apk" # Setup platform argument [[ $2 == "v7" ]] && arch+=(armeabi-v7a) @@ -39,12 +39,29 @@ type=$1 [[ $2 == "v8" ]] && rustup target install aarch64-linux-android # Build native code -mkdir -p android/app/src/main/jniLibs cargo install cargo-ndk + +rm -rf target/aarch64-linux-android +rm -rf target/armv7-linux-androideabi +mkdir -p android/app/src/main/jniLibs + +sed -i -e 's/"rlib"/"rlib","cdylib"/g' Cargo.toml + +# temp fix for https://stackoverflow.com/questions/57193895/error-use-of-undeclared-identifier-pthread-mutex-robust-cargo-build-liblmdb-s +success=0 +export CPPFLAGS="-DMDB_USE_ROBUST=0" && export CFLAGS="-DMDB_USE_ROBUST=0" cargo ndk -t ${arch} -o android/app/src/main/jniLibs build ${release_param} +unset CPPFLAGS && unset CFLAGS +cargo ndk -t ${arch} -o android/app/src/main/jniLibs build ${release_param} +if [ $? -eq 0 ] +then + success=1 +fi + +sed -i -e 's/"rlib","cdylib"/"rlib"/g' Cargo.toml # Build Android application and launch at all connected devices -if [ $? -eq 0 ] +if [ $success -eq 1 ] then cd android diff --git a/src/main.rs b/src/main.rs index 457ba0d..c65c6e9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,8 +14,6 @@ #![windows_subsystem = "windows"] -use egui::os::OperatingSystem; - pub fn main() { #[allow(dead_code)] #[cfg(not(target_os = "android"))] @@ -37,6 +35,7 @@ fn real_main() { use std::sync::Arc; use egui::pos2; + use egui::os::OperatingSystem; use eframe::icon_data::from_png_bytes; let platform = Desktop::default(); -- 2.39.5 From f255901a972346d4d4817188189be9168b110b1a Mon Sep 17 00:00:00 2001 From: ardocrat Date: Tue, 2 Jul 2024 17:07:37 +0300 Subject: [PATCH 24/28] build: do not clean android target --- scripts/android.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/android.sh b/scripts/android.sh index 5f1b790..297985b 100755 --- a/scripts/android.sh +++ b/scripts/android.sh @@ -40,9 +40,6 @@ type=$1 # Build native code cargo install cargo-ndk - -rm -rf target/aarch64-linux-android -rm -rf target/armv7-linux-androideabi mkdir -p android/app/src/main/jniLibs sed -i -e 's/"rlib"/"rlib","cdylib"/g' Cargo.toml @@ -51,8 +48,11 @@ sed -i -e 's/"rlib"/"rlib","cdylib"/g' Cargo.toml success=0 export CPPFLAGS="-DMDB_USE_ROBUST=0" && export CFLAGS="-DMDB_USE_ROBUST=0" cargo ndk -t ${arch} -o android/app/src/main/jniLibs build ${release_param} -unset CPPFLAGS && unset CFLAGS -cargo ndk -t ${arch} -o android/app/src/main/jniLibs build ${release_param} +if [ $? -eq 1 ] +then + unset CPPFLAGS && unset CFLAGS + cargo ndk -t ${arch} -o android/app/src/main/jniLibs build ${release_param} +fi if [ $? -eq 0 ] then success=1 -- 2.39.5 From 84d71e371df190d8803715396c3407ca4dae4f64 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Tue, 2 Jul 2024 22:34:07 +0300 Subject: [PATCH 25/28] build: fix desktop launch --- scripts/desktop.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/desktop.sh b/scripts/desktop.sh index e151ab2..c63e821 100755 --- a/scripts/desktop.sh +++ b/scripts/desktop.sh @@ -21,5 +21,5 @@ cargo build ${release_param[@]} # Start application if [ $? -eq 0 ] then - ./target/${type}/grim + ./target/${type}/grim-bin fi \ No newline at end of file -- 2.39.5 From 03a3dc097a351530d900f1e7ac526e66837192c4 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Tue, 2 Jul 2024 23:52:25 +0300 Subject: [PATCH 26/28] ui: root to content, macos related window fixes --- src/gui/app.rs | 144 +++++++++++---------- src/gui/views/{root.rs => content.rs} | 11 +- src/gui/views/mod.rs | 4 +- src/gui/views/modal.rs | 23 ++-- src/gui/views/network/content.rs | 110 ++++++---------- src/gui/views/network/metrics.rs | 4 +- src/gui/views/network/mining.rs | 4 +- src/gui/views/network/node.rs | 4 +- src/gui/views/network/settings.rs | 4 +- src/gui/views/title_panel.rs | 4 +- src/gui/views/wallets/content.rs | 44 +++---- src/gui/views/wallets/creation/creation.rs | 8 +- src/gui/views/wallets/creation/mnemonic.rs | 4 +- src/gui/views/wallets/wallet/content.rs | 14 +- src/gui/views/wallets/wallet/messages.rs | 4 +- src/gui/views/wallets/wallet/settings.rs | 4 +- src/gui/views/wallets/wallet/transport.rs | 4 +- src/gui/views/wallets/wallet/txs.rs | 6 +- src/main.rs | 7 +- src/settings/config.rs | 10 +- 20 files changed, 203 insertions(+), 214 deletions(-) rename src/gui/views/{root.rs => content.rs} (98%) diff --git a/src/gui/app.rs b/src/gui/app.rs index 5edd974..e9f9fb1 100644 --- a/src/gui/app.rs +++ b/src/gui/app.rs @@ -16,12 +16,13 @@ use std::sync::atomic::{AtomicBool, Ordering}; use lazy_static::lazy_static; use egui::{Align, Context, CursorIcon, Layout, Modifiers, Rect, ResizeDirection, Rounding, Stroke, ViewportCommand}; use egui::epaint::{RectShape}; +use egui::os::OperatingSystem; 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::{Content, TitlePanel, View}; lazy_static! { /// State to check if platform Back button was pressed. @@ -34,7 +35,7 @@ pub struct App { pub(crate) platform: Platform, /// Main ui content. - root: Root, + content: Content, /// Last window resize direction. resize_direction: Option @@ -42,16 +43,16 @@ pub struct App { impl App { pub fn new(platform: Platform) -> Self { - Self { platform, root: Root::default(), resize_direction: None } + Self { platform, content: Content::default(), resize_direction: None } } /// Draw application content. pub fn ui(&mut self, ctx: &Context) { // Handle Esc keyboard key event and platform Back button key event. - let back_button_pressed = BACK_BUTTON_PRESSED.load(Ordering::Relaxed); - if ctx.input_mut(|i| i.consume_key(Modifiers::NONE, egui::Key::Escape)) || back_button_pressed { - self.root.on_back(); - if back_button_pressed { + let back_pressed = BACK_BUTTON_PRESSED.load(Ordering::Relaxed); + if ctx.input_mut(|i| i.consume_key(Modifiers::NONE, egui::Key::Escape)) || back_pressed { + self.content.on_back(); + if back_pressed { BACK_BUTTON_PRESSED.store(false, Ordering::Relaxed); } // Request repaint to update previous content. @@ -60,9 +61,9 @@ impl App { // Handle Close event (on desktop). if ctx.input(|i| i.viewport().close_requested()) { - if !self.root.exit_allowed { + if !self.content.exit_allowed { ctx.send_viewport_cmd(ViewportCommand::CancelClose); - Root::show_exit_modal(); + Content::show_exit_modal(); } else { ctx.input(|i| { if let Some(rect) = i.viewport().inner_rect { @@ -81,10 +82,15 @@ impl App { ..Default::default() }) .show(ctx, |ui| { - if View::is_desktop() { + let is_mac_os = OperatingSystem::from_target_os() == OperatingSystem::Mac; + if View::is_desktop() && !is_mac_os { self.desktop_window_ui(ui); } else { - self.root.ui(ui, &self.platform); + if is_mac_os { + self.window_title_ui(ui); + ui.add_space(-1.0); + } + self.content.ui(ui, &self.platform); } }); } @@ -98,13 +104,13 @@ impl App { let title_stroke_rect = { let mut rect = ui.max_rect(); if !is_fullscreen { - rect = rect.shrink(Root::WINDOW_FRAME_MARGIN); + rect = rect.shrink(Content::WINDOW_FRAME_MARGIN); } rect.max.y = if !is_fullscreen { - Root::WINDOW_FRAME_MARGIN + Content::WINDOW_FRAME_MARGIN } else { 0.0 - } + Root::WINDOW_TITLE_HEIGHT + TitlePanel::DEFAULT_HEIGHT + 0.5; + } + Content::WINDOW_TITLE_HEIGHT + TitlePanel::DEFAULT_HEIGHT + 0.5; rect }; let title_stroke = RectShape { @@ -130,9 +136,9 @@ impl App { let content_stroke_rect = { let mut rect = ui.max_rect(); if !is_fullscreen { - rect = rect.shrink(Root::WINDOW_FRAME_MARGIN); + rect = rect.shrink(Content::WINDOW_FRAME_MARGIN); } - let top = Root::WINDOW_TITLE_HEIGHT + TitlePanel::DEFAULT_HEIGHT + 0.5; + let top = Content::WINDOW_TITLE_HEIGHT + TitlePanel::DEFAULT_HEIGHT + 0.5; rect.min += egui::vec2(0.0, top); rect }; @@ -154,9 +160,10 @@ impl App { // Draw window content. let mut content_rect = ui.max_rect(); if !is_fullscreen { - content_rect = content_rect.shrink(Root::WINDOW_FRAME_MARGIN); + content_rect = content_rect.shrink(Content::WINDOW_FRAME_MARGIN); } ui.allocate_ui_at_rect(content_rect, |ui| { + self.window_title_ui(ui); self.window_content(ui); }); @@ -175,11 +182,23 @@ impl App { /// Draw window content for desktop. fn window_content(&mut self, ui: &mut egui::Ui) { + let content_rect = { + let mut rect = ui.max_rect(); + rect.min.y += Content::WINDOW_TITLE_HEIGHT; + rect + }; + // Draw main content. + let mut content_ui = ui.child_ui(content_rect, *ui.layout(), None); + self.content.ui(&mut content_ui, &self.platform); + } + + /// Draw custom window title content. + fn window_title_ui(&self, ui: &mut egui::Ui) { let content_rect = ui.max_rect(); - let window_title_rect = { + let title_rect = { let mut rect = content_rect; - rect.max.y = rect.min.y + Root::WINDOW_TITLE_HEIGHT; + rect.max.y = rect.min.y + Content::WINDOW_TITLE_HEIGHT; rect }; @@ -188,7 +207,7 @@ impl App { }); let window_title_bg = RectShape { - rect: window_title_rect, + rect: title_rect, rounding: if is_fullscreen { Rounding::ZERO } else { @@ -205,33 +224,15 @@ impl App { fill_texture_id: Default::default(), uv: Rect::ZERO }; + // Draw title background. ui.painter().add(window_title_bg); - // Draw window title. - self.window_title_ui(ui, window_title_rect); - - let content_rect = { - let mut rect = content_rect; - rect.min.y = window_title_rect.max.y; - rect - }; - // Draw main content. - let mut content_ui = ui.child_ui(content_rect, *ui.layout(), None); - self.root.ui(&mut content_ui, &self.platform); - } - - /// Draw custom window title content. - fn window_title_ui(&self, ui: &mut egui::Ui, title_rect: Rect) { - let is_fullscreen = ui.ctx().input(|i| { - i.viewport().fullscreen.unwrap_or(false) - }); - let painter = ui.painter(); let interact_rect = { let mut rect = title_rect; if !is_fullscreen { - rect.min.y += Root::WINDOW_FRAME_MARGIN; + rect.min.y += Content::WINDOW_FRAME_MARGIN; } rect }; @@ -243,15 +244,15 @@ impl App { // 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(); + ui.available_width() >= (Content::SIDE_PANEL_WIDTH * 3.0) + View::get_right_inset(); + let wallet_panel_opened = self.content.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) { + self.content.wallets.showing_wallet() && !self.content.wallets.creating_wallet()) + } else if Content::is_dual_panel_mode(ui) { !wallet_panel_opened } else { - !Root::is_network_panel_open() && !wallet_panel_opened + !Content::is_network_panel_open() && !wallet_panel_opened }; let title_text = if hide_app_name { "ツ".to_string() @@ -279,7 +280,7 @@ impl App { 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(); + Content::show_exit_modal(); }); // Draw fullscreen button. @@ -323,47 +324,47 @@ impl App { // Setup area id, cursor and area rect based on direction. let (id, cursor, rect) = match direction { ResizeDirection::North => ("n", CursorIcon::ResizeNorth, { - rect.min.x += Root::WINDOW_FRAME_MARGIN * 2.0; - rect.max.y = rect.min.y + Root::WINDOW_FRAME_MARGIN; - rect.max.x -= Root::WINDOW_FRAME_MARGIN * 2.0; + rect.min.x += Content::WINDOW_FRAME_MARGIN * 2.0; + rect.max.y = rect.min.y + Content::WINDOW_FRAME_MARGIN; + rect.max.x -= Content::WINDOW_FRAME_MARGIN * 2.0; rect }), ResizeDirection::East => ("e", CursorIcon::ResizeEast, { - rect.min.y += Root::WINDOW_FRAME_MARGIN * 2.0; - rect.min.x = rect.max.x - Root::WINDOW_FRAME_MARGIN; - rect.max.y -= Root::WINDOW_FRAME_MARGIN * 2.0; + rect.min.y += Content::WINDOW_FRAME_MARGIN * 2.0; + rect.min.x = rect.max.x - Content::WINDOW_FRAME_MARGIN; + rect.max.y -= Content::WINDOW_FRAME_MARGIN * 2.0; rect }), ResizeDirection::South => ("s", CursorIcon::ResizeSouth, { - rect.min.x += Root::WINDOW_FRAME_MARGIN * 2.0; - rect.min.y = rect.max.y - Root::WINDOW_FRAME_MARGIN; - rect.max.x -= Root::WINDOW_FRAME_MARGIN * 2.0; + rect.min.x += Content::WINDOW_FRAME_MARGIN * 2.0; + rect.min.y = rect.max.y - Content::WINDOW_FRAME_MARGIN; + rect.max.x -= Content::WINDOW_FRAME_MARGIN * 2.0; rect }), ResizeDirection::West => ("w", CursorIcon::ResizeWest, { - rect.min.y += Root::WINDOW_FRAME_MARGIN * 2.0; - rect.max.x = rect.min.x + Root::WINDOW_FRAME_MARGIN; - rect.max.y -= Root::WINDOW_FRAME_MARGIN * 2.0; + rect.min.y += Content::WINDOW_FRAME_MARGIN * 2.0; + rect.max.x = rect.min.x + Content::WINDOW_FRAME_MARGIN; + rect.max.y -= Content::WINDOW_FRAME_MARGIN * 2.0; rect }), ResizeDirection::NorthWest => ("nw", CursorIcon::ResizeNorthWest, { - rect.max.y = rect.min.y + Root::WINDOW_FRAME_MARGIN * 2.0; - rect.max.x = rect.max.y + Root::WINDOW_FRAME_MARGIN * 2.0; + rect.max.y = rect.min.y + Content::WINDOW_FRAME_MARGIN * 2.0; + rect.max.x = rect.max.y + Content::WINDOW_FRAME_MARGIN * 2.0; rect }), ResizeDirection::NorthEast => ("ne", CursorIcon::ResizeNorthEast, { - rect.min.x = rect.max.x - Root::WINDOW_FRAME_MARGIN * 2.0; - rect.max.y = Root::WINDOW_FRAME_MARGIN * 2.0; + rect.min.x = rect.max.x - Content::WINDOW_FRAME_MARGIN * 2.0; + rect.max.y = Content::WINDOW_FRAME_MARGIN * 2.0; rect }), ResizeDirection::SouthEast => ("se", CursorIcon::ResizeSouthEast, { - rect.min.y = rect.max.y - Root::WINDOW_FRAME_MARGIN * 2.0; - rect.min.x = rect.max.x - Root::WINDOW_FRAME_MARGIN * 2.0; + rect.min.y = rect.max.y - Content::WINDOW_FRAME_MARGIN * 2.0; + rect.min.x = rect.max.x - Content::WINDOW_FRAME_MARGIN * 2.0; rect }), ResizeDirection::SouthWest => ("sw", CursorIcon::ResizeSouthWest, { - rect.min.y = rect.max.y - Root::WINDOW_FRAME_MARGIN * 2.0; - rect.max.x = rect.min.x + Root::WINDOW_FRAME_MARGIN * 2.0; + rect.min.y = rect.max.y - Content::WINDOW_FRAME_MARGIN * 2.0; + rect.max.x = rect.min.x + Content::WINDOW_FRAME_MARGIN * 2.0; rect }), }; @@ -391,7 +392,16 @@ impl eframe::App for App { } fn clear_color(&self, _visuals: &egui::Visuals) -> [f32; 4] { - egui::Rgba::TRANSPARENT.to_array() + if View::is_desktop() { + let is_mac_os = OperatingSystem::from_target_os() == OperatingSystem::Mac; + if is_mac_os { + Colors::fill().to_normalized_gamma_f32() + } else { + egui::Rgba::TRANSPARENT.to_array() + } + } else { + Colors::fill().to_normalized_gamma_f32() + } } } diff --git a/src/gui/views/root.rs b/src/gui/views/content.rs similarity index 98% rename from src/gui/views/root.rs rename to src/gui/views/content.rs index 16ac38a..5b46262 100644 --- a/src/gui/views/root.rs +++ b/src/gui/views/content.rs @@ -31,7 +31,7 @@ lazy_static! { } /// Contains main ui content, handles side panel state. -pub struct Root { +pub struct Content { /// Side panel [`NetworkContent`] content. network: NetworkContent, /// Central panel [`WalletsContent`] content. @@ -49,7 +49,7 @@ pub struct Root { allowed_modal_ids: Vec<&'static str> } -impl Default for Root { +impl Default for Content { fn default() -> Self { // Exit from eframe only for non-mobile platforms. let os = OperatingSystem::from_target_os(); @@ -69,7 +69,7 @@ impl Default for Root { } } -impl ModalContainer for Root { +impl ModalContainer for Content { fn modal_ids(&self) -> &Vec<&'static str> { &self.allowed_modal_ids } @@ -87,7 +87,7 @@ impl ModalContainer for Root { } } -impl Root { +impl Content { /// Identifier for exit confirmation [`Modal`]. pub const EXIT_MODAL_ID: &'static str = "exit_confirmation_modal"; /// Identifier for wallet opening [`Modal`]. @@ -153,7 +153,8 @@ impl Root { let is_fullscreen = ui.ctx().input(|i| { i.viewport().fullscreen.unwrap_or(false) }); - View::window_size(ui).0 - if View::is_desktop() && !is_fullscreen { + View::window_size(ui).0 - if View::is_desktop() && !is_fullscreen && + OperatingSystem::from_target_os() != OperatingSystem::Mac { Self::WINDOW_FRAME_MARGIN * 2.0 } else { 0.0 diff --git a/src/gui/views/mod.rs b/src/gui/views/mod.rs index e3a2f0b..3da79fa 100644 --- a/src/gui/views/mod.rs +++ b/src/gui/views/mod.rs @@ -23,8 +23,8 @@ pub use title_panel::*; mod modal; pub use modal::*; -mod root; -pub use root::*; +mod content; +pub use content::*; mod network; pub use network::*; diff --git a/src/gui/views/modal.rs b/src/gui/views/modal.rs index 3c7dd77..5917a3c 100644 --- a/src/gui/views/modal.rs +++ b/src/gui/views/modal.rs @@ -18,9 +18,10 @@ use parking_lot::RwLock; use std::sync::atomic::{AtomicBool, Ordering}; use egui::{Align2, Rect, RichText, Rounding, Stroke, Vec2}; use egui::epaint::{RectShape, Shadow}; +use egui::os::OperatingSystem; use crate::gui::Colors; -use crate::gui::views::{Root, View}; +use crate::gui::views::{Content, View}; use crate::gui::views::types::{ModalPosition, ModalState}; lazy_static! { @@ -45,7 +46,7 @@ impl Modal { /// Margin from [`Modal`] window at top/left/right. const DEFAULT_MARGIN: f32 = 8.0; /// Maximum width of the content. - const DEFAULT_WIDTH: f32 = Root::SIDE_PANEL_WIDTH - (2.0 * Self::DEFAULT_MARGIN); + const DEFAULT_WIDTH: f32 = Content::SIDE_PANEL_WIDTH - (2.0 * Self::DEFAULT_MARGIN); /// Create closeable [`Modal`] with center position. pub fn new(id: &'static str) -> Self { @@ -163,16 +164,17 @@ impl Modal { let is_fullscreen = ctx.input(|i| { i.viewport().fullscreen.unwrap_or(false) }); + let is_mac_os = OperatingSystem::from_target_os() == OperatingSystem::Mac; let mut rect = ctx.screen_rect(); - if View::is_desktop() { + if View::is_desktop() && !is_mac_os { let margin = if !is_fullscreen { - Root::WINDOW_FRAME_MARGIN + Content::WINDOW_FRAME_MARGIN } else { 0.0 }; rect = rect.shrink(margin - 0.5); - rect.min += egui::vec2(0.0, Root::WINDOW_TITLE_HEIGHT + 0.5); + rect.min += egui::vec2(0.0, Content::WINDOW_TITLE_HEIGHT + 0.5); rect.max.x += 0.5; } egui::Window::new("modal_bg_window") @@ -231,16 +233,21 @@ impl Modal { ModalPosition::CenterTop => Align2::CENTER_TOP, 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 + if View::is_desktop() { - Root::WINDOW_TITLE_HEIGHT + if !is_fullscreen { - Root::WINDOW_FRAME_MARGIN + + let is_mac_os = OperatingSystem::from_target_os() == OperatingSystem::Mac; + let extra_y = if View::is_desktop() && !is_mac_os { + Content::WINDOW_TITLE_HEIGHT + if !is_fullscreen { + Content::WINDOW_FRAME_MARGIN } else { 0.0 } } else { 0.0 }; + let y_align = View::get_top_inset() + Self::DEFAULT_MARGIN + extra_y; + 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 1f6848e..f99fd02 100644 --- a/src/gui/views/network/content.rs +++ b/src/gui/views/network/content.rs @@ -20,7 +20,7 @@ use crate::AppConfig; use crate::gui::Colors; use crate::gui::icons::{ARROWS_COUNTER_CLOCKWISE, BRIEFCASE, DATABASE, DOTS_THREE_OUTLINE_VERTICAL, FACTORY, FADERS, GAUGE, POWER}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{ConnectionsContent, NetworkMetrics, NetworkMining, NetworkNode, NetworkSettings, Root, TitlePanel, View}; +use crate::gui::views::{ConnectionsContent, NetworkMetrics, NetworkMining, NetworkNode, NetworkSettings, Content, TitlePanel, View}; use crate::gui::views::network::types::{NetworkTab, NetworkTabType}; use crate::gui::views::types::{TitleContentType, TitleType}; use crate::node::{Node, NodeError}; @@ -46,7 +46,7 @@ impl Default for NetworkContent { impl NetworkContent { pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { let show_connections = AppConfig::show_connections_network_panel(); - let dual_panel = Root::is_dual_panel_mode(ui); + let dual_panel = Content::is_dual_panel_mode(ui); // Show title panel. self.title_ui(ui, show_connections); @@ -68,7 +68,7 @@ impl NetworkContent { }) .show_inside(ui, |ui| { ui.vertical_centered(|ui| { - View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { self.tabs_ui(ui); }); }); @@ -80,20 +80,6 @@ impl NetworkContent { .resizable(false) .exact_width(ui.available_width()) .frame(egui::Frame { - outer_margin: if !show_connections { - Margin { - left: 0.0, - right: if !dual_panel { - -0.5 - } else { - 0.0 - }, - top: 0.0, - bottom: 0.0, - } - } else { - Margin::ZERO - }, ..Default::default() }) .show_animated_inside(ui, !show_connections, |ui| { @@ -117,65 +103,45 @@ impl NetworkContent { // Show connections content. egui::CentralPanel::default() .frame(egui::Frame { - outer_margin: if show_connections { - Margin { - left: 0.0, - right: if !dual_panel { - -0.5 - } else { - 0.0 - }, - top: 0.0, - bottom: 0.0, - } - } else { - Margin::ZERO + stroke: View::item_stroke(), + inner_margin: Margin { + left: if show_connections { + View::get_left_inset() + 4.0 + } else { + 0.0 + }, + right: if show_connections { + View::far_right_inset_margin(ui) + 4.0 + } else { + 0.0 + }, + top: 3.0, + bottom: if View::is_desktop() && show_connections { + 6.0 + } else { + 4.0 + }, }, + fill: Colors::button(), ..Default::default() }) .show_inside(ui, |ui| { - egui::CentralPanel::default() - .frame(egui::Frame { - stroke: View::item_stroke(), - inner_margin: Margin { - left: if show_connections { - View::get_left_inset() + 4.0 + ScrollArea::vertical() + .id_source("connections_content") + .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) + .auto_shrink([false; 2]) + .show(ui, |ui| { + ui.add_space(1.0); + ui.vertical_centered(|ui| { + let max_width = if !dual_panel { + Content::SIDE_PANEL_WIDTH * 1.3 } else { - 0.0 - }, - right: if show_connections { - View::far_right_inset_margin(ui) + 4.0 - } else { - 0.0 - }, - top: 3.0, - bottom: if View::is_desktop() && show_connections { - 6.0 - } else { - 4.0 - }, - }, - fill: Colors::button(), - ..Default::default() - }) - .show_inside(ui, |ui| { - ScrollArea::vertical() - .id_source("connections_content") - .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) - .auto_shrink([false; 2]) - .show(ui, |ui| { - ui.add_space(1.0); - ui.vertical_centered(|ui| { - let max_width = if !dual_panel { - Root::SIDE_PANEL_WIDTH * 1.3 - } else { - ui.available_width() - }; - View::max_width_ui(ui, max_width, |ui| { - self.connections.ui(ui, cb); - }); - }); + ui.available_width() + }; + View::max_width_ui(ui, max_width, |ui| { + self.connections.ui(ui, cb); }); + }); }); }); @@ -245,9 +211,9 @@ impl NetworkContent { }); } }, |ui| { - if !Root::is_dual_panel_mode(ui) { + if !Content::is_dual_panel_mode(ui) { View::title_button_big(ui, BRIEFCASE, |_| { - Root::toggle_network_panel(); + Content::toggle_network_panel(); }); } }, ui); diff --git a/src/gui/views/network/metrics.rs b/src/gui/views/network/metrics.rs index 3cb79ed..30e1129 100644 --- a/src/gui/views/network/metrics.rs +++ b/src/gui/views/network/metrics.rs @@ -19,7 +19,7 @@ use grin_servers::{DiffBlock, ServerStats}; use crate::gui::Colors; use crate::gui::icons::{AT, COINS, CUBE_TRANSPARENT, HOURGLASS_LOW, HOURGLASS_MEDIUM, TIMER}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{NetworkContent, Root, View}; +use crate::gui::views::{NetworkContent, Content, View}; use crate::gui::views::network::types::{NetworkTab, NetworkTabType}; use crate::node::Node; @@ -66,7 +66,7 @@ impl NetworkTab for NetworkMetrics { ui.add_space(1.0); ui.vertical_centered(|ui| { - View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { let stats = server_stats.as_ref().unwrap(); // Show emission and difficulty info. info_ui(ui, stats); diff --git a/src/gui/views/network/mining.rs b/src/gui/views/network/mining.rs index f02a5da..9961a5d 100644 --- a/src/gui/views/network/mining.rs +++ b/src/gui/views/network/mining.rs @@ -20,7 +20,7 @@ use grin_servers::WorkerStats; use crate::gui::Colors; use crate::gui::icons::{BARBELL, CLOCK_AFTERNOON, CPU, CUBE, FADERS, FOLDER_DASHED, FOLDER_SIMPLE_MINUS, FOLDER_SIMPLE_PLUS, HARD_DRIVES, PLUGS, PLUGS_CONNECTED, POLYGON}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{NetworkContent, Root, View}; +use crate::gui::views::{NetworkContent, Content, View}; use crate::gui::views::network::setup::StratumSetup; use crate::gui::views::network::types::{NetworkTab, NetworkTabType}; use crate::node::{Node, NodeConfig}; @@ -82,7 +82,7 @@ impl NetworkTab for NetworkMining { .show(ui, |ui| { ui.add_space(1.0); ui.vertical_centered(|ui| { - View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { self.stratum_server_setup.ui(ui, cb); }); }); diff --git a/src/gui/views/network/node.rs b/src/gui/views/network/node.rs index ba40bad..db75190 100644 --- a/src/gui/views/network/node.rs +++ b/src/gui/views/network/node.rs @@ -19,7 +19,7 @@ use grin_servers::PeerStats; use crate::gui::Colors; use crate::gui::icons::{AT, CUBE, DEVICES, FLOW_ARROW, HANDSHAKE, PACKAGE, SHARE_NETWORK}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{NetworkContent, Root, View}; +use crate::gui::views::{NetworkContent, Content, View}; use crate::gui::views::network::types::{NetworkTab, NetworkTabType}; use crate::node::{Node, NodeConfig}; @@ -60,7 +60,7 @@ impl NetworkTab for NetworkNode { .show(ui, |ui| { ui.add_space(2.0); ui.vertical_centered(|ui| { - View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { // Show node stats content. node_stats_ui(ui); }); diff --git a/src/gui/views/network/settings.rs b/src/gui/views/network/settings.rs index 6f40452..7705d87 100644 --- a/src/gui/views/network/settings.rs +++ b/src/gui/views/network/settings.rs @@ -18,7 +18,7 @@ use egui::scroll_area::ScrollBarVisibility; use crate::gui::Colors; use crate::gui::icons::ARROW_COUNTER_CLOCKWISE; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Modal, Root, View}; +use crate::gui::views::{Modal, Content, View}; use crate::gui::views::network::setup::{DandelionSetup, NodeSetup, P2PSetup, PoolSetup, StratumSetup}; use crate::gui::views::network::types::{NetworkTab, NetworkTabType}; use crate::gui::views::types::{ModalContainer, ModalPosition}; @@ -91,7 +91,7 @@ impl NetworkTab for NetworkSettings { .show(ui, |ui| { ui.add_space(1.0); ui.vertical_centered(|ui| { - View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { // Draw node setup section. self.node.ui(ui, cb); diff --git a/src/gui/views/title_panel.rs b/src/gui/views/title_panel.rs index e3a7b84..c14b13d 100644 --- a/src/gui/views/title_panel.rs +++ b/src/gui/views/title_panel.rs @@ -16,7 +16,7 @@ use egui::{Margin, Id}; use egui_extras::{Size, Strip, StripBuilder}; use crate::gui::Colors; -use crate::gui::views::{Root, View}; +use crate::gui::views::{Content, View}; use crate::gui::views::types::{TitleContentType, TitleType}; /// Title panel with left/right action buttons and text in the middle. @@ -66,7 +66,7 @@ impl TitlePanel { StripBuilder::new(ui) .size(Size::exact(Self::DEFAULT_HEIGHT)) .size(if dual_title { - Size::exact(Root::SIDE_PANEL_WIDTH - 2.0 * Self::DEFAULT_HEIGHT) + Size::exact(Content::SIDE_PANEL_WIDTH - 2.0 * Self::DEFAULT_HEIGHT) } else { Size::remainder() }) diff --git a/src/gui/views/wallets/content.rs b/src/gui/views/wallets/content.rs index f854aeb..1e0fe16 100644 --- a/src/gui/views/wallets/content.rs +++ b/src/gui/views/wallets/content.rs @@ -20,7 +20,7 @@ use crate::AppConfig; use crate::gui::Colors; use crate::gui::icons::{ARROW_LEFT, CARET_RIGHT, COMPUTER_TOWER, FOLDER_LOCK, FOLDER_OPEN, GEAR, GLOBE, GLOBE_SIMPLE, LOCK_KEY, PLUS, SIDEBAR_SIMPLE, SPINNER, SUITCASE, WARNING_CIRCLE}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Modal, Root, TitlePanel, View}; +use crate::gui::views::{Modal, Content, TitlePanel, View}; use crate::gui::views::types::{ModalContainer, ModalPosition, TextEditOptions, TitleContentType, TitleType}; use crate::gui::views::wallets::creation::WalletCreation; use crate::gui::views::wallets::types::WalletTabType; @@ -103,19 +103,22 @@ impl WalletsContent { let wallet_panel_width = self.wallet_panel_width(ui, empty_list, dual_panel, show_wallet); let content_width = ui.available_width(); - let root_dual_panel = Root::is_dual_panel_mode(ui); + let root_dual_panel = Content::is_dual_panel_mode(ui); // Flag to check if wallet list is hidden on the screen. let list_hidden = content_width == 0.0 || empty_list || create_wallet || (dual_panel && show_wallet && !self.show_wallets_at_dual_panel) || (!dual_panel && show_wallet) || - (!root_dual_panel && Root::is_network_panel_open()); + (!root_dual_panel && Content::is_network_panel_open()); + + let wallet_panel_opened = self.wallet_panel_opened(); // Show title panel. - self.title_ui(ui, dual_panel, create_wallet, show_wallet); + if !list_hidden || wallet_panel_opened { + self.title_ui(ui, dual_panel, create_wallet, show_wallet); + } // Show wallet panel content. - let wallet_panel_opened = self.wallet_panel_opened(); egui::SidePanel::right("wallet_panel") .resizable(false) .exact_width(wallet_panel_width) @@ -133,10 +136,10 @@ impl WalletsContent { ..Default::default() }) .show_animated_inside(ui, wallet_panel_opened, |ui| { - // // Do not draw content on zero width. - // if content_width == 0.0 { - // return; - // } + // Do not draw content on zero width. + if content_width == 0.0 { + return; + } if create_wallet || !show_wallet { // Show wallet creation content. self.creation_content.ui(ui, cb, |wallet| { @@ -155,7 +158,7 @@ impl WalletsContent { let mut rect = ui.available_rect_before_wrap(); let mut width = ui.available_width(); if dual_panel && self.show_wallets_at_dual_panel { - width = content_width - Root::SIDE_PANEL_WIDTH; + width = content_width - Content::SIDE_PANEL_WIDTH; } rect.set_width(width); // Show wallet content. @@ -168,11 +171,8 @@ impl WalletsContent { } }); - // Setup flag to show wallets bottom panel if wallet is not showing - // at non-dual panel mode and network is no open or showing at dual panel mode. - let show_bottom_panel = !list_hidden || dual_panel; - // Show wallets bottom panel. + let show_bottom_panel = !list_hidden || dual_panel; if show_bottom_panel { egui::TopBottomPanel::bottom("wallets_bottom_panel") .frame(egui::Frame { @@ -305,15 +305,15 @@ impl WalletsContent { self.show_wallets_at_dual_panel = !show_list; AppConfig::toggle_show_wallets_at_dual_panel(); }); - } else if !Root::is_dual_panel_mode(ui) { + } else if !Content::is_dual_panel_mode(ui) { View::title_button_big(ui, GLOBE, |_| { - Root::toggle_network_panel(); + Content::toggle_network_panel(); }); }; }, |ui| { View::title_button_big(ui, GEAR, |_| { // Show settings modal. - Modal::new(Root::SETTINGS_MODAL) + Modal::new(Content::SETTINGS_MODAL) .position(ModalPosition::CenterTop) .title(t!("settings")) .show(); @@ -334,10 +334,10 @@ impl WalletsContent { || (show_wallet && !self.show_wallets_at_dual_panel) { ui.available_width() } else { - ui.available_width() - Root::SIDE_PANEL_WIDTH + ui.available_width() - Content::SIDE_PANEL_WIDTH }; if dual_panel && show_wallet && self.show_wallets_at_dual_panel { - let min_width = Root::SIDE_PANEL_WIDTH + View::get_right_inset(); + let min_width = Content::SIDE_PANEL_WIDTH + View::get_right_inset(); f32::max(min_width, available_width) } else { available_width @@ -354,7 +354,7 @@ impl WalletsContent { .auto_shrink([false; 2]) .show(ui, |ui| { ui.vertical_centered(|ui| { - View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { // Show application logo and name. View::app_logo_name_version(ui); ui.add_space(15.0); @@ -613,7 +613,7 @@ impl WalletsContent { /// Check if it's possible to show [`WalletsContent`] and [`WalletContent`] panels at same time. fn is_dual_panel_mode(ui: &mut egui::Ui) -> bool { - let dual_panel_root = Root::is_dual_panel_mode(ui); + let dual_panel_root = Content::is_dual_panel_mode(ui); let max_width = ui.available_width(); - dual_panel_root && max_width >= (Root::SIDE_PANEL_WIDTH * 2.0) + View::get_right_inset() + dual_panel_root && max_width >= (Content::SIDE_PANEL_WIDTH * 2.0) + View::get_right_inset() } \ No newline at end of file diff --git a/src/gui/views/wallets/creation/creation.rs b/src/gui/views/wallets/creation/creation.rs index e3651b8..5e712ac 100644 --- a/src/gui/views/wallets/creation/creation.rs +++ b/src/gui/views/wallets/creation/creation.rs @@ -19,7 +19,7 @@ use grin_util::ZeroingString; use crate::gui::Colors; use crate::gui::icons::{CHECK, CLIPBOARD_TEXT, COPY, FOLDER_PLUS, SCAN, SHARE_FAT}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Modal, Root, View}; +use crate::gui::views::{Modal, Content, View}; use crate::gui::views::types::{ModalPosition, TextEditOptions}; use crate::gui::views::wallets::creation::MnemonicSetup; use crate::gui::views::wallets::creation::types::Step; @@ -84,7 +84,7 @@ impl WalletCreation { .show_inside(ui, |ui| { ui.vertical_centered(|ui| { ui.vertical_centered(|ui| { - View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 2.0, |ui| { + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 2.0, |ui| { self.step_control_ui(ui, on_create, cb); }); }); @@ -118,9 +118,9 @@ impl WalletCreation { .show(ui, |ui| { ui.vertical_centered(|ui| { let max_width = if self.step == Some(Step::SetupConnection) { - Root::SIDE_PANEL_WIDTH * 1.3 + Content::SIDE_PANEL_WIDTH * 1.3 } else { - Root::SIDE_PANEL_WIDTH * 2.0 + Content::SIDE_PANEL_WIDTH * 2.0 }; View::max_width_ui(ui, max_width, |ui| { self.step_content_ui(ui, cb); diff --git a/src/gui/views/wallets/creation/mnemonic.rs b/src/gui/views/wallets/creation/mnemonic.rs index f243a45..0df7ce1 100644 --- a/src/gui/views/wallets/creation/mnemonic.rs +++ b/src/gui/views/wallets/creation/mnemonic.rs @@ -17,7 +17,7 @@ use egui::{Id, RichText}; use crate::gui::Colors; use crate::gui::icons::PENCIL; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{CameraContent, Modal, Root, View}; +use crate::gui::views::{CameraContent, Modal, Content, View}; use crate::gui::views::types::{ModalContainer, ModalPosition, QrScanResult, TextEditOptions}; use crate::wallet::Mnemonic; use crate::wallet::types::{PhraseMode, PhraseSize}; @@ -434,7 +434,7 @@ impl MnemonicSetup { /// Calculate word list columns count based on available ui width. fn list_columns_count(ui: &mut egui::Ui) -> usize { let w = ui.available_width(); - let min_panel_w = Root::SIDE_PANEL_WIDTH - 12.0; + let min_panel_w = Content::SIDE_PANEL_WIDTH - 12.0; let double_min_panel_w = min_panel_w * 2.0; if w >= min_panel_w * 1.5 && w < double_min_panel_w { 3 diff --git a/src/gui/views/wallets/wallet/content.rs b/src/gui/views/wallets/wallet/content.rs index 7d66dd5..69244a8 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -22,7 +22,7 @@ use crate::AppConfig; use crate::gui::Colors; use crate::gui::icons::{ARROWS_CLOCKWISE, BRIDGE, CHAT_CIRCLE_TEXT, CHECK, CHECK_FAT, COPY, FOLDER_USER, GEAR_FINE, GRAPH, PACKAGE, PATH, POWER, SCAN, SPINNER, USERS_THREE}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{CameraContent, Modal, Root, View}; +use crate::gui::views::{CameraContent, Modal, Content, View}; use crate::gui::views::types::{ModalPosition, QrScanResult, TextEditOptions}; use crate::gui::views::wallets::{WalletTransactions, WalletMessages, WalletTransport, WalletSettings}; use crate::gui::views::wallets::types::{GRIN, WalletTab, WalletTabType}; @@ -80,7 +80,7 @@ impl WalletContent { // Show modal content for this ui container. self.modal_content_ui(ui, wallet, cb); - let dual_panel = Root::is_dual_panel_mode(ui); + let dual_panel = Content::is_dual_panel_mode(ui); let data = wallet.get_data(); let data_empty = data.is_none(); @@ -124,7 +124,7 @@ impl WalletContent { ui.add_space(1.0); } // Draw account info. - View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { self.account_ui(ui, wallet, data.unwrap(), cb); }); }); @@ -146,7 +146,7 @@ impl WalletContent { .show_animated_inside(ui, show_tabs, |ui| { ui.vertical_centered(|ui| { // Draw wallet tabs. - View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { self.tabs_ui(ui, wallet); }); }); @@ -576,13 +576,13 @@ impl WalletContent { } else if wallet.get_current_ext_conn().is_none() { if !Node::is_running() || Node::is_stopping() { View::center_content(ui, 108.0, |ui| { - View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.5, |ui| { + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.5, |ui| { let text = t!("wallets.enable_node", "settings" => GEAR_FINE); ui.label(RichText::new(text).size(16.0).color(Colors::inactive_text())); ui.add_space(8.0); // Show button to enable integrated node at non-dual root panel mode // or when network connections are not showing and node is not stopping - let dual_panel_root = Root::is_dual_panel_mode(ui); + let dual_panel_root = Content::is_dual_panel_mode(ui); if (!dual_panel_root || AppConfig::show_connections_network_panel()) && !Node::is_stopping() { let enable_text = format!("{} {}", POWER, t!("network.enable_node")); @@ -640,7 +640,7 @@ impl WalletContent { /// Draw wallet sync progress content. pub fn sync_progress_ui(ui: &mut egui::Ui, wallet: &Wallet) { View::center_content(ui, 162.0, |ui| { - View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.5, |ui| { + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.5, |ui| { View::big_loading_spinner(ui); ui.add_space(18.0); // Setup sync progress text. diff --git a/src/gui/views/wallets/wallet/messages.rs b/src/gui/views/wallets/wallet/messages.rs index 7da37f7..1306b1e 100644 --- a/src/gui/views/wallets/wallet/messages.rs +++ b/src/gui/views/wallets/wallet/messages.rs @@ -24,7 +24,7 @@ use parking_lot::RwLock; use crate::gui::Colors; use crate::gui::icons::{BROOM, CLIPBOARD_TEXT, COPY, DOWNLOAD_SIMPLE, PROHIBIT, QR_CODE, SCAN, UPLOAD_SIMPLE}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{CameraContent, FilePickButton, Modal, QrCodeContent, Root, View}; +use crate::gui::views::{CameraContent, FilePickButton, Modal, QrCodeContent, Content, View}; use crate::gui::views::types::{ModalPosition, QrScanResult, TextEditOptions}; use crate::gui::views::wallets::wallet::types::{SLATEPACK_MESSAGE_HINT, WalletTab, WalletTabType}; use crate::gui::views::wallets::wallet::WalletContent; @@ -144,7 +144,7 @@ impl WalletTab for WalletMessages { .auto_shrink([false; 2]) .show(ui, |ui| { ui.vertical_centered(|ui| { - View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { self.ui(ui, wallet, cb); }); }); diff --git a/src/gui/views/wallets/wallet/settings.rs b/src/gui/views/wallets/wallet/settings.rs index ca7e095..bec083b 100644 --- a/src/gui/views/wallets/wallet/settings.rs +++ b/src/gui/views/wallets/wallet/settings.rs @@ -17,7 +17,7 @@ use egui::scroll_area::ScrollBarVisibility; use crate::gui::Colors; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Root, View}; +use crate::gui::views::{Content, View}; use crate::gui::views::wallets::setup::{CommonSetup, ConnectionSetup, RecoverySetup}; use crate::gui::views::wallets::wallet::types::{WalletTab, WalletTabType}; use crate::gui::views::wallets::wallet::WalletContent; @@ -80,7 +80,7 @@ impl WalletTab for WalletSettings { .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) .show(ui, |ui| { ui.vertical_centered(|ui| { - View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { // Show common wallet setup. self.common_setup.ui(ui, wallet, cb); // Show wallet connections setup. diff --git a/src/gui/views/wallets/wallet/transport.rs b/src/gui/views/wallets/wallet/transport.rs index ec5a9c8..e3192cd 100644 --- a/src/gui/views/wallets/wallet/transport.rs +++ b/src/gui/views/wallets/wallet/transport.rs @@ -26,7 +26,7 @@ use grin_wallet_libwallet::SlatepackAddress; use crate::gui::Colors; use crate::gui::icons::{CHECK_CIRCLE, COPY, DOTS_THREE_CIRCLE, EXPORT, GEAR_SIX, GLOBE_SIMPLE, POWER, QR_CODE, SHIELD_CHECKERED, SHIELD_SLASH, WARNING_CIRCLE, X_CIRCLE}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{CameraContent, Modal, QrCodeContent, Root, View}; +use crate::gui::views::{CameraContent, Modal, QrCodeContent, Content, View}; use crate::gui::views::types::{ModalPosition, TextEditOptions}; use crate::gui::views::wallets::wallet::types::{WalletTab, WalletTabType}; use crate::gui::views::wallets::wallet::WalletContent; @@ -106,7 +106,7 @@ impl WalletTab for WalletTransport { .auto_shrink([false; 2]) .show(ui, |ui| { ui.vertical_centered(|ui| { - View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { self.ui(ui, wallet, cb); }); }); diff --git a/src/gui/views/wallets/wallet/txs.rs b/src/gui/views/wallets/wallet/txs.rs index 55f05ba..1fd7053 100644 --- a/src/gui/views/wallets/wallet/txs.rs +++ b/src/gui/views/wallets/wallet/txs.rs @@ -25,7 +25,7 @@ use parking_lot::RwLock; use crate::gui::Colors; use crate::gui::icons::{ARROW_CIRCLE_DOWN, ARROW_CIRCLE_UP, ARROW_CLOCKWISE, BRIDGE, BROOM, CALENDAR_CHECK, CHAT_CIRCLE_TEXT, CHECK, CHECK_CIRCLE, CLIPBOARD_TEXT, COPY, DOTS_THREE_CIRCLE, FILE_ARCHIVE, FILE_TEXT, GEAR_FINE, HASH_STRAIGHT, PROHIBIT, QR_CODE, SCAN, X_CIRCLE}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{CameraContent, FilePickButton, Modal, PullToRefresh, QrCodeContent, Root, View}; +use crate::gui::views::{CameraContent, FilePickButton, Modal, PullToRefresh, QrCodeContent, Content, View}; use crate::gui::views::types::ModalPosition; use crate::gui::views::wallets::types::WalletTab; use crate::gui::views::wallets::wallet::types::{GRIN, SLATEPACK_MESSAGE_HINT, WalletTabType}; @@ -145,7 +145,7 @@ impl WalletTransactions { let amount_conf = data.info.amount_awaiting_confirmation; let amount_fin = data.info.amount_awaiting_finalization; let amount_locked = data.info.amount_locked; - View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { // Show non-zero awaiting confirmation amount. if amount_conf != 0 { let awaiting_conf = amount_to_hr_string(amount_conf, true); @@ -222,7 +222,7 @@ impl WalletTransactions { .auto_shrink([false; 2]) .show_rows(ui, TX_ITEM_HEIGHT, txs.len(), |ui, row_range| { ui.add_space(1.0); - View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { let padding = amount_conf != 0 || amount_fin != 0 || amount_locked != 0; for index in row_range { let tx = txs.get(index).unwrap(); diff --git a/src/main.rs b/src/main.rs index c65c6e9..957aa07 100644 --- a/src/main.rs +++ b/src/main.rs @@ -69,9 +69,14 @@ fn real_main() { } // Setup window decorations. + let is_mac_os = OperatingSystem::from_target_os() == OperatingSystem::Mac; viewport = viewport + .with_fullsize_content_view(true) + .with_title_shown(false) + .with_titlebar_buttons_shown(false) + .with_titlebar_shown(false) .with_transparent(true) - .with_decorations(false); + .with_decorations(is_mac_os); let mut options = eframe::NativeOptions { viewport, diff --git a/src/settings/config.rs b/src/settings/config.rs index 0f93df4..65290a3 100644 --- a/src/settings/config.rs +++ b/src/settings/config.rs @@ -14,7 +14,7 @@ use grin_core::global::ChainTypes; use serde_derive::{Deserialize, Serialize}; -use crate::gui::views::Root; +use crate::gui::views::Content; use crate::node::{NodeConfig, PeersConfig}; use crate::Settings; @@ -71,15 +71,15 @@ impl Default for AppConfig { impl AppConfig { /// Desktop window frame margin sum, horizontal or vertical. - const FRAME_MARGIN: f32 = Root::WINDOW_FRAME_MARGIN * 2.0; + const FRAME_MARGIN: f32 = Content::WINDOW_FRAME_MARGIN * 2.0; /// Default desktop window width. - pub const DEFAULT_WIDTH: f32 = Root::SIDE_PANEL_WIDTH * 3.0 + Self::FRAME_MARGIN; + pub const DEFAULT_WIDTH: f32 = Content::SIDE_PANEL_WIDTH * 3.0 + Self::FRAME_MARGIN; /// Default desktop window height. pub const DEFAULT_HEIGHT: f32 = 706.0; /// Minimal desktop window width. - pub const MIN_WIDTH: f32 = Root::SIDE_PANEL_WIDTH + Self::FRAME_MARGIN; + pub const MIN_WIDTH: f32 = Content::SIDE_PANEL_WIDTH + Self::FRAME_MARGIN; /// Minimal desktop window height. - pub const MIN_HEIGHT: f32 = 630.0 + Root::WINDOW_TITLE_HEIGHT + Self::FRAME_MARGIN; + pub const MIN_HEIGHT: f32 = 630.0 + Content::WINDOW_TITLE_HEIGHT + Self::FRAME_MARGIN; /// Application configuration file name. pub const FILE_NAME: &'static str = "app.toml"; -- 2.39.5 From b56b3f7cfc763fa087b1ed36329d5409442f3674 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Wed, 3 Jul 2024 13:41:01 +0300 Subject: [PATCH 27/28] build: apk release opt level --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 1eadf60..38611ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ crate-type = ["rlib"] [profile.release-apk] inherits = "release" strip = true -opt-level = "z" +opt-level = 3 lto = true codegen-units = 1 panic = "abort" -- 2.39.5 From fd1d74803b8de9d1244821df5a3060d5d6aa7c9a Mon Sep 17 00:00:00 2001 From: ardocrat Date: Wed, 3 Jul 2024 13:43:13 +0300 Subject: [PATCH 28/28] ui: show wallets content on panels switch --- src/gui/views/wallets/content.rs | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/src/gui/views/wallets/content.rs b/src/gui/views/wallets/content.rs index 1e0fe16..a05f6fe 100644 --- a/src/gui/views/wallets/content.rs +++ b/src/gui/views/wallets/content.rs @@ -111,14 +111,11 @@ impl WalletsContent { || (!dual_panel && show_wallet) || (!root_dual_panel && Content::is_network_panel_open()); - let wallet_panel_opened = self.wallet_panel_opened(); - // Show title panel. - if !list_hidden || wallet_panel_opened { - self.title_ui(ui, dual_panel, create_wallet, show_wallet); - } + self.title_ui(ui, dual_panel, create_wallet, show_wallet); // Show wallet panel content. + let wallet_panel_opened = self.wallet_panel_opened(); egui::SidePanel::right("wallet_panel") .resizable(false) .exact_width(wallet_panel_width) @@ -136,10 +133,6 @@ impl WalletsContent { ..Default::default() }) .show_animated_inside(ui, wallet_panel_opened, |ui| { - // Do not draw content on zero width. - if content_width == 0.0 { - return; - } if create_wallet || !show_wallet { // Show wallet creation content. self.creation_content.ui(ui, cb, |wallet| { @@ -218,11 +211,7 @@ impl WalletsContent { } }) .show_inside(ui, |ui| { - if list_hidden { - return; - } - // Update ui after 1 sec at single panel mode. - if !dual_panel { + if !list_hidden && !dual_panel { ui.ctx().request_repaint_after(Duration::from_millis(1000)); } self.wallet_list_ui(ui, cb); -- 2.39.5