diff --git a/src/gui/app.rs b/src/gui/app.rs index d3ae3cb..5c0ff5b 100755 --- a/src/gui/app.rs +++ b/src/gui/app.rs @@ -59,8 +59,10 @@ impl App { if View::is_desktop() { self.platform.set_context(ctx); } - // Check connections availability. - ExternalConnection::check(None, ctx); + // Check connections availability at dual panel mode. + if Content::is_dual_panel_mode(ctx) && AppConfig::show_connections_network_panel() { + ExternalConnection::check(None, ctx); + } // Setup visuals. crate::setup_visuals(ctx); } diff --git a/src/gui/views/content.rs b/src/gui/views/content.rs index 23019d1..6135b8d 100644 --- a/src/gui/views/content.rs +++ b/src/gui/views/content.rs @@ -12,31 +12,35 @@ // See the License for the specific language governing permissions and // limitations under the License. +use egui::os::OperatingSystem; +use egui::RichText; +use lazy_static::lazy_static; use std::fs; use std::sync::atomic::{AtomicBool, Ordering}; -use egui::os::OperatingSystem; -use egui::{Align, Layout, RichText, StrokeKind}; -use lazy_static::lazy_static; -use crate::gui::Colors; +use crate::gui::icons::FILE_X; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Modal, View}; +use crate::gui::views::network::NetworkContent; use crate::gui::views::types::{ModalContainer, ModalPosition}; +use crate::gui::views::wallets::WalletsContent; +use crate::gui::views::{Modal, View}; +use crate::gui::Colors; use crate::node::Node; use crate::{AppConfig, Settings}; -use crate::gui::icons::{CHECK, CHECK_FAT, FILE_X}; -use crate::gui::views::network::NetworkContent; -use crate::gui::views::wallets::WalletsContent; lazy_static! { /// Global state to check if [`NetworkContent`] panel is open. static ref NETWORK_PANEL_OPEN: AtomicBool = AtomicBool::new(false); + + /// Global state to check if [`NetworkContent`] panel is open. + static ref SETTINGS_PANEL_OPEN: AtomicBool = AtomicBool::new(false); } /// Contains main ui content, handles side panel state. pub struct Content { /// Side panel [`NetworkContent`] content. network: NetworkContent, + /// Central panel [`WalletsContent`] content. pub wallets: WalletsContent, @@ -70,7 +74,6 @@ impl Default for Content { first_draw: true, allowed_modal_ids: vec![ Self::EXIT_CONFIRMATION_MODAL, - Self::SETTINGS_MODAL, ANDROID_INTEGRATED_NODE_WARNING_MODAL, CRASH_REPORT_MODAL ], @@ -89,7 +92,6 @@ impl ModalContainer for Content { cb: &dyn PlatformCallbacks) { match modal.id { Self::EXIT_CONFIRMATION_MODAL => self.exit_modal_content(ui, modal, cb), - Self::SETTINGS_MODAL => self.settings_modal_ui(ui), ANDROID_INTEGRATED_NODE_WARNING_MODAL => self.android_warning_modal_ui(ui), CRASH_REPORT_MODAL => self.crash_report_modal_ui(ui, cb), _ => {} @@ -100,8 +102,6 @@ impl ModalContainer for Content { impl Content { /// Identifier for exit confirmation [`Modal`]. pub const EXIT_CONFIRMATION_MODAL: &'static str = "exit_confirmation_modal"; - /// Identifier for wallet opening [`Modal`]. - pub const SETTINGS_MODAL: &'static str = "settings_modal"; /// Default width of side panel at application UI. pub const SIDE_PANEL_WIDTH: f32 = 400.0; @@ -114,7 +114,10 @@ impl Content { self.current_modal_ui(ui, cb); let dual_panel = Self::is_dual_panel_mode(ui.ctx()); - let (is_panel_open, panel_width) = network_panel_state_width(ui.ctx(), dual_panel); + let (is_panel_open, mut panel_width) = network_panel_state_width(ui.ctx(), dual_panel); + if self.network.showing_settings() { + panel_width = ui.available_width(); + } // Show network content. egui::SidePanel::left("network_panel") @@ -244,122 +247,6 @@ impl Content { } } - /// Draw creating wallet name/password input [`Modal`] content. - pub fn settings_modal_ui(&mut self, ui: &mut egui::Ui) { - ui.add_space(6.0); - - // Show theme selection. - Self::theme_selection_ui(ui); - - ui.add_space(8.0); - View::horizontal_line(ui, Colors::item_stroke()); - ui.add_space(6.0); - - ui.vertical_centered(|ui| { - ui.label(RichText::new(format!("{}:", t!("language"))) - .size(16.0) - .color(Colors::gray()) - ); - }); - ui.add_space(8.0); - - // Draw available list of languages to select. - let locales = rust_i18n::available_locales!(); - for (index, locale) in locales.iter().enumerate() { - Self::language_item_ui(locale, ui, index, locales.len()); - } - - ui.add_space(8.0); - - // Show button to close modal. - ui.vertical_centered_justified(|ui| { - View::button(ui, t!("close"), Colors::white_or_black(false), || { - Modal::close(); - }); - }); - ui.add_space(6.0); - } - - /// Draw theme selection content. - fn theme_selection_ui(ui: &mut egui::Ui) { - ui.vertical_centered(|ui| { - ui.label(RichText::new(t!("theme")).size(16.0).color(Colors::gray())); - }); - - let saved_use_dark = AppConfig::dark_theme().unwrap_or(false); - let mut selected_use_dark = saved_use_dark; - - ui.add_space(8.0); - ui.columns(2, |columns| { - columns[0].vertical_centered(|ui| { - View::radio_value(ui, &mut selected_use_dark, false, t!("light")); - }); - columns[1].vertical_centered(|ui| { - View::radio_value(ui, &mut selected_use_dark, true, t!("dark")); - }) - }); - ui.add_space(8.0); - - if saved_use_dark != selected_use_dark { - AppConfig::set_dark_theme(selected_use_dark); - crate::setup_visuals(ui.ctx()); - } - } - - /// Draw language selection item content. - fn language_item_ui(locale: &str, ui: &mut egui::Ui, index: usize, len: usize) { - // Setup layout size. - let mut rect = ui.available_rect_before_wrap(); - rect.set_height(50.0); - - // Draw round background. - let bg_rect = rect.clone(); - let item_rounding = View::item_rounding(index, len, false); - ui.painter().rect(bg_rect, - item_rounding, - Colors::fill(), - View::item_stroke(), - StrokeKind::Middle); - - ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { - // Draw button to select language. - let is_current = if let Some(lang) = AppConfig::locale() { - lang == locale - } else { - rust_i18n::locale() == locale - }; - if !is_current { - View::item_button(ui, View::item_rounding(index, len, true), CHECK, None, || { - rust_i18n::set_locale(locale); - AppConfig::save_locale(locale); - Modal::close(); - }); - } else { - ui.add_space(14.0); - ui.label(RichText::new(CHECK_FAT).size(20.0).color(Colors::green())); - ui.add_space(14.0); - } - - let layout_size = ui.available_size(); - ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| { - ui.add_space(12.0); - ui.vertical(|ui| { - // Draw language name. - ui.add_space(12.0); - let color = if is_current { - Colors::title(false) - } else { - Colors::gray() - }; - ui.label(RichText::new(t!("lang_name", locale = locale)) - .size(17.0) - .color(color)); - ui.add_space(3.0); - }); - }); - }); - } - /// Draw content for integrated node warning [`Modal`] on Android. fn android_warning_modal_ui(&mut self, ui: &mut egui::Ui) { ui.add_space(6.0); diff --git a/src/gui/views/mod.rs b/src/gui/views/mod.rs index 6ebf756..4e024fd 100644 --- a/src/gui/views/mod.rs +++ b/src/gui/views/mod.rs @@ -27,8 +27,8 @@ mod content; pub use content::*; pub mod network; - pub mod wallets; +pub mod settings; mod camera; pub use camera::*; diff --git a/src/gui/views/network/content.rs b/src/gui/views/network/content.rs index fa74961..cdf2dbd 100644 --- a/src/gui/views/network/content.rs +++ b/src/gui/views/network/content.rs @@ -17,11 +17,12 @@ use egui::scroll_area::ScrollBarVisibility; 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::icons::{ARROWS_COUNTER_CLOCKWISE, ARROW_LEFT, BRIEFCASE, DATABASE, DOTS_THREE_OUTLINE_VERTICAL, FACTORY, FADERS, GAUGE, GEAR, POWER}; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Content, TitlePanel, View}; use crate::gui::views::network::{ConnectionsContent, NetworkMetrics, NetworkMining, NetworkNode, NetworkSettings}; use crate::gui::views::network::types::{NodeTab, NodeTabType}; +use crate::gui::views::settings::SettingsContent; use crate::gui::views::types::{LinePosition, TitleContentType, TitleType}; use crate::node::{Node, NodeConfig, NodeError}; use crate::wallet::ExternalConnection; @@ -32,6 +33,9 @@ pub struct NetworkContent { node_tab_content: Box, /// Connections content. connections: ConnectionsContent, + + /// Application settings content. + settings_content: Option, } impl Default for NetworkContent { @@ -39,12 +43,14 @@ impl Default for NetworkContent { Self { node_tab_content: Box::new(NetworkNode::default()), connections: ConnectionsContent::default(), + settings_content: None, } } } impl NetworkContent { pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { + let show_settings = self.showing_settings(); let show_connections = AppConfig::show_connections_network_panel(); let dual_panel = Content::is_dual_panel_mode(ui.ctx()); @@ -52,8 +58,8 @@ impl NetworkContent { self.title_ui(ui, dual_panel, show_connections); // Show integrated node tabs content. - if !show_connections { - egui::TopBottomPanel::bottom("node_tabs") + if !show_connections && !show_settings { + egui::TopBottomPanel::bottom("network_tabs_content") .min_height(0.5) .resizable(false) .frame(egui::Frame { @@ -83,14 +89,14 @@ impl NetworkContent { }); } - // Show integrated node tab content. - egui::SidePanel::right("node_tab_content") + // Show settings or integrated node content. + egui::SidePanel::right("network_side_content") .resizable(false) .exact_width(ui.available_width()) .frame(egui::Frame { ..Default::default() }) - .show_animated_inside(ui, !show_connections, |ui| { + .show_animated_inside(ui, show_settings || !show_connections, |ui| { egui::CentralPanel::default() .frame(egui::Frame { inner_margin: Margin { @@ -103,7 +109,11 @@ impl NetworkContent { }) .show_inside(ui, |ui| { let rect = ui.available_rect_before_wrap(); - if self.node_tab_content.get_type() != NodeTabType::Settings { + if let Some(c) = &mut self.settings_content { + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { + c.ui(ui); + }); + } else if self.node_tab_content.get_type() != NodeTabType::Settings { View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { let node_err = Node::get_error(); if let Some(err) = node_err { @@ -192,6 +202,11 @@ impl NetworkContent { } } + /// Check if application settings content is showing. + pub fn showing_settings(&self) -> bool { + self.settings_content.is_some() + } + /// Draw tab buttons at bottom of the screen. fn tabs_ui(&mut self, ui: &mut egui::Ui) { ui.vertical_centered(|ui| { @@ -229,11 +244,15 @@ impl NetworkContent { /// Draw title content. fn title_ui(&mut self, ui: &mut egui::Ui, dual_panel: bool, show_connections: bool) { + let show_settings = self.showing_settings(); + // Setup values for title panel. let title_text = self.node_tab_content.get_type().title(); let subtitle_text = Node::get_sync_status_text(); let not_syncing = Node::not_syncing(); - let title_content = if !show_connections { + let title_content = if show_settings { + TitleContentType::Title(t!("settings")) + } else if !show_connections { TitleContentType::WithSubTitle(title_text, subtitle_text, !not_syncing) } else { TitleContentType::Title(t!("network.connections")) @@ -241,16 +260,24 @@ impl NetworkContent { // Draw title panel. TitlePanel::new(Id::from("network_title_panel")).ui(TitleType::Single(title_content), |ui| { - if !show_connections { + if show_settings { + View::title_button_big(ui, ARROW_LEFT, |_| { + self.settings_content = None; + }); + } else if !show_connections { View::title_button_big(ui, DOTS_THREE_OUTLINE_VERTICAL, |ui| { AppConfig::toggle_show_connections_network_panel(); if AppConfig::show_connections_network_panel() { ExternalConnection::check(None, ui.ctx()); } }); + } else if !dual_panel { + View::title_button_big(ui, GEAR, |_| { + self.settings_content = Some(SettingsContent::default()); + }); } }, |ui| { - if !dual_panel { + if !dual_panel && !show_settings { View::title_button_big(ui, BRIEFCASE, |_| { Content::toggle_network_panel(); }); diff --git a/src/gui/views/settings/content.rs b/src/gui/views/settings/content.rs new file mode 100644 index 0000000..9120870 --- /dev/null +++ b/src/gui/views/settings/content.rs @@ -0,0 +1,145 @@ +// Copyright 2025 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 eframe::emath::Align; +use eframe::epaint::StrokeKind; +use egui::{Layout, RichText}; +use crate::AppConfig; +use crate::gui::Colors; +use crate::gui::icons::{CHECK, CHECK_FAT}; +use crate::gui::views::{Modal, View}; + +/// Application settings content. +pub struct SettingsContent { + +} + + +impl Default for SettingsContent { + fn default() -> Self { + Self { + + } + } +} + +impl SettingsContent { + /// Draw settings content. + pub fn ui(&mut self, ui: &mut egui::Ui) { + ui.add_space(6.0); + + // Show theme selection. + Self::theme_selection_ui(ui); + + ui.add_space(8.0); + View::horizontal_line(ui, Colors::item_stroke()); + ui.add_space(6.0); + + ui.vertical_centered(|ui| { + ui.label(RichText::new(format!("{}:", t!("language"))) + .size(16.0) + .color(Colors::gray()) + ); + }); + ui.add_space(8.0); + + // Draw available list of languages to select. + let locales = rust_i18n::available_locales!(); + for (index, locale) in locales.iter().enumerate() { + Self::language_item_ui(locale, ui, index, locales.len()); + } + } + + /// Draw theme selection content. + fn theme_selection_ui(ui: &mut egui::Ui) { + ui.vertical_centered(|ui| { + ui.label(RichText::new(t!("theme")).size(16.0).color(Colors::gray())); + }); + + let saved_use_dark = AppConfig::dark_theme().unwrap_or(false); + let mut selected_use_dark = saved_use_dark; + + ui.add_space(8.0); + ui.columns(2, |columns| { + columns[0].vertical_centered(|ui| { + View::radio_value(ui, &mut selected_use_dark, false, t!("light")); + }); + columns[1].vertical_centered(|ui| { + View::radio_value(ui, &mut selected_use_dark, true, t!("dark")); + }) + }); + ui.add_space(8.0); + + if saved_use_dark != selected_use_dark { + AppConfig::set_dark_theme(selected_use_dark); + crate::setup_visuals(ui.ctx()); + } + + ui.add_space(6.0); + } + + /// Draw language selection item content. + fn language_item_ui(locale: &str, ui: &mut egui::Ui, index: usize, len: usize) { + // Setup layout size. + let mut rect = ui.available_rect_before_wrap(); + rect.set_height(50.0); + + // Draw round background. + let bg_rect = rect.clone(); + let item_rounding = View::item_rounding(index, len, false); + ui.painter().rect(bg_rect, + item_rounding, + Colors::fill(), + View::item_stroke(), + StrokeKind::Middle); + + ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { + // Draw button to select language. + let is_current = if let Some(lang) = AppConfig::locale() { + lang == locale + } else { + rust_i18n::locale() == locale + }; + if !is_current { + View::item_button(ui, View::item_rounding(index, len, true), CHECK, None, || { + rust_i18n::set_locale(locale); + AppConfig::save_locale(locale); + Modal::close(); + }); + } else { + ui.add_space(14.0); + ui.label(RichText::new(CHECK_FAT).size(20.0).color(Colors::green())); + ui.add_space(14.0); + } + + let layout_size = ui.available_size(); + ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| { + ui.add_space(12.0); + ui.vertical(|ui| { + // Draw language name. + ui.add_space(12.0); + let color = if is_current { + Colors::title(false) + } else { + Colors::gray() + }; + ui.label(RichText::new(t!("lang_name", locale = locale)) + .size(17.0) + .color(color)); + ui.add_space(3.0); + }); + }); + }); + } +} \ No newline at end of file diff --git a/src/gui/views/settings/mod.rs b/src/gui/views/settings/mod.rs new file mode 100644 index 0000000..b0ce095 --- /dev/null +++ b/src/gui/views/settings/mod.rs @@ -0,0 +1,16 @@ +// Copyright 2025 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. + +mod content; +pub use content::*; \ No newline at end of file diff --git a/src/gui/views/wallets/content.rs b/src/gui/views/wallets/content.rs index d8fc098..710e80d 100644 --- a/src/gui/views/wallets/content.rs +++ b/src/gui/views/wallets/content.rs @@ -21,6 +21,7 @@ use crate::gui::Colors; use crate::gui::icons::{ARROW_LEFT, CARET_RIGHT, COMPUTER_TOWER, FOLDER_OPEN, FOLDER_PLUS, GEAR, GLOBE, GLOBE_SIMPLE, LOCK_KEY, PLUS, SIDEBAR_SIMPLE, SUITCASE}; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, Content, TitlePanel, View}; +use crate::gui::views::settings::SettingsContent; use crate::gui::views::types::{ModalContainer, ModalPosition, LinePosition, TitleContentType, TitleType}; use crate::gui::views::wallets::creation::WalletCreation; use crate::gui::views::wallets::modals::{AddWalletModal, OpenWalletModal, WalletConnectionModal, WalletsModal}; @@ -49,6 +50,9 @@ pub struct WalletsContent { /// Wallet creation content. creation_content: Option, + /// Settings content. + settings_content: Option, + /// Flag to show [`Wallet`] list at dual panel mode. show_wallets_at_dual_panel: bool, @@ -70,6 +74,7 @@ impl Default for WalletsContent { conn_selection_content: None, wallet_content: None, creation_content: None, + settings_content: None, show_wallets_at_dual_panel: AppConfig::show_wallets_at_dual_panel(), modal_ids: vec![ ADD_WALLET_MODAL, @@ -100,7 +105,7 @@ impl ModalContainer for WalletsContent { ); }); } - if self.creation_content.is_some() { + if self.creating_wallet() { self.add_wallet_modal_content = None; } }, @@ -147,11 +152,12 @@ impl WalletsContent { pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { self.current_modal_ui(ui, cb); + let showing_settings = self.showing_settings(); let creating_wallet = self.creating_wallet(); - let showing_wallet = self.showing_wallet() && !creating_wallet; + let showing_wallet = self.showing_wallet() && !creating_wallet && !showing_settings; let dual_panel = Self::is_dual_panel_mode(ui); let content_width = ui.available_width(); - let list_hidden = creating_wallet || self.wallets.list().is_empty() + let list_hidden = showing_settings || creating_wallet || self.wallets.list().is_empty() || (showing_wallet && self.wallet_content.as_ref().unwrap().qr_scan_content.is_some()) || (dual_panel && showing_wallet && !self.show_wallets_at_dual_panel) || (!dual_panel && showing_wallet); @@ -253,7 +259,13 @@ impl WalletsContent { ..Default::default() }) .show_inside(ui, |ui| { - if self.creation_content.is_some() { + if self.showing_settings() { + if let Some(c) = &mut self.settings_content { + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { + c.ui(ui); + }); + } + } else if self.creating_wallet() { let creation = self.creation_content.as_mut().unwrap(); let pass = creation.pass.clone(); let mut created = false; @@ -306,6 +318,11 @@ impl WalletsContent { self.creation_content.is_some() } + /// Check if application settings are showing. + pub fn showing_settings(&self) -> bool { + self.settings_content.is_some() + } + /// Handle data from deeplink or opened file. pub fn on_data(&mut self, ui: &mut egui::Ui, data: Option) { let wallets_size = self.wallets.list().len(); @@ -349,7 +366,11 @@ impl WalletsContent { /// Handle Back key event returning `false` when event was handled. pub fn on_back(&mut self, cb: &dyn PlatformCallbacks) -> bool { - if self.creation_content.is_some() { + if self.showing_settings() { + // Close settings. + self.settings_content = None; + return false; + } else if self.creating_wallet() { // Close wallet creation. let creation = self.creation_content.as_mut().unwrap(); if creation.on_back() { @@ -380,6 +401,7 @@ impl WalletsContent { show_wallet: bool, cb: &dyn PlatformCallbacks) { let show_list = self.show_wallets_at_dual_panel; + let showing_settings = self.showing_settings(); let creating_wallet = self.creating_wallet(); let qr_scan = { let mut scan = false; @@ -390,7 +412,7 @@ impl WalletsContent { }; // Setup title. let title_content = if show_wallet && (!dual_panel - || (dual_panel && !show_list)) && !creating_wallet { + || (dual_panel && !show_list)) && !creating_wallet && !showing_settings { let wallet_content = self.wallet_content.as_ref().unwrap(); let wallet_tab_type = wallet_content.current_tab.get_type(); let title_text = if qr_scan { @@ -405,14 +427,17 @@ impl WalletsContent { TitleType::Single(TitleContentType::WithSubTitle(title_text, subtitle_text, false)) } } else { - let title_text = if qr_scan { + let title_text = if showing_settings { + t!("settings") + } else if qr_scan { t!("scan_qr") } else if creating_wallet { t!("wallets.add") } else { t!("wallets.title") }; - let dual_title = !qr_scan && !creating_wallet && show_wallet && dual_panel; + let dual_title = !showing_settings && !qr_scan && !creating_wallet && + show_wallet && dual_panel; if dual_title { let wallet_content = self.wallet_content.as_ref().unwrap(); let wallet_tab_type = wallet_content.current_tab.get_type(); @@ -430,8 +455,14 @@ impl WalletsContent { }; // Draw title panel. + let mut show_settings = false; + let showing_settings = self.showing_settings(); TitlePanel::new(Id::new("wallets_title_panel")).ui(title_content, |ui| { - if show_wallet && !dual_panel { + if self.showing_settings() { + View::title_button_big(ui, ARROW_LEFT, |_| { + self.settings_content = None; + }); + } else if show_wallet && !dual_panel { View::title_button_big(ui, ARROW_LEFT, |_| { let wallet_qr_scan = self.wallet_content .as_ref() @@ -445,7 +476,7 @@ impl WalletsContent { } self.wallet_content = None; }); - } else if self.creation_content.is_some() { + } else if self.creating_wallet() { let mut close = false; if let Some(creation) = self.creation_content.as_mut() { View::title_button_big(ui, ARROW_LEFT, |_| { @@ -478,16 +509,18 @@ impl WalletsContent { View::title_button_big(ui, GLOBE, |_| { Content::toggle_network_panel(); }); - }; + } }, |ui| { - View::title_button_big(ui, GEAR, |_| { - // Show settings modal. - Modal::new(Content::SETTINGS_MODAL) - .position(ModalPosition::CenterTop) - .title(t!("settings")) - .show(); - }); + if !showing_settings { + View::title_button_big(ui, GEAR, |_| { + // Show application settings. + show_settings = true; + }); + } }, ui); + if show_settings { + self.settings_content = Some(SettingsContent::default()); + } } /// Draw list of wallets.