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
|
title: Accounts
|
||||||
network:
|
network:
|
||||||
self: Network
|
self: Network
|
||||||
|
@ -59,7 +61,7 @@ network_mining:
|
||||||
enable_server: Enable server
|
enable_server: Enable server
|
||||||
disable_server: Disable 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: '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
|
rewards_wallet: Wallet for rewards
|
||||||
server: Stratum server
|
server: Stratum server
|
||||||
address: Address
|
address: Address
|
||||||
|
@ -85,8 +87,6 @@ network_settings:
|
||||||
foreign_api_secret: Foreign API token
|
foreign_api_secret: Foreign API token
|
||||||
disabled: Disabled
|
disabled: Disabled
|
||||||
enabled: Enabled
|
enabled: Enabled
|
||||||
copy: Copy
|
|
||||||
paste: Paste
|
|
||||||
ftl: The Future Time Limit (FTL)
|
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.
|
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
|
not_valid_value: Entered value is not valid
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
screen_accounts:
|
copy: Копировать
|
||||||
|
paste: Вставить
|
||||||
|
accounts:
|
||||||
title: Аккаунты
|
title: Аккаунты
|
||||||
network:
|
network:
|
||||||
self: Сеть
|
self: Сеть
|
||||||
|
@ -59,7 +61,7 @@ network_mining:
|
||||||
enable_server: Включить сервер
|
enable_server: Включить сервер
|
||||||
disable_server: Выключить сервер
|
disable_server: Выключить сервер
|
||||||
info: 'Сервер майнинга запущен, вы можете изменить его настройки, выбрав %{settings} внизу экрана. Данные обновляются, когда устройства подключены.'
|
info: 'Сервер майнинга запущен, вы можете изменить его настройки, выбрав %{settings} внизу экрана. Данные обновляются, когда устройства подключены.'
|
||||||
info_settings: Для изменения настроек запущенного сервера потребуется перезапуск узла.
|
restart_server_required: Для применения изменений потребуется перезапуск Stratum сервера.
|
||||||
rewards_wallet: Кошелёк для наград
|
rewards_wallet: Кошелёк для наград
|
||||||
server: Stratum сервер
|
server: Stratum сервер
|
||||||
address: Адрес
|
address: Адрес
|
||||||
|
@ -85,8 +87,6 @@ network_settings:
|
||||||
foreign_api_secret: Foreign API токен
|
foreign_api_secret: Foreign API токен
|
||||||
disabled: Отключен
|
disabled: Отключен
|
||||||
enabled: Включен
|
enabled: Включен
|
||||||
copy: Копировать
|
|
||||||
paste: Вставить
|
|
||||||
ftl: Предел Будущего Времени (FTL)
|
ftl: Предел Будущего Времени (FTL)
|
||||||
ftl_description: Насколько далеко в будущем, относительно локального времени узла в секундах, может находиться временная метка на новом блоке для его принятия.
|
ftl_description: Насколько далеко в будущем, относительно локального времени узла в секундах, может находиться временная метка на новом блоке для его принятия.
|
||||||
not_valid_value: Введено недопустимое значение
|
not_valid_value: Введено недопустимое значение
|
||||||
|
|
|
@ -14,11 +14,10 @@
|
||||||
|
|
||||||
use egui::{Context, RichText, Stroke};
|
use egui::{Context, RichText, Stroke};
|
||||||
use egui::os::OperatingSystem;
|
use egui::os::OperatingSystem;
|
||||||
|
use crate::gui::Colors;
|
||||||
|
|
||||||
use crate::gui::{Colors, Navigator};
|
|
||||||
use crate::gui::platform::PlatformCallbacks;
|
use crate::gui::platform::PlatformCallbacks;
|
||||||
use crate::gui::screens::Root;
|
use crate::gui::views::{Modal, ModalContainer, Root, View};
|
||||||
use crate::gui::views::{ModalContainer, View};
|
|
||||||
use crate::node::Node;
|
use crate::node::Node;
|
||||||
|
|
||||||
/// To be implemented at platform-specific module.
|
/// To be implemented at platform-specific module.
|
||||||
|
@ -27,16 +26,12 @@ pub struct PlatformApp<Platform> {
|
||||||
pub(crate) platform: 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 {
|
pub struct App {
|
||||||
/// Main ui container.
|
/// Main ui content.
|
||||||
root: Root,
|
root: Root,
|
||||||
|
/// Check if app exit is allowed on close event of [`eframe::App`] platform implementation.
|
||||||
/// Check if app exit is allowed on close event callback.
|
|
||||||
pub(crate) exit_allowed: bool,
|
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.
|
/// Flag to show exit progress at modal.
|
||||||
show_exit_progress: bool,
|
show_exit_progress: bool,
|
||||||
/// List of allowed modal ids for this [`ModalContainer`].
|
/// List of allowed modal ids for this [`ModalContainer`].
|
||||||
|
@ -47,14 +42,13 @@ impl Default for App {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let os = OperatingSystem::from_target_os();
|
let os = OperatingSystem::from_target_os();
|
||||||
// Exit from eframe only for non-mobile platforms.
|
// 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 {
|
Self {
|
||||||
root: Root::default(),
|
root: Root::default(),
|
||||||
exit_allowed: allow_to_exit,
|
exit_allowed,
|
||||||
exit_requested: false,
|
|
||||||
show_exit_progress: false,
|
show_exit_progress: false,
|
||||||
allowed_modal_ids: vec![
|
allowed_modal_ids: vec![
|
||||||
Navigator::EXIT_MODAL
|
Self::EXIT_MODAL
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,25 +61,22 @@ impl ModalContainer for App {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
|
/// Identifier for exit confirmation [`Modal`].
|
||||||
|
pub const EXIT_MODAL: &'static str = "exit_confirmation";
|
||||||
|
|
||||||
/// Draw content on main screen panel.
|
/// Draw content on main screen panel.
|
||||||
pub fn ui(&mut self, ctx: &Context, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) {
|
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()
|
egui::CentralPanel::default()
|
||||||
.frame(egui::Frame {
|
.frame(egui::Frame {
|
||||||
fill: Colors::FILL,
|
fill: Colors::FILL,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.show(ctx, |ui| {
|
.show(ctx, |ui| {
|
||||||
// Draw exit modal content if it's open or exit requested.
|
// Draw modal content if it's open.
|
||||||
let modal_id = Navigator::is_modal_open();
|
if self.can_draw_modal() {
|
||||||
if modal_id.is_some() && self.can_show_modal(modal_id.unwrap()) {
|
|
||||||
self.exit_modal_content(ui, frame, cb);
|
self.exit_modal_content(ui, frame, cb);
|
||||||
}
|
}
|
||||||
|
// Draw main content.
|
||||||
self.root.ui(ui, frame, cb);
|
self.root.ui(ui, frame, cb);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -95,7 +86,7 @@ impl App {
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
frame: &mut eframe::Frame,
|
frame: &mut eframe::Frame,
|
||||||
cb: &dyn PlatformCallbacks) {
|
cb: &dyn PlatformCallbacks) {
|
||||||
Navigator::modal_ui(ui, |ui, modal| {
|
Modal::ui(ui, |ui, modal| {
|
||||||
if self.show_exit_progress {
|
if self.show_exit_progress {
|
||||||
if !Node::is_running() {
|
if !Node::is_running() {
|
||||||
self.exit(frame, cb);
|
self.exit(frame, cb);
|
||||||
|
@ -156,13 +147,12 @@ impl App {
|
||||||
cb.exit();
|
cb.exit();
|
||||||
}
|
}
|
||||||
OperatingSystem::IOS => {
|
OperatingSystem::IOS => {
|
||||||
//TODO: exit on iOS
|
//TODO: exit on iOS.
|
||||||
}
|
}
|
||||||
OperatingSystem::Nix | OperatingSystem::Mac | OperatingSystem::Windows => {
|
OperatingSystem::Nix | OperatingSystem::Mac | OperatingSystem::Windows => {
|
||||||
self.exit_allowed = true;
|
self.exit_allowed = true;
|
||||||
frame.close();
|
frame.close();
|
||||||
}
|
}
|
||||||
// Web
|
|
||||||
OperatingSystem::Unknown => {}
|
OperatingSystem::Unknown => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -249,31 +239,10 @@ impl App {
|
||||||
|
|
||||||
ctx.set_style(style);
|
ctx.set_style(style);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
/// Show exit confirmation modal.
|
||||||
#[cfg(target_os = "android")]
|
pub fn show_exit_modal() {
|
||||||
#[allow(non_snake_case)]
|
let exit_modal = Modal::new(Self::EXIT_MODAL).title(t!("modal_exit.exit"));
|
||||||
#[no_mangle]
|
Modal::show(exit_modal);
|
||||||
/// 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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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;
|
mod app;
|
||||||
pub use app::{App, PlatformApp};
|
pub use app::{App, PlatformApp};
|
||||||
|
|
||||||
mod navigator;
|
|
||||||
pub use navigator::Navigator;
|
|
||||||
|
|
||||||
mod colors;
|
mod colors;
|
||||||
pub use colors::Colors;
|
pub use colors::Colors;
|
||||||
|
|
||||||
pub mod platform;
|
pub mod platform;
|
||||||
pub mod screens;
|
|
||||||
pub mod views;
|
pub mod views;
|
||||||
pub mod icons;
|
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 {
|
fn on_close_event(&mut self) -> bool {
|
||||||
self.app.exit_requested = true;
|
App::show_exit_modal();
|
||||||
self.app.exit_allowed
|
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
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::gui::platform::PlatformCallbacks;
|
mod content;
|
||||||
use crate::gui::screens::ScreenId;
|
pub use content::*;
|
||||||
|
|
||||||
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) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -12,26 +12,30 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use egui::Frame;
|
use crate::gui::Colors;
|
||||||
|
use crate::gui::icons::{GLOBE, PLUS};
|
||||||
use crate::gui::icons::{ARROW_CIRCLE_LEFT, GLOBE, PLUS};
|
|
||||||
use crate::gui::{Colors, Navigator};
|
|
||||||
use crate::gui::platform::PlatformCallbacks;
|
use crate::gui::platform::PlatformCallbacks;
|
||||||
use crate::gui::screens::{Screen, ScreenId};
|
use crate::gui::views::{Root, TitleAction, TitlePanel, View};
|
||||||
use crate::gui::views::{TitlePanel, TitleAction, View};
|
|
||||||
|
|
||||||
#[derive(Default)]
|
/// Accounts central panel content.
|
||||||
pub struct Accounts;
|
pub struct AccountsContent {
|
||||||
|
/// List of accounts.
|
||||||
|
list: Vec<String>
|
||||||
|
}
|
||||||
|
|
||||||
impl Screen for Accounts {
|
impl Default for AccountsContent {
|
||||||
fn id(&self) -> ScreenId {
|
fn default() -> Self {
|
||||||
ScreenId::Accounts
|
Self {
|
||||||
|
list: vec![],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) {
|
impl AccountsContent {
|
||||||
TitlePanel::ui(t!("screen_accounts.title"), if !View::is_dual_panel_mode(frame) {
|
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, || {
|
TitleAction::new(GLOBE, || {
|
||||||
Navigator::toggle_side_panel();
|
Root::toggle_network_panel();
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -40,19 +44,13 @@ impl Screen for Accounts {
|
||||||
}), ui);
|
}), ui);
|
||||||
|
|
||||||
egui::CentralPanel::default()
|
egui::CentralPanel::default()
|
||||||
.frame(Frame {
|
.frame(egui::Frame {
|
||||||
stroke: View::DEFAULT_STROKE,
|
stroke: View::DEFAULT_STROKE,
|
||||||
fill: Colors::FILL_DARK,
|
fill: Colors::FILL_DARK,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.show_inside(ui, |ui| {
|
.show_inside(ui, |ui| {
|
||||||
ui.label(format!("{}Here we go 10000 ツ", ARROW_CIRCLE_LEFT));
|
//TODO: accounts list
|
||||||
if ui.button("TEST").clicked() {
|
|
||||||
Navigator::to(ScreenId::Account)
|
|
||||||
};
|
|
||||||
if ui.button(format!("{}BACK ", ARROW_CIRCLE_LEFT)).clicked() {
|
|
||||||
Navigator::back()
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -21,5 +21,11 @@ pub use title_panel::*;
|
||||||
mod modal;
|
mod modal;
|
||||||
pub use modal::*;
|
pub use modal::*;
|
||||||
|
|
||||||
|
mod root;
|
||||||
|
pub use root::*;
|
||||||
|
|
||||||
mod network;
|
mod network;
|
||||||
pub use network::*;
|
pub use network::*;
|
||||||
|
|
||||||
|
mod accounts;
|
||||||
|
pub use accounts::*;
|
|
@ -14,21 +14,34 @@
|
||||||
|
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::{RwLock, RwLockWriteGuard};
|
||||||
|
|
||||||
use egui::{Align2, RichText, Rounding, Stroke, Vec2};
|
use egui::{Align2, RichText, Rounding, Stroke, Vec2};
|
||||||
use egui::epaint::RectShape;
|
use egui::epaint::RectShape;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
use crate::gui::Colors;
|
use crate::gui::Colors;
|
||||||
use crate::gui::views::View;
|
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 {
|
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>;
|
fn modal_ids(&self) -> &Vec<&'static str>;
|
||||||
|
|
||||||
/// Check if it's possible to show modal.
|
/// Check if it's possible to draw [`Modal`] at current ui container.
|
||||||
fn can_show_modal(&self, id: &'static str) -> bool {
|
fn can_draw_modal(&self) -> bool {
|
||||||
self.modal_ids().contains(&id)
|
let modal_id = Modal::opened();
|
||||||
|
modal_id.is_some() && self.modal_ids().contains(&modal_id.unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +51,7 @@ pub enum ModalPosition {
|
||||||
Center
|
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 {
|
pub struct Modal {
|
||||||
/// Identifier for modal.
|
/// Identifier for modal.
|
||||||
pub(crate) id: &'static str,
|
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 {
|
pub fn position(mut self, position: ModalPosition) -> Self {
|
||||||
self.position = position;
|
self.position = position;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if Modal is open.
|
/// Check if [`Modal`] is open.
|
||||||
pub fn is_open(&self) -> bool {
|
pub fn is_open(&self) -> bool {
|
||||||
self.open.load(Ordering::Relaxed)
|
self.open.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark Modal closed.
|
/// Mark [`Modal`] closed.
|
||||||
pub fn close(&self) {
|
pub fn close(&self) {
|
||||||
self.open.store(false, Ordering::Relaxed);
|
self.open.store(false, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Setup possibility to close Modal.
|
/// Setup possibility to close [`Modal`].
|
||||||
pub fn closeable(self, closeable: bool) -> Self {
|
pub fn closeable(self, closeable: bool) -> Self {
|
||||||
self.closeable.store(closeable, Ordering::Relaxed);
|
self.closeable.store(closeable, Ordering::Relaxed);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Disable possibility to close Modal.
|
/// Disable possibility to close [`Modal`].
|
||||||
pub fn disable_closing(&self) {
|
pub fn disable_closing(&self) {
|
||||||
self.closeable.store(false, Ordering::Relaxed);
|
self.closeable.store(false, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if Modal is closeable.
|
/// Check if [`Modal`] is closeable.
|
||||||
pub fn is_closeable(&self) -> bool {
|
pub fn is_closeable(&self) -> bool {
|
||||||
self.closeable.load(Ordering::Relaxed)
|
self.closeable.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
@ -105,8 +118,64 @@ impl Modal {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show Modal with provided content.
|
/// Set [`Modal`] instance to show at ui.
|
||||||
pub fn ui(&self, ui: &mut egui::Ui, add_content: impl FnOnce(&mut egui::Ui, &Modal)) {
|
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();
|
let mut rect = ui.ctx().screen_rect();
|
||||||
egui::Window::new("modal_bg_window")
|
egui::Window::new("modal_bg_window")
|
||||||
.title_bar(false)
|
.title_bar(false)
|
||||||
|
|
|
@ -12,11 +12,11 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
mod container;
|
mod content;
|
||||||
mod metrics;
|
mod metrics;
|
||||||
mod mining;
|
mod mining;
|
||||||
mod settings;
|
mod settings;
|
||||||
mod node;
|
mod node;
|
||||||
mod configs;
|
mod configs;
|
||||||
|
|
||||||
pub use container::*;
|
pub use content::*;
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
use egui::{Id, RichText, TextStyle, Ui, Widget};
|
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::icons::{CLOCK_COUNTDOWN, GRAPH, TIMER, WATCH};
|
||||||
use crate::gui::platform::PlatformCallbacks;
|
use crate::gui::platform::PlatformCallbacks;
|
||||||
use crate::gui::views::{Modal, ModalPosition, View};
|
use crate::gui::views::{Modal, ModalPosition, View};
|
||||||
|
@ -116,7 +116,7 @@ impl DandelionSetup {
|
||||||
let epoch_modal = Modal::new(Self::EPOCH_MODAL)
|
let epoch_modal = Modal::new(Self::EPOCH_MODAL)
|
||||||
.position(ModalPosition::CenterTop)
|
.position(ModalPosition::CenterTop)
|
||||||
.title(t!("network_settings.change_value"));
|
.title(t!("network_settings.change_value"));
|
||||||
Navigator::show_modal(epoch_modal);
|
Modal::show(epoch_modal);
|
||||||
cb.show_keyboard();
|
cb.show_keyboard();
|
||||||
});
|
});
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
@ -201,7 +201,7 @@ impl DandelionSetup {
|
||||||
let embargo_modal = Modal::new(Self::EMBARGO_MODAL)
|
let embargo_modal = Modal::new(Self::EMBARGO_MODAL)
|
||||||
.position(ModalPosition::CenterTop)
|
.position(ModalPosition::CenterTop)
|
||||||
.title(t!("network_settings.change_value"));
|
.title(t!("network_settings.change_value"));
|
||||||
Navigator::show_modal(embargo_modal);
|
Modal::show(embargo_modal);
|
||||||
cb.show_keyboard();
|
cb.show_keyboard();
|
||||||
});
|
});
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
@ -286,7 +286,7 @@ impl DandelionSetup {
|
||||||
let aggregation_modal = Modal::new(Self::AGGREGATION_MODAL)
|
let aggregation_modal = Modal::new(Self::AGGREGATION_MODAL)
|
||||||
.position(ModalPosition::CenterTop)
|
.position(ModalPosition::CenterTop)
|
||||||
.title(t!("network_settings.change_value"));
|
.title(t!("network_settings.change_value"));
|
||||||
Navigator::show_modal(aggregation_modal);
|
Modal::show(aggregation_modal);
|
||||||
cb.show_keyboard();
|
cb.show_keyboard();
|
||||||
});
|
});
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
@ -371,7 +371,7 @@ impl DandelionSetup {
|
||||||
let embargo_modal = Modal::new(Self::STEM_PROBABILITY_MODAL)
|
let embargo_modal = Modal::new(Self::STEM_PROBABILITY_MODAL)
|
||||||
.position(ModalPosition::CenterTop)
|
.position(ModalPosition::CenterTop)
|
||||||
.title(t!("network_settings.change_value"));
|
.title(t!("network_settings.change_value"));
|
||||||
Navigator::show_modal(embargo_modal);
|
Modal::show(embargo_modal);
|
||||||
cb.show_keyboard();
|
cb.show_keyboard();
|
||||||
});
|
});
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
|
|
@ -14,13 +14,14 @@
|
||||||
|
|
||||||
use eframe::emath::Align;
|
use eframe::emath::Align;
|
||||||
use egui::{Id, Layout, RichText, TextStyle, Ui, Widget};
|
use egui::{Id, Layout, RichText, TextStyle, Ui, Widget};
|
||||||
|
use egui::os::OperatingSystem;
|
||||||
use grin_core::global::ChainTypes;
|
use grin_core::global::ChainTypes;
|
||||||
|
|
||||||
use crate::AppConfig;
|
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::icons::{CLIPBOARD_TEXT, CLOCK_CLOCKWISE, COMPUTER_TOWER, COPY, PLUG, POWER, SHIELD, SHIELD_SLASH};
|
||||||
use crate::gui::platform::PlatformCallbacks;
|
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::gui::views::network::settings::NetworkSettings;
|
||||||
use crate::node::{Node, NodeConfig};
|
use crate::node::{Node, NodeConfig};
|
||||||
|
|
||||||
|
@ -114,7 +115,7 @@ impl NodeSetup {
|
||||||
// Autorun node setup.
|
// Autorun node setup.
|
||||||
ui.vertical_centered(|ui| {
|
ui.vertical_centered(|ui| {
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
NetworkContainer::autorun_node_ui(ui);
|
NetworkContent::autorun_node_ui(ui);
|
||||||
if Node::is_running() {
|
if Node::is_running() {
|
||||||
ui.add_space(2.0);
|
ui.add_space(2.0);
|
||||||
ui.label(RichText::new(t!("network_settings.restart_node_required"))
|
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)
|
let port_modal = Modal::new(Self::API_PORT_MODAL)
|
||||||
.position(ModalPosition::CenterTop)
|
.position(ModalPosition::CenterTop)
|
||||||
.title(t!("network_settings.change_value"));
|
.title(t!("network_settings.change_value"));
|
||||||
Navigator::show_modal(port_modal);
|
Modal::show(port_modal);
|
||||||
cb.show_keyboard();
|
cb.show_keyboard();
|
||||||
});
|
});
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
@ -338,7 +339,7 @@ impl NodeSetup {
|
||||||
let port_modal = Modal::new(modal_id)
|
let port_modal = Modal::new(modal_id)
|
||||||
.position(ModalPosition::CenterTop)
|
.position(ModalPosition::CenterTop)
|
||||||
.title(t!("network_settings.change_value"));
|
.title(t!("network_settings.change_value"));
|
||||||
Navigator::show_modal(port_modal);
|
Modal::show(port_modal);
|
||||||
cb.show_keyboard();
|
cb.show_keyboard();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -354,7 +355,7 @@ impl NodeSetup {
|
||||||
ui.label(RichText::new(description)
|
ui.label(RichText::new(description)
|
||||||
.size(18.0)
|
.size(18.0)
|
||||||
.color(Colors::GRAY));
|
.color(Colors::GRAY));
|
||||||
ui.add_space(8.0);
|
ui.add_space(6.0);
|
||||||
|
|
||||||
// Draw API port text edit.
|
// Draw API port text edit.
|
||||||
let text_edit_resp = egui::TextEdit::singleline(&mut self.secret_edit)
|
let text_edit_resp = egui::TextEdit::singleline(&mut self.secret_edit)
|
||||||
|
@ -367,36 +368,34 @@ impl NodeSetup {
|
||||||
cb.show_keyboard();
|
cb.show_keyboard();
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.add_space(12.0);
|
// Show buttons to copy/paste text for Android.
|
||||||
|
if OperatingSystem::from_target_os() == OperatingSystem::Android {
|
||||||
|
ui.add_space(12.0);
|
||||||
|
ui.scope(|ui| {
|
||||||
|
// Setup spacing between buttons.
|
||||||
|
ui.spacing_mut().item_spacing = egui::Vec2::new(12.0, 0.0);
|
||||||
|
|
||||||
// Show buttons to copy/paste text.
|
let mut buttons_rect = ui.available_rect_before_wrap();
|
||||||
ui.scope(|ui| {
|
buttons_rect.set_height(46.0);
|
||||||
// Setup spacing between buttons.
|
ui.allocate_ui_at_rect(buttons_rect, |ui| {
|
||||||
ui.spacing_mut().item_spacing = egui::Vec2::new(12.0, 0.0);
|
|
||||||
|
|
||||||
let mut buttons_rect = ui.available_rect_before_wrap();
|
ui.columns(2, |columns| {
|
||||||
buttons_rect.set_height(46.0);
|
columns[0].with_layout(Layout::right_to_left(Align::Center), |ui| {
|
||||||
ui.allocate_ui_at_rect(buttons_rect, |ui| {
|
let copy_title = format!("{} {}", COPY, t!("copy"));
|
||||||
|
View::button(ui, copy_title, Colors::WHITE, || {
|
||||||
ui.columns(2, |columns| {
|
cb.copy_string_to_buffer(self.secret_edit.clone());
|
||||||
columns[0].with_layout(Layout::right_to_left(Align::Center), |ui| {
|
});
|
||||||
let copy_title = format!("{} {}", COPY, t!("network_settings.copy"));
|
});
|
||||||
View::button(ui, copy_title, Colors::WHITE, || {
|
columns[1].with_layout(Layout::left_to_right(Align::Center), |ui| {
|
||||||
cb.copy_string_to_buffer(self.secret_edit.clone());
|
let paste_title = format!("{} {}", CLIPBOARD_TEXT, t!("paste"));
|
||||||
});
|
View::button(ui, paste_title, Colors::WHITE, || {
|
||||||
});
|
self.secret_edit = cb.get_string_from_buffer();
|
||||||
columns[1].with_layout(Layout::left_to_right(Align::Center), |ui| {
|
});
|
||||||
let paste_title = format!("{} {}",
|
});
|
||||||
CLIPBOARD_TEXT,
|
|
||||||
t!("network_settings.paste"));
|
|
||||||
View::button(ui, paste_title, Colors::WHITE, || {
|
|
||||||
self.secret_edit = cb.get_string_from_buffer();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
// Show reminder to restart enabled node.
|
// Show reminder to restart enabled node.
|
||||||
NetworkSettings::node_restart_required_ui(ui);
|
NetworkSettings::node_restart_required_ui(ui);
|
||||||
|
@ -455,7 +454,7 @@ impl NodeSetup {
|
||||||
let ftl_modal = Modal::new(Self::FTL_MODAL)
|
let ftl_modal = Modal::new(Self::FTL_MODAL)
|
||||||
.position(ModalPosition::CenterTop)
|
.position(ModalPosition::CenterTop)
|
||||||
.title(t!("network_settings.change_value"));
|
.title(t!("network_settings.change_value"));
|
||||||
Navigator::show_modal(ftl_modal);
|
Modal::show(ftl_modal);
|
||||||
cb.show_keyboard();
|
cb.show_keyboard();
|
||||||
});
|
});
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
|
|
@ -17,7 +17,7 @@ use egui_extras::{Size, StripBuilder};
|
||||||
use grin_core::global::ChainTypes;
|
use grin_core::global::ChainTypes;
|
||||||
|
|
||||||
use crate::AppConfig;
|
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::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::platform::PlatformCallbacks;
|
||||||
use crate::gui::views::{Modal, ModalPosition, View};
|
use crate::gui::views::{Modal, ModalPosition, View};
|
||||||
|
@ -215,7 +215,7 @@ impl P2PSetup {
|
||||||
let port_modal = Modal::new(Self::PORT_MODAL)
|
let port_modal = Modal::new(Self::PORT_MODAL)
|
||||||
.position(ModalPosition::CenterTop)
|
.position(ModalPosition::CenterTop)
|
||||||
.title(t!("network_settings.change_value"));
|
.title(t!("network_settings.change_value"));
|
||||||
Navigator::show_modal(port_modal);
|
Modal::show(port_modal);
|
||||||
cb.show_keyboard();
|
cb.show_keyboard();
|
||||||
});
|
});
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
@ -374,7 +374,7 @@ impl P2PSetup {
|
||||||
let peer_modal = Modal::new(modal_id)
|
let peer_modal = Modal::new(modal_id)
|
||||||
.position(ModalPosition::CenterTop)
|
.position(ModalPosition::CenterTop)
|
||||||
.title(modal_title);
|
.title(modal_title);
|
||||||
Navigator::show_modal(peer_modal);
|
Modal::show(peer_modal);
|
||||||
cb.show_keyboard();
|
cb.show_keyboard();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -561,7 +561,7 @@ impl P2PSetup {
|
||||||
let ban_modal = Modal::new(Self::BAN_WINDOW_MODAL)
|
let ban_modal = Modal::new(Self::BAN_WINDOW_MODAL)
|
||||||
.position(ModalPosition::CenterTop)
|
.position(ModalPosition::CenterTop)
|
||||||
.title(t!("network_settings.change_value"));
|
.title(t!("network_settings.change_value"));
|
||||||
Navigator::show_modal(ban_modal);
|
Modal::show(ban_modal);
|
||||||
cb.show_keyboard();
|
cb.show_keyboard();
|
||||||
});
|
});
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
@ -652,7 +652,7 @@ impl P2PSetup {
|
||||||
let max_inbound_modal = Modal::new(Self::MAX_INBOUND_MODAL)
|
let max_inbound_modal = Modal::new(Self::MAX_INBOUND_MODAL)
|
||||||
.position(ModalPosition::CenterTop)
|
.position(ModalPosition::CenterTop)
|
||||||
.title(t!("network_settings.change_value"));
|
.title(t!("network_settings.change_value"));
|
||||||
Navigator::show_modal(max_inbound_modal);
|
Modal::show(max_inbound_modal);
|
||||||
cb.show_keyboard();
|
cb.show_keyboard();
|
||||||
});
|
});
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
@ -738,7 +738,7 @@ impl P2PSetup {
|
||||||
let max_outbound = Modal::new(Self::MAX_OUTBOUND_MODAL)
|
let max_outbound = Modal::new(Self::MAX_OUTBOUND_MODAL)
|
||||||
.position(ModalPosition::CenterTop)
|
.position(ModalPosition::CenterTop)
|
||||||
.title(t!("network_settings.change_value"));
|
.title(t!("network_settings.change_value"));
|
||||||
Navigator::show_modal(max_outbound);
|
Modal::show(max_outbound);
|
||||||
cb.show_keyboard();
|
cb.show_keyboard();
|
||||||
});
|
});
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
@ -824,7 +824,7 @@ impl P2PSetup {
|
||||||
let min_outbound = Modal::new(Self::MIN_OUTBOUND_MODAL)
|
let min_outbound = Modal::new(Self::MIN_OUTBOUND_MODAL)
|
||||||
.position(ModalPosition::CenterTop)
|
.position(ModalPosition::CenterTop)
|
||||||
.title(t!("network_settings.change_value"));
|
.title(t!("network_settings.change_value"));
|
||||||
Navigator::show_modal(min_outbound);
|
Modal::show(min_outbound);
|
||||||
cb.show_keyboard();
|
cb.show_keyboard();
|
||||||
});
|
});
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
use egui::{Id, RichText, TextStyle, Ui, Widget};
|
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::icons::{BEZIER_CURVE, BOUNDING_BOX, CHART_SCATTER, CIRCLES_THREE, CLOCK_COUNTDOWN, HAND_COINS};
|
||||||
use crate::gui::platform::PlatformCallbacks;
|
use crate::gui::platform::PlatformCallbacks;
|
||||||
use crate::gui::views::{Modal, ModalPosition, View};
|
use crate::gui::views::{Modal, ModalPosition, View};
|
||||||
|
@ -118,7 +118,7 @@ impl PoolSetup {
|
||||||
let fee_modal = Modal::new(Self::FEE_BASE_MODAL)
|
let fee_modal = Modal::new(Self::FEE_BASE_MODAL)
|
||||||
.position(ModalPosition::CenterTop)
|
.position(ModalPosition::CenterTop)
|
||||||
.title(t!("network_settings.change_value"));
|
.title(t!("network_settings.change_value"));
|
||||||
Navigator::show_modal(fee_modal);
|
Modal::show(fee_modal);
|
||||||
cb.show_keyboard();
|
cb.show_keyboard();
|
||||||
});
|
});
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
@ -203,7 +203,7 @@ impl PoolSetup {
|
||||||
let reorg_modal = Modal::new(Self::REORG_PERIOD_MODAL)
|
let reorg_modal = Modal::new(Self::REORG_PERIOD_MODAL)
|
||||||
.position(ModalPosition::CenterTop)
|
.position(ModalPosition::CenterTop)
|
||||||
.title(t!("network_settings.change_value"));
|
.title(t!("network_settings.change_value"));
|
||||||
Navigator::show_modal(reorg_modal);
|
Modal::show(reorg_modal);
|
||||||
cb.show_keyboard();
|
cb.show_keyboard();
|
||||||
});
|
});
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
@ -288,7 +288,7 @@ impl PoolSetup {
|
||||||
let size_modal = Modal::new(Self::POOL_SIZE_MODAL)
|
let size_modal = Modal::new(Self::POOL_SIZE_MODAL)
|
||||||
.position(ModalPosition::CenterTop)
|
.position(ModalPosition::CenterTop)
|
||||||
.title(t!("network_settings.change_value"));
|
.title(t!("network_settings.change_value"));
|
||||||
Navigator::show_modal(size_modal);
|
Modal::show(size_modal);
|
||||||
cb.show_keyboard();
|
cb.show_keyboard();
|
||||||
});
|
});
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
@ -373,7 +373,7 @@ impl PoolSetup {
|
||||||
let stem_modal = Modal::new(Self::STEMPOOL_SIZE_MODAL)
|
let stem_modal = Modal::new(Self::STEMPOOL_SIZE_MODAL)
|
||||||
.position(ModalPosition::CenterTop)
|
.position(ModalPosition::CenterTop)
|
||||||
.title(t!("network_settings.change_value"));
|
.title(t!("network_settings.change_value"));
|
||||||
Navigator::show_modal(stem_modal);
|
Modal::show(stem_modal);
|
||||||
cb.show_keyboard();
|
cb.show_keyboard();
|
||||||
});
|
});
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
@ -458,7 +458,7 @@ impl PoolSetup {
|
||||||
let weight_modal = Modal::new(Self::MAX_WEIGHT_MODAL)
|
let weight_modal = Modal::new(Self::MAX_WEIGHT_MODAL)
|
||||||
.position(ModalPosition::CenterTop)
|
.position(ModalPosition::CenterTop)
|
||||||
.title(t!("network_settings.change_value"));
|
.title(t!("network_settings.change_value"));
|
||||||
Navigator::show_modal(weight_modal);
|
Modal::show(weight_modal);
|
||||||
cb.show_keyboard();
|
cb.show_keyboard();
|
||||||
});
|
});
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
use egui::{Id, RichText, TextStyle, Ui, Widget};
|
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::icons::{BARBELL, HARD_DRIVES, PLUG, TIMER};
|
||||||
use crate::gui::platform::PlatformCallbacks;
|
use crate::gui::platform::PlatformCallbacks;
|
||||||
use crate::gui::views::{Modal, ModalPosition, View};
|
use crate::gui::views::{Modal, ModalPosition, View};
|
||||||
|
@ -98,13 +98,15 @@ impl StratumSetup {
|
||||||
View::checkbox(ui, stratum_enabled, t!("network.autorun"), || {
|
View::checkbox(ui, stratum_enabled, t!("network.autorun"), || {
|
||||||
NodeConfig::toggle_stratum_autorun();
|
NodeConfig::toggle_stratum_autorun();
|
||||||
});
|
});
|
||||||
ui.add_space(4.0);
|
|
||||||
|
|
||||||
// Show message to restart node after changing of stratum settings
|
// Show reminder to restart running server.
|
||||||
ui.label(RichText::new(t!("network_mining.info_settings"))
|
if Node::get_stratum_stats().is_running {
|
||||||
.size(16.0)
|
ui.add_space(2.0);
|
||||||
.color(Colors::INACTIVE_TEXT)
|
ui.label(RichText::new(t!("network_mining.restart_server_required"))
|
||||||
);
|
.size(16.0)
|
||||||
|
.color(Colors::INACTIVE_TEXT)
|
||||||
|
);
|
||||||
|
}
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -164,7 +166,7 @@ impl StratumSetup {
|
||||||
let port_modal = Modal::new(Self::STRATUM_PORT_MODAL)
|
let port_modal = Modal::new(Self::STRATUM_PORT_MODAL)
|
||||||
.position(ModalPosition::CenterTop)
|
.position(ModalPosition::CenterTop)
|
||||||
.title(t!("network_settings.change_value"));
|
.title(t!("network_settings.change_value"));
|
||||||
Navigator::show_modal(port_modal);
|
Modal::show(port_modal);
|
||||||
cb.show_keyboard();
|
cb.show_keyboard();
|
||||||
});
|
});
|
||||||
ui.add_space(12.0);
|
ui.add_space(12.0);
|
||||||
|
@ -206,6 +208,8 @@ impl StratumSetup {
|
||||||
ui.label(RichText::new(t!("network_settings.port_unavailable"))
|
ui.label(RichText::new(t!("network_settings.port_unavailable"))
|
||||||
.size(18.0)
|
.size(18.0)
|
||||||
.color(Colors::RED));
|
.color(Colors::RED));
|
||||||
|
} else {
|
||||||
|
server_restart_required_ui(ui);
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.add_space(12.0);
|
ui.add_space(12.0);
|
||||||
|
@ -269,7 +273,7 @@ impl StratumSetup {
|
||||||
let time_modal = Modal::new(Self::ATTEMPT_TIME_MODAL)
|
let time_modal = Modal::new(Self::ATTEMPT_TIME_MODAL)
|
||||||
.position(ModalPosition::CenterTop)
|
.position(ModalPosition::CenterTop)
|
||||||
.title(t!("network_settings.change_value"));
|
.title(t!("network_settings.change_value"));
|
||||||
Navigator::show_modal(time_modal);
|
Modal::show(time_modal);
|
||||||
cb.show_keyboard();
|
cb.show_keyboard();
|
||||||
});
|
});
|
||||||
ui.add_space(12.0);
|
ui.add_space(12.0);
|
||||||
|
@ -303,7 +307,7 @@ impl StratumSetup {
|
||||||
.size(18.0)
|
.size(18.0)
|
||||||
.color(Colors::RED));
|
.color(Colors::RED));
|
||||||
} else {
|
} else {
|
||||||
NetworkSettings::node_restart_required_ui(ui);
|
server_restart_required_ui(ui);
|
||||||
}
|
}
|
||||||
ui.add_space(12.0);
|
ui.add_space(12.0);
|
||||||
});
|
});
|
||||||
|
@ -355,7 +359,7 @@ impl StratumSetup {
|
||||||
let diff_modal = Modal::new(Self::MIN_SHARE_DIFF_MODAL)
|
let diff_modal = Modal::new(Self::MIN_SHARE_DIFF_MODAL)
|
||||||
.position(ModalPosition::CenterTop)
|
.position(ModalPosition::CenterTop)
|
||||||
.title(t!("network_settings.change_value"));
|
.title(t!("network_settings.change_value"));
|
||||||
Navigator::show_modal(diff_modal);
|
Modal::show(diff_modal);
|
||||||
cb.show_keyboard();
|
cb.show_keyboard();
|
||||||
});
|
});
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
@ -389,7 +393,7 @@ impl StratumSetup {
|
||||||
.size(18.0)
|
.size(18.0)
|
||||||
.color(Colors::RED));
|
.color(Colors::RED));
|
||||||
} else {
|
} else {
|
||||||
NetworkSettings::node_restart_required_ui(ui);
|
server_restart_required_ui(ui);
|
||||||
}
|
}
|
||||||
ui.add_space(12.0);
|
ui.add_space(12.0);
|
||||||
});
|
});
|
||||||
|
@ -423,4 +427,15 @@ impl StratumSetup {
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 grin_chain::SyncStatus;
|
||||||
|
|
||||||
use crate::AppConfig;
|
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::icons::{CARDHOLDER, DATABASE, DOTS_THREE_OUTLINE_VERTICAL, FACTORY, FADERS, GAUGE, POWER};
|
||||||
use crate::gui::platform::PlatformCallbacks;
|
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::dandelion::DandelionSetup;
|
||||||
use crate::gui::views::network::configs::node::NodeSetup;
|
use crate::gui::views::network::configs::node::NodeSetup;
|
||||||
use crate::gui::views::network::configs::p2p::P2PSetup;
|
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);
|
fn on_modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub enum NetworkTabType {
|
pub enum NetworkTabType {
|
||||||
Node,
|
Node,
|
||||||
|
@ -48,7 +49,7 @@ pub enum NetworkTabType {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NetworkTabType {
|
impl NetworkTabType {
|
||||||
pub fn name(&self) -> String {
|
pub fn title(&self) -> String {
|
||||||
match *self {
|
match *self {
|
||||||
NetworkTabType::Node => { t!("network.node") }
|
NetworkTabType::Node => { t!("network.node") }
|
||||||
NetworkTabType::Metrics => { t!("network.metrics") }
|
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>,
|
current_tab: Box<dyn NetworkTab>,
|
||||||
|
/// [`Modal`] ids allowed at this ui container.
|
||||||
modal_ids: Vec<&'static str>,
|
modal_ids: Vec<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for NetworkContainer {
|
impl Default for NetworkContent {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
current_tab: Box::new(NetworkNode::default()),
|
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> {
|
fn modal_ids(&self) -> &Vec<&'static str> {
|
||||||
self.modal_ids.as_ref()
|
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) {
|
pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) {
|
||||||
// Show modal content if it's opened.
|
// Show modal content if it's opened.
|
||||||
let modal_id = Navigator::is_modal_open();
|
if self.can_draw_modal() {
|
||||||
if modal_id.is_some() && self.can_show_modal(modal_id.unwrap()) {
|
Modal::ui(ui, |ui, modal| {
|
||||||
Navigator::modal_ui(ui, |ui, modal| {
|
|
||||||
self.current_tab.as_mut().on_modal_ui(ui, modal, cb);
|
self.current_tab.as_mut().on_modal_ui(ui, modal, cb);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -169,24 +172,26 @@ impl NetworkContainer {
|
||||||
// Setup vertical padding inside tab button.
|
// Setup vertical padding inside tab button.
|
||||||
ui.style_mut().spacing.button_padding = egui::vec2(0.0, 8.0);
|
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| {
|
ui.columns(4, |columns| {
|
||||||
columns[0].vertical_centered_justified(|ui| {
|
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());
|
self.current_tab = Box::new(NetworkNode::default());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
columns[1].vertical_centered_justified(|ui| {
|
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());
|
self.current_tab = Box::new(NetworkMetrics::default());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
columns[2].vertical_centered_justified(|ui| {
|
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());
|
self.current_tab = Box::new(NetworkMining::default());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
columns[3].vertical_centered_justified(|ui| {
|
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());
|
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.
|
/// Draw title content.
|
||||||
fn title_ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
|
fn title_ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
|
||||||
StripBuilder::new(ui)
|
StripBuilder::new(ui)
|
||||||
|
@ -217,10 +217,10 @@ impl NetworkContainer {
|
||||||
self.title_text_ui(builder);
|
self.title_text_ui(builder);
|
||||||
});
|
});
|
||||||
strip.cell(|ui| {
|
strip.cell(|ui| {
|
||||||
if !View::is_dual_panel_mode(frame) {
|
if !Root::is_dual_panel_mode(frame) {
|
||||||
ui.centered_and_justified(|ui| {
|
ui.centered_and_justified(|ui| {
|
||||||
View::title_button(ui, CARDHOLDER, || {
|
View::title_button(ui, CARDHOLDER, || {
|
||||||
Navigator::toggle_side_panel();
|
Root::toggle_network_panel();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -237,7 +237,7 @@ impl NetworkContainer {
|
||||||
strip.cell(|ui| {
|
strip.cell(|ui| {
|
||||||
ui.add_space(4.0);
|
ui.add_space(4.0);
|
||||||
ui.vertical_centered(|ui| {
|
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)
|
.size(19.0)
|
||||||
.color(Colors::TITLE));
|
.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) {
|
pub fn autorun_node_ui(ui: &mut egui::Ui) {
|
||||||
let autostart = AppConfig::autostart_node();
|
let autostart = AppConfig::autostart_node();
|
||||||
View::checkbox(ui, autostart, t!("network.autorun"), || {
|
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::icons::{AT, COINS, CUBE_TRANSPARENT, HASH, HOURGLASS_LOW, HOURGLASS_MEDIUM, TIMER};
|
||||||
use crate::gui::platform::PlatformCallbacks;
|
use crate::gui::platform::PlatformCallbacks;
|
||||||
use crate::gui::views::network::{NetworkTab, NetworkTabType};
|
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;
|
use crate::node::Node;
|
||||||
|
|
||||||
|
/// Chain metrics tab content.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct NetworkMetrics;
|
pub struct NetworkMetrics;
|
||||||
|
|
||||||
|
@ -39,7 +40,7 @@ impl NetworkTab for NetworkMetrics {
|
||||||
let server_stats = Node::get_stats();
|
let server_stats = Node::get_stats();
|
||||||
// Show message to enable node when it's not running.
|
// Show message to enable node when it's not running.
|
||||||
if !Node::is_running() {
|
if !Node::is_running() {
|
||||||
NetworkContainer::disabled_node_ui(ui);
|
NetworkContent::disabled_node_ui(ui);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +95,7 @@ impl NetworkTab for NetworkMetrics {
|
||||||
});
|
});
|
||||||
ui.add_space(4.0);
|
ui.add_space(4.0);
|
||||||
|
|
||||||
// Show difficulty adjustment window info
|
// Show difficulty adjustment window info.
|
||||||
let difficulty_title = t!(
|
let difficulty_title = t!(
|
||||||
"network_metrics.difficulty_window",
|
"network_metrics.difficulty_window",
|
||||||
"size" => stats.diff_stats.window_size
|
"size" => stats.diff_stats.window_size
|
||||||
|
@ -122,7 +123,7 @@ impl NetworkTab for NetworkMetrics {
|
||||||
});
|
});
|
||||||
ui.add_space(4.0);
|
ui.add_space(4.0);
|
||||||
|
|
||||||
// Show difficulty adjustment window blocks
|
// Show difficulty adjustment window blocks.
|
||||||
let blocks_size = stats.diff_stats.last_blocks.len();
|
let blocks_size = stats.diff_stats.last_blocks.len();
|
||||||
ScrollArea::vertical()
|
ScrollArea::vertical()
|
||||||
.id_source("difficulty_scroll")
|
.id_source("difficulty_scroll")
|
||||||
|
@ -130,7 +131,7 @@ impl NetworkTab for NetworkMetrics {
|
||||||
.stick_to_bottom(true)
|
.stick_to_bottom(true)
|
||||||
.show_rows(
|
.show_rows(
|
||||||
ui,
|
ui,
|
||||||
DIFF_BLOCK_UI_HEIGHT,
|
BLOCK_ITEM_HEIGHT,
|
||||||
blocks_size,
|
blocks_size,
|
||||||
|ui, row_range| {
|
|ui, row_range| {
|
||||||
for index in row_range {
|
for index in row_range {
|
||||||
|
@ -144,7 +145,7 @@ impl NetworkTab for NetworkMetrics {
|
||||||
} else {
|
} else {
|
||||||
[false, false]
|
[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) {}
|
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.
|
// Add space before the first item.
|
||||||
if rounding[0] {
|
if rounding[0] {
|
||||||
ui.add_space(4.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.horizontal(|ui| {
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
|
// Draw round background.
|
||||||
let mut rect = ui.available_rect_before_wrap();
|
let mut rect = ui.available_rect_before_wrap();
|
||||||
rect.set_height(DIFF_BLOCK_UI_HEIGHT);
|
rect.set_height(BLOCK_ITEM_HEIGHT);
|
||||||
ui.painter().rect(
|
ui.painter().rect(
|
||||||
rect,
|
rect,
|
||||||
Rounding {
|
Rounding {
|
||||||
|
|
|
@ -20,11 +20,12 @@ use grin_servers::WorkerStats;
|
||||||
use crate::gui::Colors;
|
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::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::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::{NetworkTab, NetworkTabType};
|
||||||
use crate::gui::views::network::configs::stratum::StratumSetup;
|
use crate::gui::views::network::configs::stratum::StratumSetup;
|
||||||
use crate::node::{Node, NodeConfig};
|
use crate::node::{Node, NodeConfig};
|
||||||
|
|
||||||
|
/// Mining tab content.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct NetworkMining {
|
pub struct NetworkMining {
|
||||||
stratum_server_setup: StratumSetup
|
stratum_server_setup: StratumSetup
|
||||||
|
@ -40,7 +41,7 @@ impl NetworkTab for NetworkMining {
|
||||||
|
|
||||||
// Show message to enable node when it's not running.
|
// Show message to enable node when it's not running.
|
||||||
if !Node::is_running() {
|
if !Node::is_running() {
|
||||||
NetworkContainer::disabled_node_ui(ui);
|
NetworkContent::disabled_node_ui(ui);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,7 +170,7 @@ impl NetworkTab for NetworkMining {
|
||||||
.id_source("stratum_workers_scroll")
|
.id_source("stratum_workers_scroll")
|
||||||
.show_rows(
|
.show_rows(
|
||||||
ui,
|
ui,
|
||||||
WORKER_UI_HEIGHT,
|
WORKER_ITEM_HEIGHT,
|
||||||
workers_size,
|
workers_size,
|
||||||
|ui, row_range| {
|
|ui, row_range| {
|
||||||
for index in row_range {
|
for index in row_range {
|
||||||
|
@ -183,7 +184,7 @@ impl NetworkTab for NetworkMining {
|
||||||
} else {
|
} else {
|
||||||
[false, false]
|
[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.
|
// Add space before the first item.
|
||||||
if rounding[0] {
|
if rounding[0] {
|
||||||
ui.add_space(4.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.horizontal_wrapped(|ui| {
|
||||||
ui.vertical_centered_justified(|ui| {
|
ui.vertical_centered_justified(|ui| {
|
||||||
|
// Draw round background.
|
||||||
let mut rect = ui.available_rect_before_wrap();
|
let mut rect = ui.available_rect_before_wrap();
|
||||||
rect.set_height(WORKER_UI_HEIGHT);
|
rect.set_height(WORKER_ITEM_HEIGHT);
|
||||||
ui.painter().rect(
|
ui.painter().rect(
|
||||||
rect,
|
rect,
|
||||||
Rounding {
|
Rounding {
|
||||||
|
@ -232,94 +235,68 @@ fn draw_workers_stats(ui: &mut egui::Ui, ws: &WorkerStats, rounding: [bool; 2])
|
||||||
);
|
);
|
||||||
|
|
||||||
ui.add_space(2.0);
|
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 {
|
let (status_text, status_icon, status_color) = match ws.is_connected {
|
||||||
true => (t!("network_mining.connected"), PLUGS_CONNECTED, Colors::BLACK),
|
true => (t!("network_mining.connected"), PLUGS_CONNECTED, Colors::BLACK),
|
||||||
false => (t!("network_mining.disconnected"), PLUGS, Colors::INACTIVE_TEXT)
|
false => (t!("network_mining.disconnected"), PLUGS, Colors::INACTIVE_TEXT)
|
||||||
};
|
};
|
||||||
ui.add_space(5.0);
|
let status_line_text = format!("{} {} {}", status_icon, ws.id, status_text);
|
||||||
ui.heading(RichText::new(status_icon)
|
ui.heading(RichText::new(status_line_text)
|
||||||
.color(status_color)
|
.color(status_color)
|
||||||
.size(18.0));
|
.size(18.0));
|
||||||
ui.add_space(2.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.add_space(6.0);
|
||||||
ui.heading(RichText::new(BARBELL)
|
|
||||||
.color(Colors::TITLE)
|
|
||||||
.size(16.0));
|
|
||||||
ui.add_space(4.0);
|
|
||||||
// Draw difficulty.
|
// 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)
|
.color(Colors::TITLE)
|
||||||
.size(16.0));
|
.size(16.0));
|
||||||
ui.add_space(6.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.
|
// 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)
|
.color(Colors::GREEN)
|
||||||
.size(16.0));
|
.size(16.0));
|
||||||
ui.add_space(6.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.
|
// 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)
|
.color(Colors::RED)
|
||||||
.size(16.0));
|
.size(16.0));
|
||||||
ui.add_space(6.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.
|
// 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)
|
.color(Colors::GRAY)
|
||||||
.size(16.0));
|
.size(16.0));
|
||||||
ui.add_space(6.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.
|
// 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)
|
.color(Colors::TITLE)
|
||||||
.size(16.0));
|
.size(16.0));
|
||||||
});
|
});
|
||||||
ui.horizontal_top(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.add_space(6.0);
|
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
|
// Draw block time
|
||||||
let seen = ws.last_seen.duration_since(std::time::UNIX_EPOCH).unwrap().as_secs();
|
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 naive_datetime = NaiveDateTime::from_timestamp_opt(seen as i64, 0).unwrap();
|
||||||
let datetime: DateTime<Utc> = DateTime::from_utc(naive_datetime, Utc);
|
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)
|
.color(Colors::GRAY)
|
||||||
.size(16.0));
|
.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::icons::{AT, CUBE, DEVICES, FLOW_ARROW, HANDSHAKE, PACKAGE, PLUGS_CONNECTED, SHARE_NETWORK};
|
||||||
use crate::gui::platform::PlatformCallbacks;
|
use crate::gui::platform::PlatformCallbacks;
|
||||||
use crate::gui::views::{Modal, View};
|
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;
|
use crate::node::Node;
|
||||||
|
|
||||||
|
/// Integrated node tab content.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct NetworkNode;
|
pub struct NetworkNode;
|
||||||
|
|
||||||
|
@ -35,7 +36,7 @@ impl NetworkTab for NetworkNode {
|
||||||
let server_stats = Node::get_stats();
|
let server_stats = Node::get_stats();
|
||||||
// Show message to enable node when it's not running.
|
// Show message to enable node when it's not running.
|
||||||
if !Node::is_running() {
|
if !Node::is_running() {
|
||||||
NetworkContainer::disabled_node_ui(ui);
|
NetworkContent::disabled_node_ui(ui);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +176,7 @@ impl NetworkTab for NetworkNode {
|
||||||
[false, false]
|
[false, false]
|
||||||
};
|
};
|
||||||
ui.vertical_centered(|ui| {
|
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 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| {
|
ui.vertical(|ui| {
|
||||||
// Draw round background.
|
// Draw round background.
|
||||||
let mut rect = ui.available_rect_before_wrap();
|
let mut rect = ui.available_rect_before_wrap();
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
use egui::{RichText, ScrollArea};
|
use egui::{RichText, ScrollArea};
|
||||||
|
|
||||||
use crate::gui::{Colors, Navigator};
|
use crate::gui::Colors;
|
||||||
use crate::gui::icons::ARROW_COUNTER_CLOCKWISE;
|
use crate::gui::icons::ARROW_COUNTER_CLOCKWISE;
|
||||||
use crate::gui::platform::PlatformCallbacks;
|
use crate::gui::platform::PlatformCallbacks;
|
||||||
use crate::gui::views::{Modal, ModalPosition, View};
|
use crate::gui::views::{Modal, ModalPosition, View};
|
||||||
|
@ -141,7 +141,7 @@ impl NetworkSettings {
|
||||||
let reset_modal = Modal::new(Self::RESET_SETTINGS_MODAL)
|
let reset_modal = Modal::new(Self::RESET_SETTINGS_MODAL)
|
||||||
.position(ModalPosition::Center)
|
.position(ModalPosition::Center)
|
||||||
.title(t!("modal.confirmation"));
|
.title(t!("modal.confirmation"));
|
||||||
Navigator::show_modal(reset_modal);
|
Modal::show(reset_modal);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Show reminder to restart enabled node.
|
// Show reminder to restart enabled node.
|
||||||
|
@ -207,7 +207,7 @@ impl NetworkSettings {
|
||||||
let port_modal = Modal::new(Self::NODE_RESTART_REQUIRED_MODAL)
|
let port_modal = Modal::new(Self::NODE_RESTART_REQUIRED_MODAL)
|
||||||
.position(ModalPosition::Center)
|
.position(ModalPosition::Center)
|
||||||
.title(t!("network.settings"));
|
.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.
|
/// Default stroke around views.
|
||||||
pub const DEFAULT_STROKE: Stroke = Stroke { width: 1.0, color: Colors::STROKE };
|
pub const DEFAULT_STROKE: Stroke = Stroke { width: 1.0, color: Colors::STROKE };
|
||||||
|
|
||||||
/// Default width of side panel at application UI.
|
/// Cut long text with ﹍ character.
|
||||||
pub const SIDE_PANEL_MIN_WIDTH: i64 = 400;
|
fn ellipsize(text: String, size: f32, color: Color32) -> LayoutJob {
|
||||||
|
|
||||||
/// 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) {
|
|
||||||
let mut job = LayoutJob::single_section(text, TextFormat {
|
let mut job = LayoutJob::single_section(text, TextFormat {
|
||||||
font_id: FontId::proportional(size), color, ..Default::default()
|
font_id: FontId::proportional(size), color, ..Default::default()
|
||||||
});
|
});
|
||||||
|
@ -51,7 +37,12 @@ impl View {
|
||||||
overflow_character: Option::from('﹍'),
|
overflow_character: Option::from('﹍'),
|
||||||
..Default::default()
|
..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.
|
/// Draw horizontally centered sub-title with space below.
|
||||||
|
@ -121,7 +112,7 @@ impl View {
|
||||||
|
|
||||||
/// Draw [`Button`] with specified background fill color.
|
/// Draw [`Button`] with specified background fill color.
|
||||||
pub fn button(ui: &mut egui::Ui, text: String, fill_color: Color32, action: impl FnOnce()) {
|
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)
|
let br = Button::new(button_text)
|
||||||
.stroke(Self::DEFAULT_STROKE)
|
.stroke(Self::DEFAULT_STROKE)
|
||||||
.fill(fill_color)
|
.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);
|
NODE_STATE.start_stratum_needed.store(false, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -643,4 +643,17 @@ pub extern "C" fn Java_mw_gri_android_BackgroundService_exitAppAfterNodeStop(
|
||||||
) -> jni::sys::jboolean {
|
) -> jni::sys::jboolean {
|
||||||
let exit_needed = !Node::is_running() && NODE_STATE.exit_after_stop.load(Ordering::Relaxed);
|
let exit_needed = !Node::is_running() && NODE_STATE.exit_after_stop.load(Ordering::Relaxed);
|
||||||
return exit_needed as jni::sys::jboolean;
|
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