From a364daf52effa9b713307a57e3bc6aebb1451b09 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Sat, 31 May 2025 09:11:07 +0300 Subject: [PATCH] ui: network and storage settings modules, language selection modal --- src/gui/views/network/content.rs | 18 ++- src/gui/views/settings/content.rs | 142 +++--------------- src/gui/views/settings/interface.rs | 214 ++++++++++++++++++++++++++++ src/gui/views/settings/mod.rs | 4 + src/gui/views/settings/network.rs | 49 +++++++ src/gui/views/settings/storage.rs | 49 +++++++ src/gui/views/wallets/content.rs | 18 ++- 7 files changed, 369 insertions(+), 125 deletions(-) create mode 100644 src/gui/views/settings/interface.rs create mode 100644 src/gui/views/settings/network.rs create mode 100644 src/gui/views/settings/storage.rs diff --git a/src/gui/views/network/content.rs b/src/gui/views/network/content.rs index 4bbe676..94f1861 100644 --- a/src/gui/views/network/content.rs +++ b/src/gui/views/network/content.rs @@ -110,9 +110,21 @@ impl NetworkContent { .show_inside(ui, |ui| { let rect = ui.available_rect_before_wrap(); if let Some(c) = &mut self.settings_content { - View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { - c.ui(ui, cb); - }); + ScrollArea::vertical() + .id_salt("app_settings_network") + .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) + .auto_shrink([false; 2]) + .show(ui, |ui| { + ui.add_space(1.0); + ui.vertical_centered(|ui| { + // Show application settings content. + View::max_width_ui(ui, + Content::SIDE_PANEL_WIDTH * 1.3, + |ui| { + c.ui(ui, cb); + }); + }); + }); } 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(); diff --git a/src/gui/views/settings/content.rs b/src/gui/views/settings/content.rs index 700df29..cef2909 100644 --- a/src/gui/views/settings/content.rs +++ b/src/gui/views/settings/content.rs @@ -12,144 +12,48 @@ // 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::platform::PlatformCallbacks; -use crate::gui::views::{Modal, View}; +use crate::gui::views::settings::interface::InterfaceSettingsContent; +use crate::gui::views::settings::network::NetworkSettingsContent; +use crate::gui::views::settings::storage::StorageSettingsContent; use crate::gui::views::types::ContentContainer; +use crate::gui::views::View; /// Application settings content. pub struct SettingsContent { - + /// User interface settings. + interface_settings: InterfaceSettingsContent, + /// Network communication settings. + network_settings: NetworkSettingsContent, + /// File storage settings. + storage_settings: StorageSettingsContent } - impl Default for SettingsContent { fn default() -> Self { Self { - - } - } -} - -impl ContentContainer for SettingsContent { - fn modal_ids(&self) -> Vec<&'static str> { - vec![] - } - - fn modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { - } - - fn container_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { - 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()); + interface_settings: InterfaceSettingsContent::default(), + network_settings: NetworkSettingsContent::default(), + storage_settings: StorageSettingsContent::default(), } } } impl SettingsContent { - /// 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; + /// Draw application settings content. + pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { + // Show interface settings. + self.interface_settings.ui(ui, cb); 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()); - } - + View::horizontal_line(ui, Colors::stroke()); ui.add_space(6.0); - } + + // Show network settings. + //self.network_settings.ui(ui, cb); - /// 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); - }); - }); - }); + // Show storage settings. + // self.storage_settings.ui(ui, cb); } } \ No newline at end of file diff --git a/src/gui/views/settings/interface.rs b/src/gui/views/settings/interface.rs new file mode 100644 index 0000000..370adbf --- /dev/null +++ b/src/gui/views/settings/interface.rs @@ -0,0 +1,214 @@ +// 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 egui::{Align, Layout, RichText, ScrollArea, StrokeKind}; +use egui::scroll_area::ScrollBarVisibility; +use crate::gui::icons::{CHECK, CHECK_FAT, GLOBE_SIMPLE, PENCIL}; +use crate::gui::platform::PlatformCallbacks; +use crate::gui::views::types::{ContentContainer, ModalPosition}; +use crate::gui::views::{Modal, View}; +use crate::gui::Colors; +use crate::AppConfig; + +/// User interface settings content. +pub struct InterfaceSettingsContent { + /// Current locale. + locale: String, +} + +/// Identifier for language selection [`Modal`]. +const LANGUAGE_SELECTION_MODAL: &'static str = "language_selection_modal"; + +impl ContentContainer for InterfaceSettingsContent { + fn modal_ids(&self) -> Vec<&'static str> { + vec![ + LANGUAGE_SELECTION_MODAL + ] + } + + fn modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, _: &dyn PlatformCallbacks) { + match modal.id { + LANGUAGE_SELECTION_MODAL => self.language_selection_ui(ui), + _ => {} + } + } + + fn container_ui(&mut self, ui: &mut egui::Ui, _: &dyn PlatformCallbacks) { + ui.add_space(5.0); + + // Draw theme selection. + 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. + self.language_item_ui(self.locale.clone().as_str(), ui, true, 0, 1); + ui.add_space(4.0); + } +} + +impl Default for InterfaceSettingsContent { + fn default() -> Self { + let locale = if let Some(lang) = AppConfig::locale() { + lang + } else { + rust_i18n::locale() + }; + Self { + locale, + } + } +} + +impl InterfaceSettingsContent { + /// Draw language selection content. + fn language_selection_ui(&mut self, ui: &mut egui::Ui) { + ui.add_space(4.0); + ScrollArea::vertical() + .max_height(373.0) + .id_salt("select_language_scroll") + .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) + .auto_shrink([true; 2]) + .show(ui, |ui| { + ui.add_space(2.0); + ui.vertical_centered(|ui| { + let locales = rust_i18n::available_locales!(); + for (index, locale) in locales.iter().enumerate() { + self.language_item_ui(locale, ui, false, index, locales.len()); + } + }); + }); + + ui.add_space(6.0); + View::horizontal_line(ui, Colors::item_stroke()); + ui.add_space(6.0); + + // Show button to close modal. + ui.vertical_centered_justified(|ui| { + View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || { + Modal::close(); + }); + }); + ui.add_space(6.0); + } + + /// Draw language selection item content. + fn language_item_ui(&mut self, locale: &str, ui: &mut egui::Ui, edit: bool, index: usize, len: usize) { + // Setup layout size. + let mut rect = ui.available_rect_before_wrap(); + if edit { + rect.set_height(56.0); + } else { + 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| { + if edit { + View::item_button(ui, View::item_rounding(index, len, true), PENCIL, None, || { + // Show language selection modal. + Modal::new(LANGUAGE_SELECTION_MODAL) + .position(ModalPosition::Center) + .title(t!("language")) + .show(); + }); + 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 connections URL. + ui.add_space(4.0); + View::ellipsize_text(ui, + t!("lang_name", locale = locale), + 18.0, + Colors::title(false)); + ui.add_space(1.0); + + // Setup connection status text. + let value = format!("{} {}", + GLOBE_SIMPLE, + t!("language")); + ui.label(RichText::new(value).size(15.0).color(Colors::gray())); + ui.add_space(3.0); + }); + }); + } else { + // 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); + self.locale = locale.to_string(); + 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 index b0ce095..6bd64a6 100644 --- a/src/gui/views/settings/mod.rs +++ b/src/gui/views/settings/mod.rs @@ -13,4 +13,8 @@ // limitations under the License. mod content; +mod interface; +mod network; +mod storage; + pub use content::*; \ No newline at end of file diff --git a/src/gui/views/settings/network.rs b/src/gui/views/settings/network.rs new file mode 100644 index 0000000..98ed4c0 --- /dev/null +++ b/src/gui/views/settings/network.rs @@ -0,0 +1,49 @@ +// 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 crate::gui::icons::COMPUTER_TOWER; +use crate::gui::platform::PlatformCallbacks; +use crate::gui::views::types::ContentContainer; +use crate::gui::views::{Modal, View}; +use crate::gui::Colors; + +/// Network communication settings content. +pub struct NetworkSettingsContent { + +} + +impl ContentContainer for NetworkSettingsContent { + fn modal_ids(&self) -> Vec<&'static str> { + vec![ + + ] + } + + fn modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + } + + fn container_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { + View::sub_title(ui, format!("{} {}", COMPUTER_TOWER, t!("network.self"))); + View::horizontal_line(ui, Colors::stroke()); + ui.add_space(6.0); + } +} + +impl Default for NetworkSettingsContent { + fn default() -> Self { + Self { + + } + } +} \ No newline at end of file diff --git a/src/gui/views/settings/storage.rs b/src/gui/views/settings/storage.rs new file mode 100644 index 0000000..9587b69 --- /dev/null +++ b/src/gui/views/settings/storage.rs @@ -0,0 +1,49 @@ +// 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 crate::gui::icons::DATABASE; +use crate::gui::platform::PlatformCallbacks; +use crate::gui::views::types::ContentContainer; +use crate::gui::views::{Modal, View}; +use crate::gui::Colors; + +/// File storage settings content. +pub struct StorageSettingsContent { + +} + +impl ContentContainer for StorageSettingsContent { + fn modal_ids(&self) -> Vec<&'static str> { + vec![ + + ] + } + + fn modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + } + + fn container_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { + View::sub_title(ui, format!("{} {}", DATABASE, t!("network_node.data"))); + View::horizontal_line(ui, Colors::stroke()); + ui.add_space(6.0); + } +} + +impl Default for StorageSettingsContent { + fn default() -> Self { + Self { + + } + } +} \ No newline at end of file diff --git a/src/gui/views/wallets/content.rs b/src/gui/views/wallets/content.rs index e6a3f83..3a7d17b 100644 --- a/src/gui/views/wallets/content.rs +++ b/src/gui/views/wallets/content.rs @@ -241,9 +241,21 @@ impl ContentContainer for WalletsContent { .show_inside(ui, |ui| { 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, cb); - }); + ScrollArea::vertical() + .id_salt("app_settings_wallets") + .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) + .auto_shrink([false; 2]) + .show(ui, |ui| { + ui.add_space(1.0); + ui.vertical_centered(|ui| { + // Show application settings content. + View::max_width_ui(ui, + Content::SIDE_PANEL_WIDTH * 1.3, + |ui| { + c.ui(ui, cb); + }); + }); + }); } } else if self.creating_wallet() { let creation = self.creation_content.as_mut().unwrap();