config + ui + node: all server config values, stratum config ui refactoring, global modal position fix, modal id refactoring, remove chain data on node launch error

This commit is contained in:
ardocrat
2023-06-23 22:12:30 +03:00
parent 4ad134c5e3
commit 1346b04bc9
20 changed files with 906 additions and 241 deletions
+5 -4
View File
@@ -64,7 +64,6 @@ impl App {
/// Setup application styles.
pub fn setup_visuals(ctx: &Context) {
// Setup style
let mut style = (*ctx.style()).clone();
// Setup spacing for buttons.
style.spacing.button_padding = egui::vec2(12.0, 8.0);
@@ -76,14 +75,16 @@ impl App {
style.spacing.icon_width = 24.0;
style.spacing.icon_width_inner = 14.0;
style.spacing.icon_spacing = 10.0;
// Setup style
ctx.set_style(style);
// Setup visuals
let mut visuals = egui::Visuals::light();
// Setup selection color.
visuals.selection.stroke = Stroke { width: 1.0, color: Colors::TEXT };
visuals.selection.bg_fill = Colors::GOLD;
// Disable stroke around panels by default
visuals.widgets.noninteractive.bg_stroke = Stroke::NONE;
// Setup visuals
ctx.set_visuals(visuals);
}
+4 -2
View File
@@ -25,10 +25,12 @@ impl Colors {
pub const GOLD: Color32 = Color32::from_rgb(255, 215, 0);
pub const GREEN: Color32 = Color32::from_rgb(0, 0x64, 0);
pub const RED: Color32 = Color32::from_rgb(0x8B, 0, 0);
pub const FILL: Color32 = Color32::from_gray(240);
pub const FILL: Color32 = Color32::from_gray(244);
pub const FILL_DARK: Color32 = Color32::from_gray(232);
pub const TITLE: Color32 = Color32::from_gray(60);
pub const TEXT: Color32 = Color32::from_gray(80);
pub const BUTTON: Color32 = Color32::from_gray(70);
pub const TEXT_BUTTON: Color32 = Color32::from_gray(70);
pub const BUTTON: Color32 = Color32::from_gray(249);
pub const GRAY: Color32 = Color32::from_gray(120);
pub const STROKE: Color32 = Color32::from_gray(190);
pub const INACTIVE_TEXT: Color32 = Color32::from_gray(150);
+5 -2
View File
@@ -19,7 +19,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
use lazy_static::lazy_static;
use crate::gui::screens::ScreenId;
use crate::gui::views::{Modal, ModalId, ModalLocation};
use crate::gui::views::{Modal, ModalLocation};
lazy_static! {
/// Static [`Navigator`] state to be accessible from anywhere.
@@ -53,6 +53,9 @@ impl Default for Navigator {
}
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();
@@ -118,7 +121,7 @@ impl Navigator {
/// Open exit confirmation [`Modal`] with provided [NAVIGATOR_STATE] lock.
fn open_exit_modal_nav(mut w_nav: RwLockWriteGuard<Navigator>) {
let m = Modal::new(ModalId::Exit, ModalLocation::Global).title(t!("modal_exit.exit"));
let m = Modal::new(Self::EXIT_MODAL, ModalLocation::Global).title(t!("modal_exit.exit"));
w_nav.global_modal = Some(m);
}
+2 -1
View File
@@ -15,7 +15,7 @@
use egui::Frame;
use crate::gui::icons::{ARROW_CIRCLE_LEFT, GLOBE, PLUS};
use crate::gui::Navigator;
use crate::gui::{Colors, Navigator};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::screens::{Screen, ScreenId};
use crate::gui::views::{TitlePanel, TitlePanelAction, View};
@@ -43,6 +43,7 @@ impl Screen for Accounts {
egui::CentralPanel::default()
.frame(Frame {
stroke: View::DEFAULT_STROKE,
fill: Colors::FILL_DARK,
..Default::default()
})
.show_inside(ui, |ui| {
+27 -22
View File
@@ -17,7 +17,7 @@ use std::cmp::min;
use crate::gui::{App, Colors, Navigator};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::screens::{Account, Accounts, Screen, ScreenId};
use crate::gui::views::{ModalId, ModalLocation, Network, View};
use crate::gui::views::{ModalLocation, Network, View};
use crate::node::Node;
pub struct Root {
@@ -67,10 +67,9 @@ impl Root {
ui: &mut egui::Ui,
frame: &mut eframe::Frame,
cb: &dyn PlatformCallbacks) {
let location = ModalLocation::Global;
Navigator::modal_ui(ui, location, |ui, modal| {
Navigator::modal_ui(ui, ModalLocation::Global, |ui, modal| {
match modal.id {
ModalId::Exit => {
Navigator::EXIT_MODAL => {
if self.show_exit_progress {
if !Node::is_running() {
App::exit(frame, cb);
@@ -89,30 +88,36 @@ impl Root {
ui.label(t!("modal_exit.description"));
});
ui.add_space(10.0);
// Setup spacing between buttons
ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0);
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal_exit.exit"), Colors::WHITE, || {
if !Node::is_running() {
App::exit(frame, cb);
// Show modal buttons.
ui.scope(|ui| {
// Setup spacing between buttons.
ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0);
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal_exit.exit"), Colors::WHITE, || {
if !Node::is_running() {
App::exit(frame, cb);
modal.close();
} else {
Node::stop(true);
modal.disable_closing();
self.show_exit_progress = true;
}
});
});
columns[1].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::WHITE, || {
modal.close();
} else {
Node::stop(true);
modal.disable_closing();
self.show_exit_progress = true;
}
});
});
columns[1].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::WHITE, || {
modal.close();
});
});
});
ui.add_space(6.0);
});
ui.add_space(6.0);
}
}
_ => {}
}
});
}
+3 -1
View File
@@ -27,4 +27,6 @@ pub use network::*;
mod network_node;
mod network_settings;
mod network_metrics;
mod network_mining;
mod network_mining;
mod settings_stratum;
mod settings_node_server;
+19 -17
View File
@@ -21,11 +21,6 @@ use egui::epaint::RectShape;
use crate::gui::Colors;
use crate::gui::views::View;
/// Identifier for [`Modal`] content to draw at [`Modal::ui`].
pub enum ModalId {
Exit
}
/// Location for [`Modal`] at application UI.
pub enum ModalLocation {
/// To draw globally above side panel and screen.
@@ -46,8 +41,8 @@ pub enum ModalPosition {
/// Stores data to draw dialog box/popup at UI, powered by [`egui::Window`].
pub struct Modal {
/// Identifier for content.
pub(crate) id: ModalId,
/// Identifier for modal.
pub(crate) id: &'static str,
/// Location at UI.
pub(crate) location: ModalLocation,
/// Position on the screen.
@@ -65,7 +60,7 @@ impl Modal {
const DEFAULT_WIDTH: i64 = 380;
/// Create open and closeable Modal with center position.
pub fn new(id: ModalId, location: ModalLocation) -> Self {
pub fn new(id: &'static str, location: ModalLocation) -> Self {
Self {
id,
location,
@@ -116,9 +111,7 @@ impl Modal {
/// Show Modal with provided content.
pub fn ui(&self, ui: &mut egui::Ui, add_content: impl FnOnce(&mut egui::Ui, &Modal)) {
let width = min(ui.available_width() as i64 - 20, Self::DEFAULT_WIDTH) as f32;
// Show background Window at full available size
// Show background Window at full available size.
egui::Window::new(self.window_id(true))
.title_bar(false)
.resizable(false)
@@ -133,13 +126,17 @@ impl Modal {
ui.set_min_size(ui.available_size());
});
// Show main content Window at given position
// Choose width of modal content.
let width = min(ui.available_width() as i64 - 20, Self::DEFAULT_WIDTH) as f32;
// Show main content Window at given position.
let (content_align, content_offset) = self.modal_position();
let layer_id = egui::Window::new(self.window_id(false))
.title_bar(false)
.resizable(false)
.collapsible(false)
.default_width(width)
.anchor(self.modal_position(), Vec2::default())
.anchor(content_align, content_offset)
.frame(egui::Frame {
rounding: Rounding::same(8.0),
fill: Colors::YELLOW,
@@ -152,7 +149,7 @@ impl Modal {
self.draw_content(ui, add_content);
}).unwrap().response.layer_id;
// Always show main content Window above background Window
// Always show main content Window above background Window.
ui.ctx().move_to_top(layer_id);
}
@@ -173,11 +170,16 @@ impl Modal {
}
/// Get [`egui::Window`] position based on [`ModalPosition`].
fn modal_position(&self) -> Align2 {
match self.position {
fn modal_position(&self) -> (Align2, Vec2) {
let align = match self.position {
ModalPosition::CenterTop => { Align2::CENTER_TOP }
ModalPosition::Center => { Align2::CENTER_CENTER }
}
};
let offset = match self.position {
ModalPosition::CenterTop => { Vec2::new(0.0, 20.0) }
ModalPosition::Center => { Vec2::new(0.0, 0.0) }
};
(align, offset)
}
/// Draw provided content.
+33 -3
View File
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::net::{IpAddr, Ipv4Addr, SocketAddrV4, TcpListener};
use std::str::FromStr;
use std::time::Duration;
use egui::{Color32, lerp, Rgba, RichText, Stroke};
@@ -22,17 +24,18 @@ use grin_chain::SyncStatus;
use crate::gui::{Colors, Navigator};
use crate::gui::icons::{CARDHOLDER, DATABASE, DOTS_THREE_OUTLINE_VERTICAL, FACTORY, FADERS, GAUGE};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Modal, ModalLocation, View};
use crate::gui::views::network_metrics::NetworkMetrics;
use crate::gui::views::network_mining::NetworkMining;
use crate::gui::views::network_node::NetworkNode;
use crate::gui::views::network_settings::NetworkSettings;
use crate::gui::views::View;
use crate::node::Node;
use crate::Settings;
pub trait NetworkTab {
fn get_type(&self) -> NetworkTabType;
fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks);
fn on_modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks);
}
#[derive(PartialEq)]
@@ -68,13 +71,19 @@ impl Default for Network {
impl Network {
pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) {
// Show modal if it's opened.
if Navigator::is_modal_open(ModalLocation::SidePanel) {
Navigator::modal_ui(ui, ModalLocation::SidePanel, |ui, modal| {
self.current_tab.as_mut().on_modal_ui(ui, modal, cb);
});
}
egui::TopBottomPanel::top("network_title")
.resizable(false)
.frame(egui::Frame {
fill: Colors::YELLOW,
inner_margin: Margin::same(0.0),
outer_margin: Margin::same(0.0),
stroke: Stroke::NONE,
..Default::default()
})
.show_inside(ui, |ui| {
@@ -217,7 +226,8 @@ impl Network {
});
}
pub fn disabled_server_content(ui: &mut egui::Ui) {
/// Content to draw when node is disabled.
pub fn disabled_node_ui(ui: &mut egui::Ui) {
View::center_content(ui, 162.0, |ui| {
let text = t!("network.disabled_server", "dots" => DOTS_THREE_OUTLINE_VERTICAL);
ui.label(RichText::new(text)
@@ -241,5 +251,25 @@ impl Network {
});
});
}
/// List of available IP addresses.
pub fn get_ip_list() -> Vec<IpAddr> {
let mut addresses = Vec::new();
for net_if in pnet::datalink::interfaces() {
for ip in net_if.ips {
if ip.is_ipv4() {
addresses.push(ip.ip());
}
}
}
addresses
}
/// Check whether a port is available on the provided host.
pub fn is_port_available(host: &str, port: u16) -> bool {
let ip_addr = Ipv4Addr::from_str(host).unwrap();
let ipv4 = SocketAddrV4::new(ip_addr, port);
TcpListener::bind(ipv4).is_ok()
}
}
+6 -4
View File
@@ -13,13 +13,13 @@
// limitations under the License.
use chrono::{DateTime, NaiveDateTime, Utc};
use egui::{RichText, Rounding, ScrollArea, Stroke};
use egui::{RichText, Rounding, ScrollArea, Stroke, Ui};
use grin_servers::DiffBlock;
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, View};
use crate::gui::views::{Modal, Network, NetworkTab, NetworkTabType, View};
use crate::node::Node;
#[derive(Default)]
@@ -39,7 +39,7 @@ impl NetworkTab for NetworkMetrics {
// Show message when node is not running or loading spinner when metrics are not available.
if server_stats.is_none() || server_stats.as_ref().unwrap().diff_stats.height == 0 {
if !Node::is_running() {
Network::disabled_server_content(ui);
Network::disabled_node_ui(ui);
} else {
View::center_content(ui, 162.0, |ui| {
View::big_loading_spinner(ui);
@@ -113,9 +113,9 @@ impl NetworkTab for NetworkMetrics {
// Show difficulty adjustment window blocks
let blocks_size = stats.diff_stats.last_blocks.len();
ScrollArea::vertical()
.id_source("difficulty_scroll")
.auto_shrink([false; 2])
.stick_to_bottom(true)
.id_source("difficulty_scroll")
.show_rows(
ui,
DIFF_BLOCK_UI_HEIGHT,
@@ -137,6 +137,8 @@ impl NetworkTab for NetworkMetrics {
},
);
}
fn on_modal_ui(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {}
}
const DIFF_BLOCK_UI_HEIGHT: f32 = 76.60;
+37 -135
View File
@@ -12,24 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::fmt::format;
use std::net::IpAddr;
use std::str::FromStr;
use chrono::{DateTime, NaiveDateTime, Utc};
use egui::{RichText, Rounding, ScrollArea, Stroke};
use egui::{Response, RichText, Rounding, ScrollArea, Stroke, TextStyle, Widget};
use grin_chain::SyncStatus;
use grin_servers::WorkerStats;
use pnet::ipnetwork::IpNetwork;
use crate::gui::Colors;
use crate::gui::icons::{BARBELL, CLOCK_AFTERNOON, COMPUTER_TOWER, CPU, CUBE, FADERS, FOLDER_DASHED, FOLDER_NOTCH_MINUS, FOLDER_NOTCH_PLUS, PLUGS, PLUGS_CONNECTED, POLYGON, WRENCH};
use crate::gui::{Colors, Navigator};
use crate::gui::icons::{BARBELL, CLOCK_AFTERNOON, COMPUTER_TOWER, CPU, CUBE, FADERS, FOLDER_DASHED, FOLDER_NOTCH_MINUS, FOLDER_NOTCH_PLUS, PLUGS, PLUGS_CONNECTED, POLYGON};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Network, NetworkTab, NetworkTabType, View};
use crate::node::Node;
use crate::gui::views::{Modal, Network, NetworkTab, NetworkTabType, View};
use crate::gui::views::settings_stratum::StratumServerSetup;
use crate::node::{Node, NodeConfig};
use crate::Settings;
#[derive(Default)]
pub struct NetworkMining;
pub struct NetworkMining {
stratum_server_setup: StratumServerSetup
}
impl NetworkTab for NetworkMining {
fn get_type(&self) -> NetworkTabType {
@@ -42,7 +41,7 @@ impl NetworkTab for NetworkMining {
// Show message when node is not running or loading spinner when mining are not available.
if !server_stats.is_some() || Node::get_sync_status().unwrap() != SyncStatus::NoSync {
if !Node::is_running() {
Network::disabled_server_content(ui);
Network::disabled_node_ui(ui);
} else {
View::center_content(ui, 162.0, |ui| {
View::big_loading_spinner(ui);
@@ -58,137 +57,31 @@ impl NetworkTab for NetworkMining {
let stratum_stats = &server_stats.as_ref().unwrap().stratum_stats;
// Stratum server address + port from config.
let saved_stratum_addr = Settings::node_config_to_read()
.members.clone()
.server.stratum_mining_config.unwrap()
.stratum_server_addr.unwrap();
let (stratum_addr, stratum_port) = saved_stratum_addr.split_once(":").unwrap();
// List of available ip addresses.
let mut addresses = Vec::new();
for net_if in pnet::datalink::interfaces() {
for ip in net_if.ips {
if ip.is_ipv4() {
addresses.push(ip.ip());
}
}
}
// Show error message when IP addresses are not available on the system.
if addresses.is_empty() {
View::center_content(ui, 52.0, |ui| {
ui.label(RichText::new(t!("network_mining.no_ip_addresses"))
.size(16.0)
.color(Colors::INACTIVE_TEXT)
);
});
return;
}
// Show stratum server setup when mining server is not enabled.
// Show stratum server setup when mining server is not running.
if !stratum_stats.is_running && !Node::is_stratum_server_starting() {
ScrollArea::vertical()
.id_source("stratum_server_setup")
.auto_shrink([false; 2])
.show(ui, |ui| {
ui.add_space(6.0);
View::sub_title(ui,
format!("{} {}", WRENCH, t!("network_mining.server_setup")));
ui.add_space(4.0);
View::horizontal_line(ui, Colors::ITEM_STROKE);
ui.add_space(6.0);
self.stratum_server_setup.ui(ui, cb);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_mining.choose_ip_address"))
.size(16.0)
.color(Colors::GRAY)
);
ui.add_space(10.0);
if addresses.len() != 0 {
let saved_ip_addr = &IpAddr::from_str(stratum_addr).unwrap();
let mut selected_addr = saved_ip_addr;
// Set first IP address as current if saved is not present at system.
if !addresses.contains(selected_addr) {
selected_addr = addresses.get(0).unwrap();
}
// Show available IP addresses on the system.
let _ = addresses.chunks(2).map(|x| {
if x.len() == 2 {
ui.columns(2, |columns| {
let addr0 = x.get(0).unwrap();
columns[0].vertical_centered(|ui| {
View::radio_value(ui,
&mut selected_addr,
addr0,
addr0.to_string());
});
let addr1 = x.get(1).unwrap();
columns[1].vertical_centered(|ui| {
View::radio_value(ui,
&mut selected_addr,
addr1,
addr1.to_string());
})
});
ui.add_space(12.0);
} else {
let addr = x.get(0).unwrap();
View::radio_value(ui,
&mut selected_addr,
addr,
addr.to_string());
ui.add_space(4.0);
}
}).collect::<Vec<_>>();
// Save stratum server address at config if it was changed.
if saved_ip_addr != selected_addr {
let addr_to_save = format!("{}:{}", selected_addr, stratum_port);
let mut w_node_config = Settings::node_config_to_update();
w_node_config.members
.server.stratum_mining_config.as_mut().unwrap()
.stratum_server_addr = Some(addr_to_save);
w_node_config.save();
}
}
ui.label(RichText::new(t!("network_mining.change_port"))
.size(16.0)
.color(Colors::GRAY)
);
// Show button to choose server port.
ui.add_space(6.0);
View::button(ui, stratum_port.to_string(), Colors::WHITE, || {
//TODO: Open modal to change value
cb.show_keyboard();
});
ui.add_space(14.0);
View::horizontal_line(ui, Colors::ITEM_STROKE);
ui.add_space(6.0);
// Show message about stratum server config.
let text = t!(
"network_mining.server_setting",
"address" => saved_stratum_addr,
"settings" => FADERS
);
let text = t!("network_mining.server_setting", "settings" => FADERS);
ui.label(RichText::new(text)
.size(16.0)
.color(Colors::INACTIVE_TEXT)
);
ui.add_space(8.0);
ui.add_space(4.0);
// Show button to enable server.
View::button(ui, t!("network_mining.enable_server"), Colors::GOLD, || {
Node::start_stratum_server();
});
ui.add_space(2.0);
// Show button to enable stratum server if port is available.
if self.stratum_server_setup.stratum_port_available {
ui.add_space(6.0);
View::button(ui, t!("network_mining.enable_server"), Colors::GOLD, || {
Node::start_stratum_server();
});
ui.add_space(2.0);
}
let stratum_enabled = Settings::node_config_to_read()
.members.clone()
@@ -218,9 +111,10 @@ impl NetworkTab for NetworkMining {
View::sub_title(ui, format!("{} {}", COMPUTER_TOWER, t!("network_mining.server")));
ui.columns(2, |columns| {
columns[0].vertical_centered(|ui| {
let (stratum_addr, stratum_port) = NodeConfig::get_stratum_address_port();
View::rounded_box(ui,
saved_stratum_addr,
t!("network_mining.ip_address"),
format!("{}:{}", stratum_addr, stratum_port),
t!("network_mining.address"),
[true, false, true, false]);
});
columns[1].vertical_centered(|ui| {
@@ -302,7 +196,6 @@ impl NetworkTab for NetworkMining {
ui.add_space(4.0);
View::horizontal_line(ui, Colors::ITEM_STROKE);
ui.add_space(4.0);
ScrollArea::vertical()
.auto_shrink([false; 2])
.id_source("stratum_workers_scroll")
@@ -322,7 +215,7 @@ impl NetworkTab for NetworkMining {
} else {
[false, false]
};
draw_worker_stats(ui, worker, rounding)
draw_workers_stats(ui, worker, rounding)
}
},
);
@@ -335,11 +228,20 @@ impl NetworkTab for NetworkMining {
});
}
}
fn on_modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
match modal.id {
StratumServerSetup::STRATUM_PORT_MODAL => {
self.stratum_server_setup.stratum_port_modal_ui(ui, modal, cb);
},
_ => {}
}
}
}
const WORKER_UI_HEIGHT: f32 = 77.0;
fn draw_worker_stats(ui: &mut egui::Ui, ws: &WorkerStats, rounding: [bool; 2]) {
fn draw_workers_stats(ui: &mut egui::Ui, ws: &WorkerStats, rounding: [bool; 2]) {
// Add space before the first item.
if rounding[0] {
ui.add_space(4.0);
+6 -3
View File
@@ -13,13 +13,13 @@
// limitations under the License.
use eframe::epaint::Stroke;
use egui::{RichText, Rounding, ScrollArea};
use egui::{RichText, Rounding, ScrollArea, Ui};
use grin_servers::PeerStats;
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::{Network, NetworkTab, NetworkTabType, View};
use crate::gui::views::{Modal, Network, NetworkTab, NetworkTabType, View};
use crate::node::Node;
#[derive(Default)]
@@ -35,7 +35,7 @@ impl NetworkTab for NetworkNode {
// Show message when node is not running or loading spinner when stats are not available.
if !server_stats.is_some() {
if !Node::is_running() {
Network::disabled_server_content(ui);
Network::disabled_node_ui(ui);
} else {
ui.centered_and_justified(|ui| {
View::big_loading_spinner(ui);
@@ -47,6 +47,7 @@ impl NetworkTab for NetworkNode {
let stats = server_stats.as_ref().unwrap();
ScrollArea::vertical()
.id_source("integrated_node")
.auto_shrink([false; 2])
.show(ui, |ui| {
// Show header info.
@@ -180,6 +181,8 @@ impl NetworkTab for NetworkNode {
}
});
}
fn on_modal_ui(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {}
}
fn draw_peer_stats(ui: &mut egui::Ui, peer: &PeerStats, rounding: [bool; 2]) {
+12 -1
View File
@@ -12,9 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use egui::Ui;
use grin_core::global::ChainTypes;
use crate::gui::Colors;
use crate::gui::icons::COMPUTER_TOWER;
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{NetworkTab, NetworkTabType};
use crate::gui::views::{Modal, NetworkTab, NetworkTabType, View};
use crate::Settings;
#[derive(Default)]
@@ -26,5 +29,13 @@ impl NetworkTab for NetworkSettings {
}
fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
View::sub_title(ui, format!("{} {}", COMPUTER_TOWER, t!("network_settings.server")));
View::horizontal_line(ui, Colors::ITEM_STROKE);
ui.add_space(4.0);
}
fn on_modal_ui(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
}
}
+16
View File
@@ -0,0 +1,16 @@
// 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.
// /// Integrated node server setup ui section.
// struct
+247
View File
@@ -0,0 +1,247 @@
// 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::net::IpAddr;
use std::str::FromStr;
use egui::{RichText, TextStyle, Widget};
use crate::gui::{Colors, Navigator};
use crate::gui::icons::WRENCH;
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Modal, ModalLocation, ModalPosition, Network, View};
use crate::node::NodeConfig;
/// Stratum server setup ui section.
pub struct StratumServerSetup {
/// Stratum address to be used inside edit modal.
stratum_address_edit: String,
/// Stratum port to be used inside edit modal.
stratum_port_edit: String,
/// Flag to check if stratum port is available inside edit modal.
port_available_edit: bool,
/// Flag to check if stratum port is available from saved config value.
pub(crate) stratum_port_available: bool
}
impl Default for StratumServerSetup {
fn default() -> Self {
let (stratum_address, stratum_port) = NodeConfig::get_stratum_address_port();
let is_port_available = Network::is_port_available(stratum_address.as_str(), stratum_port);
Self {
stratum_address_edit: stratum_address,
stratum_port_edit: stratum_port.to_string(),
port_available_edit: is_port_available,
stratum_port_available: is_port_available
}
}
}
impl StratumServerSetup {
/// Identifier for stratum port [`Modal`].
pub const STRATUM_PORT_MODAL: &'static str = "stratum_port";
pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
View::sub_title(ui, format!("{} {}", WRENCH, t!("network_mining.server_setup")));
View::horizontal_line(ui, Colors::ITEM_STROKE);
ui.add_space(4.0);
// Show error message when IP addresses are not available on the system.
let mut addresses = Network::get_ip_list();
if addresses.is_empty() {
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_mining.no_ip_addresses"))
.size(16.0)
.color(Colors::INACTIVE_TEXT)
);
ui.add_space(6.0);
});
return;
}
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_settings.ip_address"))
.size(16.0)
.color(Colors::GRAY)
);
ui.add_space(6.0);
// Show stratum IP address setup.
Self::ip_address_setup_ui(ui, addresses);
View::horizontal_line(ui, Colors::ITEM_STROKE);
ui.add_space(6.0);
ui.label(RichText::new(t!("network_settings.port"))
.size(16.0)
.color(Colors::GRAY)
);
// Show button to choose stratum server port.
ui.add_space(6.0);
let (stratum_address, stratum_port) = NodeConfig::get_stratum_address_port();
View::button(ui, stratum_port.to_string(), Colors::BUTTON, || {
// Setup values for modal.
self.stratum_address_edit = stratum_address.clone();
self.stratum_port_edit = stratum_port.to_string();
self.port_available_edit = Network::is_port_available(
stratum_address.as_str(),
stratum_port
);
// Show stratum port modal.
let port_modal = Modal::new(Self::STRATUM_PORT_MODAL,
ModalLocation::SidePanel)
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_port"));
Navigator::open_modal(port_modal);
cb.show_keyboard();
});
ui.add_space(12.0);
// Show error when stratum server port is unavailable.
if !self.stratum_port_available {
ui.label(RichText::new(t!("network_mining.port_unavailable"))
.size(16.0)
.color(Colors::RED));
ui.add_space(12.0);
}
View::horizontal_line(ui, Colors::ITEM_STROKE);
ui.add_space(6.0);
});
}
/// Draw stratum port [`Modal`] content.
pub fn stratum_port_modal_ui(&mut self,
ui: &mut egui::Ui,
modal: &Modal,
cb: &dyn PlatformCallbacks) {
ui.add_space(6.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_settings.enter_value"))
.size(16.0)
.color(Colors::GRAY));
ui.add_space(8.0);
// Draw stratum port text edit.
let text_edit_resp = egui::TextEdit::singleline(&mut self.stratum_port_edit)
.font(TextStyle::Button)
.desired_width(48.0)
.cursor_at_end(true)
.ui(ui);
text_edit_resp.request_focus();
if text_edit_resp.clicked() {
cb.show_keyboard();
}
// Show error when specified port is unavailable.
if !self.port_available_edit {
ui.add_space(12.0);
ui.label(RichText::new(t!("network_mining.port_unavailable"))
.size(16.0)
.color(Colors::RED));
}
ui.add_space(12.0);
// Show modal buttons.
ui.scope(|ui| {
// Setup spacing between buttons.
ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0);
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::WHITE, || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
columns[1].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.save"), Colors::WHITE, || {
// Check if port is available.
let port_parse = self.stratum_port_edit.parse::<u16>();
let is_available = port_parse.is_ok() && Network::is_port_available(
self.stratum_address_edit.as_str(),
port_parse.unwrap()
);
self.port_available_edit = is_available;
// Save port at config if it's available.
if self.port_available_edit {
NodeConfig::save_stratum_address_port(
self.stratum_address_edit.clone(),
self.stratum_port_edit.clone()
);
self.stratum_port_available = true;
cb.hide_keyboard();
modal.close();
}
});
});
});
ui.add_space(6.0);
});
});
}
/// Show stratum IP address setup.
fn ip_address_setup_ui(ui: &mut egui::Ui, addresses: Vec<IpAddr>) {
let (addr, port) = NodeConfig::get_stratum_address_port();
let saved_ip_addr = &IpAddr::from_str(addr.as_str()).unwrap();
let mut selected_addr = saved_ip_addr;
// Set first IP address as current if saved is not present at system.
if !addresses.contains(selected_addr) {
selected_addr = addresses.get(0).unwrap();
}
// Show available IP addresses on the system.
let _ = addresses.chunks(2).map(|x| {
if x.len() == 2 {
ui.columns(2, |columns| {
let addr0 = x.get(0).unwrap();
columns[0].vertical_centered(|ui| {
View::radio_value(ui,
&mut selected_addr,
addr0,
addr0.to_string());
});
let addr1 = x.get(1).unwrap();
columns[1].vertical_centered(|ui| {
View::radio_value(ui,
&mut selected_addr,
addr1,
addr1.to_string());
})
});
} else {
let addr = x.get(0).unwrap();
View::radio_value(ui,
&mut selected_addr,
addr,
addr.to_string());
}
ui.add_space(10.0);
}).collect::<Vec<_>>();
// Save stratum server address at config if it was changed.
if saved_ip_addr != selected_addr {
NodeConfig::save_stratum_address_port(selected_addr.to_string(), port.to_string());
}
}
}
+2 -2
View File
@@ -115,7 +115,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 br = Button::new(RichText::new(text.to_uppercase()).size(18.0).color(Colors::BUTTON))
let br = Button::new(RichText::new(text.to_uppercase()).size(18.0).color(Colors::TEXT_BUTTON))
.stroke(Self::DEFAULT_STROKE)
.fill(fill_color)
.ui(ui);
@@ -207,7 +207,7 @@ impl View {
/// Draw the button that looks like checkbox with callback on check.
pub fn checkbox(ui: &mut egui::Ui, checked: bool, text: String, callback: impl FnOnce()) {
let (text_value, color) = match checked {
true => { (format!("{} {}", CHECK_SQUARE, text), Colors::BUTTON) }
true => { (format!("{} {}", CHECK_SQUARE, text), Colors::TEXT_BUTTON) }
false => { (format!("{} {}", SQUARE, text), Colors::TEXT) }
};
let br = Button::new(RichText::new(text_value).size(18.0).color(color))