ui: refactoring navigation and modal state, stratum content fixes
This commit is contained in:
parent
5542698467
commit
1b8c126e0d
26 changed files with 429 additions and 583 deletions
|
@ -1,4 +1,6 @@
|
|||
screen_accounts:
|
||||
copy: Copy
|
||||
paste: Paste
|
||||
accounts:
|
||||
title: Accounts
|
||||
network:
|
||||
self: Network
|
||||
|
@ -59,7 +61,7 @@ network_mining:
|
|||
enable_server: Enable server
|
||||
disable_server: Disable server
|
||||
info: 'Mining server is enabled, you can change its settings by selecting %{settings} at the bottom of the screen. Data is updating when devices are connected.'
|
||||
info_settings: To change the settings of enabled server, you will need to restart the node.
|
||||
restart_server_required: Server restart is required to apply changes.
|
||||
rewards_wallet: Wallet for rewards
|
||||
server: Stratum server
|
||||
address: Address
|
||||
|
@ -85,8 +87,6 @@ network_settings:
|
|||
foreign_api_secret: Foreign API token
|
||||
disabled: Disabled
|
||||
enabled: Enabled
|
||||
copy: Copy
|
||||
paste: Paste
|
||||
ftl: The Future Time Limit (FTL)
|
||||
ftl_description: Limit on how far into the future, relative to a node's local time in seconds, the timestamp on a new block can be, in order for the block to be accepted.
|
||||
not_valid_value: Entered value is not valid
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
screen_accounts:
|
||||
copy: Копировать
|
||||
paste: Вставить
|
||||
accounts:
|
||||
title: Аккаунты
|
||||
network:
|
||||
self: Сеть
|
||||
|
@ -59,7 +61,7 @@ network_mining:
|
|||
enable_server: Включить сервер
|
||||
disable_server: Выключить сервер
|
||||
info: 'Сервер майнинга запущен, вы можете изменить его настройки, выбрав %{settings} внизу экрана. Данные обновляются, когда устройства подключены.'
|
||||
info_settings: Для изменения настроек запущенного сервера потребуется перезапуск узла.
|
||||
restart_server_required: Для применения изменений потребуется перезапуск Stratum сервера.
|
||||
rewards_wallet: Кошелёк для наград
|
||||
server: Stratum сервер
|
||||
address: Адрес
|
||||
|
@ -85,8 +87,6 @@ network_settings:
|
|||
foreign_api_secret: Foreign API токен
|
||||
disabled: Отключен
|
||||
enabled: Включен
|
||||
copy: Копировать
|
||||
paste: Вставить
|
||||
ftl: Предел Будущего Времени (FTL)
|
||||
ftl_description: Насколько далеко в будущем, относительно локального времени узла в секундах, может находиться временная метка на новом блоке для его принятия.
|
||||
not_valid_value: Введено недопустимое значение
|
||||
|
|
|
@ -14,11 +14,10 @@
|
|||
|
||||
use egui::{Context, RichText, Stroke};
|
||||
use egui::os::OperatingSystem;
|
||||
use crate::gui::Colors;
|
||||
|
||||
use crate::gui::{Colors, Navigator};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::screens::Root;
|
||||
use crate::gui::views::{ModalContainer, View};
|
||||
use crate::gui::views::{Modal, ModalContainer, Root, View};
|
||||
use crate::node::Node;
|
||||
|
||||
/// To be implemented at platform-specific module.
|
||||
|
@ -27,16 +26,12 @@ pub struct PlatformApp<Platform> {
|
|||
pub(crate) platform: Platform,
|
||||
}
|
||||
|
||||
/// Contains main ui content, handles application exit and visual style setup.
|
||||
/// Contains main ui, handles exit and visual style setup.
|
||||
pub struct App {
|
||||
/// Main ui container.
|
||||
/// Main ui content.
|
||||
root: Root,
|
||||
|
||||
/// Check if app exit is allowed on close event callback.
|
||||
/// Check if app exit is allowed on close event of [`eframe::App`] platform implementation.
|
||||
pub(crate) exit_allowed: bool,
|
||||
/// Called from callback of [`eframe::App`] platform implementation on close event.
|
||||
pub(crate) exit_requested: bool,
|
||||
|
||||
/// Flag to show exit progress at modal.
|
||||
show_exit_progress: bool,
|
||||
/// List of allowed modal ids for this [`ModalContainer`].
|
||||
|
@ -47,14 +42,13 @@ impl Default for App {
|
|||
fn default() -> Self {
|
||||
let os = OperatingSystem::from_target_os();
|
||||
// Exit from eframe only for non-mobile platforms.
|
||||
let allow_to_exit = os == OperatingSystem::Android || os == OperatingSystem::IOS;
|
||||
let exit_allowed = os == OperatingSystem::Android || os == OperatingSystem::IOS;
|
||||
Self {
|
||||
root: Root::default(),
|
||||
exit_allowed: allow_to_exit,
|
||||
exit_requested: false,
|
||||
exit_allowed,
|
||||
show_exit_progress: false,
|
||||
allowed_modal_ids: vec![
|
||||
Navigator::EXIT_MODAL
|
||||
Self::EXIT_MODAL
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -67,25 +61,22 @@ impl ModalContainer for App {
|
|||
}
|
||||
|
||||
impl App {
|
||||
/// Identifier for exit confirmation [`Modal`].
|
||||
pub const EXIT_MODAL: &'static str = "exit_confirmation";
|
||||
|
||||
/// Draw content on main screen panel.
|
||||
pub fn ui(&mut self, ctx: &Context, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) {
|
||||
// Show exit modal if window closing was requested.
|
||||
if self.exit_requested {
|
||||
Navigator::show_exit_modal();
|
||||
self.exit_requested = false;
|
||||
}
|
||||
// Draw main content.
|
||||
egui::CentralPanel::default()
|
||||
.frame(egui::Frame {
|
||||
fill: Colors::FILL,
|
||||
..Default::default()
|
||||
})
|
||||
.show(ctx, |ui| {
|
||||
// Draw exit modal content if it's open or exit requested.
|
||||
let modal_id = Navigator::is_modal_open();
|
||||
if modal_id.is_some() && self.can_show_modal(modal_id.unwrap()) {
|
||||
// Draw modal content if it's open.
|
||||
if self.can_draw_modal() {
|
||||
self.exit_modal_content(ui, frame, cb);
|
||||
}
|
||||
// Draw main content.
|
||||
self.root.ui(ui, frame, cb);
|
||||
});
|
||||
}
|
||||
|
@ -95,7 +86,7 @@ impl App {
|
|||
ui: &mut egui::Ui,
|
||||
frame: &mut eframe::Frame,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
Navigator::modal_ui(ui, |ui, modal| {
|
||||
Modal::ui(ui, |ui, modal| {
|
||||
if self.show_exit_progress {
|
||||
if !Node::is_running() {
|
||||
self.exit(frame, cb);
|
||||
|
@ -156,13 +147,12 @@ impl App {
|
|||
cb.exit();
|
||||
}
|
||||
OperatingSystem::IOS => {
|
||||
//TODO: exit on iOS
|
||||
//TODO: exit on iOS.
|
||||
}
|
||||
OperatingSystem::Nix | OperatingSystem::Mac | OperatingSystem::Windows => {
|
||||
self.exit_allowed = true;
|
||||
frame.close();
|
||||
}
|
||||
// Web
|
||||
OperatingSystem::Unknown => {}
|
||||
}
|
||||
}
|
||||
|
@ -249,31 +239,10 @@ impl App {
|
|||
|
||||
ctx.set_style(style);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "android")]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
/// Calling when back button is pressed on Android.
|
||||
pub extern "C" fn Java_mw_gri_android_MainActivity_onBackButtonPress(
|
||||
_env: jni::JNIEnv,
|
||||
_class: jni::objects::JObject,
|
||||
_activity: jni::objects::JObject,
|
||||
) {
|
||||
Navigator::back();
|
||||
/// Show exit confirmation modal.
|
||||
pub fn show_exit_modal() {
|
||||
let exit_modal = Modal::new(Self::EXIT_MODAL).title(t!("modal_exit.exit"));
|
||||
Modal::show(exit_modal);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "android")]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
/// Calling on unexpected Android application termination (removal from recent apps).
|
||||
pub extern "C" fn Java_mw_gri_android_MainActivity_onTermination(
|
||||
_env: jni::JNIEnv,
|
||||
_class: jni::objects::JObject,
|
||||
_activity: jni::objects::JObject,
|
||||
) {
|
||||
Node::stop(false);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,13 +16,9 @@
|
|||
mod app;
|
||||
pub use app::{App, PlatformApp};
|
||||
|
||||
mod navigator;
|
||||
pub use navigator::Navigator;
|
||||
|
||||
mod colors;
|
||||
pub use colors::Colors;
|
||||
|
||||
pub mod platform;
|
||||
pub mod screens;
|
||||
pub mod views;
|
||||
pub mod icons;
|
|
@ -1,156 +0,0 @@
|
|||
// Copyright 2023 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 std::collections::BTreeSet;
|
||||
use std::sync::{RwLock, RwLockWriteGuard};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use crate::gui::screens::ScreenId;
|
||||
use crate::gui::views::Modal;
|
||||
|
||||
lazy_static! {
|
||||
/// Static [`Navigator`] state to be accessible from anywhere.
|
||||
static ref NAVIGATOR_STATE: RwLock<Navigator> = RwLock::new(Navigator::default());
|
||||
}
|
||||
|
||||
/// Logic of common navigation at ui for screens and modals.
|
||||
pub struct Navigator {
|
||||
/// Screen identifiers in navigation stack.
|
||||
screen_stack: BTreeSet<ScreenId>,
|
||||
/// Indicator if side panel is open.
|
||||
side_panel_open: AtomicBool,
|
||||
/// Modal window to show.
|
||||
modal: Option<Modal>,
|
||||
}
|
||||
|
||||
impl Default for Navigator {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
screen_stack: BTreeSet::new(),
|
||||
side_panel_open: AtomicBool::new(false),
|
||||
modal: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Navigator {
|
||||
/// Identifier for exit [`Modal`].
|
||||
pub const EXIT_MODAL: &'static str = "exit";
|
||||
|
||||
/// Initialize navigation from provided [`ScreenId`].
|
||||
pub fn init(from: ScreenId) {
|
||||
let mut w_nav = NAVIGATOR_STATE.write().unwrap();
|
||||
w_nav.screen_stack.clear();
|
||||
w_nav.screen_stack.insert(from);
|
||||
}
|
||||
|
||||
/// Check if provided [`ScreenId`] is current.
|
||||
pub fn is_current(id: &ScreenId) -> bool {
|
||||
let r_nav = NAVIGATOR_STATE.read().unwrap();
|
||||
r_nav.screen_stack.last().unwrap() == id
|
||||
}
|
||||
|
||||
/// Navigate to screen with provided [`ScreenId`].
|
||||
pub fn to(id: ScreenId) {
|
||||
NAVIGATOR_STATE.write().unwrap().screen_stack.insert(id);
|
||||
}
|
||||
|
||||
/// Go back at navigation stack, close showing modals first.
|
||||
pub fn back() {
|
||||
let mut w_nav = NAVIGATOR_STATE.write().unwrap();
|
||||
|
||||
// If Modal is showing and closeable, remove it from Navigator.
|
||||
if w_nav.modal.is_some() {
|
||||
let modal = w_nav.modal.as_ref().unwrap();
|
||||
if modal.is_closeable() {
|
||||
w_nav.modal = None;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Go back at screen stack or show exit confirmation Modal.
|
||||
if w_nav.screen_stack.len() > 1 {
|
||||
w_nav.screen_stack.pop_last();
|
||||
} else {
|
||||
Self::show_exit_modal_nav(w_nav);
|
||||
}
|
||||
}
|
||||
|
||||
/// Set exit confirmation [`Modal`].
|
||||
pub fn show_exit_modal() {
|
||||
let w_nav = NAVIGATOR_STATE.write().unwrap();
|
||||
Self::show_exit_modal_nav(w_nav);
|
||||
}
|
||||
|
||||
/// Set exit confirmation [`Modal`] with provided [NAVIGATOR_STATE] lock.
|
||||
fn show_exit_modal_nav(mut w_nav: RwLockWriteGuard<Navigator>) {
|
||||
let m = Modal::new(Self::EXIT_MODAL).title(t!("modal_exit.exit"));
|
||||
w_nav.modal = Some(m);
|
||||
}
|
||||
|
||||
/// Set [`Modal`] to show.
|
||||
pub fn show_modal(modal: Modal) {
|
||||
let mut w_nav = NAVIGATOR_STATE.write().unwrap();
|
||||
w_nav.modal = Some(modal);
|
||||
}
|
||||
|
||||
/// Check if [`Modal`] is open by returning its id, remove it from [`Navigator`] if it's closed.
|
||||
pub fn is_modal_open() -> Option<&'static str> {
|
||||
// Check if Modal is showing.
|
||||
{
|
||||
if NAVIGATOR_STATE.read().unwrap().modal.is_none() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if Modal is open.
|
||||
let (is_open, id) = {
|
||||
let r_nav = NAVIGATOR_STATE.read().unwrap();
|
||||
let modal = r_nav.modal.as_ref().unwrap();
|
||||
(modal.is_open(), modal.id)
|
||||
};
|
||||
|
||||
// If Modal is not open, remove it from navigator state.
|
||||
if !is_open {
|
||||
let mut w_nav = NAVIGATOR_STATE.write().unwrap();
|
||||
w_nav.modal = None;
|
||||
return None;
|
||||
}
|
||||
Some(id)
|
||||
}
|
||||
|
||||
/// Draw showing [`Modal`] content if it's opened.
|
||||
pub fn modal_ui(ui: &mut egui::Ui, add_content: impl FnOnce(&mut egui::Ui, &Modal)) {
|
||||
if let Some(modal) = &NAVIGATOR_STATE.read().unwrap().modal {
|
||||
if modal.is_open() {
|
||||
modal.ui(ui, add_content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Change state of side panel to opposite.
|
||||
pub fn toggle_side_panel() {
|
||||
let r_nav = NAVIGATOR_STATE.read().unwrap();
|
||||
let is_open = r_nav.side_panel_open.load(Ordering::Relaxed);
|
||||
r_nav.side_panel_open.store(!is_open, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Check if side panel is open.
|
||||
pub fn is_side_panel_open() -> bool {
|
||||
let r_nav = NAVIGATOR_STATE.read().unwrap();
|
||||
r_nav.side_panel_open.load(Ordering::Relaxed)
|
||||
}
|
||||
}
|
|
@ -47,7 +47,7 @@ impl eframe::App for PlatformApp<Desktop> {
|
|||
}
|
||||
|
||||
fn on_close_event(&mut self) -> bool {
|
||||
self.app.exit_requested = true;
|
||||
App::show_exit_modal();
|
||||
self.app.exit_allowed
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
// Copyright 2023 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.
|
||||
|
||||
pub use account::Account;
|
||||
pub use accounts::Accounts;
|
||||
pub use root::Root;
|
||||
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
|
||||
mod root;
|
||||
mod accounts;
|
||||
mod account;
|
||||
|
||||
#[derive(Ord, Eq, PartialOrd, PartialEq)]
|
||||
pub enum ScreenId {
|
||||
Accounts,
|
||||
Account,
|
||||
}
|
||||
|
||||
pub trait Screen {
|
||||
fn id(&self) -> ScreenId;
|
||||
fn ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
frame: &mut eframe::Frame,
|
||||
cb: &dyn PlatformCallbacks);
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
// Copyright 2023 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 std::cmp::min;
|
||||
use crate::gui::Navigator;
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::screens::{Account, Accounts, Screen, ScreenId};
|
||||
use crate::gui::views::{NetworkContainer, View};
|
||||
|
||||
/// Main ui container.
|
||||
pub struct Root {
|
||||
screens: Vec<Box<dyn Screen>>,
|
||||
network_panel: NetworkContainer,
|
||||
}
|
||||
|
||||
impl Default for Root {
|
||||
fn default() -> Self {
|
||||
Navigator::init(ScreenId::Accounts);
|
||||
|
||||
Self {
|
||||
screens: vec![
|
||||
Box::new(Accounts::default()),
|
||||
Box::new(Account::default())
|
||||
],
|
||||
network_panel: NetworkContainer::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Root {
|
||||
pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) {
|
||||
let (is_panel_open, panel_width) = Self::side_panel_state_width(frame);
|
||||
egui::SidePanel::left("network_panel")
|
||||
.resizable(false)
|
||||
.exact_width(panel_width)
|
||||
.frame(egui::Frame::default())
|
||||
.show_animated_inside(ui, is_panel_open, |ui| {
|
||||
self.network_panel.ui(ui, frame, cb);
|
||||
});
|
||||
|
||||
egui::CentralPanel::default()
|
||||
.frame(egui::Frame::default())
|
||||
.show_inside(ui, |ui| {
|
||||
self.show_current_screen(ui, frame, cb);
|
||||
});
|
||||
}
|
||||
|
||||
/// Show current screen at central panel based on [`Navigator`] state.
|
||||
fn show_current_screen(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
frame: &mut eframe::Frame,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
let Self { screens, .. } = self;
|
||||
for screen in screens.iter_mut() {
|
||||
if Navigator::is_current(&screen.id()) {
|
||||
screen.ui(ui, frame, cb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get side panel state and width.
|
||||
fn side_panel_state_width(frame: &mut eframe::Frame) -> (bool, f32) {
|
||||
let dual_panel_mode = View::is_dual_panel_mode(frame);
|
||||
let is_panel_open = dual_panel_mode || Navigator::is_side_panel_open();
|
||||
let panel_width = if dual_panel_mode {
|
||||
min(frame.info().window_info.size.x as i64, View::SIDE_PANEL_MIN_WIDTH) as f32
|
||||
} else {
|
||||
frame.info().window_info.size.x
|
||||
};
|
||||
(is_panel_open, panel_width)
|
||||
}
|
||||
}
|
|
@ -12,30 +12,5 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::screens::ScreenId;
|
||||
|
||||
pub struct Account {
|
||||
|
||||
}
|
||||
|
||||
impl Default for Account {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl super::Screen for Account {
|
||||
fn id(&self) -> ScreenId {
|
||||
ScreenId::Account
|
||||
}
|
||||
|
||||
fn ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
frame: &mut eframe::Frame,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
|
||||
}
|
||||
}
|
||||
mod content;
|
||||
pub use content::*;
|
|
@ -12,26 +12,30 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use egui::Frame;
|
||||
|
||||
use crate::gui::icons::{ARROW_CIRCLE_LEFT, GLOBE, PLUS};
|
||||
use crate::gui::{Colors, Navigator};
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::{GLOBE, PLUS};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::screens::{Screen, ScreenId};
|
||||
use crate::gui::views::{TitlePanel, TitleAction, View};
|
||||
use crate::gui::views::{Root, TitleAction, TitlePanel, View};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Accounts;
|
||||
|
||||
impl Screen for Accounts {
|
||||
fn id(&self) -> ScreenId {
|
||||
ScreenId::Accounts
|
||||
/// Accounts central panel content.
|
||||
pub struct AccountsContent {
|
||||
/// List of accounts.
|
||||
list: Vec<String>
|
||||
}
|
||||
|
||||
fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) {
|
||||
TitlePanel::ui(t!("screen_accounts.title"), if !View::is_dual_panel_mode(frame) {
|
||||
impl Default for AccountsContent {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
list: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AccountsContent {
|
||||
pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) {
|
||||
TitlePanel::ui(t!("accounts.title"), if !Root::is_dual_panel_mode(frame) {
|
||||
TitleAction::new(GLOBE, || {
|
||||
Navigator::toggle_side_panel();
|
||||
Root::toggle_network_panel();
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
@ -40,19 +44,13 @@ impl Screen for Accounts {
|
|||
}), ui);
|
||||
|
||||
egui::CentralPanel::default()
|
||||
.frame(Frame {
|
||||
.frame(egui::Frame {
|
||||
stroke: View::DEFAULT_STROKE,
|
||||
fill: Colors::FILL_DARK,
|
||||
..Default::default()
|
||||
})
|
||||
.show_inside(ui, |ui| {
|
||||
ui.label(format!("{}Here we go 10000 ツ", ARROW_CIRCLE_LEFT));
|
||||
if ui.button("TEST").clicked() {
|
||||
Navigator::to(ScreenId::Account)
|
||||
};
|
||||
if ui.button(format!("{}BACK ", ARROW_CIRCLE_LEFT)).clicked() {
|
||||
Navigator::back()
|
||||
};
|
||||
//TODO: accounts list
|
||||
});
|
||||
}
|
||||
}
|
|
@ -21,5 +21,11 @@ pub use title_panel::*;
|
|||
mod modal;
|
||||
pub use modal::*;
|
||||
|
||||
mod root;
|
||||
pub use root::*;
|
||||
|
||||
mod network;
|
||||
pub use network::*;
|
||||
|
||||
mod accounts;
|
||||
pub use accounts::*;
|
|
@ -14,21 +14,34 @@
|
|||
|
||||
use std::cmp::min;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{RwLock, RwLockWriteGuard};
|
||||
|
||||
use egui::{Align2, RichText, Rounding, Stroke, Vec2};
|
||||
use egui::epaint::RectShape;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::views::View;
|
||||
|
||||
/// Contains modal ids to draw at current container if possible.
|
||||
lazy_static! {
|
||||
/// Showing [`Modal`] state to be accessible from different ui parts.
|
||||
static ref MODAL_STATE: RwLock<ModalState> = RwLock::new(ModalState::default());
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ModalState {
|
||||
modal: Option<Modal>
|
||||
}
|
||||
|
||||
/// Contains ids to draw current [`Modal`] at this ui container if it's possible.
|
||||
pub trait ModalContainer {
|
||||
/// List of modal ids to show at current view container.
|
||||
/// List of [`Modal`] ids to draw at current ui container.
|
||||
fn modal_ids(&self) -> &Vec<&'static str>;
|
||||
|
||||
/// Check if it's possible to show modal.
|
||||
fn can_show_modal(&self, id: &'static str) -> bool {
|
||||
self.modal_ids().contains(&id)
|
||||
/// Check if it's possible to draw [`Modal`] at current ui container.
|
||||
fn can_draw_modal(&self) -> bool {
|
||||
let modal_id = Modal::opened();
|
||||
modal_id.is_some() && self.modal_ids().contains(&modal_id.unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,7 +51,7 @@ pub enum ModalPosition {
|
|||
Center
|
||||
}
|
||||
|
||||
/// Stores data to draw dialog box/popup at UI, powered by [`egui::Window`].
|
||||
/// Stores data to draw modal [`egui::Window`] at ui.
|
||||
pub struct Modal {
|
||||
/// Identifier for modal.
|
||||
pub(crate) id: &'static str,
|
||||
|
@ -67,34 +80,34 @@ impl Modal {
|
|||
}
|
||||
}
|
||||
|
||||
/// Setup position of Modal on the screen.
|
||||
/// Setup position of [`Modal`] on the screen.
|
||||
pub fn position(mut self, position: ModalPosition) -> Self {
|
||||
self.position = position;
|
||||
self
|
||||
}
|
||||
|
||||
/// Check if Modal is open.
|
||||
/// Check if [`Modal`] is open.
|
||||
pub fn is_open(&self) -> bool {
|
||||
self.open.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Mark Modal closed.
|
||||
/// Mark [`Modal`] closed.
|
||||
pub fn close(&self) {
|
||||
self.open.store(false, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Setup possibility to close Modal.
|
||||
/// Setup possibility to close [`Modal`].
|
||||
pub fn closeable(self, closeable: bool) -> Self {
|
||||
self.closeable.store(closeable, Ordering::Relaxed);
|
||||
self
|
||||
}
|
||||
|
||||
/// Disable possibility to close Modal.
|
||||
/// Disable possibility to close [`Modal`].
|
||||
pub fn disable_closing(&self) {
|
||||
self.closeable.store(false, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Check if Modal is closeable.
|
||||
/// Check if [`Modal`] is closeable.
|
||||
pub fn is_closeable(&self) -> bool {
|
||||
self.closeable.load(Ordering::Relaxed)
|
||||
}
|
||||
|
@ -105,8 +118,64 @@ impl Modal {
|
|||
self
|
||||
}
|
||||
|
||||
/// Show Modal with provided content.
|
||||
pub fn ui(&self, ui: &mut egui::Ui, add_content: impl FnOnce(&mut egui::Ui, &Modal)) {
|
||||
/// Set [`Modal`] instance to show at ui.
|
||||
pub fn show(modal: Modal) {
|
||||
let mut w_nav = MODAL_STATE.write().unwrap();
|
||||
w_nav.modal = Some(modal);
|
||||
}
|
||||
|
||||
/// Remove [`Modal`] from [`MODAL_STATE`] if it's showing and can be closed.
|
||||
/// Return `false` if Modal existed in [`MODAL_STATE`] before call.
|
||||
pub fn on_back() -> bool {
|
||||
let mut w_state = MODAL_STATE.write().unwrap();
|
||||
|
||||
// If Modal is showing and closeable, remove it from state.
|
||||
if w_state.modal.is_some() {
|
||||
let modal = w_state.modal.as_ref().unwrap();
|
||||
if modal.is_closeable() {
|
||||
w_state.modal = None;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Return id of opened [`Modal`] or remove its instance from [`MODAL_STATE`] if it was closed.
|
||||
pub fn opened() -> Option<&'static str> {
|
||||
// Check if Modal is showing.
|
||||
{
|
||||
if MODAL_STATE.read().unwrap().modal.is_none() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if Modal is open.
|
||||
let (is_open, id) = {
|
||||
let r_state = MODAL_STATE.read().unwrap();
|
||||
let modal = r_state.modal.as_ref().unwrap();
|
||||
(modal.is_open(), modal.id)
|
||||
};
|
||||
|
||||
// If Modal is not open, remove it from navigator state.
|
||||
if !is_open {
|
||||
let mut w_state = MODAL_STATE.write().unwrap();
|
||||
w_state.modal = None;
|
||||
return None;
|
||||
}
|
||||
Some(id)
|
||||
}
|
||||
|
||||
/// Draw opened [`Modal`] content.
|
||||
pub fn ui(ui: &mut egui::Ui, add_content: impl FnOnce(&mut egui::Ui, &Modal)) {
|
||||
if let Some(modal) = &MODAL_STATE.read().unwrap().modal {
|
||||
if modal.is_open() {
|
||||
modal.draw(ui, add_content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw Modal with provided content.
|
||||
fn draw(&self, ui: &mut egui::Ui, add_content: impl FnOnce(&mut egui::Ui, &Modal)) {
|
||||
let mut rect = ui.ctx().screen_rect();
|
||||
egui::Window::new("modal_bg_window")
|
||||
.title_bar(false)
|
||||
|
|
|
@ -12,11 +12,11 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
mod container;
|
||||
mod content;
|
||||
mod metrics;
|
||||
mod mining;
|
||||
mod settings;
|
||||
mod node;
|
||||
mod configs;
|
||||
|
||||
pub use container::*;
|
||||
pub use content::*;
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
use egui::{Id, RichText, TextStyle, Ui, Widget};
|
||||
|
||||
use crate::gui::{Colors, Navigator};
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::{CLOCK_COUNTDOWN, GRAPH, TIMER, WATCH};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{Modal, ModalPosition, View};
|
||||
|
@ -116,7 +116,7 @@ impl DandelionSetup {
|
|||
let epoch_modal = Modal::new(Self::EPOCH_MODAL)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("network_settings.change_value"));
|
||||
Navigator::show_modal(epoch_modal);
|
||||
Modal::show(epoch_modal);
|
||||
cb.show_keyboard();
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
@ -201,7 +201,7 @@ impl DandelionSetup {
|
|||
let embargo_modal = Modal::new(Self::EMBARGO_MODAL)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("network_settings.change_value"));
|
||||
Navigator::show_modal(embargo_modal);
|
||||
Modal::show(embargo_modal);
|
||||
cb.show_keyboard();
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
@ -286,7 +286,7 @@ impl DandelionSetup {
|
|||
let aggregation_modal = Modal::new(Self::AGGREGATION_MODAL)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("network_settings.change_value"));
|
||||
Navigator::show_modal(aggregation_modal);
|
||||
Modal::show(aggregation_modal);
|
||||
cb.show_keyboard();
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
@ -371,7 +371,7 @@ impl DandelionSetup {
|
|||
let embargo_modal = Modal::new(Self::STEM_PROBABILITY_MODAL)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("network_settings.change_value"));
|
||||
Navigator::show_modal(embargo_modal);
|
||||
Modal::show(embargo_modal);
|
||||
cb.show_keyboard();
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
|
|
@ -14,13 +14,14 @@
|
|||
|
||||
use eframe::emath::Align;
|
||||
use egui::{Id, Layout, RichText, TextStyle, Ui, Widget};
|
||||
use egui::os::OperatingSystem;
|
||||
use grin_core::global::ChainTypes;
|
||||
|
||||
use crate::AppConfig;
|
||||
use crate::gui::{Colors, Navigator};
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::{CLIPBOARD_TEXT, CLOCK_CLOCKWISE, COMPUTER_TOWER, COPY, PLUG, POWER, SHIELD, SHIELD_SLASH};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{Modal, ModalPosition, NetworkContainer, View};
|
||||
use crate::gui::views::{Modal, ModalPosition, NetworkContent, View};
|
||||
use crate::gui::views::network::settings::NetworkSettings;
|
||||
use crate::node::{Node, NodeConfig};
|
||||
|
||||
|
@ -114,7 +115,7 @@ impl NodeSetup {
|
|||
// Autorun node setup.
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.add_space(6.0);
|
||||
NetworkContainer::autorun_node_ui(ui);
|
||||
NetworkContent::autorun_node_ui(ui);
|
||||
if Node::is_running() {
|
||||
ui.add_space(2.0);
|
||||
ui.label(RichText::new(t!("network_settings.restart_node_required"))
|
||||
|
@ -224,7 +225,7 @@ impl NodeSetup {
|
|||
let port_modal = Modal::new(Self::API_PORT_MODAL)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("network_settings.change_value"));
|
||||
Navigator::show_modal(port_modal);
|
||||
Modal::show(port_modal);
|
||||
cb.show_keyboard();
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
@ -338,7 +339,7 @@ impl NodeSetup {
|
|||
let port_modal = Modal::new(modal_id)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("network_settings.change_value"));
|
||||
Navigator::show_modal(port_modal);
|
||||
Modal::show(port_modal);
|
||||
cb.show_keyboard();
|
||||
});
|
||||
}
|
||||
|
@ -354,7 +355,7 @@ impl NodeSetup {
|
|||
ui.label(RichText::new(description)
|
||||
.size(18.0)
|
||||
.color(Colors::GRAY));
|
||||
ui.add_space(8.0);
|
||||
ui.add_space(6.0);
|
||||
|
||||
// Draw API port text edit.
|
||||
let text_edit_resp = egui::TextEdit::singleline(&mut self.secret_edit)
|
||||
|
@ -367,9 +368,9 @@ impl NodeSetup {
|
|||
cb.show_keyboard();
|
||||
}
|
||||
|
||||
// Show buttons to copy/paste text for Android.
|
||||
if OperatingSystem::from_target_os() == OperatingSystem::Android {
|
||||
ui.add_space(12.0);
|
||||
|
||||
// Show buttons to copy/paste text.
|
||||
ui.scope(|ui| {
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(12.0, 0.0);
|
||||
|
@ -380,23 +381,21 @@ impl NodeSetup {
|
|||
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].with_layout(Layout::right_to_left(Align::Center), |ui| {
|
||||
let copy_title = format!("{} {}", COPY, t!("network_settings.copy"));
|
||||
let copy_title = format!("{} {}", COPY, t!("copy"));
|
||||
View::button(ui, copy_title, Colors::WHITE, || {
|
||||
cb.copy_string_to_buffer(self.secret_edit.clone());
|
||||
});
|
||||
});
|
||||
columns[1].with_layout(Layout::left_to_right(Align::Center), |ui| {
|
||||
let paste_title = format!("{} {}",
|
||||
CLIPBOARD_TEXT,
|
||||
t!("network_settings.paste"));
|
||||
let paste_title = format!("{} {}", CLIPBOARD_TEXT, t!("paste"));
|
||||
View::button(ui, paste_title, Colors::WHITE, || {
|
||||
self.secret_edit = cb.get_string_from_buffer();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// Show reminder to restart enabled node.
|
||||
NetworkSettings::node_restart_required_ui(ui);
|
||||
|
@ -455,7 +454,7 @@ impl NodeSetup {
|
|||
let ftl_modal = Modal::new(Self::FTL_MODAL)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("network_settings.change_value"));
|
||||
Navigator::show_modal(ftl_modal);
|
||||
Modal::show(ftl_modal);
|
||||
cb.show_keyboard();
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
|
|
@ -17,7 +17,7 @@ use egui_extras::{Size, StripBuilder};
|
|||
use grin_core::global::ChainTypes;
|
||||
|
||||
use crate::AppConfig;
|
||||
use crate::gui::{Colors, Navigator};
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::{HANDSHAKE, PLUG, TRASH, GLOBE_SIMPLE, PLUS_CIRCLE, ARROW_FAT_LINES_UP, ARROW_FAT_LINES_DOWN, ARROW_FAT_LINE_UP, PROHIBIT_INSET};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{Modal, ModalPosition, View};
|
||||
|
@ -215,7 +215,7 @@ impl P2PSetup {
|
|||
let port_modal = Modal::new(Self::PORT_MODAL)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("network_settings.change_value"));
|
||||
Navigator::show_modal(port_modal);
|
||||
Modal::show(port_modal);
|
||||
cb.show_keyboard();
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
@ -374,7 +374,7 @@ impl P2PSetup {
|
|||
let peer_modal = Modal::new(modal_id)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(modal_title);
|
||||
Navigator::show_modal(peer_modal);
|
||||
Modal::show(peer_modal);
|
||||
cb.show_keyboard();
|
||||
});
|
||||
}
|
||||
|
@ -561,7 +561,7 @@ impl P2PSetup {
|
|||
let ban_modal = Modal::new(Self::BAN_WINDOW_MODAL)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("network_settings.change_value"));
|
||||
Navigator::show_modal(ban_modal);
|
||||
Modal::show(ban_modal);
|
||||
cb.show_keyboard();
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
@ -652,7 +652,7 @@ impl P2PSetup {
|
|||
let max_inbound_modal = Modal::new(Self::MAX_INBOUND_MODAL)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("network_settings.change_value"));
|
||||
Navigator::show_modal(max_inbound_modal);
|
||||
Modal::show(max_inbound_modal);
|
||||
cb.show_keyboard();
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
@ -738,7 +738,7 @@ impl P2PSetup {
|
|||
let max_outbound = Modal::new(Self::MAX_OUTBOUND_MODAL)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("network_settings.change_value"));
|
||||
Navigator::show_modal(max_outbound);
|
||||
Modal::show(max_outbound);
|
||||
cb.show_keyboard();
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
@ -824,7 +824,7 @@ impl P2PSetup {
|
|||
let min_outbound = Modal::new(Self::MIN_OUTBOUND_MODAL)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("network_settings.change_value"));
|
||||
Navigator::show_modal(min_outbound);
|
||||
Modal::show(min_outbound);
|
||||
cb.show_keyboard();
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
use egui::{Id, RichText, TextStyle, Ui, Widget};
|
||||
|
||||
use crate::gui::{Colors, Navigator};
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::{BEZIER_CURVE, BOUNDING_BOX, CHART_SCATTER, CIRCLES_THREE, CLOCK_COUNTDOWN, HAND_COINS};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{Modal, ModalPosition, View};
|
||||
|
@ -118,7 +118,7 @@ impl PoolSetup {
|
|||
let fee_modal = Modal::new(Self::FEE_BASE_MODAL)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("network_settings.change_value"));
|
||||
Navigator::show_modal(fee_modal);
|
||||
Modal::show(fee_modal);
|
||||
cb.show_keyboard();
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
@ -203,7 +203,7 @@ impl PoolSetup {
|
|||
let reorg_modal = Modal::new(Self::REORG_PERIOD_MODAL)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("network_settings.change_value"));
|
||||
Navigator::show_modal(reorg_modal);
|
||||
Modal::show(reorg_modal);
|
||||
cb.show_keyboard();
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
@ -288,7 +288,7 @@ impl PoolSetup {
|
|||
let size_modal = Modal::new(Self::POOL_SIZE_MODAL)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("network_settings.change_value"));
|
||||
Navigator::show_modal(size_modal);
|
||||
Modal::show(size_modal);
|
||||
cb.show_keyboard();
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
@ -373,7 +373,7 @@ impl PoolSetup {
|
|||
let stem_modal = Modal::new(Self::STEMPOOL_SIZE_MODAL)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("network_settings.change_value"));
|
||||
Navigator::show_modal(stem_modal);
|
||||
Modal::show(stem_modal);
|
||||
cb.show_keyboard();
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
@ -458,7 +458,7 @@ impl PoolSetup {
|
|||
let weight_modal = Modal::new(Self::MAX_WEIGHT_MODAL)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("network_settings.change_value"));
|
||||
Navigator::show_modal(weight_modal);
|
||||
Modal::show(weight_modal);
|
||||
cb.show_keyboard();
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
use egui::{Id, RichText, TextStyle, Ui, Widget};
|
||||
|
||||
use crate::gui::{Colors, Navigator};
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::{BARBELL, HARD_DRIVES, PLUG, TIMER};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{Modal, ModalPosition, View};
|
||||
|
@ -98,13 +98,15 @@ impl StratumSetup {
|
|||
View::checkbox(ui, stratum_enabled, t!("network.autorun"), || {
|
||||
NodeConfig::toggle_stratum_autorun();
|
||||
});
|
||||
ui.add_space(4.0);
|
||||
|
||||
// Show message to restart node after changing of stratum settings
|
||||
ui.label(RichText::new(t!("network_mining.info_settings"))
|
||||
// Show reminder to restart running server.
|
||||
if Node::get_stratum_stats().is_running {
|
||||
ui.add_space(2.0);
|
||||
ui.label(RichText::new(t!("network_mining.restart_server_required"))
|
||||
.size(16.0)
|
||||
.color(Colors::INACTIVE_TEXT)
|
||||
);
|
||||
}
|
||||
ui.add_space(8.0);
|
||||
});
|
||||
|
||||
|
@ -164,7 +166,7 @@ impl StratumSetup {
|
|||
let port_modal = Modal::new(Self::STRATUM_PORT_MODAL)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("network_settings.change_value"));
|
||||
Navigator::show_modal(port_modal);
|
||||
Modal::show(port_modal);
|
||||
cb.show_keyboard();
|
||||
});
|
||||
ui.add_space(12.0);
|
||||
|
@ -206,6 +208,8 @@ impl StratumSetup {
|
|||
ui.label(RichText::new(t!("network_settings.port_unavailable"))
|
||||
.size(18.0)
|
||||
.color(Colors::RED));
|
||||
} else {
|
||||
server_restart_required_ui(ui);
|
||||
}
|
||||
|
||||
ui.add_space(12.0);
|
||||
|
@ -269,7 +273,7 @@ impl StratumSetup {
|
|||
let time_modal = Modal::new(Self::ATTEMPT_TIME_MODAL)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("network_settings.change_value"));
|
||||
Navigator::show_modal(time_modal);
|
||||
Modal::show(time_modal);
|
||||
cb.show_keyboard();
|
||||
});
|
||||
ui.add_space(12.0);
|
||||
|
@ -303,7 +307,7 @@ impl StratumSetup {
|
|||
.size(18.0)
|
||||
.color(Colors::RED));
|
||||
} else {
|
||||
NetworkSettings::node_restart_required_ui(ui);
|
||||
server_restart_required_ui(ui);
|
||||
}
|
||||
ui.add_space(12.0);
|
||||
});
|
||||
|
@ -355,7 +359,7 @@ impl StratumSetup {
|
|||
let diff_modal = Modal::new(Self::MIN_SHARE_DIFF_MODAL)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("network_settings.change_value"));
|
||||
Navigator::show_modal(diff_modal);
|
||||
Modal::show(diff_modal);
|
||||
cb.show_keyboard();
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
@ -389,7 +393,7 @@ impl StratumSetup {
|
|||
.size(18.0)
|
||||
.color(Colors::RED));
|
||||
} else {
|
||||
NetworkSettings::node_restart_required_ui(ui);
|
||||
server_restart_required_ui(ui);
|
||||
}
|
||||
ui.add_space(12.0);
|
||||
});
|
||||
|
@ -424,3 +428,14 @@ impl StratumSetup {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Reminder to restart enabled node to show on edit setting at [`Modal`].
|
||||
pub fn server_restart_required_ui(ui: &mut Ui) {
|
||||
if Node::get_stratum_stats().is_running {
|
||||
ui.add_space(12.0);
|
||||
ui.label(RichText::new(t!("network_mining.restart_server_required"))
|
||||
.size(16.0)
|
||||
.color(Colors::GREEN)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -18,10 +18,10 @@ use egui_extras::{Size, StripBuilder};
|
|||
use grin_chain::SyncStatus;
|
||||
|
||||
use crate::AppConfig;
|
||||
use crate::gui::{Colors, Navigator};
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::{CARDHOLDER, DATABASE, DOTS_THREE_OUTLINE_VERTICAL, FACTORY, FADERS, GAUGE, POWER};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{Modal, ModalContainer, TitlePanel, View};
|
||||
use crate::gui::views::{Modal, ModalContainer, Root, TitlePanel, View};
|
||||
use crate::gui::views::network::configs::dandelion::DandelionSetup;
|
||||
use crate::gui::views::network::configs::node::NodeSetup;
|
||||
use crate::gui::views::network::configs::p2p::P2PSetup;
|
||||
|
@ -39,6 +39,7 @@ pub trait NetworkTab {
|
|||
fn on_modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks);
|
||||
}
|
||||
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum NetworkTabType {
|
||||
Node,
|
||||
|
@ -48,7 +49,7 @@ pub enum NetworkTabType {
|
|||
}
|
||||
|
||||
impl NetworkTabType {
|
||||
pub fn name(&self) -> String {
|
||||
pub fn title(&self) -> String {
|
||||
match *self {
|
||||
NetworkTabType::Node => { t!("network.node") }
|
||||
NetworkTabType::Metrics => { t!("network.metrics") }
|
||||
|
@ -58,12 +59,15 @@ impl NetworkTabType {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct NetworkContainer {
|
||||
/// Network side panel content.
|
||||
pub struct NetworkContent {
|
||||
/// Current tab view to show at ui.
|
||||
current_tab: Box<dyn NetworkTab>,
|
||||
/// [`Modal`] ids allowed at this ui container.
|
||||
modal_ids: Vec<&'static str>,
|
||||
}
|
||||
|
||||
impl Default for NetworkContainer {
|
||||
impl Default for NetworkContent {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
current_tab: Box::new(NetworkNode::default()),
|
||||
|
@ -106,18 +110,17 @@ impl Default for NetworkContainer {
|
|||
}
|
||||
}
|
||||
|
||||
impl ModalContainer for NetworkContainer {
|
||||
impl ModalContainer for NetworkContent {
|
||||
fn modal_ids(&self) -> &Vec<&'static str> {
|
||||
self.modal_ids.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl NetworkContainer {
|
||||
impl NetworkContent {
|
||||
pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) {
|
||||
// Show modal content if it's opened.
|
||||
let modal_id = Navigator::is_modal_open();
|
||||
if modal_id.is_some() && self.can_show_modal(modal_id.unwrap()) {
|
||||
Navigator::modal_ui(ui, |ui, modal| {
|
||||
if self.can_draw_modal() {
|
||||
Modal::ui(ui, |ui, modal| {
|
||||
self.current_tab.as_mut().on_modal_ui(ui, modal, cb);
|
||||
});
|
||||
}
|
||||
|
@ -169,24 +172,26 @@ impl NetworkContainer {
|
|||
// Setup vertical padding inside tab button.
|
||||
ui.style_mut().spacing.button_padding = egui::vec2(0.0, 8.0);
|
||||
|
||||
// Draw tab buttons.
|
||||
let current_type = self.current_tab.get_type();
|
||||
ui.columns(4, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::tab_button(ui, DATABASE, self.is_current_tab(NetworkTabType::Node), || {
|
||||
View::tab_button(ui, DATABASE, current_type == NetworkTabType::Node, || {
|
||||
self.current_tab = Box::new(NetworkNode::default());
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::tab_button(ui, GAUGE, self.is_current_tab(NetworkTabType::Metrics), || {
|
||||
View::tab_button(ui, GAUGE, current_type == NetworkTabType::Metrics, || {
|
||||
self.current_tab = Box::new(NetworkMetrics::default());
|
||||
});
|
||||
});
|
||||
columns[2].vertical_centered_justified(|ui| {
|
||||
View::tab_button(ui, FACTORY, self.is_current_tab(NetworkTabType::Mining), || {
|
||||
View::tab_button(ui, FACTORY, current_type == NetworkTabType::Mining, || {
|
||||
self.current_tab = Box::new(NetworkMining::default());
|
||||
});
|
||||
});
|
||||
columns[3].vertical_centered_justified(|ui| {
|
||||
View::tab_button(ui, FADERS, self.is_current_tab(NetworkTabType::Settings), || {
|
||||
View::tab_button(ui, FADERS, current_type == NetworkTabType::Settings, || {
|
||||
self.current_tab = Box::new(NetworkSettings::default());
|
||||
});
|
||||
});
|
||||
|
@ -194,11 +199,6 @@ impl NetworkContainer {
|
|||
});
|
||||
}
|
||||
|
||||
/// Check if current tab equals providing [`NetworkTabType`].
|
||||
fn is_current_tab(&self, tab_type: NetworkTabType) -> bool {
|
||||
self.current_tab.get_type() == tab_type
|
||||
}
|
||||
|
||||
/// Draw title content.
|
||||
fn title_ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
|
||||
StripBuilder::new(ui)
|
||||
|
@ -217,10 +217,10 @@ impl NetworkContainer {
|
|||
self.title_text_ui(builder);
|
||||
});
|
||||
strip.cell(|ui| {
|
||||
if !View::is_dual_panel_mode(frame) {
|
||||
if !Root::is_dual_panel_mode(frame) {
|
||||
ui.centered_and_justified(|ui| {
|
||||
View::title_button(ui, CARDHOLDER, || {
|
||||
Navigator::toggle_side_panel();
|
||||
Root::toggle_network_panel();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -237,7 +237,7 @@ impl NetworkContainer {
|
|||
strip.cell(|ui| {
|
||||
ui.add_space(4.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(self.current_tab.get_type().name().to_uppercase())
|
||||
ui.label(RichText::new(self.current_tab.get_type().title().to_uppercase())
|
||||
.size(19.0)
|
||||
.color(Colors::TITLE));
|
||||
});
|
||||
|
@ -291,7 +291,7 @@ impl NetworkContainer {
|
|||
});
|
||||
}
|
||||
|
||||
/// Draw checkbox with setting to run node on app launch.
|
||||
/// Draw checkbox to run integrated node on application launch.
|
||||
pub fn autorun_node_ui(ui: &mut egui::Ui) {
|
||||
let autostart = AppConfig::autostart_node();
|
||||
View::checkbox(ui, autostart, t!("network.autorun"), || {
|
|
@ -20,9 +20,10 @@ use crate::gui::Colors;
|
|||
use crate::gui::icons::{AT, COINS, CUBE_TRANSPARENT, HASH, HOURGLASS_LOW, HOURGLASS_MEDIUM, TIMER};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::network::{NetworkTab, NetworkTabType};
|
||||
use crate::gui::views::{Modal, NetworkContainer, View};
|
||||
use crate::gui::views::{Modal, NetworkContent, View};
|
||||
use crate::node::Node;
|
||||
|
||||
/// Chain metrics tab content.
|
||||
#[derive(Default)]
|
||||
pub struct NetworkMetrics;
|
||||
|
||||
|
@ -39,7 +40,7 @@ impl NetworkTab for NetworkMetrics {
|
|||
let server_stats = Node::get_stats();
|
||||
// Show message to enable node when it's not running.
|
||||
if !Node::is_running() {
|
||||
NetworkContainer::disabled_node_ui(ui);
|
||||
NetworkContent::disabled_node_ui(ui);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -94,7 +95,7 @@ impl NetworkTab for NetworkMetrics {
|
|||
});
|
||||
ui.add_space(4.0);
|
||||
|
||||
// Show difficulty adjustment window info
|
||||
// Show difficulty adjustment window info.
|
||||
let difficulty_title = t!(
|
||||
"network_metrics.difficulty_window",
|
||||
"size" => stats.diff_stats.window_size
|
||||
|
@ -122,7 +123,7 @@ impl NetworkTab for NetworkMetrics {
|
|||
});
|
||||
ui.add_space(4.0);
|
||||
|
||||
// Show difficulty adjustment window blocks
|
||||
// Show difficulty adjustment window blocks.
|
||||
let blocks_size = stats.diff_stats.last_blocks.len();
|
||||
ScrollArea::vertical()
|
||||
.id_source("difficulty_scroll")
|
||||
|
@ -130,7 +131,7 @@ impl NetworkTab for NetworkMetrics {
|
|||
.stick_to_bottom(true)
|
||||
.show_rows(
|
||||
ui,
|
||||
DIFF_BLOCK_UI_HEIGHT,
|
||||
BLOCK_ITEM_HEIGHT,
|
||||
blocks_size,
|
||||
|ui, row_range| {
|
||||
for index in row_range {
|
||||
|
@ -144,7 +145,7 @@ impl NetworkTab for NetworkMetrics {
|
|||
} else {
|
||||
[false, false]
|
||||
};
|
||||
draw_diff_block(ui, db, rounding)
|
||||
block_item_ui(ui, db, rounding)
|
||||
}
|
||||
},
|
||||
);
|
||||
|
@ -153,9 +154,10 @@ impl NetworkTab for NetworkMetrics {
|
|||
fn on_modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {}
|
||||
}
|
||||
|
||||
const DIFF_BLOCK_UI_HEIGHT: f32 = 78.30;
|
||||
const BLOCK_ITEM_HEIGHT: f32 = 78.30;
|
||||
|
||||
fn draw_diff_block(ui: &mut egui::Ui, db: &DiffBlock, rounding: [bool; 2]) {
|
||||
/// Draw block difficulty item.
|
||||
fn block_item_ui(ui: &mut egui::Ui, db: &DiffBlock, rounding: [bool; 2]) {
|
||||
// Add space before the first item.
|
||||
if rounding[0] {
|
||||
ui.add_space(4.0);
|
||||
|
@ -164,8 +166,9 @@ fn draw_diff_block(ui: &mut egui::Ui, db: &DiffBlock, rounding: [bool; 2]) {
|
|||
ui.horizontal(|ui| {
|
||||
ui.add_space(6.0);
|
||||
ui.vertical(|ui| {
|
||||
// Draw round background.
|
||||
let mut rect = ui.available_rect_before_wrap();
|
||||
rect.set_height(DIFF_BLOCK_UI_HEIGHT);
|
||||
rect.set_height(BLOCK_ITEM_HEIGHT);
|
||||
ui.painter().rect(
|
||||
rect,
|
||||
Rounding {
|
||||
|
|
|
@ -20,11 +20,12 @@ use grin_servers::WorkerStats;
|
|||
use crate::gui::Colors;
|
||||
use crate::gui::icons::{BARBELL, CLOCK_AFTERNOON, CPU, CUBE, FADERS, FOLDER_DASHED, FOLDER_NOTCH_MINUS, FOLDER_NOTCH_PLUS, HARD_DRIVES, PLUGS, PLUGS_CONNECTED, POLYGON};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{Modal, NetworkContainer, View};
|
||||
use crate::gui::views::{Modal, NetworkContent, View};
|
||||
use crate::gui::views::network::{NetworkTab, NetworkTabType};
|
||||
use crate::gui::views::network::configs::stratum::StratumSetup;
|
||||
use crate::node::{Node, NodeConfig};
|
||||
|
||||
/// Mining tab content.
|
||||
#[derive(Default)]
|
||||
pub struct NetworkMining {
|
||||
stratum_server_setup: StratumSetup
|
||||
|
@ -40,7 +41,7 @@ impl NetworkTab for NetworkMining {
|
|||
|
||||
// Show message to enable node when it's not running.
|
||||
if !Node::is_running() {
|
||||
NetworkContainer::disabled_node_ui(ui);
|
||||
NetworkContent::disabled_node_ui(ui);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -169,7 +170,7 @@ impl NetworkTab for NetworkMining {
|
|||
.id_source("stratum_workers_scroll")
|
||||
.show_rows(
|
||||
ui,
|
||||
WORKER_UI_HEIGHT,
|
||||
WORKER_ITEM_HEIGHT,
|
||||
workers_size,
|
||||
|ui, row_range| {
|
||||
for index in row_range {
|
||||
|
@ -183,7 +184,7 @@ impl NetworkTab for NetworkMining {
|
|||
} else {
|
||||
[false, false]
|
||||
};
|
||||
draw_workers_stats(ui, worker, rounding)
|
||||
worker_item_ui(ui, worker, rounding)
|
||||
}
|
||||
},
|
||||
);
|
||||
|
@ -207,9 +208,10 @@ impl NetworkTab for NetworkMining {
|
|||
}
|
||||
}
|
||||
|
||||
const WORKER_UI_HEIGHT: f32 = 77.0;
|
||||
const WORKER_ITEM_HEIGHT: f32 = 77.0;
|
||||
|
||||
fn draw_workers_stats(ui: &mut egui::Ui, ws: &WorkerStats, rounding: [bool; 2]) {
|
||||
/// Draw worker statistics item.
|
||||
fn worker_item_ui(ui: &mut egui::Ui, ws: &WorkerStats, rounding: [bool; 2]) {
|
||||
// Add space before the first item.
|
||||
if rounding[0] {
|
||||
ui.add_space(4.0);
|
||||
|
@ -217,8 +219,9 @@ fn draw_workers_stats(ui: &mut egui::Ui, ws: &WorkerStats, rounding: [bool; 2])
|
|||
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.vertical_centered_justified(|ui| {
|
||||
// Draw round background.
|
||||
let mut rect = ui.available_rect_before_wrap();
|
||||
rect.set_height(WORKER_UI_HEIGHT);
|
||||
rect.set_height(WORKER_ITEM_HEIGHT);
|
||||
ui.painter().rect(
|
||||
rect,
|
||||
Rounding {
|
||||
|
@ -232,94 +235,68 @@ fn draw_workers_stats(ui: &mut egui::Ui, ws: &WorkerStats, rounding: [bool; 2])
|
|||
);
|
||||
|
||||
ui.add_space(2.0);
|
||||
ui.horizontal_top(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_space(5.0);
|
||||
|
||||
// Draw worker connection status.
|
||||
let (status_text, status_icon, status_color) = match ws.is_connected {
|
||||
true => (t!("network_mining.connected"), PLUGS_CONNECTED, Colors::BLACK),
|
||||
false => (t!("network_mining.disconnected"), PLUGS, Colors::INACTIVE_TEXT)
|
||||
};
|
||||
ui.add_space(5.0);
|
||||
ui.heading(RichText::new(status_icon)
|
||||
let status_line_text = format!("{} {} {}", status_icon, ws.id, status_text);
|
||||
ui.heading(RichText::new(status_line_text)
|
||||
.color(status_color)
|
||||
.size(18.0));
|
||||
ui.add_space(2.0);
|
||||
|
||||
// Draw worker ID.
|
||||
ui.heading(RichText::new(&ws.id)
|
||||
.color(status_color)
|
||||
.size(18.0));
|
||||
ui.add_space(3.0);
|
||||
|
||||
// Draw worker status.
|
||||
ui.heading(RichText::new(status_text)
|
||||
.color(status_color)
|
||||
.size(18.0));
|
||||
});
|
||||
ui.horizontal_top(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_space(6.0);
|
||||
ui.heading(RichText::new(BARBELL)
|
||||
.color(Colors::TITLE)
|
||||
.size(16.0));
|
||||
ui.add_space(4.0);
|
||||
|
||||
// Draw difficulty.
|
||||
ui.heading(RichText::new(ws.pow_difficulty.to_string())
|
||||
let diff_text = format!("{} {}", BARBELL, ws.pow_difficulty);
|
||||
ui.heading(RichText::new(diff_text)
|
||||
.color(Colors::TITLE)
|
||||
.size(16.0));
|
||||
ui.add_space(6.0);
|
||||
|
||||
ui.heading(RichText::new(FOLDER_NOTCH_PLUS)
|
||||
.color(Colors::GREEN)
|
||||
.size(16.0));
|
||||
ui.add_space(3.0);
|
||||
// Draw accepted shares.
|
||||
ui.heading(RichText::new(ws.num_accepted.to_string())
|
||||
let accepted_text = format!("{} {}", FOLDER_NOTCH_PLUS, ws.num_accepted);
|
||||
ui.heading(RichText::new(accepted_text)
|
||||
.color(Colors::GREEN)
|
||||
.size(16.0));
|
||||
ui.add_space(6.0);
|
||||
|
||||
ui.heading(RichText::new(FOLDER_NOTCH_MINUS)
|
||||
.color(Colors::RED)
|
||||
.size(16.0));
|
||||
ui.add_space(3.0);
|
||||
// Draw rejected shares.
|
||||
ui.heading(RichText::new(ws.num_rejected.to_string())
|
||||
let rejected_text = format!("{} {}", FOLDER_NOTCH_MINUS, ws.num_rejected);
|
||||
ui.heading(RichText::new(rejected_text)
|
||||
.color(Colors::RED)
|
||||
.size(16.0));
|
||||
ui.add_space(6.0);
|
||||
|
||||
ui.heading(RichText::new(FOLDER_DASHED)
|
||||
.color(Colors::GRAY)
|
||||
.size(16.0));
|
||||
ui.add_space(3.0);
|
||||
// Draw stale shares.
|
||||
ui.heading(RichText::new(ws.num_stale.to_string())
|
||||
let stale_text = format!("{} {}", FOLDER_DASHED, ws.num_stale);
|
||||
ui.heading(RichText::new(stale_text)
|
||||
.color(Colors::GRAY)
|
||||
.size(16.0));
|
||||
ui.add_space(6.0);
|
||||
|
||||
ui.heading(RichText::new(CUBE)
|
||||
.color(Colors::TITLE)
|
||||
.size(16.0));
|
||||
ui.add_space(3.0);
|
||||
// Draw blocks found.
|
||||
ui.heading(RichText::new(ws.num_blocks_found.to_string())
|
||||
let blocks_found_text = format!("{} {}", CUBE, ws.num_blocks_found);
|
||||
ui.heading(RichText::new(blocks_found_text)
|
||||
.color(Colors::TITLE)
|
||||
.size(16.0));
|
||||
});
|
||||
ui.horizontal_top(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_space(6.0);
|
||||
ui.heading(RichText::new(CLOCK_AFTERNOON)
|
||||
.color(Colors::TITLE)
|
||||
.size(16.0));
|
||||
ui.add_space(4.0);
|
||||
|
||||
// Draw block time
|
||||
let seen = ws.last_seen.duration_since(std::time::UNIX_EPOCH).unwrap().as_secs();
|
||||
let naive_datetime = NaiveDateTime::from_timestamp_opt(seen as i64, 0).unwrap();
|
||||
let datetime: DateTime<Utc> = DateTime::from_utc(naive_datetime, Utc);
|
||||
ui.heading(RichText::new(datetime.to_string())
|
||||
let date_text = format!("{} {}", CLOCK_AFTERNOON, datetime);
|
||||
ui.heading(RichText::new(date_text)
|
||||
.color(Colors::GRAY)
|
||||
.size(16.0));
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -20,9 +20,10 @@ use crate::gui::Colors;
|
|||
use crate::gui::icons::{AT, CUBE, DEVICES, FLOW_ARROW, HANDSHAKE, PACKAGE, PLUGS_CONNECTED, SHARE_NETWORK};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{Modal, View};
|
||||
use crate::gui::views::network::{NetworkContainer, NetworkTab, NetworkTabType};
|
||||
use crate::gui::views::network::{NetworkContent, NetworkTab, NetworkTabType};
|
||||
use crate::node::Node;
|
||||
|
||||
/// Integrated node tab content.
|
||||
#[derive(Default)]
|
||||
pub struct NetworkNode;
|
||||
|
||||
|
@ -35,7 +36,7 @@ impl NetworkTab for NetworkNode {
|
|||
let server_stats = Node::get_stats();
|
||||
// Show message to enable node when it's not running.
|
||||
if !Node::is_running() {
|
||||
NetworkContainer::disabled_node_ui(ui);
|
||||
NetworkContent::disabled_node_ui(ui);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -175,7 +176,7 @@ impl NetworkTab for NetworkNode {
|
|||
[false, false]
|
||||
};
|
||||
ui.vertical_centered(|ui| {
|
||||
draw_peer_stats(ui, ps, rounding);
|
||||
peer_item_ui(ui, ps, rounding);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -185,7 +186,8 @@ impl NetworkTab for NetworkNode {
|
|||
fn on_modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {}
|
||||
}
|
||||
|
||||
fn draw_peer_stats(ui: &mut egui::Ui, peer: &PeerStats, rounding: [bool; 2]) {
|
||||
/// Draw connected peer info item.
|
||||
fn peer_item_ui(ui: &mut egui::Ui, peer: &PeerStats, rounding: [bool; 2]) {
|
||||
ui.vertical(|ui| {
|
||||
// Draw round background.
|
||||
let mut rect = ui.available_rect_before_wrap();
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
use egui::{RichText, ScrollArea};
|
||||
|
||||
use crate::gui::{Colors, Navigator};
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::ARROW_COUNTER_CLOCKWISE;
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{Modal, ModalPosition, View};
|
||||
|
@ -141,7 +141,7 @@ impl NetworkSettings {
|
|||
let reset_modal = Modal::new(Self::RESET_SETTINGS_MODAL)
|
||||
.position(ModalPosition::Center)
|
||||
.title(t!("modal.confirmation"));
|
||||
Navigator::show_modal(reset_modal);
|
||||
Modal::show(reset_modal);
|
||||
});
|
||||
|
||||
// Show reminder to restart enabled node.
|
||||
|
@ -207,7 +207,7 @@ impl NetworkSettings {
|
|||
let port_modal = Modal::new(Self::NODE_RESTART_REQUIRED_MODAL)
|
||||
.position(ModalPosition::Center)
|
||||
.title(t!("network.settings"));
|
||||
Navigator::show_modal(port_modal);
|
||||
Modal::show(port_modal);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
110
src/gui/views/root.rs
Normal file
110
src/gui/views/root.rs
Normal file
|
@ -0,0 +1,110 @@
|
|||
// Copyright 2023 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 std::cmp::min;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use crate::gui::App;
|
||||
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{AccountsContent, Modal, NetworkContent};
|
||||
|
||||
lazy_static! {
|
||||
/// To check if side panel is open from any part of ui.
|
||||
static ref NETWORK_PANEL_OPEN: AtomicBool = AtomicBool::new(false);
|
||||
}
|
||||
|
||||
/// Main ui content, handles network panel state modal state.
|
||||
#[derive(Default)]
|
||||
pub struct Root {
|
||||
network: NetworkContent,
|
||||
accounts: AccountsContent,
|
||||
}
|
||||
|
||||
impl Root {
|
||||
/// Default width of side panel at application UI.
|
||||
pub const SIDE_PANEL_MIN_WIDTH: i64 = 400;
|
||||
|
||||
pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) {
|
||||
let (is_panel_open, panel_width) = Self::side_panel_state_width(frame);
|
||||
egui::SidePanel::left("network_panel")
|
||||
.resizable(false)
|
||||
.exact_width(panel_width)
|
||||
.frame(egui::Frame::default())
|
||||
.show_animated_inside(ui, is_panel_open, |ui| {
|
||||
self.network.ui(ui, frame, cb);
|
||||
});
|
||||
|
||||
egui::CentralPanel::default()
|
||||
.frame(egui::Frame::default())
|
||||
.show_inside(ui, |ui| {
|
||||
self.accounts.ui(ui, frame, cb);
|
||||
});
|
||||
}
|
||||
|
||||
/// Get side panel state and width.
|
||||
fn side_panel_state_width(frame: &mut eframe::Frame) -> (bool, f32) {
|
||||
let dual_panel_mode = Self::is_dual_panel_mode(frame);
|
||||
let is_panel_open = dual_panel_mode || Self::is_network_panel_open();
|
||||
let panel_width = if dual_panel_mode {
|
||||
min(frame.info().window_info.size.x as i64, Self::SIDE_PANEL_MIN_WIDTH) as f32
|
||||
} else {
|
||||
frame.info().window_info.size.x
|
||||
};
|
||||
(is_panel_open, panel_width)
|
||||
}
|
||||
|
||||
/// Check if ui can show [`NetworkContent`] and [`AccountsContent`] at same time.
|
||||
pub fn is_dual_panel_mode(frame: &mut eframe::Frame) -> bool {
|
||||
let w = frame.info().window_info.size.x;
|
||||
let h = frame.info().window_info.size.y;
|
||||
// Screen is wide if width is greater than height or just 20% smaller.
|
||||
let is_wide_screen = w > h || w + (w * 0.2) >= h;
|
||||
// Dual panel mode is available when window is wide and its width is at least 2 times
|
||||
// greater than minimal width of the side panel.
|
||||
is_wide_screen && w >= Self::SIDE_PANEL_MIN_WIDTH as f32 * 2.0
|
||||
}
|
||||
|
||||
/// Toggle [`Network`] panel state.
|
||||
pub fn toggle_network_panel() {
|
||||
let is_open = NETWORK_PANEL_OPEN.load(Ordering::Relaxed);
|
||||
NETWORK_PANEL_OPEN.store(!is_open, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Check if side panel is open.
|
||||
pub fn is_network_panel_open() -> bool {
|
||||
NETWORK_PANEL_OPEN.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Handle back button press event.
|
||||
fn on_back() {
|
||||
if Modal::on_back() {
|
||||
App::show_exit_modal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "android")]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
/// Handle back button press event from Android.
|
||||
pub extern "C" fn Java_mw_gri_android_MainActivity_onBackButtonPress(
|
||||
_env: jni::JNIEnv,
|
||||
_class: jni::objects::JObject,
|
||||
_activity: jni::objects::JObject,
|
||||
) {
|
||||
Root::on_back();
|
||||
}
|
|
@ -26,22 +26,8 @@ impl View {
|
|||
/// Default stroke around views.
|
||||
pub const DEFAULT_STROKE: Stroke = Stroke { width: 1.0, color: Colors::STROKE };
|
||||
|
||||
/// Default width of side panel at application UI.
|
||||
pub const SIDE_PANEL_MIN_WIDTH: i64 = 400;
|
||||
|
||||
/// Check if ui can show side panel and screen at same time.
|
||||
pub fn is_dual_panel_mode(frame: &mut eframe::Frame) -> bool {
|
||||
let w = frame.info().window_info.size.x;
|
||||
let h = frame.info().window_info.size.y;
|
||||
// Screen is wide if width is greater than height or just 20% smaller.
|
||||
let is_wide_screen = w > h || w + (w * 0.2) >= h;
|
||||
// Dual panel mode is available when window is wide and its width is at least 2 times
|
||||
// greater than minimal width of the side panel.
|
||||
is_wide_screen && w >= Self::SIDE_PANEL_MIN_WIDTH as f32 * 2.0
|
||||
}
|
||||
|
||||
/// Show and cut long text with ﹍ character.
|
||||
pub fn ellipsize_text(ui: &mut egui::Ui, text: String, size: f32, color: Color32) {
|
||||
/// Cut long text with ﹍ character.
|
||||
fn ellipsize(text: String, size: f32, color: Color32) -> LayoutJob {
|
||||
let mut job = LayoutJob::single_section(text, TextFormat {
|
||||
font_id: FontId::proportional(size), color, ..Default::default()
|
||||
});
|
||||
|
@ -51,7 +37,12 @@ impl View {
|
|||
overflow_character: Option::from('﹍'),
|
||||
..Default::default()
|
||||
};
|
||||
ui.label(job);
|
||||
job
|
||||
}
|
||||
|
||||
/// Show ellipsized text.
|
||||
pub fn ellipsize_text(ui: &mut egui::Ui, text: String, size: f32, color: Color32) {
|
||||
ui.label(Self::ellipsize(text, size, color));
|
||||
}
|
||||
|
||||
/// Draw horizontally centered sub-title with space below.
|
||||
|
@ -121,7 +112,7 @@ impl View {
|
|||
|
||||
/// Draw [`Button`] with specified background fill color.
|
||||
pub fn button(ui: &mut egui::Ui, text: String, fill_color: Color32, action: impl FnOnce()) {
|
||||
let button_text = RichText::new(text.to_uppercase()).size(18.0).color(Colors::TEXT_BUTTON);
|
||||
let button_text = Self::ellipsize(text.to_uppercase(), 18.0, Colors::TEXT_BUTTON);
|
||||
let br = Button::new(button_text)
|
||||
.stroke(Self::DEFAULT_STROKE)
|
||||
.fill(fill_color)
|
||||
|
|
|
@ -260,7 +260,7 @@ impl Node {
|
|||
}
|
||||
}
|
||||
|
||||
if stratum_start_requested {
|
||||
if stratum_start_requested && NODE_STATE.stratum_stats.read().is_running {
|
||||
NODE_STATE.start_stratum_needed.store(false, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
|
@ -644,3 +644,16 @@ pub extern "C" fn Java_mw_gri_android_BackgroundService_exitAppAfterNodeStop(
|
|||
let exit_needed = !Node::is_running() && NODE_STATE.exit_after_stop.load(Ordering::Relaxed);
|
||||
return exit_needed as jni::sys::jboolean;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "android")]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
/// Handle unexpected application termination on Android (removal from recent apps).
|
||||
pub extern "C" fn Java_mw_gri_android_MainActivity_onTermination(
|
||||
_env: jni::JNIEnv,
|
||||
_class: jni::objects::JObject,
|
||||
_activity: jni::objects::JObject,
|
||||
) {
|
||||
Node::stop(false);
|
||||
}
|
Loading…
Reference in a new issue