mirror of
https://github.com/Cyrix126/gupaxx.git
synced 2024-12-21 22:29:22 +00:00
feat: reworked UI for size/spacing/resizing/responsivness/alignment/coherence on every panels.
feat: reduction in code dupplication related to UI feat: update header of XvB tab feat: upgrade egui to git main feat: upgrade deps
This commit is contained in:
parent
55b39cf2c3
commit
50b3336156
38 changed files with 3477 additions and 4101 deletions
601
Cargo.lock
generated
601
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
15
Cargo.toml
15
Cargo.toml
|
@ -31,8 +31,11 @@ benri = "0.1.12"
|
|||
bytes = "1.8.0"
|
||||
dirs = "5.0.1"
|
||||
#--------------------------------------------------------------------------------
|
||||
egui = "0.29.1"
|
||||
egui_extras = {version="0.29.1", features = ["image"] }
|
||||
# egui = "0.29.1"
|
||||
egui = {git="https://github.com/emilk/egui"}
|
||||
# egui_extras = {version="0.29.1", features = ["image"] }
|
||||
egui_extras = {git="https://github.com/emilk/egui", features = ["image"] }
|
||||
|
||||
## 2023-12-28: https://github.com/hinto-janai/gupax/issues/68
|
||||
##
|
||||
## 2024-03-18: Both `glow` and `wgpu` seem to crash:
|
||||
|
@ -75,7 +78,8 @@ enclose = "1.2.0"
|
|||
bounded-vec-deque = {version="0.1.1", default-features=false}
|
||||
cfg-if = "1.0"
|
||||
flexi_logger = "0.29"
|
||||
eframe = {version="0.29.1", features=["wgpu"]}
|
||||
# eframe = {version="0.29.1", features=["wgpu"]}
|
||||
eframe = {git="https://github.com/emilk/egui", features=["wgpu"]}
|
||||
strum = {version="0.26", features=["derive"]}
|
||||
# Unix dependencies
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
|
@ -97,7 +101,8 @@ sudo = "0.6.0"
|
|||
# linked as well which causes problems, so statically link it.
|
||||
lzma-sys = { version = "0.1", features = ["static"] }
|
||||
[dev-dependencies]
|
||||
egui = {version="0.29.1", features=["callstack"]}
|
||||
# egui = {version="0.29.1", features=["callstack"]}
|
||||
egui = {git="https://github.com/emilk/egui", features=["callstack"]}
|
||||
|
||||
# [target.'cfg(not(target_os = "macos"))'.dependencies]
|
||||
# tls-api-native-tls = "0.9.0"
|
||||
|
@ -107,7 +112,7 @@ egui = {version="0.29.1", features=["callstack"]}
|
|||
# glow start on windows but not wgpu
|
||||
# need the same version that eframe is using with egui_wgpu
|
||||
# feature angle to enable support for old cpu on Windows
|
||||
wgpu = {version = "22.1", features=["angle"]}
|
||||
wgpu = {version = "23.0", features=["angle"]}
|
||||
zip = "2.2.0"
|
||||
is_elevated = "0.1.2"
|
||||
|
||||
|
|
119
src/app/mod.rs
119
src/app/mod.rs
|
@ -1,5 +1,13 @@
|
|||
use crate::APP_DEFAULT_HEIGHT;
|
||||
use crate::APP_DEFAULT_WIDTH;
|
||||
use crate::GUPAX_TAB_ABOUT;
|
||||
use crate::GUPAX_TAB_GUPAX;
|
||||
use crate::GUPAX_TAB_NODE;
|
||||
use crate::GUPAX_TAB_P2POOL;
|
||||
use crate::GUPAX_TAB_STATUS;
|
||||
use crate::GUPAX_TAB_XMRIG;
|
||||
use crate::GUPAX_TAB_XMRIG_PROXY;
|
||||
use crate::GUPAX_TAB_XVB;
|
||||
use crate::GUPAX_VERSION;
|
||||
use crate::OS;
|
||||
use crate::cli::Cli;
|
||||
|
@ -47,6 +55,7 @@ use log::debug;
|
|||
use log::error;
|
||||
use log::info;
|
||||
use log::warn;
|
||||
use panels::middle::common::list_poolnode::PoolNode;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::path::PathBuf;
|
||||
|
@ -87,10 +96,10 @@ pub struct App {
|
|||
pub update: Arc<Mutex<Update>>, // State for update data [update.rs]
|
||||
pub file_window: Arc<Mutex<FileWindow>>, // State for the path selector in [Gupax]
|
||||
pub ping: Arc<Mutex<Ping>>, // Ping data found in [node.rs]
|
||||
pub og_node_vec: Vec<(String, Node)>, // Manual Node database
|
||||
pub node_vec: Vec<(String, Node)>, // Manual Node database
|
||||
pub og_pool_vec: Vec<(String, Pool)>, // Manual Pool database
|
||||
pub pool_vec: Vec<(String, Pool)>, // Manual Pool database
|
||||
pub og_node_vec: Vec<(String, PoolNode)>, // Manual Node database
|
||||
pub node_vec: Vec<(String, PoolNode)>, // Manual Node database
|
||||
pub og_pool_vec: Vec<(String, PoolNode)>, // Manual Pool database
|
||||
pub pool_vec: Vec<(String, PoolNode)>, // Manual Pool database
|
||||
pub diff: bool, // This bool indicates state changes
|
||||
// Restart state:
|
||||
// If Gupax updated itself, this represents that the
|
||||
|
@ -136,7 +145,7 @@ pub struct App {
|
|||
// Static stuff
|
||||
pub benchmarks: Vec<Benchmark>, // XMRig CPU benchmarks
|
||||
pub pid: sysinfo::Pid, // Gupax's PID
|
||||
pub max_threads: usize, // Max amount of detected system threads
|
||||
pub max_threads: u16, // Max amount of detected system threads
|
||||
pub now: Instant, // Internal timer
|
||||
pub exe: String, // Path for [Gupax] binary
|
||||
pub dir: String, // Directory [Gupax] binary is in
|
||||
|
@ -311,7 +320,7 @@ impl App {
|
|||
pub_sys,
|
||||
benchmarks,
|
||||
pid,
|
||||
max_threads: benri::threads!(),
|
||||
max_threads: benri::threads!() as u16,
|
||||
now,
|
||||
admin: false,
|
||||
exe: String::new(),
|
||||
|
@ -511,65 +520,83 @@ impl App {
|
|||
}
|
||||
// Handle [node_vec] overflow
|
||||
info!("App Init | Handling [node_vec] overflow");
|
||||
if og.p2pool.selected_index > app.og_node_vec.len() {
|
||||
if og.p2pool.selected_node.index > app.og_node_vec.len() {
|
||||
warn!(
|
||||
"App | Overflowing manual node index [{} > {}]",
|
||||
og.p2pool.selected_index,
|
||||
og.p2pool.selected_node.index,
|
||||
app.og_node_vec.len()
|
||||
);
|
||||
let (name, node) = match app.og_node_vec.first() {
|
||||
Some(zero) => zero.clone(),
|
||||
None => Node::new_tuple(),
|
||||
};
|
||||
og.p2pool.selected_index = 0;
|
||||
og.p2pool.selected_name.clone_from(&name);
|
||||
og.p2pool.selected_ip.clone_from(&node.ip);
|
||||
og.p2pool.selected_rpc.clone_from(&node.rpc);
|
||||
og.p2pool.selected_zmq.clone_from(&node.zmq);
|
||||
app.state.p2pool.selected_index = 0;
|
||||
app.state.p2pool.selected_name = name;
|
||||
app.state.p2pool.selected_ip = node.ip;
|
||||
app.state.p2pool.selected_rpc = node.rpc;
|
||||
app.state.p2pool.selected_zmq = node.zmq;
|
||||
og.p2pool.selected_node.index = 0;
|
||||
og.p2pool.selected_node.name.clone_from(&name);
|
||||
og.p2pool
|
||||
.selected_node
|
||||
.ip
|
||||
.clone_from(&node.ip().to_string());
|
||||
og.p2pool
|
||||
.selected_node
|
||||
.rpc
|
||||
.clone_from(&node.port().to_string());
|
||||
og.p2pool
|
||||
.selected_node
|
||||
.zmq_rig
|
||||
.clone_from(&node.custom().to_string());
|
||||
app.state.p2pool.selected_node.index = 0;
|
||||
app.state.p2pool.selected_node.name = name;
|
||||
app.state.p2pool.selected_node.ip = node.ip().to_string();
|
||||
app.state.p2pool.selected_node.rpc = node.port().to_string();
|
||||
app.state.p2pool.selected_node.zmq_rig = node.custom().to_string();
|
||||
}
|
||||
// Handle [pool_vec] overflow
|
||||
info!("App Init | Handling [pool_vec] overflow...");
|
||||
if og.xmrig.selected_index > app.og_pool_vec.len() {
|
||||
if og.xmrig.selected_pool.index > app.og_pool_vec.len() {
|
||||
warn!(
|
||||
"App | Overflowing manual pool index [{} > {}], resetting to 1",
|
||||
og.xmrig.selected_index,
|
||||
og.xmrig.selected_pool.index,
|
||||
app.og_pool_vec.len()
|
||||
);
|
||||
let (name, pool) = match app.og_pool_vec.first() {
|
||||
Some(zero) => zero.clone(),
|
||||
None => Pool::new_tuple(),
|
||||
};
|
||||
og.xmrig.selected_index = 0;
|
||||
og.xmrig.selected_name.clone_from(&name);
|
||||
og.xmrig.selected_ip.clone_from(&pool.ip);
|
||||
og.xmrig.selected_port.clone_from(&pool.port);
|
||||
app.state.xmrig.selected_index = 0;
|
||||
app.state.xmrig.selected_name = name;
|
||||
app.state.xmrig.selected_ip = pool.ip;
|
||||
app.state.xmrig.selected_port = pool.port;
|
||||
if og.xmrig_proxy.selected_index > app.og_pool_vec.len() {
|
||||
og.xmrig.selected_pool.index = 0;
|
||||
og.xmrig.selected_pool.name.clone_from(&name);
|
||||
og.xmrig.selected_pool.ip.clone_from(&pool.ip().to_string());
|
||||
og.xmrig
|
||||
.selected_pool
|
||||
.rpc
|
||||
.clone_from(&pool.port().to_string());
|
||||
app.state.xmrig.selected_pool.index = 0;
|
||||
app.state.xmrig.selected_pool.name = name;
|
||||
app.state.xmrig.selected_pool.ip = pool.ip().to_string();
|
||||
app.state.xmrig.selected_pool.rpc = pool.port().to_string();
|
||||
if og.xmrig_proxy.selected_pool.index > app.og_pool_vec.len() {
|
||||
warn!(
|
||||
"App | Overflowing manual pool index [{} > {}], resetting to 1",
|
||||
og.xmrig_proxy.selected_index,
|
||||
og.xmrig_proxy.selected_pool.index,
|
||||
app.og_pool_vec.len()
|
||||
);
|
||||
let (name, pool) = match app.og_pool_vec.first() {
|
||||
Some(zero) => zero.clone(),
|
||||
None => Pool::new_tuple(),
|
||||
};
|
||||
og.xmrig_proxy.selected_index = 0;
|
||||
og.xmrig_proxy.selected_name.clone_from(&name);
|
||||
og.xmrig_proxy.selected_ip.clone_from(&pool.ip);
|
||||
og.xmrig_proxy.selected_port.clone_from(&pool.port);
|
||||
app.state.xmrig_proxy.selected_index = 0;
|
||||
app.state.xmrig_proxy.selected_name = name;
|
||||
app.state.xmrig_proxy.selected_ip = pool.ip;
|
||||
app.state.xmrig_proxy.selected_port = pool.port;
|
||||
og.xmrig_proxy.selected_pool.index = 0;
|
||||
og.xmrig_proxy.selected_pool.name.clone_from(&name);
|
||||
og.xmrig_proxy
|
||||
.selected_pool
|
||||
.ip
|
||||
.clone_from(&pool.ip().to_string());
|
||||
og.xmrig_proxy
|
||||
.selected_pool
|
||||
.rpc
|
||||
.clone_from(&pool.port().to_string());
|
||||
app.state.xmrig_proxy.selected_pool.index = 0;
|
||||
app.state.xmrig_proxy.selected_pool.name = name;
|
||||
app.state.xmrig_proxy.selected_pool.ip = pool.ip().to_string();
|
||||
app.state.xmrig_proxy.selected_pool.rpc = pool.port().to_string();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -648,7 +675,7 @@ impl App {
|
|||
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
pub fn gather_backup_hosts(&self) -> Option<Vec<Node>> {
|
||||
pub fn gather_backup_hosts(&self) -> Option<Vec<PoolNode>> {
|
||||
if !self.state.p2pool.backup_host {
|
||||
return None;
|
||||
}
|
||||
|
@ -695,7 +722,7 @@ impl App {
|
|||
zmq: zmq.into(),
|
||||
};
|
||||
|
||||
vec.push(node);
|
||||
vec.push(PoolNode::Node(node));
|
||||
}
|
||||
|
||||
if vec.is_empty() {
|
||||
|
@ -747,6 +774,18 @@ impl Tab {
|
|||
Tab::Xvb => Some(ProcessName::Xvb),
|
||||
}
|
||||
}
|
||||
pub fn msg_default_tab(&self) -> &str {
|
||||
match self {
|
||||
Tab::About => GUPAX_TAB_ABOUT,
|
||||
Tab::Status => GUPAX_TAB_STATUS,
|
||||
Tab::Gupax => GUPAX_TAB_GUPAX,
|
||||
Tab::Node => GUPAX_TAB_NODE,
|
||||
Tab::P2pool => GUPAX_TAB_P2POOL,
|
||||
Tab::Xmrig => GUPAX_TAB_XMRIG,
|
||||
Tab::XmrigProxy => GUPAX_TAB_XMRIG_PROXY,
|
||||
Tab::Xvb => GUPAX_TAB_XVB,
|
||||
}
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------- [Restart] Enum
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
|
|
|
@ -207,16 +207,30 @@ impl crate::app::App {
|
|||
let restart_msg = format!("Restart {}", name);
|
||||
if process.waiting {
|
||||
ui.add_enabled_ui(false, |ui| {
|
||||
ui.add_sized(size, Button::new("⟲"))
|
||||
ui.add_sized(size, Button::new("▶"))
|
||||
.on_disabled_hover_text(process.run_middle_msg());
|
||||
ui.add(Separator::default().grow(0.0));
|
||||
ui.add_sized(size, Button::new("⏹"))
|
||||
.on_disabled_hover_text(process.run_middle_msg());
|
||||
ui.add(Separator::default().grow(0.0));
|
||||
ui.add_sized(size, Button::new("▶"))
|
||||
ui.add_sized(size, Button::new("⟲"))
|
||||
.on_disabled_hover_text(process.run_middle_msg());
|
||||
});
|
||||
} else if process.alive {
|
||||
ui.add_enabled_ui(false, |ui| {
|
||||
ui.add_sized(size, Button::new("▶"))
|
||||
.on_disabled_hover_text(start_msg)
|
||||
});
|
||||
ui.add(Separator::default().grow(0.0));
|
||||
if key.is_down() && !wants_input
|
||||
|| ui
|
||||
.add_sized(size, Button::new("⏹"))
|
||||
.on_hover_text(stop_msg)
|
||||
.clicked()
|
||||
{
|
||||
process.stop(&self.helper);
|
||||
}
|
||||
ui.add(Separator::default().grow(0.0));
|
||||
if key.is_up() && !wants_input
|
||||
|| ui
|
||||
.add_sized(size, Button::new("⟲"))
|
||||
|
@ -274,29 +288,7 @@ impl crate::app::App {
|
|||
}
|
||||
}
|
||||
}
|
||||
ui.add(Separator::default().grow(0.0));
|
||||
if key.is_down() && !wants_input
|
||||
|| ui
|
||||
.add_sized(size, Button::new("⏹"))
|
||||
.on_hover_text(stop_msg)
|
||||
.clicked()
|
||||
{
|
||||
process.stop(&self.helper);
|
||||
}
|
||||
ui.add(Separator::default().grow(0.0));
|
||||
ui.add_enabled_ui(false, |ui| {
|
||||
ui.add_sized(size, Button::new("▶"))
|
||||
.on_disabled_hover_text(start_msg)
|
||||
});
|
||||
} else {
|
||||
ui.add_enabled_ui(false, |ui| {
|
||||
ui.add_sized(size, Button::new("⟲"))
|
||||
.on_disabled_hover_text(restart_msg);
|
||||
ui.add(Separator::default().grow(0.0));
|
||||
ui.add_sized(size, Button::new("⏹"))
|
||||
.on_disabled_hover_text(stop_msg);
|
||||
ui.add(Separator::default().grow(0.0));
|
||||
});
|
||||
let text_err = self.start_ready(process).err().unwrap_or_default();
|
||||
let ui_enabled = text_err.is_empty();
|
||||
ui.add_enabled_ui(ui_enabled, |ui| {
|
||||
|
@ -354,6 +346,14 @@ impl crate::app::App {
|
|||
}
|
||||
}
|
||||
});
|
||||
ui.add_enabled_ui(false, |ui| {
|
||||
ui.add_sized(size, Button::new("⏹"))
|
||||
.on_disabled_hover_text(stop_msg);
|
||||
ui.add(Separator::default().grow(0.0));
|
||||
ui.add_sized(size, Button::new("⟲"))
|
||||
.on_disabled_hover_text(restart_msg);
|
||||
ui.add(Separator::default().grow(0.0));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
73
src/app/panels/middle/common/console.rs
Normal file
73
src/app/panels/middle/common/console.rs
Normal file
|
@ -0,0 +1,73 @@
|
|||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use egui::{Label, TextEdit, TextStyle, Ui};
|
||||
|
||||
use crate::{DARK_GRAY, helper::Process, miscs::height_txt_before_button, regex::num_lines};
|
||||
|
||||
pub fn console(ui: &mut Ui, text: &str) {
|
||||
let nb_lines = num_lines(text);
|
||||
let height = ui.available_height() / 2.8;
|
||||
egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| {
|
||||
ui.style_mut().override_text_style = Some(TextStyle::Small);
|
||||
egui::ScrollArea::vertical()
|
||||
.stick_to_bottom(true)
|
||||
.max_width(ui.available_width())
|
||||
.max_height(height)
|
||||
.auto_shrink([false; 2])
|
||||
// .show_viewport(ui, |ui, _| {
|
||||
.show_rows(
|
||||
ui,
|
||||
ui.text_style_height(&TextStyle::Small),
|
||||
nb_lines,
|
||||
|ui, row_range| {
|
||||
for i in row_range {
|
||||
if let Some(line) = text.lines().nth(i) {
|
||||
ui.label(line);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// input args
|
||||
pub fn input_args_field(
|
||||
ui: &mut Ui,
|
||||
buffer: &mut String,
|
||||
process: &Arc<Mutex<Process>>,
|
||||
hint: &str,
|
||||
hover: &str,
|
||||
) {
|
||||
ui.style_mut().spacing.text_edit_width = ui.available_width();
|
||||
let response = ui
|
||||
.add(TextEdit::hint_text(TextEdit::singleline(buffer), hint))
|
||||
.on_hover_text(hover);
|
||||
// If the user pressed enter, dump buffer contents into the process STDIN
|
||||
if response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) {
|
||||
response.request_focus(); // Get focus back
|
||||
let buffer = std::mem::take(buffer); // Take buffer
|
||||
let mut process = process.lock().unwrap();
|
||||
if process.is_alive() {
|
||||
process.input.push(buffer);
|
||||
} // Push only if alive
|
||||
}
|
||||
}
|
||||
|
||||
// Command arguments
|
||||
pub fn start_options_field(ui: &mut Ui, arguments: &mut String, hint: &str, hover: &str) {
|
||||
ui.group(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_sized(
|
||||
[0.0, height_txt_before_button(ui, &TextStyle::Body)],
|
||||
Label::new("Command arguments:"),
|
||||
);
|
||||
ui.style_mut().spacing.text_edit_width = ui.available_width();
|
||||
ui.add(TextEdit::hint_text(TextEdit::singleline(arguments), hint))
|
||||
.on_hover_text(hover);
|
||||
arguments.truncate(1024);
|
||||
})
|
||||
});
|
||||
if !arguments.is_empty() {
|
||||
ui.disable();
|
||||
}
|
||||
}
|
83
src/app/panels/middle/common/header_tab.rs
Normal file
83
src/app/panels/middle/common/header_tab.rs
Normal file
|
@ -0,0 +1,83 @@
|
|||
use egui::{Hyperlink, Image, Separator, TextStyle, TextWrapMode, Ui};
|
||||
use log::debug;
|
||||
|
||||
use crate::SPACE;
|
||||
/// logo first, first hyperlink will be the header, description under.
|
||||
/// will take care of centering widgets if boerder weight is more than 0.
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
pub fn header_tab(
|
||||
ui: &mut Ui,
|
||||
logo: Option<Image>,
|
||||
// text, link, hover text
|
||||
links: &[(&str, &str, &str)],
|
||||
subtitle: Option<String>,
|
||||
one_line_center: bool,
|
||||
) {
|
||||
// width - logo and links and separators divided by double the size of logo (can't know width of links).
|
||||
ui.style_mut().wrap_mode = Some(TextWrapMode::Extend);
|
||||
ui.style_mut().override_text_style = Some(TextStyle::Heading);
|
||||
ui.add_space(SPACE);
|
||||
if one_line_center {
|
||||
let height = 64.0;
|
||||
let nb_links = links.len();
|
||||
let border_weight = ((ui.available_width()
|
||||
- ((height * 4.0 * nb_links as f32) + if logo.is_some() { height * 2.0 } else { 0.0 }))
|
||||
/ (height * 2.0))
|
||||
.max(0.0) as usize;
|
||||
// nb_columns add logo if exist plus number of links with separator for each + number of column for border space
|
||||
let nb_columns = if logo.is_some() { 1 } else { 0 } + (links.len() * 2) + border_weight * 2;
|
||||
ui.columns(nb_columns, |col| {
|
||||
// first column for left border
|
||||
for n in 0..(border_weight) {
|
||||
col[n].vertical_centered(|ui| ui.add_space(0.0));
|
||||
debug!("left side space: {}", n);
|
||||
}
|
||||
// jump first column, stop less 2, because begin at 0 and end with space column.
|
||||
let mut nb_col = border_weight;
|
||||
if let Some(logo) = logo {
|
||||
debug!("logo: {}", nb_col);
|
||||
col[nb_col].vertical_centered(|ui| ui.add_sized([height, height], logo));
|
||||
nb_col += 1;
|
||||
}
|
||||
for link in links {
|
||||
debug!("separator: {}", nb_col);
|
||||
col[nb_col].vertical_centered(|ui| {
|
||||
ui.add_sized(
|
||||
[height / 8.0, height],
|
||||
Separator::default().vertical().spacing(height / 8.0),
|
||||
)
|
||||
});
|
||||
nb_col += 1;
|
||||
|
||||
debug!("link: {}", nb_col);
|
||||
col[nb_col].vertical_centered(|ui| {
|
||||
ui.add_sized(
|
||||
[ui.available_width(), height],
|
||||
Hyperlink::from_label_and_url(link.0, link.1),
|
||||
);
|
||||
});
|
||||
nb_col += 1;
|
||||
}
|
||||
|
||||
for n in nb_col..(nb_col + border_weight) {
|
||||
debug!("right side border space: {}", n);
|
||||
col[n].vertical_centered(|ui| ui.add_space(0.0));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// top down
|
||||
ui.vertical_centered(|ui| {
|
||||
if let Some(source) = logo {
|
||||
ui.add(source);
|
||||
}
|
||||
for link in links {
|
||||
ui.hyperlink_to(link.0, link.1);
|
||||
}
|
||||
ui.style_mut().override_text_style = Some(TextStyle::Body);
|
||||
});
|
||||
}
|
||||
if let Some(desc) = subtitle {
|
||||
ui.label(desc);
|
||||
}
|
||||
ui.add_space(SPACE);
|
||||
}
|
330
src/app/panels/middle/common/list_poolnode.rs
Normal file
330
src/app/panels/middle/common/list_poolnode.rs
Normal file
|
@ -0,0 +1,330 @@
|
|||
use egui::{Button, ComboBox, RichText, SelectableLabel, Ui};
|
||||
use log::{debug, info};
|
||||
|
||||
use crate::{
|
||||
LIST_ADD, LIST_CLEAR, LIST_DELETE, LIST_SAVE,
|
||||
disk::{node::Node, pool::Pool, state::SelectedPoolNode},
|
||||
};
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum PoolNode {
|
||||
Node(Node),
|
||||
Pool(Pool),
|
||||
}
|
||||
|
||||
impl PoolNode {
|
||||
pub fn ip(&self) -> &str {
|
||||
match &self {
|
||||
PoolNode::Node(n) => &n.ip,
|
||||
PoolNode::Pool(p) => &p.ip,
|
||||
}
|
||||
}
|
||||
pub fn port(&self) -> &str {
|
||||
match &self {
|
||||
PoolNode::Node(n) => &n.rpc,
|
||||
PoolNode::Pool(p) => &p.port,
|
||||
}
|
||||
}
|
||||
pub fn custom(&self) -> &str {
|
||||
match &self {
|
||||
PoolNode::Node(n) => &n.zmq,
|
||||
PoolNode::Pool(p) => &p.rig,
|
||||
}
|
||||
}
|
||||
pub fn custom_name(&self) -> &str {
|
||||
match &self {
|
||||
PoolNode::Node(_) => "ZMQ",
|
||||
PoolNode::Pool(_) => "rig",
|
||||
}
|
||||
}
|
||||
fn set_ip(&mut self, new_ip: String) {
|
||||
match self {
|
||||
PoolNode::Node(n) => n.ip = new_ip,
|
||||
PoolNode::Pool(p) => p.ip = new_ip,
|
||||
}
|
||||
}
|
||||
fn set_port(&mut self, new_port: String) {
|
||||
match self {
|
||||
PoolNode::Node(n) => n.rpc = new_port,
|
||||
PoolNode::Pool(p) => p.port = new_port,
|
||||
}
|
||||
}
|
||||
fn set_custom(&mut self, new_custom: String) {
|
||||
match self {
|
||||
PoolNode::Node(n) => n.zmq = new_custom,
|
||||
PoolNode::Pool(p) => p.rig = new_custom,
|
||||
}
|
||||
}
|
||||
// pub fn from_vec_node(vec_node: Vec<(String, Node)>) -> Vec<(String, Self)> {
|
||||
// vec_node
|
||||
// .into_iter()
|
||||
// .map(|(name, node)| (name, PoolNode::Node(node)))
|
||||
// .collect()
|
||||
// }
|
||||
// pub fn from_vec_pool(vec_node: Vec<(String, Pool)>) -> Vec<(String, Self)> {
|
||||
// vec_node
|
||||
// .into_iter()
|
||||
// .map(|(name, pool)| (name, PoolNode::Pool(pool)))
|
||||
// .collect()
|
||||
// }
|
||||
}
|
||||
/// compatible for P2Pool and Xmrig/Proxy
|
||||
/// current is (name, ip, port, zmq/rig)
|
||||
pub fn list_poolnode(
|
||||
ui: &mut Ui,
|
||||
current: &mut (&mut String, &mut String, &mut String, &mut String),
|
||||
selected: &mut SelectedPoolNode,
|
||||
node_vec: &mut Vec<(String, PoolNode)>,
|
||||
incorrect_input: bool,
|
||||
) {
|
||||
ui.vertical(|ui| {
|
||||
ui.spacing_mut().item_spacing.y = 0.0;
|
||||
// ui.spacing_mut().button_padding.x = ui.available_width() / 2.0;
|
||||
let width = ui.available_width();
|
||||
// [Manual node selection]
|
||||
// [Ping List]
|
||||
debug!("P2Pool Tab | Rendering [Node List]");
|
||||
// [Menu]
|
||||
menu_list_node(ui, node_vec, width, selected, current);
|
||||
let node_vec_len = node_vec.len();
|
||||
// [Add/Save]
|
||||
ui.horizontal(|ui| {
|
||||
add_save_node(
|
||||
ui,
|
||||
selected,
|
||||
node_vec,
|
||||
current,
|
||||
node_vec_len,
|
||||
incorrect_input,
|
||||
);
|
||||
});
|
||||
// [Delete]
|
||||
ui.horizontal(|ui| {
|
||||
delete_node(ui, selected, node_vec, current, node_vec_len);
|
||||
});
|
||||
// [Clear]
|
||||
ui.horizontal(|ui| {
|
||||
clear_node(ui, current);
|
||||
});
|
||||
});
|
||||
}
|
||||
// slider H/s
|
||||
|
||||
fn clear_node(ui: &mut Ui, current: &mut (&mut String, &mut String, &mut String, &mut String)) {
|
||||
ui.add_enabled_ui(
|
||||
!current.0.is_empty()
|
||||
|| !current.1.is_empty()
|
||||
|| !current.2.is_empty()
|
||||
|| !current.3.is_empty(),
|
||||
|ui| {
|
||||
if ui
|
||||
.add_sized([ui.available_width(), 0.0], Button::new("Clear"))
|
||||
.on_hover_text(LIST_CLEAR)
|
||||
.clicked()
|
||||
{
|
||||
current.0.clear();
|
||||
current.1.clear();
|
||||
current.2.clear();
|
||||
current.3.clear();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
fn menu_list_node(
|
||||
ui: &mut Ui,
|
||||
node_vec: &mut [(String, PoolNode)],
|
||||
width: f32,
|
||||
selected: &mut SelectedPoolNode,
|
||||
current: &mut (&mut String, &mut String, &mut String, &mut String),
|
||||
) {
|
||||
let text = RichText::new(format!("{}. {}", selected.index + 1, selected.name));
|
||||
ComboBox::from_id_salt("manual_nodes")
|
||||
.selected_text(text)
|
||||
.width(width)
|
||||
.show_ui(ui, |ui| {
|
||||
for (n, (name, node)) in node_vec.iter().enumerate() {
|
||||
let text = RichText::new(format!(
|
||||
"{}. {}\n IP: {}\n RPC: {}\n {}: {}",
|
||||
n + 1,
|
||||
name,
|
||||
node.ip(),
|
||||
node.port(),
|
||||
node.custom_name(),
|
||||
node.custom()
|
||||
));
|
||||
if ui
|
||||
.add(SelectableLabel::new(selected.name == **name, text))
|
||||
.clicked()
|
||||
{
|
||||
selected.index = n;
|
||||
let node = node.clone();
|
||||
selected.name.clone_from(name);
|
||||
selected.ip.clone_from(&node.ip().to_string());
|
||||
selected.rpc.clone_from(&node.port().to_string());
|
||||
selected.zmq_rig.clone_from(&node.custom().to_string());
|
||||
current.0.clone_from(name);
|
||||
*current.1 = node.ip().to_string();
|
||||
*current.2 = node.port().to_string();
|
||||
*current.3 = node.custom().to_string();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
fn add_save_node(
|
||||
ui: &mut Ui,
|
||||
selected: &mut SelectedPoolNode,
|
||||
node_vec: &mut Vec<(String, PoolNode)>,
|
||||
current: &mut (&mut String, &mut String, &mut String, &mut String),
|
||||
node_vec_len: usize,
|
||||
incorrect_input: bool,
|
||||
) {
|
||||
// list should never be empty unless state edited by hand.
|
||||
let is_node = matches!(node_vec[0].1, PoolNode::Node(_));
|
||||
// [Add/Save]
|
||||
let mut exists = false;
|
||||
let mut save_diff = true;
|
||||
let mut existing_index = 0;
|
||||
for (name, node) in node_vec.iter() {
|
||||
if *name == *current.0 {
|
||||
exists = true;
|
||||
if *current.1 == node.ip() && *current.2 == node.port() && *current.3 == node.custom() {
|
||||
save_diff = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
existing_index += 1;
|
||||
}
|
||||
let text = if exists { LIST_SAVE } else { LIST_ADD };
|
||||
let text = format!(
|
||||
"{}\n Currently selected node: {}. {}\n Current amount of {}: {}/1000",
|
||||
text,
|
||||
selected.index + 1,
|
||||
selected.name,
|
||||
if is_node { "nodes" } else { "pools" },
|
||||
node_vec_len
|
||||
);
|
||||
// If the node already exists, show [Save] and mutate the already existing node
|
||||
if exists {
|
||||
ui.add_enabled_ui(!incorrect_input && save_diff, |ui| {
|
||||
if ui
|
||||
.add_sized([ui.available_width(), 0.0], Button::new("Save"))
|
||||
.on_hover_text(text)
|
||||
.clicked()
|
||||
{
|
||||
let ip = current.1.clone();
|
||||
let rpc = current.2.clone();
|
||||
// zmq can be rig in case of Pool
|
||||
let zmq = current.3.clone();
|
||||
let poolnode = &mut node_vec[existing_index].1;
|
||||
poolnode.set_ip(ip);
|
||||
poolnode.set_port(rpc);
|
||||
poolnode.set_custom(zmq);
|
||||
info!(
|
||||
"Node | S | [index: {}, name: \"{}\", ip: \"{}\", rpc: {}, {}: {}]",
|
||||
existing_index + 1,
|
||||
current.0,
|
||||
current.1,
|
||||
current.2,
|
||||
poolnode.custom_name(),
|
||||
current.3
|
||||
);
|
||||
selected.index = existing_index;
|
||||
selected.ip.clone_from(current.1);
|
||||
selected.rpc.clone_from(current.2);
|
||||
selected.zmq_rig.clone_from(current.3);
|
||||
}
|
||||
});
|
||||
// Else, add to the list
|
||||
} else {
|
||||
ui.add_enabled_ui(!incorrect_input && node_vec_len < 1000, |ui| {
|
||||
if ui
|
||||
.add_sized([ui.available_width(), 0.0], Button::new("Add"))
|
||||
.on_hover_text(text)
|
||||
.clicked()
|
||||
{
|
||||
let ip = current.1.clone();
|
||||
let rpc = current.2.clone();
|
||||
// zmq can be rig in case of Pool
|
||||
let zmq = current.3.clone();
|
||||
let poolnode = match node_vec[existing_index].1 {
|
||||
PoolNode::Node(_) => PoolNode::Node(Node { ip, rpc, zmq }),
|
||||
PoolNode::Pool(_) => PoolNode::Pool(Pool {
|
||||
rig: zmq,
|
||||
ip,
|
||||
port: rpc,
|
||||
}),
|
||||
};
|
||||
info!(
|
||||
"Node | A | [index: {}, name: \"{}\", ip: \"{}\", rpc: {}, {}: {}]",
|
||||
node_vec_len,
|
||||
current.0,
|
||||
current.1,
|
||||
current.2,
|
||||
poolnode.custom_name(),
|
||||
current.3
|
||||
);
|
||||
node_vec.push((current.0.clone(), poolnode));
|
||||
selected.index = node_vec_len;
|
||||
selected.name.clone_from(current.0);
|
||||
selected.ip.clone_from(current.1);
|
||||
selected.rpc.clone_from(current.2);
|
||||
selected.zmq_rig.clone_from(current.3);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn delete_node(
|
||||
ui: &mut Ui,
|
||||
selected: &mut SelectedPoolNode,
|
||||
node_vec: &mut Vec<(String, PoolNode)>,
|
||||
current: &mut (&mut String, &mut String, &mut String, &mut String),
|
||||
node_vec_len: usize,
|
||||
) {
|
||||
ui.add_enabled_ui(node_vec_len > 1, |ui| {
|
||||
let text = format!(
|
||||
"{}\n Currently selected node: {}. {}\n Current amount of nodes: {}/1000",
|
||||
LIST_DELETE,
|
||||
selected.index + 1,
|
||||
selected.name,
|
||||
node_vec_len
|
||||
);
|
||||
if ui
|
||||
.add_sized([ui.available_width(), 0.0], Button::new("Delete"))
|
||||
.on_hover_text(text)
|
||||
.clicked()
|
||||
{
|
||||
let new_name;
|
||||
let new_node;
|
||||
match selected.index {
|
||||
0 => {
|
||||
new_name = node_vec[1].0.clone();
|
||||
new_node = node_vec[1].1.clone();
|
||||
node_vec.remove(0);
|
||||
}
|
||||
_ => {
|
||||
node_vec.remove(selected.index);
|
||||
selected.index -= 1;
|
||||
new_name = node_vec[selected.index].0.clone();
|
||||
new_node = node_vec[selected.index].1.clone();
|
||||
}
|
||||
};
|
||||
selected.name.clone_from(&new_name);
|
||||
selected.ip = new_node.ip().to_string();
|
||||
selected.rpc = new_node.port().to_string();
|
||||
selected.zmq_rig = new_node.custom().to_string();
|
||||
*current.0 = new_name;
|
||||
*current.1 = new_node.ip().to_string();
|
||||
*current.2 = new_node.port().to_string();
|
||||
*current.3 = new_node.custom().to_string();
|
||||
info!(
|
||||
"Node | D | [index: {}, name: \"{}\", ip: \"{}\", port: {}, {}: {}]",
|
||||
selected.index,
|
||||
selected.name,
|
||||
selected.ip,
|
||||
selected.rpc,
|
||||
new_node.custom_name(),
|
||||
selected.zmq_rig
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
4
src/app/panels/middle/common/mod.rs
Normal file
4
src/app/panels/middle/common/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
pub mod console;
|
||||
pub mod header_tab;
|
||||
pub mod list_poolnode;
|
||||
pub mod state_edit_field;
|
212
src/app/panels/middle/common/state_edit_field.rs
Normal file
212
src/app/panels/middle/common/state_edit_field.rs
Normal file
|
@ -0,0 +1,212 @@
|
|||
use std::ops::RangeInclusive;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use egui::{Color32, Label, RichText, Slider, TextEdit};
|
||||
use egui::{TextStyle, Ui};
|
||||
|
||||
use crate::components::gupax::{FileType, FileWindow};
|
||||
use crate::disk::state::Gupax;
|
||||
use crate::miscs::height_txt_before_button;
|
||||
use crate::regex::Regexes;
|
||||
use crate::{
|
||||
GREEN, GUPAX_SELECT, LIGHT_GRAY, NODE_DB_DIR, NODE_DB_PATH_EMPTY, NODE_PATH_OK, RED, SPACE,
|
||||
};
|
||||
|
||||
pub fn slider_state_field(
|
||||
ui: &mut Ui,
|
||||
description: &str,
|
||||
hover_msg: &str,
|
||||
field: &mut u16,
|
||||
range: RangeInclusive<u16>,
|
||||
) {
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_sized(
|
||||
[0.0, height_txt_before_button(ui, &TextStyle::Body)],
|
||||
Label::new(description),
|
||||
);
|
||||
// not sure what's the right calculation to make
|
||||
ui.style_mut().spacing.slider_width = (ui.available_width()
|
||||
- ui.spacing().item_spacing.x * 4.0
|
||||
- ui.spacing().scroll.bar_width
|
||||
- SPACE * 2.0
|
||||
+ 2.0)
|
||||
.max(80.0);
|
||||
ui.add_sized(
|
||||
[0.0, height_txt_before_button(ui, &TextStyle::Body)],
|
||||
Slider::new(field, range),
|
||||
)
|
||||
.on_hover_text(hover_msg);
|
||||
});
|
||||
}
|
||||
|
||||
pub struct StateTextEdit<'a> {
|
||||
description: &'a str,
|
||||
max_ch: u8,
|
||||
help_msg: &'a str,
|
||||
validations: &'a [fn(&str) -> bool],
|
||||
text_edit_width: f32,
|
||||
text_style: TextStyle,
|
||||
}
|
||||
#[allow(unused)]
|
||||
impl<'a> StateTextEdit<'a> {
|
||||
pub fn new(ui: &Ui) -> Self {
|
||||
StateTextEdit {
|
||||
description: "",
|
||||
max_ch: 30,
|
||||
help_msg: "",
|
||||
validations: &[],
|
||||
text_edit_width: ui.text_style_height(&TextStyle::Body) * 18.0,
|
||||
text_style: TextStyle::Body,
|
||||
}
|
||||
}
|
||||
pub fn build(self, ui: &mut Ui, state_field: &mut String) -> bool {
|
||||
let mut valid = false;
|
||||
ui.horizontal_centered(|ui| {
|
||||
let color;
|
||||
let symbol;
|
||||
let mut input_validated = true;
|
||||
let len;
|
||||
let inside_space;
|
||||
for v in self.validations {
|
||||
if !v(state_field) {
|
||||
input_validated = false;
|
||||
}
|
||||
}
|
||||
if state_field.is_empty() {
|
||||
symbol = "➖";
|
||||
color = Color32::LIGHT_GRAY;
|
||||
} else if input_validated {
|
||||
symbol = "✔";
|
||||
color = Color32::from_rgb(100, 230, 100);
|
||||
valid = true;
|
||||
} else {
|
||||
symbol = "❌";
|
||||
color = Color32::from_rgb(230, 50, 50);
|
||||
}
|
||||
match self.max_ch {
|
||||
x if x >= 100 => {
|
||||
len = format!("{:03}", state_field.len());
|
||||
inside_space = "";
|
||||
}
|
||||
10..99 => {
|
||||
len = format!("{:02}", state_field.len());
|
||||
inside_space = " ";
|
||||
}
|
||||
_ => {
|
||||
len = format!("{}", state_field.len());
|
||||
inside_space = " ";
|
||||
}
|
||||
}
|
||||
let text = format!(
|
||||
"{}[{}{}/{}{}]{}",
|
||||
self.description, inside_space, len, self.max_ch, inside_space, symbol
|
||||
);
|
||||
ui.add_sized(
|
||||
[0.0, height_txt_before_button(ui, &self.text_style)],
|
||||
Label::new(RichText::new(text).color(color)),
|
||||
);
|
||||
// allocate the size to leave half of the total width free.
|
||||
ui.spacing_mut().text_edit_width = self.text_edit_width;
|
||||
ui.text_edit_singleline(state_field)
|
||||
.on_hover_text(self.help_msg);
|
||||
state_field.truncate(self.max_ch.into());
|
||||
});
|
||||
valid
|
||||
}
|
||||
pub fn description(mut self, description: &'a str) -> Self {
|
||||
self.description = description;
|
||||
self
|
||||
}
|
||||
pub fn max_ch(mut self, max_ch: u8) -> Self {
|
||||
self.max_ch = max_ch;
|
||||
self
|
||||
}
|
||||
pub fn help_msg(mut self, help_msg: &'a str) -> Self {
|
||||
self.help_msg = help_msg;
|
||||
self
|
||||
}
|
||||
pub fn validations(mut self, validations: &'a [fn(&str) -> bool]) -> Self {
|
||||
self.validations = validations;
|
||||
self
|
||||
}
|
||||
pub fn text_style(mut self, text_style: TextStyle) -> Self {
|
||||
self.text_style = text_style;
|
||||
self
|
||||
}
|
||||
pub fn text_edit_width(mut self, text_edit_width: f32) -> Self {
|
||||
self.text_edit_width = text_edit_width;
|
||||
self
|
||||
}
|
||||
pub fn text_edit_width_half_left(mut self, ui: &Ui) -> Self {
|
||||
self.text_edit_width = ui.available_width() / 2.0;
|
||||
self
|
||||
}
|
||||
pub fn text_edit_width_same_as_max_ch(mut self, ui: &Ui) -> Self {
|
||||
self.text_edit_width = ui.text_style_height(&self.text_style) * self.max_ch as f32;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// path to choose
|
||||
pub fn path_db_field(ui: &mut Ui, path: &mut String, file_window: &Arc<Mutex<FileWindow>>) {
|
||||
ui.horizontal(|ui| {
|
||||
let symbol;
|
||||
let color;
|
||||
let hover;
|
||||
if path.is_empty() {
|
||||
symbol = "➖";
|
||||
color = LIGHT_GRAY;
|
||||
hover = NODE_DB_PATH_EMPTY;
|
||||
} else if !Gupax::path_is_dir(path) {
|
||||
symbol = "❌";
|
||||
color = RED;
|
||||
hover = NODE_DB_DIR;
|
||||
} else {
|
||||
symbol = "✔";
|
||||
color = GREEN;
|
||||
hover = NODE_PATH_OK;
|
||||
}
|
||||
let text = ["Node Database Directory ", symbol].concat();
|
||||
ui.add_sized(
|
||||
[0.0, height_txt_before_button(ui, &TextStyle::Body)],
|
||||
Label::new(RichText::new(text).color(color)),
|
||||
);
|
||||
let window_busy = file_window.lock().unwrap().thread;
|
||||
ui.add_enabled_ui(!window_busy, |ui| {
|
||||
if ui.button("Open").on_hover_text(GUPAX_SELECT).clicked() {
|
||||
Gupax::spawn_file_window_thread(file_window, FileType::NodeDB);
|
||||
}
|
||||
ui.spacing_mut().text_edit_width = ui.available_width();
|
||||
ui.text_edit_singleline(path).on_hover_text(hover);
|
||||
});
|
||||
});
|
||||
}
|
||||
pub fn monero_address_field(address: &mut String, ui: &mut Ui, hover: &str) {
|
||||
ui.group(|ui| {
|
||||
let text;
|
||||
let color;
|
||||
let len = format!("{:02}", address.len());
|
||||
if address.is_empty() {
|
||||
text = format!("Monero Address [{}/95] ➖", len);
|
||||
color = Color32::LIGHT_GRAY;
|
||||
} else if Regexes::addr_ok(address) {
|
||||
text = format!("Monero Address [{}/95] ✔", len);
|
||||
color = Color32::from_rgb(100, 230, 100);
|
||||
} else {
|
||||
text = format!("Monero Address [{}/95] ❌", len);
|
||||
color = Color32::from_rgb(230, 50, 50);
|
||||
}
|
||||
ui.style_mut().spacing.text_edit_width = ui.available_width();
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(text).color(color));
|
||||
// ui.set_max_width(95.0 * 3.0);
|
||||
ui.add_space(SPACE);
|
||||
ui.add(
|
||||
TextEdit::hint_text(TextEdit::singleline(address), "4...")
|
||||
.horizontal_align(egui::Align::Center),
|
||||
)
|
||||
.on_hover_text(hover);
|
||||
address.truncate(95);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -5,10 +5,13 @@ use crate::components::gupax::*;
|
|||
use crate::components::update::Update;
|
||||
use crate::components::update::check_binary_path;
|
||||
use crate::disk::state::*;
|
||||
use crate::miscs::height_txt_before_button;
|
||||
use log::debug;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use strum::EnumCount;
|
||||
use strum::IntoEnumIterator;
|
||||
impl Gupax {
|
||||
#[inline(always)] // called once
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
@ -20,7 +23,6 @@ impl Gupax {
|
|||
file_window: &Arc<Mutex<FileWindow>>,
|
||||
error_state: &mut ErrorState,
|
||||
restart: &Arc<Mutex<Restart>>,
|
||||
size: Vec2,
|
||||
_frame: &mut eframe::Frame,
|
||||
_ctx: &egui::Context,
|
||||
ui: &mut egui::Ui,
|
||||
|
@ -28,33 +30,30 @@ impl Gupax {
|
|||
) {
|
||||
// Update button + Progress bar
|
||||
debug!("Gupaxx Tab | Rendering [Update] button + progress bar");
|
||||
let height_font = ui.text_style_height(&TextStyle::Body);
|
||||
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||
ui.style_mut().spacing.item_spacing = [height_font, height_font].into();
|
||||
ui.group(|ui| {
|
||||
let button = if self.simple {
|
||||
size.y / 5.0
|
||||
} else {
|
||||
size.y / 15.0
|
||||
};
|
||||
let height = if self.simple {
|
||||
size.y / 5.0
|
||||
} else {
|
||||
size.y / 10.0
|
||||
};
|
||||
let width = size.x - SPACE;
|
||||
let updating = *update.lock().unwrap().updating.lock().unwrap();
|
||||
ui.vertical(|ui| {
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.add_space(height_font);
|
||||
ui.style_mut().spacing.button_padding = ui.style().spacing.button_padding * 3.0;
|
||||
// If [Gupax] is being built for a Linux distro,
|
||||
// disable built-in updating completely.
|
||||
#[cfg(feature = "distro")]
|
||||
ui.disable();
|
||||
#[cfg(feature = "distro")]
|
||||
ui.add_sized([width, button], Button::new("Updates are disabled"))
|
||||
// ui.add_sized([width, button], Button::new("Updates are disabled"))
|
||||
// .on_disabled_hover_text(DISTRO_NO_UPDATE);
|
||||
ui.button("Updates are disabled")
|
||||
.on_disabled_hover_text(DISTRO_NO_UPDATE);
|
||||
#[cfg(not(feature = "distro"))]
|
||||
ui.add_enabled_ui(!updating && *restart.lock().unwrap() == Restart::No, |ui| {
|
||||
#[cfg(not(feature = "distro"))]
|
||||
// if ui
|
||||
// .add_sized([width, button], Button::new("Check for updates"))
|
||||
if ui
|
||||
.add_sized([width, button], Button::new("Check for updates"))
|
||||
.button("Check for updates")
|
||||
.on_hover_text(GUPAX_UPDATE)
|
||||
.clicked()
|
||||
{
|
||||
|
@ -68,8 +67,6 @@ impl Gupax {
|
|||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
ui.vertical(|ui| {
|
||||
ui.add_enabled_ui(updating, |ui| {
|
||||
let prog = *update.lock().unwrap().prog.lock().unwrap();
|
||||
let msg = format!(
|
||||
|
@ -78,479 +75,346 @@ impl Gupax {
|
|||
prog,
|
||||
"%"
|
||||
);
|
||||
ui.add_sized([width, height * 1.4], Label::new(RichText::new(msg)));
|
||||
let height = height / 2.0;
|
||||
let size = vec2(width, height);
|
||||
ui.label(msg);
|
||||
if updating {
|
||||
ui.add_sized(size, Spinner::new().size(height));
|
||||
ui.spinner();
|
||||
} else {
|
||||
ui.add_sized(size, Label::new("..."));
|
||||
ui.label("...");
|
||||
}
|
||||
ui.add_sized(
|
||||
size,
|
||||
ProgressBar::new(
|
||||
update.lock().unwrap().prog.lock().unwrap().round() / 100.0,
|
||||
),
|
||||
);
|
||||
ui.add(ProgressBar::new(
|
||||
update.lock().unwrap().prog.lock().unwrap().round() / 100.0,
|
||||
));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
debug!("Gupaxx Tab | Rendering bool buttons");
|
||||
ui.horizontal(|ui| {
|
||||
ui.group(|ui| {
|
||||
egui::ScrollArea::horizontal().show(ui, |ui| {
|
||||
let width = (size.x - SPACE * 17.0) / 8.0;
|
||||
let height = if self.simple {
|
||||
size.y / 10.0
|
||||
} else {
|
||||
size.y / 15.0
|
||||
};
|
||||
let size = vec2(width, height);
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Small);
|
||||
ui.add_sized(size, Checkbox::new(&mut self.auto_update, "Auto-Update"))
|
||||
.on_hover_text(GUPAX_AUTO_UPDATE);
|
||||
ui.separator();
|
||||
ui.add_sized(size, Checkbox::new(&mut self.bundled, "Bundle"))
|
||||
.on_hover_text(GUPAX_BUNDLED_UPDATE);
|
||||
ui.separator();
|
||||
ui.add_sized(size, Checkbox::new(&mut self.auto_node, "Auto-Node"))
|
||||
.on_hover_text(GUPAX_AUTO_NODE);
|
||||
ui.separator();
|
||||
ui.add_sized(size, Checkbox::new(&mut self.auto_p2pool, "Auto-P2Pool"))
|
||||
.on_hover_text(GUPAX_AUTO_P2POOL);
|
||||
ui.separator();
|
||||
ui.add_sized(size, Checkbox::new(&mut self.auto_xmrig, "Auto-XMRig"))
|
||||
.on_hover_text(GUPAX_AUTO_XMRIG);
|
||||
ui.separator();
|
||||
ui.add_sized(size, Checkbox::new(&mut self.auto_xp, "Auto-Proxy"))
|
||||
.on_hover_text(GUPAX_AUTO_XMRIG_PROXY);
|
||||
ui.separator();
|
||||
ui.add_sized(size, Checkbox::new(&mut self.auto_xvb, "Auto-XvB"))
|
||||
.on_hover_text(GUPAX_AUTO_XVB);
|
||||
ui.separator();
|
||||
ui.add_sized(
|
||||
size,
|
||||
Checkbox::new(&mut self.ask_before_quit, "Confirm quit"),
|
||||
)
|
||||
.on_hover_text(GUPAX_ASK_BEFORE_QUIT);
|
||||
ui.separator();
|
||||
ui.add_sized(
|
||||
size,
|
||||
Checkbox::new(&mut self.save_before_quit, "Save on quit"),
|
||||
)
|
||||
.on_hover_text(GUPAX_SAVE_BEFORE_QUIT);
|
||||
});
|
||||
// debug!("Gupaxx Tab | Rendering bool buttons");
|
||||
ui.group(|ui| {
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.add(Label::new(
|
||||
RichText::new("Default Behaviour")
|
||||
.underline()
|
||||
.color(LIGHT_GRAY),
|
||||
))
|
||||
});
|
||||
ui.separator();
|
||||
self.horizontal_flex_auto_start(ui, AutoStart::ALL);
|
||||
});
|
||||
|
||||
if self.simple {
|
||||
return;
|
||||
}
|
||||
|
||||
debug!("Gupaxx Tab | Rendering P2Pool/XMRig path selection");
|
||||
// P2Pool/XMRig binary path selection
|
||||
debug!("Gupaxx Tab | Rendering Node/P2Pool/XMRig/XMRig-Proxy path selection");
|
||||
// need to clone bool so file_window is not locked across a thread
|
||||
let window_busy = file_window.lock().unwrap().thread.to_owned();
|
||||
let height = size.y / 28.0;
|
||||
let text_edit = (ui.available_width() / 10.0) - SPACE;
|
||||
ui.group(|ui| {
|
||||
ui.add_sized(
|
||||
[ui.available_width(), height / 2.0],
|
||||
Label::new(
|
||||
RichText::new("Node/P2Pool/XMRig/XMRig-Proxy PATHs")
|
||||
.underline()
|
||||
.color(LIGHT_GRAY),
|
||||
),
|
||||
)
|
||||
.on_hover_text("Gupaxx is online");
|
||||
ui.separator();
|
||||
ui.horizontal(|ui| {
|
||||
if self.node_path.is_empty() {
|
||||
ui.add_sized(
|
||||
[text_edit, height],
|
||||
Label::new(RichText::new("Node Binary Path ➖").color(LIGHT_GRAY)),
|
||||
)
|
||||
.on_hover_text(NODE_PATH_EMPTY);
|
||||
} else if !Self::path_is_file(&self.node_path) {
|
||||
ui.add_sized(
|
||||
[text_edit, height],
|
||||
Label::new(RichText::new("Node Binary Path ❌").color(RED)),
|
||||
)
|
||||
.on_hover_text(NODE_PATH_NOT_FILE);
|
||||
} else if !check_binary_path(&self.node_path, ProcessName::Node) {
|
||||
ui.add_sized(
|
||||
[text_edit, height],
|
||||
Label::new(RichText::new("Node Binary Path ❌").color(RED)),
|
||||
)
|
||||
.on_hover_text(NODE_PATH_NOT_VALID);
|
||||
} else {
|
||||
ui.add_sized(
|
||||
[text_edit, height],
|
||||
Label::new(RichText::new("Node Binary Path ✔").color(GREEN)),
|
||||
)
|
||||
.on_hover_text(NODE_PATH_OK);
|
||||
}
|
||||
ui.spacing_mut().text_edit_width = ui.available_width() - SPACE;
|
||||
ui.add_enabled_ui(!window_busy, |ui| {
|
||||
if ui.button("Open").on_hover_text(GUPAX_SELECT).clicked() {
|
||||
Self::spawn_file_window_thread(file_window, FileType::Node);
|
||||
}
|
||||
ui.add_sized(
|
||||
[ui.available_width(), height],
|
||||
TextEdit::singleline(&mut self.node_path),
|
||||
)
|
||||
.on_hover_text(GUPAX_PATH_NODE);
|
||||
});
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
if self.p2pool_path.is_empty() {
|
||||
ui.add_sized(
|
||||
[text_edit, height],
|
||||
Label::new(RichText::new("P2Pool Binary Path ➖").color(LIGHT_GRAY)),
|
||||
)
|
||||
.on_hover_text(P2POOL_PATH_EMPTY);
|
||||
} else if !Self::path_is_file(&self.p2pool_path) {
|
||||
ui.add_sized(
|
||||
[text_edit, height],
|
||||
Label::new(RichText::new("P2Pool Binary Path ❌").color(RED)),
|
||||
)
|
||||
.on_hover_text(P2POOL_PATH_NOT_FILE);
|
||||
} else if !check_binary_path(&self.p2pool_path, ProcessName::P2pool) {
|
||||
ui.add_sized(
|
||||
[text_edit, height],
|
||||
Label::new(RichText::new("P2Pool Binary Path ❌").color(RED)),
|
||||
)
|
||||
.on_hover_text(P2POOL_PATH_NOT_VALID);
|
||||
} else {
|
||||
ui.add_sized(
|
||||
[text_edit, height],
|
||||
Label::new(RichText::new("P2Pool Binary Path ✔").color(GREEN)),
|
||||
)
|
||||
.on_hover_text(P2POOL_PATH_OK);
|
||||
}
|
||||
ui.spacing_mut().text_edit_width = ui.available_width() - SPACE;
|
||||
ui.add_enabled_ui(!window_busy, |ui| {
|
||||
if ui.button("Open").on_hover_text(GUPAX_SELECT).clicked() {
|
||||
Self::spawn_file_window_thread(file_window, FileType::P2pool);
|
||||
}
|
||||
ui.add_sized(
|
||||
[ui.available_width(), height],
|
||||
TextEdit::singleline(&mut self.p2pool_path),
|
||||
)
|
||||
.on_hover_text(GUPAX_PATH_P2POOL);
|
||||
});
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
if self.xmrig_path.is_empty() {
|
||||
ui.add_sized(
|
||||
[text_edit, height],
|
||||
Label::new(RichText::new("XMRig Binary Path ➖").color(LIGHT_GRAY)),
|
||||
)
|
||||
.on_hover_text(XMRIG_PATH_EMPTY);
|
||||
} else if !Self::path_is_file(&self.xmrig_path) {
|
||||
ui.add_sized(
|
||||
[text_edit, height],
|
||||
Label::new(RichText::new("XMRig Binary Path ❌").color(RED)),
|
||||
)
|
||||
.on_hover_text(XMRIG_PATH_NOT_FILE);
|
||||
} else if !check_binary_path(&self.xmrig_path, ProcessName::Xmrig) {
|
||||
ui.add_sized(
|
||||
[text_edit, height],
|
||||
Label::new(RichText::new("XMRig Binary Path ❌").color(RED)),
|
||||
)
|
||||
.on_hover_text(XMRIG_PATH_NOT_VALID);
|
||||
} else {
|
||||
ui.add_sized(
|
||||
[text_edit, height],
|
||||
Label::new(RichText::new("XMRig Binary Path ✔").color(GREEN)),
|
||||
)
|
||||
.on_hover_text(XMRIG_PATH_OK);
|
||||
}
|
||||
ui.spacing_mut().text_edit_width = ui.available_width() - SPACE;
|
||||
ui.add_enabled_ui(!window_busy, |ui| {
|
||||
if ui.button("Open").on_hover_text(GUPAX_SELECT).clicked() {
|
||||
Self::spawn_file_window_thread(file_window, FileType::Xmrig);
|
||||
}
|
||||
ui.add_sized(
|
||||
[ui.available_width(), height],
|
||||
TextEdit::singleline(&mut self.xmrig_path),
|
||||
)
|
||||
.on_hover_text(GUPAX_PATH_XMRIG);
|
||||
});
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
if self.xmrig_proxy_path.is_empty() {
|
||||
ui.add_sized(
|
||||
[text_edit, height],
|
||||
Label::new(
|
||||
RichText::new("XMRig-Proxy Binary Path ➖").color(LIGHT_GRAY),
|
||||
),
|
||||
)
|
||||
.on_hover_text(XMRIG_PROXY_PATH_EMPTY);
|
||||
} else if !Self::path_is_file(&self.xmrig_proxy_path) {
|
||||
ui.add_sized(
|
||||
[text_edit, height],
|
||||
Label::new(RichText::new("XMRig-Proxy Binary Path ❌").color(RED)),
|
||||
)
|
||||
.on_hover_text(XMRIG_PROXY_PATH_NOT_FILE);
|
||||
} else if !crate::components::update::check_binary_path(
|
||||
&self.xmrig_proxy_path,
|
||||
ProcessName::XmrigProxy,
|
||||
) {
|
||||
ui.add_sized(
|
||||
[text_edit, height],
|
||||
Label::new(RichText::new("XMRig-Proxy Binary Path ❌").color(RED)),
|
||||
)
|
||||
.on_hover_text(XMRIG_PROXY_PATH_NOT_VALID);
|
||||
} else {
|
||||
ui.add_sized(
|
||||
[text_edit, height],
|
||||
Label::new(RichText::new("XMRig-Proxy Binary Path ✔").color(GREEN)),
|
||||
)
|
||||
.on_hover_text(XMRIG_PROXY_PATH_OK);
|
||||
}
|
||||
ui.spacing_mut().text_edit_width = ui.available_width() - SPACE;
|
||||
ui.add_enabled_ui(!window_busy, |ui| {
|
||||
if ui.button("Open").on_hover_text(GUPAX_SELECT).clicked() {
|
||||
Self::spawn_file_window_thread(file_window, FileType::XmrigProxy);
|
||||
}
|
||||
ui.add_sized(
|
||||
[ui.available_width(), height],
|
||||
TextEdit::singleline(&mut self.xmrig_proxy_path),
|
||||
)
|
||||
.on_hover_text(GUPAX_PATH_XMRIG_PROXY);
|
||||
ui.push_id(2, |ui| {
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.add(Label::new(
|
||||
RichText::new("Node/P2Pool/XMRig/XMRig-Proxy PATHs")
|
||||
.underline()
|
||||
.color(LIGHT_GRAY),
|
||||
))
|
||||
.on_hover_text("Gupaxx is online");
|
||||
});
|
||||
ui.separator();
|
||||
ScrollArea::horizontal().show(ui, |ui| {
|
||||
ui.vertical(|ui| {
|
||||
BundledProcess::iter().for_each(|name| {
|
||||
path_binary(
|
||||
self.path_binary(&name),
|
||||
name.process_name(),
|
||||
ui,
|
||||
window_busy,
|
||||
file_window,
|
||||
)
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
let mut guard = file_window.lock().unwrap();
|
||||
if guard.picked_p2pool {
|
||||
self.p2pool_path.clone_from(&guard.p2pool_path);
|
||||
guard.picked_p2pool = false;
|
||||
}
|
||||
if guard.picked_xmrig {
|
||||
self.xmrig_path.clone_from(&guard.xmrig_path);
|
||||
guard.picked_xmrig = false;
|
||||
}
|
||||
if guard.picked_xp {
|
||||
self.xmrig_proxy_path.clone_from(&guard.xmrig_proxy_path);
|
||||
guard.picked_xp = false;
|
||||
}
|
||||
if guard.picked_node {
|
||||
self.node_path.clone_from(&guard.node_path);
|
||||
guard.picked_node = false;
|
||||
}
|
||||
drop(guard);
|
||||
});
|
||||
let mut guard = file_window.lock().unwrap();
|
||||
if guard.picked_p2pool {
|
||||
self.p2pool_path.clone_from(&guard.p2pool_path);
|
||||
guard.picked_p2pool = false;
|
||||
}
|
||||
if guard.picked_xmrig {
|
||||
self.xmrig_path.clone_from(&guard.xmrig_path);
|
||||
guard.picked_xmrig = false;
|
||||
}
|
||||
if guard.picked_xp {
|
||||
self.xmrig_proxy_path.clone_from(&guard.xmrig_proxy_path);
|
||||
guard.picked_xp = false;
|
||||
}
|
||||
if guard.picked_node {
|
||||
self.node_path.clone_from(&guard.node_path);
|
||||
guard.picked_node = false;
|
||||
}
|
||||
drop(guard);
|
||||
|
||||
let height = ui.available_height() / 6.0;
|
||||
|
||||
// Saved [Tab]
|
||||
debug!("Gupaxx Tab | Rendering [Tab] selector");
|
||||
ui.group(|ui| {
|
||||
let width = (size.x / 7.0) - (SPACE * 1.93);
|
||||
let size = vec2(width, height);
|
||||
ui.add_sized(
|
||||
[ui.available_width(), height / 2.0],
|
||||
Label::new(RichText::new("Default Tab").underline().color(LIGHT_GRAY)),
|
||||
)
|
||||
.on_hover_text(GUPAX_TAB);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.add(Label::new(
|
||||
RichText::new("Default Tab").underline().color(LIGHT_GRAY),
|
||||
))
|
||||
.on_hover_text(GUPAX_TAB);
|
||||
});
|
||||
ui.separator();
|
||||
ui.horizontal(|ui| {
|
||||
if ui
|
||||
.add_sized(size, SelectableLabel::new(self.tab == Tab::About, "About"))
|
||||
.on_hover_text(GUPAX_TAB_ABOUT)
|
||||
.clicked()
|
||||
{
|
||||
self.tab = Tab::About;
|
||||
}
|
||||
ui.separator();
|
||||
if ui
|
||||
.add_sized(
|
||||
size,
|
||||
SelectableLabel::new(self.tab == Tab::Status, "Status"),
|
||||
)
|
||||
.on_hover_text(GUPAX_TAB_STATUS)
|
||||
.clicked()
|
||||
{
|
||||
self.tab = Tab::Status;
|
||||
}
|
||||
ui.separator();
|
||||
if ui
|
||||
.add_sized(size, SelectableLabel::new(self.tab == Tab::Gupax, "Gupaxx"))
|
||||
.on_hover_text(GUPAX_TAB_GUPAX)
|
||||
.clicked()
|
||||
{
|
||||
self.tab = Tab::Gupax;
|
||||
}
|
||||
ui.separator();
|
||||
if ui
|
||||
.add_sized(size, SelectableLabel::new(self.tab == Tab::Node, "Node"))
|
||||
.on_hover_text(GUPAX_TAB_NODE)
|
||||
.clicked()
|
||||
{
|
||||
self.tab = Tab::Node;
|
||||
}
|
||||
ui.separator();
|
||||
if ui
|
||||
.add_sized(
|
||||
size,
|
||||
SelectableLabel::new(self.tab == Tab::P2pool, "P2Pool"),
|
||||
)
|
||||
.on_hover_text(GUPAX_TAB_P2POOL)
|
||||
.clicked()
|
||||
{
|
||||
self.tab = Tab::P2pool;
|
||||
}
|
||||
ui.separator();
|
||||
if ui
|
||||
.add_sized(size, SelectableLabel::new(self.tab == Tab::Xmrig, "XMRig"))
|
||||
.on_hover_text(GUPAX_TAB_XMRIG)
|
||||
.clicked()
|
||||
{
|
||||
self.tab = Tab::Xmrig;
|
||||
}
|
||||
if ui
|
||||
.add_sized(size, SelectableLabel::new(self.tab == Tab::Xvb, "XvB"))
|
||||
.on_hover_text(GUPAX_TAB_XVB)
|
||||
.clicked()
|
||||
{
|
||||
self.tab = Tab::Xvb;
|
||||
}
|
||||
})
|
||||
ui.push_id(1, |ui| {
|
||||
ScrollArea::horizontal().show(ui, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
let width = (ui.available_width() / Tab::COUNT as f32)
|
||||
- (ui.spacing().button_padding.y * 2.0
|
||||
+ ui.spacing().item_spacing.x)
|
||||
- SPACE;
|
||||
Tab::iter().enumerate().for_each(|(count, tab)| {
|
||||
if ui
|
||||
.add_sized(
|
||||
[width, height_txt_before_button(ui, &TextStyle::Button)],
|
||||
SelectableLabel::new(self.tab == tab, tab.to_string()),
|
||||
)
|
||||
.on_hover_text(tab.msg_default_tab())
|
||||
.clicked()
|
||||
{
|
||||
self.tab = tab;
|
||||
}
|
||||
|
||||
if count + 1 != Tab::COUNT {
|
||||
ui.separator();
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Gupax App resolution sliders
|
||||
debug!("Gupaxx Tab | Rendering resolution sliders");
|
||||
ui.group(|ui| {
|
||||
ui.add_sized(
|
||||
[ui.available_width(), height / 2.0],
|
||||
Label::new(
|
||||
RichText::new("Width/Height Adjust")
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.add(Label::new(
|
||||
RichText::new("Width/Height/Scaling Adjustment")
|
||||
.underline()
|
||||
.color(LIGHT_GRAY),
|
||||
),
|
||||
)
|
||||
.on_hover_text(GUPAX_ADJUST);
|
||||
ui.separator();
|
||||
ui.vertical(|ui| {
|
||||
let width = size.x / 10.0;
|
||||
ui.spacing_mut().icon_width = width / 25.0;
|
||||
ui.spacing_mut().slider_width = width * 7.6;
|
||||
match self.ratio {
|
||||
Ratio::None => (),
|
||||
Ratio::Width => {
|
||||
let width = self.selected_width as f64;
|
||||
let height = (width / 1.333).round();
|
||||
self.selected_height = height as u16;
|
||||
}
|
||||
Ratio::Height => {
|
||||
let height = self.selected_height as f64;
|
||||
let width = (height * 1.333).round();
|
||||
self.selected_width = width as u16;
|
||||
}
|
||||
}
|
||||
let height = height / 3.5;
|
||||
let size = vec2(width, height);
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_enabled_ui(self.ratio != Ratio::Height, |ui| {
|
||||
ui.add_sized(
|
||||
size,
|
||||
Label::new(format!(
|
||||
" Width [{}-{}]:",
|
||||
APP_MIN_WIDTH as u16, APP_MAX_WIDTH as u16
|
||||
)),
|
||||
);
|
||||
ui.add_sized(
|
||||
size,
|
||||
Slider::new(
|
||||
&mut self.selected_width,
|
||||
APP_MIN_WIDTH as u16..=APP_MAX_WIDTH as u16,
|
||||
),
|
||||
)
|
||||
.on_hover_text(GUPAX_WIDTH);
|
||||
});
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_enabled_ui(self.ratio != Ratio::Width, |ui| {
|
||||
ui.add_sized(
|
||||
size,
|
||||
Label::new(format!(
|
||||
"Height [{}-{}]:",
|
||||
APP_MIN_HEIGHT as u16, APP_MAX_HEIGHT as u16
|
||||
)),
|
||||
);
|
||||
ui.add_sized(
|
||||
size,
|
||||
Slider::new(
|
||||
&mut self.selected_height,
|
||||
APP_MIN_HEIGHT as u16..=APP_MAX_HEIGHT as u16,
|
||||
),
|
||||
)
|
||||
.on_hover_text(GUPAX_HEIGHT);
|
||||
});
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_sized(
|
||||
size,
|
||||
Label::new(format!("Scaling [{APP_MIN_SCALE}..{APP_MAX_SCALE}]:")),
|
||||
);
|
||||
ui.add_sized(
|
||||
size,
|
||||
Slider::new(&mut self.selected_scale, APP_MIN_SCALE..=APP_MAX_SCALE)
|
||||
.step_by(0.1),
|
||||
)
|
||||
.on_hover_text(GUPAX_SCALE);
|
||||
});
|
||||
))
|
||||
.on_hover_text(GUPAX_ADJUST);
|
||||
ui.separator();
|
||||
});
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Button);
|
||||
ui.separator();
|
||||
// Width/Height locks
|
||||
ui.horizontal(|ui| {
|
||||
use Ratio::*;
|
||||
let width = (size.x / 4.0) - (SPACE * 1.5);
|
||||
let size = vec2(width, height);
|
||||
if ui
|
||||
.add_sized(
|
||||
size,
|
||||
SelectableLabel::new(self.ratio == Width, "Lock to width"),
|
||||
)
|
||||
.on_hover_text(GUPAX_LOCK_WIDTH)
|
||||
.clicked()
|
||||
{
|
||||
self.ratio = Width;
|
||||
}
|
||||
ui.separator();
|
||||
if ui
|
||||
.add_sized(
|
||||
size,
|
||||
SelectableLabel::new(self.ratio == Height, "Lock to height"),
|
||||
)
|
||||
.on_hover_text(GUPAX_LOCK_HEIGHT)
|
||||
.clicked()
|
||||
{
|
||||
self.ratio = Height;
|
||||
}
|
||||
ui.separator();
|
||||
if ui
|
||||
.add_sized(size, SelectableLabel::new(self.ratio == None, "No lock"))
|
||||
.on_hover_text(GUPAX_NO_LOCK)
|
||||
.clicked()
|
||||
{
|
||||
self.ratio = None;
|
||||
}
|
||||
if ui
|
||||
.add_sized(size, Button::new("Set"))
|
||||
.on_hover_text(GUPAX_SET)
|
||||
.clicked()
|
||||
{
|
||||
let size =
|
||||
Vec2::new(self.selected_width as f32, self.selected_height as f32);
|
||||
ui.ctx()
|
||||
.send_viewport_cmd(egui::viewport::ViewportCommand::InnerSize(size));
|
||||
*must_resize = true;
|
||||
}
|
||||
})
|
||||
ScrollArea::horizontal().show(ui, |ui| {
|
||||
ui.vertical(|ui| {
|
||||
match self.ratio {
|
||||
Ratio::None => (),
|
||||
Ratio::Width => {
|
||||
let width = self.selected_width as f64;
|
||||
let height = (width / 1.333).round();
|
||||
self.selected_height = height as u16;
|
||||
}
|
||||
Ratio::Height => {
|
||||
let height = self.selected_height as f64;
|
||||
let width = (height * 1.333).round();
|
||||
self.selected_width = width as u16;
|
||||
}
|
||||
}
|
||||
// let height = height / 3.5;
|
||||
// let size = vec2(width, height);
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_enabled_ui(self.ratio != Ratio::Height, |ui| {
|
||||
ui.label(format!(
|
||||
" Width [{}-{}]:",
|
||||
APP_MIN_WIDTH as u16, APP_MAX_WIDTH as u16
|
||||
));
|
||||
ui.add(Slider::new(
|
||||
&mut self.selected_width,
|
||||
APP_MIN_WIDTH as u16..=APP_MAX_WIDTH as u16,
|
||||
))
|
||||
.on_hover_text(GUPAX_WIDTH);
|
||||
});
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_enabled_ui(self.ratio != Ratio::Width, |ui| {
|
||||
ui.label(format!(
|
||||
" Height [{}-{}]:",
|
||||
APP_MIN_HEIGHT as u16, APP_MAX_HEIGHT as u16
|
||||
));
|
||||
ui.add(Slider::new(
|
||||
&mut self.selected_height,
|
||||
APP_MIN_HEIGHT as u16..=APP_MAX_HEIGHT as u16,
|
||||
))
|
||||
.on_hover_text(GUPAX_HEIGHT);
|
||||
});
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(format!(" Scaling [{APP_MIN_SCALE}..{APP_MAX_SCALE}]:"));
|
||||
ui.add(
|
||||
Slider::new(
|
||||
&mut self.selected_scale,
|
||||
APP_MIN_SCALE..=APP_MAX_SCALE,
|
||||
)
|
||||
.step_by(0.1),
|
||||
)
|
||||
.on_hover_text(GUPAX_SCALE);
|
||||
});
|
||||
});
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Button);
|
||||
ui.separator();
|
||||
// Width/Height locks
|
||||
ui.vertical(|ui| {
|
||||
use Ratio::*;
|
||||
ui.horizontal(|ui| {
|
||||
if ui
|
||||
.selectable_label(self.ratio == Width, "Lock to width")
|
||||
.on_hover_text(GUPAX_LOCK_WIDTH)
|
||||
.clicked()
|
||||
{
|
||||
self.ratio = Width;
|
||||
}
|
||||
ui.separator();
|
||||
if ui
|
||||
.selectable_label(self.ratio == Height, "Lock to height")
|
||||
.on_hover_text(GUPAX_LOCK_HEIGHT)
|
||||
.clicked()
|
||||
{
|
||||
self.ratio = Height;
|
||||
}
|
||||
ui.separator();
|
||||
if ui
|
||||
.selectable_label(self.ratio == None, "No lock")
|
||||
.on_hover_text(GUPAX_NO_LOCK)
|
||||
.clicked()
|
||||
{
|
||||
self.ratio = None;
|
||||
}
|
||||
ui.separator();
|
||||
if ui.button("Set").on_hover_text(GUPAX_SET).clicked() {
|
||||
let size = Vec2::new(
|
||||
self.selected_width as f32,
|
||||
self.selected_height as f32,
|
||||
);
|
||||
ui.ctx().send_viewport_cmd(
|
||||
egui::viewport::ViewportCommand::InnerSize(size),
|
||||
);
|
||||
*must_resize = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
/// widget: AutoStart variant and selectable label (true) or checkbox (false)
|
||||
pub fn horizontal_flex_auto_start(&mut self, ui: &mut Ui, auto_starts: &[AutoStart]) {
|
||||
let text_style = TextStyle::Button;
|
||||
// let height = ui.style().text_styles.get(&text_style).unwrap().size;
|
||||
ui.style_mut().override_text_style = Some(text_style);
|
||||
// width = (width - / number of tab) - (space between widget * 2.0 + space of separator / 2.0)
|
||||
// ui.style_mut().spacing.item_spacing.x = 4.0;
|
||||
let spacing = 2.0;
|
||||
// ui.with_layout(egui::Layout::left_to_right(egui::Align::Center), |ui| {
|
||||
// ui.horizontal(|ui| {
|
||||
ScrollArea::horizontal().show(ui, |ui| {
|
||||
ui.with_layout(egui::Layout::left_to_right(egui::Align::Min), |ui| {
|
||||
let width = (((ui.available_width()) / auto_starts.len() as f32)
|
||||
- ((ui.style().spacing.item_spacing.x * 2.0) + (spacing / 2.0)))
|
||||
.max(0.0);
|
||||
// TODO: calculate minimum width needed, if ui.available width is less, show items on two lines, then on 3 etc..
|
||||
// checkbox padding + item spacing + text + separator
|
||||
let size = [width, 0.0];
|
||||
let len = auto_starts.iter().len();
|
||||
for (count, auto) in auto_starts.iter().enumerate() {
|
||||
ui.horizontal(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
let mut is_checked = self.auto.is_enabled(auto);
|
||||
let widget = Checkbox::new(&mut is_checked, auto.to_string());
|
||||
|
||||
if ui
|
||||
.add_sized(size, widget)
|
||||
.on_hover_text(auto.help_msg())
|
||||
.clicked()
|
||||
{
|
||||
self.auto.enable(auto, is_checked);
|
||||
}
|
||||
});
|
||||
// add a space to prevent selectable button to be at the same line as the end of the top bar. Make it the same spacing as separators.
|
||||
ui.add_space(spacing * 4.0);
|
||||
});
|
||||
if count + 1 != len {
|
||||
ui.add(Separator::default().spacing(spacing).vertical());
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
fn path_binary(
|
||||
path: &mut String,
|
||||
name: ProcessName,
|
||||
ui: &mut Ui,
|
||||
window_busy: bool,
|
||||
file_window: &Arc<Mutex<FileWindow>>,
|
||||
) {
|
||||
// align correctly even with different length of name by adapting the space just after.
|
||||
let flex_space = " ".repeat(
|
||||
ProcessName::iter()
|
||||
.enumerate()
|
||||
.max_by(|(_, a), (_, b)| {
|
||||
a.to_string()
|
||||
.len()
|
||||
.partial_cmp(&b.to_string().len())
|
||||
.expect("ProcessName should have values")
|
||||
})
|
||||
.expect("Iterator cant' be empty")
|
||||
.1
|
||||
.to_string()
|
||||
.len()
|
||||
- name.to_string().len()
|
||||
+ 1,
|
||||
);
|
||||
let msg = format!(" {name}{flex_space}Binary Path");
|
||||
// need to precise the height of text or there will be an misalignment with the button if it's bigger than the text.
|
||||
let height =
|
||||
(ui.style().spacing.button_padding.y * 2.0) + ui.text_style_height(&TextStyle::Body);
|
||||
ui.horizontal(|ui| {
|
||||
if path.is_empty() {
|
||||
ui.add_sized(
|
||||
[0.0, height],
|
||||
Label::new(RichText::new(msg + " ➖").color(LIGHT_GRAY)),
|
||||
)
|
||||
.on_hover_text(name.msg_binary_path_empty());
|
||||
} else if !Gupax::path_is_file(path) {
|
||||
ui.add_sized(
|
||||
[0.0, height],
|
||||
Label::new(RichText::new(msg + " ❌").color(RED)),
|
||||
)
|
||||
.on_hover_text(name.msg_binary_path_not_file());
|
||||
} else if !check_binary_path(path, name) {
|
||||
ui.add_sized(
|
||||
[0.0, height],
|
||||
Label::new(RichText::new(msg + " ❌").color(RED)),
|
||||
)
|
||||
.on_hover_text(name.msg_binary_path_invalid());
|
||||
} else {
|
||||
ui.add_sized(
|
||||
[0.0, height],
|
||||
Label::new(RichText::new(msg + " ✔").color(GREEN)),
|
||||
)
|
||||
.on_hover_text(name.msg_binary_path_ok());
|
||||
}
|
||||
ui.spacing_mut().text_edit_width = (ui.available_width() - SPACE).max(0.0);
|
||||
ui.add_enabled_ui(!window_busy, |ui| {
|
||||
if ui.button("Open").on_hover_text(GUPAX_SELECT).clicked() {
|
||||
Gupax::spawn_file_window_thread(
|
||||
file_window,
|
||||
name.file_type()
|
||||
.expect("XvB process should not be called in a function related to path"),
|
||||
);
|
||||
}
|
||||
ui.text_edit_singleline(path)
|
||||
.on_hover_text(name.msg_path_edit());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
use crate::app::Tab;
|
||||
use crate::app::eframe_impl::ProcessStatesGui;
|
||||
use crate::app::keys::KeyPressed;
|
||||
use crate::components::gupax::FileWindow;
|
||||
use crate::helper::ProcessName;
|
||||
use crate::regex::REGEXES;
|
||||
use crate::utils::constants::*;
|
||||
use common::state_edit_field::StateTextEdit;
|
||||
use egui::*;
|
||||
use log::debug;
|
||||
mod about;
|
||||
pub mod common;
|
||||
mod gupax;
|
||||
mod node;
|
||||
mod p2pool;
|
||||
|
@ -47,7 +51,6 @@ impl crate::app::App {
|
|||
self.max_threads,
|
||||
&self.gupax_p2pool_api,
|
||||
&self.benchmarks,
|
||||
self.size,
|
||||
ctx,
|
||||
ui,
|
||||
);
|
||||
|
@ -62,7 +65,6 @@ impl crate::app::App {
|
|||
&self.file_window,
|
||||
&mut self.error_state,
|
||||
&self.restart,
|
||||
self.size,
|
||||
frame,
|
||||
ctx,
|
||||
ui,
|
||||
|
@ -76,7 +78,6 @@ impl crate::app::App {
|
|||
&self.node,
|
||||
&self.node_api,
|
||||
&mut self.node_stdin,
|
||||
self.size,
|
||||
&self.file_window,
|
||||
ui,
|
||||
);
|
||||
|
@ -91,7 +92,6 @@ impl crate::app::App {
|
|||
&self.p2pool,
|
||||
&self.p2pool_api,
|
||||
&mut self.p2pool_stdin,
|
||||
self.size,
|
||||
ctx,
|
||||
ui,
|
||||
);
|
||||
|
@ -104,7 +104,6 @@ impl crate::app::App {
|
|||
&self.xmrig,
|
||||
&self.xmrig_api,
|
||||
&mut self.xmrig_stdin,
|
||||
self.size,
|
||||
ctx,
|
||||
ui,
|
||||
);
|
||||
|
@ -117,7 +116,6 @@ impl crate::app::App {
|
|||
&mut self.pool_vec,
|
||||
&self.xmrig_proxy_api,
|
||||
&mut self.xmrig_proxy_stdin,
|
||||
self.size,
|
||||
ui,
|
||||
);
|
||||
}
|
||||
|
@ -125,7 +123,6 @@ impl crate::app::App {
|
|||
debug!("App | Entering [XvB] Tab");
|
||||
crate::disk::state::Xvb::show(
|
||||
&mut self.state.xvb,
|
||||
self.size,
|
||||
&self.state.p2pool.address,
|
||||
ctx,
|
||||
ui,
|
||||
|
@ -139,3 +136,49 @@ impl crate::app::App {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Common widgets that will appears on multiple panels.
|
||||
|
||||
// header
|
||||
|
||||
// console
|
||||
|
||||
// sliders in/out peers/log
|
||||
|
||||
// menu node
|
||||
|
||||
// premade state edit field
|
||||
// return boolean to know if the field input is validated.
|
||||
fn rpc_port_field(field: &mut String, ui: &mut Ui) -> bool {
|
||||
StateTextEdit::new(ui)
|
||||
.description(" RPC PORT ")
|
||||
.max_ch(5)
|
||||
.help_msg(NODE_API_PORT)
|
||||
.validations(&[|x| REGEXES.port.is_match(x)])
|
||||
.build(ui, field)
|
||||
}
|
||||
fn zmq_port_field(field: &mut String, ui: &mut Ui) -> bool {
|
||||
StateTextEdit::new(ui)
|
||||
.description(" ZMQ PORT ")
|
||||
.max_ch(5)
|
||||
.help_msg(NODE_ZMQ_PORT)
|
||||
.validations(&[|x| REGEXES.port.is_match(x)])
|
||||
.build(ui, field)
|
||||
}
|
||||
fn rpc_bind_field(field: &mut String, ui: &mut Ui) -> bool {
|
||||
StateTextEdit::new(ui)
|
||||
.description("RPC BIND IP ")
|
||||
.max_ch(255)
|
||||
.help_msg(NODE_API_BIND)
|
||||
.validations(&[|x| REGEXES.ipv4.is_match(x), |x| REGEXES.domain.is_match(x)])
|
||||
.build(ui, field)
|
||||
}
|
||||
|
||||
fn zmq_bind_field(field: &mut String, ui: &mut Ui) -> bool {
|
||||
StateTextEdit::new(ui)
|
||||
.description("API BIND IP ")
|
||||
.max_ch(255)
|
||||
.help_msg(NODE_ZMQ_BIND)
|
||||
.validations(&[|x| REGEXES.ipv4.is_match(x), |x| REGEXES.domain.is_match(x)])
|
||||
.build(ui, field)
|
||||
}
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
use crate::app::panels::middle::common::console::{console, input_args_field, start_options_field};
|
||||
use crate::app::panels::middle::common::state_edit_field::{path_db_field, slider_state_field};
|
||||
use crate::app::panels::middle::{rpc_bind_field, rpc_port_field, zmq_bind_field, zmq_port_field};
|
||||
use crate::{
|
||||
GUPAX_SELECT, NODE_API_BIND, NODE_API_PORT, NODE_ARGUMENTS, NODE_DB_DIR, NODE_DB_PATH_EMPTY,
|
||||
NODE_DNS_BLOCKLIST, NODE_DNS_CHECKPOINT, NODE_INPUT, NODE_PATH_OK, NODE_PRUNNING, NODE_URL,
|
||||
NODE_ZMQ_BIND, NODE_ZMQ_PORT,
|
||||
NODE_ARGUMENTS, NODE_DNS_BLOCKLIST, NODE_DNS_CHECKPOINT, NODE_INPUT, NODE_PRUNNING, NODE_URL,
|
||||
};
|
||||
use egui::{Color32, Label, RichText, Slider, TextEdit, TextStyle, Ui, Vec2};
|
||||
use regex::Regex;
|
||||
use egui::{Label, TextStyle};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use log::debug;
|
||||
|
||||
use crate::components::gupax::{FileType, FileWindow};
|
||||
use crate::disk::state::{Gupax, Node};
|
||||
use crate::components::gupax::FileWindow;
|
||||
use crate::disk::state::Node;
|
||||
use crate::helper::Process;
|
||||
use crate::helper::node::PubNodeApi;
|
||||
use crate::regex::{REGEXES, num_lines};
|
||||
use crate::utils::constants::DARK_GRAY;
|
||||
use crate::{GREEN, LIGHT_GRAY, P2POOL_IN, P2POOL_LOG, P2POOL_OUT, RED, SPACE};
|
||||
use crate::{P2POOL_IN, P2POOL_LOG, P2POOL_OUT, SPACE};
|
||||
|
||||
impl Node {
|
||||
#[inline(always)] // called once
|
||||
|
@ -24,102 +22,47 @@ impl Node {
|
|||
process: &Arc<Mutex<Process>>,
|
||||
api: &Arc<Mutex<PubNodeApi>>,
|
||||
buffer: &mut String,
|
||||
size: Vec2,
|
||||
file_window: &Arc<Mutex<FileWindow>>,
|
||||
ui: &mut egui::Ui,
|
||||
) {
|
||||
let width = size.x;
|
||||
let height = size.y;
|
||||
let space_h = height / 48.0;
|
||||
let text_height = size.y / 25.0;
|
||||
let txt_description_width = size.x * 0.1;
|
||||
ui.style_mut().override_text_style = Some(TextStyle::Body);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.add_space(SPACE);
|
||||
ui.style_mut().override_text_style = Some(TextStyle::Heading);
|
||||
ui.hyperlink_to("Monerod", NODE_URL);
|
||||
ui.style_mut().override_text_style = None;
|
||||
ui.add(Label::new("C++ Monero Node"));
|
||||
ui.add_space(SPACE);
|
||||
});
|
||||
// console output for log
|
||||
debug!("Node Tab | Rendering [Console]");
|
||||
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.add_space(space_h);
|
||||
ui.style_mut().override_text_style = Some(TextStyle::Heading);
|
||||
ui.hyperlink_to("Monerod", NODE_URL);
|
||||
ui.style_mut().override_text_style = Some(TextStyle::Body);
|
||||
ui.add(Label::new("C++ Monero Node"));
|
||||
ui.add_space(space_h);
|
||||
});
|
||||
// console output for log
|
||||
debug!("Node Tab | Rendering [Console]");
|
||||
let text = &api.lock().unwrap().output;
|
||||
ui.group(|ui| {
|
||||
let text = &api.lock().unwrap().output;
|
||||
let nb_lines = num_lines(text);
|
||||
let height = size.y / 2.8;
|
||||
let width = (size.x - (space_h / 2.0)).max(0.0);
|
||||
egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| {
|
||||
ui.style_mut().override_text_style = Some(TextStyle::Small);
|
||||
egui::ScrollArea::vertical()
|
||||
.stick_to_bottom(true)
|
||||
.max_width(width)
|
||||
.max_height(height)
|
||||
.auto_shrink([false; 2])
|
||||
// .show_viewport(ui, |ui, _| {
|
||||
.show_rows(
|
||||
ui,
|
||||
ui.text_style_height(&TextStyle::Small),
|
||||
nb_lines,
|
||||
|ui, row_range| {
|
||||
for i in row_range {
|
||||
if let Some(line) = text.lines().nth(i) {
|
||||
ui.label(line);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
console(ui, text);
|
||||
if !self.simple {
|
||||
ui.separator();
|
||||
input_args_field(
|
||||
ui,
|
||||
buffer,
|
||||
process,
|
||||
r#"Commands: help, status, set_log <level>, diff"#,
|
||||
NODE_INPUT,
|
||||
);
|
||||
}
|
||||
});
|
||||
//---------------------------------------------------------------------------------------------------- [Advanced] Console
|
||||
if !self.simple {
|
||||
ui.separator();
|
||||
let response = ui
|
||||
.add_sized(
|
||||
[width, text_height],
|
||||
TextEdit::hint_text(
|
||||
TextEdit::singleline(buffer),
|
||||
r#"Commands: help, status, set_log <level>, diff"#,
|
||||
),
|
||||
)
|
||||
.on_hover_text(NODE_INPUT);
|
||||
// If the user pressed enter, dump buffer contents into the process STDIN
|
||||
if response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) {
|
||||
response.request_focus(); // Get focus back
|
||||
let buffer = std::mem::take(buffer); // Take buffer
|
||||
let mut process = process.lock().unwrap(); // Lock
|
||||
if process.is_alive() {
|
||||
process.input.push(buffer);
|
||||
} // Push only if alive
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Arguments
|
||||
debug!("Node Tab | Rendering [Arguments]");
|
||||
ui.group(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_sized(
|
||||
[txt_description_width, text_height],
|
||||
Label::new("Command arguments:"),
|
||||
);
|
||||
ui.add_sized(
|
||||
[ui.available_width(), text_height],
|
||||
TextEdit::hint_text(
|
||||
TextEdit::singleline(&mut self.arguments),
|
||||
r#"--zmq-pub tcp://127.0.0.1:18081"#,
|
||||
),
|
||||
)
|
||||
.on_hover_text(NODE_ARGUMENTS);
|
||||
self.arguments.truncate(1024);
|
||||
})
|
||||
});
|
||||
if !self.arguments.is_empty() {
|
||||
ui.disable();
|
||||
}
|
||||
start_options_field(
|
||||
ui,
|
||||
&mut self.arguments,
|
||||
r#"--zmq-pub tcp://127.0.0.1:18081"#,
|
||||
NODE_ARGUMENTS,
|
||||
);
|
||||
//---------------------------------------------------------------------------------------------------- Prunned checkbox
|
||||
ui.add_space(space_h);
|
||||
ui.style_mut().spacing.icon_width_inner = width / 45.0;
|
||||
ui.style_mut().spacing.icon_width = width / 35.0;
|
||||
ui.style_mut().spacing.icon_spacing = space_h;
|
||||
ui.add_space(SPACE);
|
||||
debug!("Node Tab | Rendering DNS and Prunning buttons");
|
||||
ui.horizontal(|ui| {
|
||||
ui.group(|ui| {
|
||||
|
@ -134,276 +77,69 @@ impl Node {
|
|||
});
|
||||
});
|
||||
|
||||
ui.add_space(space_h);
|
||||
// idea
|
||||
// need to warn the user if local firewall is blocking port
|
||||
// need to warn the user if NAT is blocking port
|
||||
// need to show local ip address
|
||||
// need to show public ip
|
||||
// text edit width is 4x bigger than description. Which makes half of the total width on screen less a space.
|
||||
|
||||
// (width - (width - ui.available_width()) - (ui.spacing().item_spacing.x * 4.5))
|
||||
// / 2.0;
|
||||
ui.add_space(SPACE);
|
||||
// // idea
|
||||
// // need to warn the user if local firewall is blocking port
|
||||
// // need to warn the user if NAT is blocking port
|
||||
// // need to show local ip address
|
||||
// // need to show public ip
|
||||
ui.horizontal(|ui| {
|
||||
ui.group(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
rpc_bind_field(self, ui, txt_description_width, text_height, width);
|
||||
rpc_port_field(self, ui, txt_description_width, text_height, width);
|
||||
ui.add_space(space_h);
|
||||
zmq_bind_field(self, ui, txt_description_width, text_height, width);
|
||||
zmq_port_field(self, ui, txt_description_width, text_height, width);
|
||||
});
|
||||
});
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- In/Out peers
|
||||
debug!("Node Tab | Rendering sliders elements");
|
||||
ui.vertical(|ui| {
|
||||
egui::ScrollArea::horizontal().show(ui, |ui| {
|
||||
ui.group(|ui| {
|
||||
ui.style_mut().override_text_style = Some(TextStyle::Small);
|
||||
ui.horizontal(|ui| {
|
||||
// ui.label("Out peers [10-450]:");
|
||||
ui.add_sized(
|
||||
[txt_description_width, text_height],
|
||||
Label::new("Out peers [2-450]:"),
|
||||
);
|
||||
// not sure what's the right calculation to make
|
||||
ui.style_mut().spacing.slider_width = (ui.available_width()
|
||||
- ui.spacing().item_spacing.x * 4.0
|
||||
- ui.spacing().scroll.bar_width
|
||||
- (SPACE * 2.0))
|
||||
.max(0.0);
|
||||
ui.add(Slider::new(&mut self.out_peers, 2..=450))
|
||||
.on_hover_text(P2POOL_OUT);
|
||||
// ui.add_space(ui.available_width() - 4.0);
|
||||
ui.vertical(|ui| {
|
||||
rpc_bind_field(&mut self.api_ip, ui);
|
||||
rpc_port_field(&mut self.api_port, ui);
|
||||
ui.add_space(SPACE);
|
||||
zmq_bind_field(&mut self.zmq_ip, ui);
|
||||
zmq_port_field(&mut self.zmq_port, ui);
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
// ui.label("In peers [10-450]:");
|
||||
ui.add_sized(
|
||||
[txt_description_width, text_height],
|
||||
Label::new("In peers [2-450]:"),
|
||||
});
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- In/Out peers
|
||||
debug!("Node Tab | Rendering sliders elements");
|
||||
ui.vertical(|ui| {
|
||||
ui.group(|ui| {
|
||||
ui.add_space(SPACE);
|
||||
slider_state_field(
|
||||
ui,
|
||||
"Out peers [2-450]:",
|
||||
P2POOL_OUT,
|
||||
&mut self.out_peers,
|
||||
2..=450,
|
||||
);
|
||||
ui.style_mut().spacing.slider_width = (ui.available_width()
|
||||
- ui.spacing().item_spacing.x * 4.0
|
||||
- ui.spacing().scroll.bar_width
|
||||
- (SPACE * 2.0))
|
||||
.max(0.0);
|
||||
ui.add(Slider::new(&mut self.in_peers, 2..=450))
|
||||
.on_hover_text(P2POOL_IN);
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
// ui.label("Log level [ 0-4 ]:");
|
||||
ui.add_sized(
|
||||
[txt_description_width, text_height],
|
||||
Label::new("Log level [ 0-4 ] :"),
|
||||
ui.add_space(SPACE);
|
||||
slider_state_field(
|
||||
ui,
|
||||
"In peers [2-450]:",
|
||||
P2POOL_IN,
|
||||
&mut self.in_peers,
|
||||
2..=450,
|
||||
);
|
||||
ui.style_mut().spacing.slider_width = (ui.available_width()
|
||||
- ui.spacing().item_spacing.x * 4.0
|
||||
- ui.spacing().scroll.bar_width
|
||||
- (SPACE * 2.0))
|
||||
.max(0.0);
|
||||
ui.add(Slider::new(&mut self.log_level, 0..=4))
|
||||
.on_hover_text(P2POOL_LOG);
|
||||
ui.add_space(SPACE);
|
||||
slider_state_field(
|
||||
ui,
|
||||
"Log level [ 0-4 ]:",
|
||||
P2POOL_LOG,
|
||||
&mut self.log_level,
|
||||
0..=6,
|
||||
);
|
||||
ui.add_space(SPACE);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
//---------------------------------------------------------------------------------------------------- DB path
|
||||
ui.add_space(space_h);
|
||||
ui.add_space(SPACE);
|
||||
ui.group(|ui| {
|
||||
path_db_field(self, ui, txt_description_width, text_height, file_window);
|
||||
path_db_field(ui, &mut self.path_db, file_window);
|
||||
let mut guard = file_window.lock().unwrap();
|
||||
if guard.picked_nodedb {
|
||||
self.path_db.clone_from(&guard.nodedb_path);
|
||||
guard.picked_nodedb = false;
|
||||
}
|
||||
});
|
||||
ui.add_space(space_h);
|
||||
ui.add_space(SPACE);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn rpc_bind_field(
|
||||
state: &mut Node,
|
||||
ui: &mut Ui,
|
||||
txt_description_width: f32,
|
||||
text_height: f32,
|
||||
width: f32,
|
||||
) {
|
||||
state_edit_field(
|
||||
&mut state.api_ip,
|
||||
ui,
|
||||
txt_description_width,
|
||||
text_height,
|
||||
width,
|
||||
"RPC BIND IP ",
|
||||
255,
|
||||
NODE_API_BIND,
|
||||
vec![®EXES.ipv4, ®EXES.domain],
|
||||
);
|
||||
}
|
||||
|
||||
fn rpc_port_field(
|
||||
state: &mut Node,
|
||||
ui: &mut Ui,
|
||||
txt_description_width: f32,
|
||||
text_height: f32,
|
||||
width: f32,
|
||||
) {
|
||||
state_edit_field(
|
||||
&mut state.api_port,
|
||||
ui,
|
||||
txt_description_width,
|
||||
text_height,
|
||||
width,
|
||||
" RPC PORT ",
|
||||
5,
|
||||
NODE_API_PORT,
|
||||
vec![®EXES.port],
|
||||
);
|
||||
}
|
||||
fn zmq_bind_field(
|
||||
state: &mut Node,
|
||||
ui: &mut Ui,
|
||||
txt_description_width: f32,
|
||||
text_height: f32,
|
||||
width: f32,
|
||||
) {
|
||||
state_edit_field(
|
||||
&mut state.zmq_ip,
|
||||
ui,
|
||||
txt_description_width,
|
||||
text_height,
|
||||
width,
|
||||
"API BIND IP ",
|
||||
255,
|
||||
NODE_ZMQ_BIND,
|
||||
vec![®EXES.ipv4, ®EXES.domain],
|
||||
);
|
||||
}
|
||||
fn zmq_port_field(
|
||||
state: &mut Node,
|
||||
ui: &mut Ui,
|
||||
txt_description_width: f32,
|
||||
text_height: f32,
|
||||
width: f32,
|
||||
) {
|
||||
state_edit_field(
|
||||
&mut state.zmq_port,
|
||||
ui,
|
||||
txt_description_width,
|
||||
text_height,
|
||||
width,
|
||||
" ZMQ PORT ",
|
||||
5,
|
||||
NODE_ZMQ_PORT,
|
||||
vec![®EXES.port],
|
||||
);
|
||||
}
|
||||
|
||||
fn path_db_field(
|
||||
state: &mut Node,
|
||||
ui: &mut Ui,
|
||||
txt_description_width: f32,
|
||||
text_height: f32,
|
||||
file_window: &Arc<Mutex<FileWindow>>,
|
||||
) {
|
||||
ui.horizontal(|ui| {
|
||||
let symbol;
|
||||
let color;
|
||||
let hover;
|
||||
if state.path_db.is_empty() {
|
||||
symbol = "➖";
|
||||
color = LIGHT_GRAY;
|
||||
hover = NODE_DB_PATH_EMPTY;
|
||||
} else if !Gupax::path_is_dir(&state.path_db) {
|
||||
symbol = "❌";
|
||||
color = RED;
|
||||
hover = NODE_DB_DIR;
|
||||
} else {
|
||||
symbol = "✔";
|
||||
color = GREEN;
|
||||
hover = NODE_PATH_OK;
|
||||
}
|
||||
let text = ["Node Database Directory ", symbol].concat();
|
||||
ui.add_sized(
|
||||
[txt_description_width, text_height],
|
||||
Label::new(RichText::new(text).color(color)),
|
||||
);
|
||||
ui.spacing_mut().text_edit_width =
|
||||
(ui.available_width() - (ui.spacing().item_spacing.x * 8.0) - SPACE * 2.0).max(0.0);
|
||||
let window_busy = file_window.lock().unwrap().thread;
|
||||
ui.add_enabled_ui(!window_busy, |ui| {
|
||||
if ui.button("Open").on_hover_text(GUPAX_SELECT).clicked() {
|
||||
Gupax::spawn_file_window_thread(file_window, FileType::NodeDB);
|
||||
}
|
||||
ui.text_edit_singleline(&mut state.path_db)
|
||||
.on_hover_text(hover);
|
||||
});
|
||||
});
|
||||
|
||||
let mut guard = file_window.lock().unwrap();
|
||||
if guard.picked_nodedb {
|
||||
state.path_db.clone_from(&guard.nodedb_path);
|
||||
guard.picked_nodedb = false;
|
||||
}
|
||||
}
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn state_edit_field(
|
||||
state_field: &mut String,
|
||||
ui: &mut Ui,
|
||||
txt_description_width: f32,
|
||||
text_height: f32,
|
||||
width: f32,
|
||||
description: &str,
|
||||
max_ch: u8,
|
||||
help_msg: &str,
|
||||
validations: Vec<&Regex>,
|
||||
) {
|
||||
ui.horizontal(|ui| {
|
||||
let color;
|
||||
let symbol;
|
||||
let mut input_validated = true;
|
||||
let len;
|
||||
let inside_space;
|
||||
for v in validations {
|
||||
if !v.is_match(state_field) {
|
||||
input_validated = false;
|
||||
}
|
||||
}
|
||||
if state_field.is_empty() {
|
||||
symbol = "➖";
|
||||
color = Color32::LIGHT_GRAY;
|
||||
} else if input_validated {
|
||||
symbol = "✔";
|
||||
color = Color32::from_rgb(100, 230, 100);
|
||||
} else {
|
||||
symbol = "❌";
|
||||
color = Color32::from_rgb(230, 50, 50);
|
||||
}
|
||||
match max_ch {
|
||||
x if x >= 100 => {
|
||||
len = format!("{:03}", state_field.len());
|
||||
inside_space = "";
|
||||
}
|
||||
10..99 => {
|
||||
len = format!("{:02}", state_field.len());
|
||||
inside_space = " ";
|
||||
}
|
||||
_ => {
|
||||
len = format!("{}", state_field.len());
|
||||
inside_space = " ";
|
||||
}
|
||||
}
|
||||
let text = format!(
|
||||
"{}[{}{}/{}{}]{}",
|
||||
description, inside_space, len, max_ch, inside_space, symbol
|
||||
);
|
||||
ui.add_sized(
|
||||
[txt_description_width, text_height],
|
||||
Label::new(RichText::new(text).color(color)),
|
||||
);
|
||||
// allocate the size to leave half of the total width free.
|
||||
ui.spacing_mut().text_edit_width = ((width / 2.0)
|
||||
- (width - ui.available_width() - ui.spacing().scroll.bar_width)
|
||||
- ui.spacing().item_spacing.x * 2.5)
|
||||
.max(0.0);
|
||||
ui.text_edit_singleline(state_field).on_hover_text(help_msg);
|
||||
state_field.truncate(max_ch.into());
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,313 +1,154 @@
|
|||
use crate::disk::node::Node;
|
||||
use crate::app::panels::middle::common::list_poolnode::{PoolNode, list_poolnode};
|
||||
use crate::app::panels::middle::common::state_edit_field::{StateTextEdit, slider_state_field};
|
||||
use crate::miscs::height_txt_before_button;
|
||||
use crate::{disk::state::P2pool, utils::regex::REGEXES};
|
||||
use egui::Checkbox;
|
||||
use egui::Slider;
|
||||
use egui::{Button, Vec2};
|
||||
|
||||
use crate::constants::*;
|
||||
use egui::{Color32, ComboBox, Label, RichText, SelectableLabel, Ui};
|
||||
use egui::{Checkbox, SelectableLabel, Ui};
|
||||
use log::*;
|
||||
|
||||
impl P2pool {
|
||||
pub(super) fn advanced(
|
||||
&mut self,
|
||||
ui: &mut Ui,
|
||||
size: Vec2,
|
||||
text_edit: f32,
|
||||
node_vec: &mut Vec<(String, Node)>,
|
||||
) {
|
||||
let height = size.y / 16.0;
|
||||
let space_h = size.y / 128.0;
|
||||
pub(super) fn advanced(&mut self, ui: &mut Ui, node_vec: &mut Vec<(String, PoolNode)>) {
|
||||
// let height = size.y / 16.0;
|
||||
// let space_h = size.y / 128.0;
|
||||
debug!("P2Pool Tab | Rendering [Node List] elements");
|
||||
let mut incorrect_input = false; // This will disable [Add/Delete] on bad input
|
||||
// [Monero node IP/RPC/ZMQ]
|
||||
ui.horizontal(|ui| {
|
||||
ui.group(|ui| {
|
||||
let width = size.x/10.0;
|
||||
ui.vertical(|ui| {
|
||||
ui.spacing_mut().text_edit_width = width*3.32;
|
||||
ui.horizontal(|ui| {
|
||||
let text;
|
||||
let color;
|
||||
let len = format!("{:02}", self.name.len());
|
||||
if self.name.is_empty() {
|
||||
text = format!("Name [ {}/30 ]➖", len);
|
||||
color = Color32::LIGHT_GRAY;
|
||||
incorrect_input = true;
|
||||
} else if REGEXES.name.is_match(&self.name) {
|
||||
text = format!("Name [ {}/30 ]✔", len);
|
||||
color = Color32::from_rgb(100, 230, 100);
|
||||
} else {
|
||||
text = format!("Name [ {}/30 ]❌", len);
|
||||
color = Color32::from_rgb(230, 50, 50);
|
||||
incorrect_input = true;
|
||||
}
|
||||
ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color)));
|
||||
ui.text_edit_singleline(&mut self.name).on_hover_text(P2POOL_NAME);
|
||||
self.name.truncate(30);
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
let text;
|
||||
let color;
|
||||
let len = format!("{:03}", self.ip.len());
|
||||
if self.ip.is_empty() {
|
||||
text = format!(" IP [{}/255]➖", len);
|
||||
color = Color32::LIGHT_GRAY;
|
||||
incorrect_input = true;
|
||||
} else if self.ip == "localhost" || REGEXES.ipv4.is_match(&self.ip) || REGEXES.domain.is_match(&self.ip) {
|
||||
text = format!(" IP [{}/255]✔", len);
|
||||
color = Color32::from_rgb(100, 230, 100);
|
||||
} else {
|
||||
text = format!(" IP [{}/255]❌", len);
|
||||
color = Color32::from_rgb(230, 50, 50);
|
||||
incorrect_input = true;
|
||||
}
|
||||
ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color)));
|
||||
ui.text_edit_singleline(&mut self.ip).on_hover_text(P2POOL_NODE_IP);
|
||||
self.ip.truncate(255);
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
let text;
|
||||
let color;
|
||||
let len = self.rpc.len();
|
||||
if self.rpc.is_empty() {
|
||||
text = format!(" RPC [ {}/5 ]➖", len);
|
||||
color = Color32::LIGHT_GRAY;
|
||||
incorrect_input = true;
|
||||
} else if REGEXES.port.is_match(&self.rpc) {
|
||||
text = format!(" RPC [ {}/5 ]✔", len);
|
||||
color = Color32::from_rgb(100, 230, 100);
|
||||
} else {
|
||||
text = format!(" RPC [ {}/5 ]❌", len);
|
||||
color = Color32::from_rgb(230, 50, 50);
|
||||
incorrect_input = true;
|
||||
}
|
||||
ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color)));
|
||||
ui.text_edit_singleline(&mut self.rpc).on_hover_text(P2POOL_RPC_PORT);
|
||||
self.rpc.truncate(5);
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
let text;
|
||||
let color;
|
||||
let len = self.zmq.len();
|
||||
if self.zmq.is_empty() {
|
||||
text = format!(" ZMQ [ {}/5 ]➖", len);
|
||||
color = Color32::LIGHT_GRAY;
|
||||
incorrect_input = true;
|
||||
} else if REGEXES.port.is_match(&self.zmq) {
|
||||
text = format!(" ZMQ [ {}/5 ]✔", len);
|
||||
color = Color32::from_rgb(100, 230, 100);
|
||||
} else {
|
||||
text = format!(" ZMQ [ {}/5 ]❌", len);
|
||||
color = Color32::from_rgb(230, 50, 50);
|
||||
incorrect_input = true;
|
||||
}
|
||||
ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color)));
|
||||
ui.text_edit_singleline(&mut self.zmq).on_hover_text(P2POOL_ZMQ_PORT);
|
||||
self.zmq.truncate(5);
|
||||
});
|
||||
});
|
||||
|
||||
ui.vertical(|ui| {
|
||||
let width = ui.available_width();
|
||||
ui.add_space(1.0);
|
||||
// [Manual node selection]
|
||||
ui.spacing_mut().slider_width = width - 8.0;
|
||||
ui.spacing_mut().icon_width = width / 25.0;
|
||||
// [Ping List]
|
||||
debug!("P2Pool Tab | Rendering [Node List]");
|
||||
let text = RichText::new(format!("{}. {}", self.selected_index+1, self.selected_name));
|
||||
ComboBox::from_id_salt("manual_nodes").selected_text(text).width(width).show_ui(ui, |ui| {
|
||||
for (n, (name, node)) in node_vec.iter().enumerate() {
|
||||
let text = RichText::new(format!("{}. {}\n IP: {}\n RPC: {}\n ZMQ: {}", n+1, name, node.ip, node.rpc, node.zmq));
|
||||
if ui.add(SelectableLabel::new(self.selected_name == *name, text)).clicked() {
|
||||
self.selected_index = n;
|
||||
let node = node.clone();
|
||||
self.selected_name.clone_from(name);
|
||||
self.selected_ip.clone_from(&node.ip);
|
||||
self.selected_rpc.clone_from(&node.rpc);
|
||||
self.selected_zmq.clone_from(&node.zmq);
|
||||
self.name.clone_from(name);
|
||||
self.ip = node.ip;
|
||||
self.rpc = node.rpc;
|
||||
self.zmq = node.zmq;
|
||||
}
|
||||
}
|
||||
});
|
||||
// [Add/Save]
|
||||
let node_vec_len = node_vec.len();
|
||||
let mut exists = false;
|
||||
let mut save_diff = true;
|
||||
let mut existing_index = 0;
|
||||
for (name, node) in node_vec.iter() {
|
||||
if *name == self.name {
|
||||
exists = true;
|
||||
if self.ip == node.ip && self.rpc == node.rpc && self.zmq == node.zmq {
|
||||
save_diff = false;
|
||||
}
|
||||
break
|
||||
}
|
||||
existing_index += 1;
|
||||
}
|
||||
ui.horizontal(|ui| {
|
||||
let text = if exists { LIST_SAVE } else { LIST_ADD };
|
||||
let text = format!("{}\n Currently selected node: {}. {}\n Current amount of nodes: {}/1000", text, self.selected_index+1, self.selected_name, node_vec_len);
|
||||
// If the node already exists, show [Save] and mutate the already existing node
|
||||
if exists {
|
||||
ui.add_enabled_ui(!incorrect_input && save_diff, |ui|{
|
||||
if ui.add_sized([width, text_edit], Button::new("Save")).on_hover_text(text).clicked() {
|
||||
let node = Node {
|
||||
ip: self.ip.clone(),
|
||||
rpc: self.rpc.clone(),
|
||||
zmq: self.zmq.clone(),
|
||||
};
|
||||
node_vec[existing_index].1 = node;
|
||||
self.selected_index = existing_index;
|
||||
self.selected_ip.clone_from(&self.ip);
|
||||
self.selected_rpc.clone_from(&self.rpc);
|
||||
self.selected_zmq.clone_from(&self.zmq);
|
||||
info!("Node | S | [index: {}, name: \"{}\", ip: \"{}\", rpc: {}, zmq: {}]", existing_index+1, self.name, self.ip, self.rpc, self.zmq);
|
||||
}
|
||||
});
|
||||
// Else, add to the list
|
||||
} else {
|
||||
ui.add_enabled_ui(!incorrect_input && node_vec_len < 1000, |ui| {
|
||||
if ui.add_sized([width, text_edit], Button::new("Add")).on_hover_text(text).clicked() {
|
||||
let node = Node {
|
||||
ip: self.ip.clone(),
|
||||
rpc: self.rpc.clone(),
|
||||
zmq: self.zmq.clone(),
|
||||
};
|
||||
node_vec.push((self.name.clone(), node));
|
||||
self.selected_index = node_vec_len;
|
||||
self.selected_name.clone_from(&self.name);
|
||||
self.selected_ip.clone_from(&self.ip);
|
||||
self.selected_rpc.clone_from(&self.rpc);
|
||||
self.selected_zmq.clone_from(&self.zmq);
|
||||
info!("Node | A | [index: {}, name: \"{}\", ip: \"{}\", rpc: {}, zmq: {}]", node_vec_len, self.name, self.ip, self.rpc, self.zmq);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// [Delete]
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_enabled_ui(node_vec_len > 1, |ui|{
|
||||
let text = format!("{}\n Currently selected node: {}. {}\n Current amount of nodes: {}/1000", LIST_DELETE, self.selected_index+1, self.selected_name, node_vec_len);
|
||||
if ui.add_sized([width, text_edit], Button::new("Delete")).on_hover_text(text).clicked() {
|
||||
let new_name;
|
||||
let new_node;
|
||||
match self.selected_index {
|
||||
0 => {
|
||||
new_name = node_vec[1].0.clone();
|
||||
new_node = node_vec[1].1.clone();
|
||||
node_vec.remove(0);
|
||||
}
|
||||
_ => {
|
||||
node_vec.remove(self.selected_index);
|
||||
self.selected_index -= 1;
|
||||
new_name = node_vec[self.selected_index].0.clone();
|
||||
new_node = node_vec[self.selected_index].1.clone();
|
||||
}
|
||||
};
|
||||
self.selected_name.clone_from(&new_name);
|
||||
self.selected_ip.clone_from(&new_node.ip);
|
||||
self.selected_rpc.clone_from(&new_node.rpc);
|
||||
self.selected_zmq.clone_from(&new_node.zmq);
|
||||
self.name = new_name;
|
||||
self.ip = new_node.ip;
|
||||
self.rpc = new_node.rpc;
|
||||
self.zmq = new_node.zmq;
|
||||
info!("Node | D | [index: {}, name: \"{}\", ip: \"{}\", rpc: {}, zmq: {}]", self.selected_index, self.selected_name, self.selected_ip, self.selected_rpc, self.selected_zmq);
|
||||
}
|
||||
});
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_enabled_ui(!self.name.is_empty() || !self.ip.is_empty() || !self.rpc.is_empty() || !self.zmq.is_empty(), |ui|{
|
||||
if ui.add_sized([width, text_edit], Button::new("Clear")).on_hover_text(LIST_CLEAR).clicked() {
|
||||
self.name.clear();
|
||||
self.ip.clear();
|
||||
self.rpc.clear();
|
||||
self.zmq.clear();
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.group(|ui| {
|
||||
// let width = size.x/10.0;
|
||||
ui.vertical(|ui| {
|
||||
if !self.name_field(ui) {
|
||||
incorrect_input = false;
|
||||
}
|
||||
if !self.ip_field(ui) {
|
||||
incorrect_input = false;
|
||||
}
|
||||
if !self.rpc_port_field(ui) {
|
||||
incorrect_input = false;
|
||||
}
|
||||
if !self.zmq_port_field(ui) {
|
||||
incorrect_input = false;
|
||||
}
|
||||
});
|
||||
list_poolnode(
|
||||
ui,
|
||||
&mut (&mut self.name, &mut self.ip, &mut self.rpc, &mut self.zmq),
|
||||
&mut self.selected_node,
|
||||
node_vec,
|
||||
incorrect_input,
|
||||
);
|
||||
});
|
||||
});
|
||||
// ui.add_space(space_h);
|
||||
|
||||
debug!("P2Pool Tab | Rendering [Main/Mini/Peers/Log] elements");
|
||||
// [Main/Mini]
|
||||
ui.horizontal(|ui| {
|
||||
let height = height / 4.0;
|
||||
// let height = height / 4.0;
|
||||
ui.group(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
let width = (size.x / 4.0) - SPACE;
|
||||
let height = height + space_h;
|
||||
if ui
|
||||
.add_sized(
|
||||
[width, height],
|
||||
SelectableLabel::new(!self.mini, "P2Pool Main"),
|
||||
ui.vertical(|ui| {
|
||||
let height = height_txt_before_button(ui, &egui::TextStyle::Button) * 1.9;
|
||||
ui.horizontal(|ui| {
|
||||
let width = (ui.available_width() / 4.0) - SPACE;
|
||||
if ui
|
||||
// if ui.add_sized(, )
|
||||
// .selectable_label(!self.mini, "P2Pool Main")
|
||||
.add_sized(
|
||||
[width, height],
|
||||
SelectableLabel::new(!self.mini, "P2Pool Main"),
|
||||
)
|
||||
.on_hover_text(P2POOL_MAIN)
|
||||
.clicked()
|
||||
{
|
||||
self.mini = false;
|
||||
}
|
||||
if ui
|
||||
// .selectable_label(!self.mini, "P2Pool Mini")
|
||||
// if ui
|
||||
.add_sized(
|
||||
[width, height],
|
||||
SelectableLabel::new(self.mini, "P2Pool Mini"),
|
||||
)
|
||||
.on_hover_text(P2POOL_MINI)
|
||||
.clicked()
|
||||
{
|
||||
self.mini = true;
|
||||
}
|
||||
});
|
||||
debug!("P2Pool Tab | Rendering Backup host button");
|
||||
ui.group(|ui| {
|
||||
// [Backup host]
|
||||
ui.add_sized(
|
||||
[(ui.available_width() / 2.0) - (SPACE * 2.0), height],
|
||||
Checkbox::new(&mut self.backup_host, "Backup host"),
|
||||
)
|
||||
.on_hover_text(P2POOL_MAIN)
|
||||
.clicked()
|
||||
{
|
||||
self.mini = false;
|
||||
}
|
||||
if ui
|
||||
.add_sized(
|
||||
[width, height],
|
||||
SelectableLabel::new(self.mini, "P2Pool Mini"),
|
||||
)
|
||||
.on_hover_text(P2POOL_MINI)
|
||||
.clicked()
|
||||
{
|
||||
self.mini = true;
|
||||
}
|
||||
})
|
||||
// ui.checkbox(&mut self.backup_host, "Backup host")
|
||||
.on_hover_text(P2POOL_BACKUP_HOST_ADVANCED);
|
||||
});
|
||||
});
|
||||
});
|
||||
// [Out/In Peers] + [Log Level]
|
||||
ui.group(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
let text = (ui.available_width() / 10.0) - SPACE;
|
||||
let width = (text * 8.0) - SPACE;
|
||||
let height = height / 3.0;
|
||||
ui.style_mut().spacing.slider_width = width / 1.1;
|
||||
ui.style_mut().spacing.interact_size.y = height;
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Small);
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_sized([text, height], Label::new("Out peers [10-450]:"));
|
||||
ui.add_sized([width, height], Slider::new(&mut self.out_peers, 10..=450))
|
||||
.on_hover_text(P2POOL_OUT);
|
||||
ui.add_space(ui.available_width() - 4.0);
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_sized([text, height], Label::new(" In peers [10-450]:"));
|
||||
ui.add_sized([width, height], Slider::new(&mut self.in_peers, 10..=450))
|
||||
.on_hover_text(P2POOL_IN);
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_sized([text, height], Label::new(" Log level [0-6]:"));
|
||||
ui.add_sized([width, height], Slider::new(&mut self.log_level, 0..=6))
|
||||
.on_hover_text(P2POOL_LOG);
|
||||
});
|
||||
ui.add_space(SPACE);
|
||||
slider_state_field(
|
||||
ui,
|
||||
"Out peers [2-450]:",
|
||||
P2POOL_OUT,
|
||||
&mut self.out_peers,
|
||||
2..=450,
|
||||
);
|
||||
ui.add_space(SPACE);
|
||||
slider_state_field(
|
||||
ui,
|
||||
"In peers [2-450]:",
|
||||
P2POOL_IN,
|
||||
&mut self.in_peers,
|
||||
2..=450,
|
||||
);
|
||||
ui.add_space(SPACE);
|
||||
slider_state_field(
|
||||
ui,
|
||||
"Log level [ 0-6 ]:",
|
||||
P2POOL_LOG,
|
||||
&mut self.log_level,
|
||||
0..=6,
|
||||
);
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
debug!("P2Pool Tab | Rendering Backup host button");
|
||||
ui.group(|ui| {
|
||||
let width = size.x - SPACE;
|
||||
let height = ui.available_height();
|
||||
ui.style_mut().spacing.icon_width = height;
|
||||
ui.style_mut().spacing.icon_width_inner = height * 0.9;
|
||||
// [Backup host]
|
||||
ui.add_sized(
|
||||
[width, height],
|
||||
Checkbox::new(&mut self.backup_host, "Backup host"),
|
||||
)
|
||||
.on_hover_text(P2POOL_BACKUP_HOST_ADVANCED);
|
||||
});
|
||||
}
|
||||
fn name_field(&mut self, ui: &mut Ui) -> bool {
|
||||
StateTextEdit::new(ui)
|
||||
.description(" Name ")
|
||||
.max_ch(30)
|
||||
.help_msg(P2POOL_NAME)
|
||||
.validations(&[|x| REGEXES.name.is_match(x)])
|
||||
.build(ui, &mut self.name)
|
||||
}
|
||||
fn rpc_port_field(&mut self, ui: &mut Ui) -> bool {
|
||||
StateTextEdit::new(ui)
|
||||
.description(" RPC PORT ")
|
||||
.max_ch(5)
|
||||
.help_msg(P2POOL_RPC_PORT)
|
||||
.validations(&[|x| REGEXES.port.is_match(x)])
|
||||
.build(ui, &mut self.rpc)
|
||||
}
|
||||
fn zmq_port_field(&mut self, ui: &mut Ui) -> bool {
|
||||
StateTextEdit::new(ui)
|
||||
.description(" ZMQ PORT ")
|
||||
.max_ch(5)
|
||||
.help_msg(P2POOL_ZMQ_PORT)
|
||||
.validations(&[|x| REGEXES.port.is_match(x)])
|
||||
.build(ui, &mut self.zmq)
|
||||
}
|
||||
fn ip_field(&mut self, ui: &mut Ui) -> bool {
|
||||
StateTextEdit::new(ui)
|
||||
.description(" IP ")
|
||||
.max_ch(255)
|
||||
.help_msg(P2POOL_NODE_IP)
|
||||
.validations(&[|x| REGEXES.ipv4.is_match(x), |x| REGEXES.domain.is_match(x)])
|
||||
.build(ui, &mut self.ip)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use crate::disk::node::Node;
|
||||
use crate::app::panels::middle::common::console::{console, input_args_field, start_options_field};
|
||||
use crate::disk::state::{P2pool, State};
|
||||
use crate::helper::p2pool::PubP2poolApi;
|
||||
use crate::regex::num_lines;
|
||||
// Gupax - GUI Uniting P2Pool And XMRig
|
||||
//
|
||||
// Copyright (c) 2022-2023 hinto-janai
|
||||
|
@ -18,12 +17,13 @@ use crate::regex::num_lines;
|
|||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
use crate::{components::node::*, constants::*, helper::*, utils::regex::Regexes};
|
||||
use egui::{Color32, Label, RichText, TextEdit, TextStyle, Vec2, vec2};
|
||||
use crate::{components::node::*, constants::*, helper::*};
|
||||
use log::*;
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use super::common::list_poolnode::PoolNode;
|
||||
|
||||
mod advanced;
|
||||
mod simple;
|
||||
|
||||
|
@ -32,143 +32,51 @@ impl P2pool {
|
|||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn show(
|
||||
&mut self,
|
||||
node_vec: &mut Vec<(String, Node)>,
|
||||
node_vec: &mut Vec<(String, PoolNode)>,
|
||||
_og: &Arc<Mutex<State>>,
|
||||
ping: &Arc<Mutex<Ping>>,
|
||||
process: &Arc<Mutex<Process>>,
|
||||
api: &Arc<Mutex<PubP2poolApi>>,
|
||||
buffer: &mut String,
|
||||
size: Vec2,
|
||||
_ctx: &egui::Context,
|
||||
ui: &mut egui::Ui,
|
||||
) {
|
||||
let height = size.y;
|
||||
let width = size.x;
|
||||
let text_edit = size.y / 25.0;
|
||||
//---------------------------------------------------------------------------------------------------- [Simple] Console
|
||||
// debug!("P2Pool Tab | Rendering [Console]");
|
||||
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||
let text = &api.lock().unwrap().output;
|
||||
ui.group(|ui| {
|
||||
let text = &api.lock().unwrap().output;
|
||||
let nb_lines = num_lines(text);
|
||||
let (height, width) = if self.simple {
|
||||
((size.y * 0.38) - SPACE, size.x - SPACE)
|
||||
} else {
|
||||
(
|
||||
if size.y < 600.0 {
|
||||
size.y * 0.22 - SPACE
|
||||
} else {
|
||||
size.y * 0.36 - SPACE
|
||||
},
|
||||
width - SPACE,
|
||||
)
|
||||
};
|
||||
egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| {
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Small);
|
||||
egui::ScrollArea::vertical()
|
||||
.stick_to_bottom(true)
|
||||
.max_width(width)
|
||||
.max_height(height)
|
||||
.auto_shrink([false; 2])
|
||||
// .show_viewport(ui, |ui, _| {
|
||||
.show_rows(
|
||||
ui,
|
||||
ui.text_style_height(&TextStyle::Small),
|
||||
nb_lines,
|
||||
|ui, row_range| {
|
||||
for i in row_range {
|
||||
if let Some(line) = text.lines().nth(i) {
|
||||
ui.label(line);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
console(ui, text);
|
||||
if !self.simple {
|
||||
//---------------------------------------------------------------------------------------------------- [Advanced] Console
|
||||
ui.separator();
|
||||
let response = ui
|
||||
.add_sized(
|
||||
[width, text_edit],
|
||||
TextEdit::hint_text(
|
||||
TextEdit::singleline(buffer),
|
||||
r#"Type a command (e.g "help" or "status") and press Enter"#,
|
||||
),
|
||||
)
|
||||
.on_hover_text(P2POOL_INPUT);
|
||||
// If the user pressed enter, dump buffer contents into the process STDIN
|
||||
if response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) {
|
||||
response.request_focus(); // Get focus back
|
||||
let buffer = std::mem::take(buffer); // Take buffer
|
||||
let mut process = process.lock().unwrap(); // Lock
|
||||
if process.is_alive() {
|
||||
process.input.push(buffer);
|
||||
} // Push only if alive
|
||||
}
|
||||
input_args_field(
|
||||
ui,
|
||||
buffer,
|
||||
process,
|
||||
r#"Type a command (e.g "help" or "status") and press Enter"#,
|
||||
P2POOL_INPUT,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Args
|
||||
if !self.simple {
|
||||
debug!("P2Pool Tab | Rendering [Arguments]");
|
||||
ui.group(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
let width = (width / 10.0) - SPACE;
|
||||
ui.add_sized([width, text_edit], Label::new("Command arguments:"));
|
||||
ui.add_sized(
|
||||
[ui.available_width(), text_edit],
|
||||
TextEdit::hint_text(
|
||||
TextEdit::singleline(&mut self.arguments),
|
||||
r#"--wallet <...> --host <...>"#,
|
||||
),
|
||||
)
|
||||
.on_hover_text(P2POOL_ARGUMENTS);
|
||||
self.arguments.truncate(1024);
|
||||
})
|
||||
});
|
||||
if !self.arguments.is_empty() {
|
||||
ui.disable()
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Address
|
||||
debug!("P2Pool Tab | Rendering [Address]");
|
||||
ui.group(|ui| {
|
||||
let width = width - SPACE;
|
||||
ui.spacing_mut().text_edit_width = (width) - (SPACE * 3.0);
|
||||
let text;
|
||||
let color;
|
||||
let len = format!("{:02}", self.address.len());
|
||||
if self.address.is_empty() {
|
||||
text = format!("Monero Address [{}/95] ➖", len);
|
||||
color = Color32::LIGHT_GRAY;
|
||||
} else if Regexes::addr_ok(&self.address) {
|
||||
text = format!("Monero Address [{}/95] ✔", len);
|
||||
color = Color32::from_rgb(100, 230, 100);
|
||||
} else {
|
||||
text = format!("Monero Address [{}/95] ❌", len);
|
||||
color = Color32::from_rgb(230, 50, 50);
|
||||
}
|
||||
ui.add_sized(
|
||||
[width, text_edit],
|
||||
Label::new(RichText::new(text).color(color)),
|
||||
start_options_field(
|
||||
ui,
|
||||
&mut self.arguments,
|
||||
r#"--wallet <...> --host <...>"#,
|
||||
P2POOL_ARGUMENTS,
|
||||
);
|
||||
ui.add_sized(
|
||||
[width, text_edit],
|
||||
TextEdit::hint_text(TextEdit::singleline(&mut self.address), "4..."),
|
||||
)
|
||||
.on_hover_text(P2POOL_ADDRESS);
|
||||
self.address.truncate(95);
|
||||
});
|
||||
}
|
||||
debug!("P2Pool Tab | Rendering [Address]");
|
||||
crate::app::panels::middle::common::state_edit_field::monero_address_field(
|
||||
&mut self.address,
|
||||
ui,
|
||||
P2POOL_ADDRESS,
|
||||
);
|
||||
|
||||
// let height = ui.available_height();
|
||||
let size = vec2(width, height);
|
||||
if self.simple {
|
||||
//---------------------------------------------------------------------------------------------------- Simple
|
||||
self.simple(ui, size, ping);
|
||||
//---------------------------------------------------------------------------------------------------- Advanced
|
||||
self.simple(ui, ping);
|
||||
} else {
|
||||
self.advanced(ui, size, text_edit, node_vec);
|
||||
self.advanced(ui, node_vec);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,47 +1,32 @@
|
|||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use crate::app::panels::middle::Hyperlink;
|
||||
use crate::app::panels::middle::ProgressBar;
|
||||
use crate::app::panels::middle::Spinner;
|
||||
use crate::components::node::Ping;
|
||||
use crate::components::node::RemoteNode;
|
||||
use crate::components::node::format_ip_location;
|
||||
use crate::components::node::format_ms;
|
||||
use crate::disk::state::P2pool;
|
||||
use crate::miscs::height_txt_before_button;
|
||||
use egui::Button;
|
||||
use egui::Checkbox;
|
||||
use egui::Vec2;
|
||||
use egui::ScrollArea;
|
||||
use egui::TextStyle;
|
||||
use egui::TextWrapMode;
|
||||
use egui::vec2;
|
||||
|
||||
use crate::constants::*;
|
||||
use egui::{Color32, ComboBox, Label, RichText, Ui};
|
||||
use egui::{Color32, ComboBox, RichText, Ui};
|
||||
use log::*;
|
||||
impl P2pool {
|
||||
pub(super) fn simple(&mut self, ui: &mut Ui, size: Vec2, ping: &Arc<Mutex<Ping>>) {
|
||||
// [Node]
|
||||
let height = size.y / 13.0;
|
||||
let space_h = size.y / 96.0;
|
||||
ui.spacing_mut().slider_width = (size.x - 16.0).max(0.0);
|
||||
ui.spacing_mut().icon_width = size.x / 25.0;
|
||||
|
||||
// [Auto-select] if we haven't already.
|
||||
// Using [Arc<Mutex<Ping>>] as an intermediary here
|
||||
// saves me the hassle of wrapping [state: State] completely
|
||||
// and [.lock().unwrap()]ing it everywhere.
|
||||
// Two atomic bools = enough to represent this data
|
||||
|
||||
// local or remote
|
||||
// button bool
|
||||
pub(super) fn simple(&mut self, ui: &mut Ui, ping: &Arc<Mutex<Ping>>) {
|
||||
ui.vertical_centered(|ui|{
|
||||
ui.add_space(space_h);
|
||||
ui.add_space(SPACE);
|
||||
ui.checkbox(&mut self.local_node, "Use a local node").on_hover_text("If checked (recommended), p2pool will automatically use the local node.\nCheck the Node tab to start a local node.\nIf unchecked, p2pool will attempt to use a remote node.");
|
||||
});
|
||||
ui.add_space(space_h * 2.0);
|
||||
|
||||
ui.add_space(SPACE * 2.0);
|
||||
// if checked, use only local node
|
||||
// if unchecked, show remote nodes.
|
||||
|
||||
// disable remote if local is checked.
|
||||
let visible = !self.local_node;
|
||||
debug!("P2Pool Tab | Running [auto-select] check");
|
||||
|
@ -54,7 +39,6 @@ impl P2pool {
|
|||
}
|
||||
drop(ping);
|
||||
}
|
||||
|
||||
ui.add_enabled_ui(visible, |ui| {
|
||||
ui.vertical(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
|
@ -74,9 +58,11 @@ impl P2pool {
|
|||
debug!("P2Pool Tab | Rendering [ComboBox] of Remote Nodes");
|
||||
let ip_location = format_ip_location(&self.node, false);
|
||||
let text = RichText::new(format!(" ⏺ {}ms | {}", ms, ip_location)).color(color);
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Small);
|
||||
ui.spacing_mut().item_spacing.y = 0.0;
|
||||
ComboBox::from_id_salt("remote_nodes")
|
||||
.selected_text(text)
|
||||
.width(size.x)
|
||||
.width(ui.available_width())
|
||||
.show_ui(ui, |ui| {
|
||||
for data in ping.lock().unwrap().nodes.iter() {
|
||||
let ms = format_ms(data.ms);
|
||||
|
@ -87,123 +73,151 @@ impl P2pool {
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
ui.add_space(space_h);
|
||||
|
||||
ui.add_space(SPACE);
|
||||
debug!("P2Pool Tab | Rendering [Select fastest ... Ping] buttons");
|
||||
ui.horizontal(|ui| {
|
||||
let width = ((size.x / 5.0) - 6.0).max(0.0);
|
||||
let size = vec2(width, height);
|
||||
// [Select random node]
|
||||
if ui
|
||||
.add_sized(size, Button::new("Select random node"))
|
||||
.on_hover_text(P2POOL_SELECT_RANDOM)
|
||||
.clicked()
|
||||
{
|
||||
self.node = RemoteNode::get_random(&self.node);
|
||||
}
|
||||
// [Select fastest node]
|
||||
if ui
|
||||
.add_sized(size, Button::new("Select fastest node"))
|
||||
.on_hover_text(P2POOL_SELECT_FASTEST)
|
||||
.clicked()
|
||||
&& ping.lock().unwrap().pinged
|
||||
{
|
||||
self.node = ping.lock().unwrap().fastest.to_string();
|
||||
}
|
||||
// [Ping Button]
|
||||
ui.add_enabled_ui(!ping.lock().unwrap().pinging, |ui| {
|
||||
if ui
|
||||
.add_sized(size, Button::new("Ping remote nodes"))
|
||||
.on_hover_text(P2POOL_PING)
|
||||
.clicked()
|
||||
{
|
||||
Ping::spawn_thread(ping);
|
||||
}
|
||||
});
|
||||
// [Last <-]
|
||||
if ui
|
||||
.add_sized(size, Button::new("⬅ Last"))
|
||||
.on_hover_text(P2POOL_SELECT_LAST)
|
||||
.clicked()
|
||||
{
|
||||
let ping = ping.lock().unwrap();
|
||||
match ping.pinged {
|
||||
true => {
|
||||
self.node = RemoteNode::get_last_from_ping(&self.node, &ping.nodes)
|
||||
ScrollArea::horizontal()
|
||||
.scroll_bar_visibility(egui::scroll_area::ScrollBarVisibility::AlwaysHidden)
|
||||
.id_salt("horizontal")
|
||||
.show(ui, |ui| {
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Button);
|
||||
ui.horizontal(|ui| {
|
||||
ui.style_mut().wrap_mode = Some(TextWrapMode::Extend);
|
||||
// ui.columns_const(|[col1, col2, col3, col4, col5]| {
|
||||
let width = ((ui.available_width() / 5.0)
|
||||
- (ui.spacing().item_spacing.x * (4.0 / 5.0)))
|
||||
.max(20.0);
|
||||
let height = height_txt_before_button(ui, &TextStyle::Button) * 2.0;
|
||||
// [Select random node]
|
||||
ui.style_mut().override_text_valign = Some(egui::Align::Center);
|
||||
if ui
|
||||
.add_sized([width, height], Button::new("Select random node"))
|
||||
.on_hover_text(P2POOL_SELECT_RANDOM)
|
||||
.clicked()
|
||||
{
|
||||
self.node = RemoteNode::get_random(&self.node);
|
||||
}
|
||||
false => self.node = RemoteNode::get_last(&self.node),
|
||||
}
|
||||
drop(ping);
|
||||
}
|
||||
// [Next ->]
|
||||
if ui
|
||||
.add_sized(size, Button::new("Next ➡"))
|
||||
.on_hover_text(P2POOL_SELECT_NEXT)
|
||||
.clicked()
|
||||
{
|
||||
let ping = ping.lock().unwrap();
|
||||
match ping.pinged {
|
||||
true => {
|
||||
self.node = RemoteNode::get_next_from_ping(&self.node, &ping.nodes)
|
||||
// [Select fastest node]
|
||||
if ui
|
||||
.add_sized([width, height], Button::new("Select fastest node"))
|
||||
.on_hover_text(P2POOL_SELECT_FASTEST)
|
||||
.clicked()
|
||||
&& ping.lock().unwrap().pinged
|
||||
{
|
||||
self.node = ping.lock().unwrap().fastest.to_string();
|
||||
}
|
||||
false => self.node = RemoteNode::get_next(&self.node),
|
||||
}
|
||||
drop(ping);
|
||||
}
|
||||
});
|
||||
// [Ping Button]
|
||||
ui.add_enabled_ui(!ping.lock().unwrap().pinging, |ui| {
|
||||
if ui
|
||||
.add_sized([width, height], Button::new("Ping remote nodes"))
|
||||
.on_hover_text(P2POOL_PING)
|
||||
.clicked()
|
||||
{
|
||||
Ping::spawn_thread(ping);
|
||||
}
|
||||
});
|
||||
// [Last <-]
|
||||
if ui
|
||||
.add_sized([width, height], Button::new("⬅ Last"))
|
||||
.on_hover_text(P2POOL_SELECT_LAST)
|
||||
.clicked()
|
||||
{
|
||||
let ping = ping.lock().unwrap();
|
||||
match ping.pinged {
|
||||
true => {
|
||||
self.node =
|
||||
RemoteNode::get_last_from_ping(&self.node, &ping.nodes)
|
||||
}
|
||||
false => self.node = RemoteNode::get_last(&self.node),
|
||||
}
|
||||
drop(ping);
|
||||
}
|
||||
// [Next ->]
|
||||
if ui
|
||||
.add_sized([width, height], Button::new("Next ➡"))
|
||||
.on_hover_text(P2POOL_SELECT_NEXT)
|
||||
.clicked()
|
||||
{
|
||||
let ping = ping.lock().unwrap();
|
||||
match ping.pinged {
|
||||
true => {
|
||||
self.node =
|
||||
RemoteNode::get_next_from_ping(&self.node, &ping.nodes)
|
||||
}
|
||||
false => self.node = RemoteNode::get_next(&self.node),
|
||||
}
|
||||
drop(ping);
|
||||
}
|
||||
});
|
||||
|
||||
ui.vertical(|ui| {
|
||||
let height = height / 2.0;
|
||||
let pinging = ping.lock().unwrap().pinging;
|
||||
ui.add_enabled_ui(pinging, |ui| {
|
||||
let prog = ping.lock().unwrap().prog.round();
|
||||
let msg =
|
||||
RichText::new(format!("{} ... {}%", ping.lock().unwrap().msg, prog));
|
||||
let height = height / 1.25;
|
||||
let size = vec2(size.x, height);
|
||||
ui.add_space(space_h);
|
||||
ui.add_sized(size, Label::new(msg));
|
||||
ui.add_space(space_h);
|
||||
if pinging {
|
||||
ui.add_sized(size, Spinner::new().size(height));
|
||||
} else {
|
||||
ui.add_sized(size, Label::new("..."));
|
||||
}
|
||||
ui.add_sized(size, ProgressBar::new(prog.round() / 100.0));
|
||||
ui.add_space(space_h);
|
||||
ui.vertical_centered(|ui| {
|
||||
// let height = height / 2.0;
|
||||
let pinging = ping.lock().unwrap().pinging;
|
||||
ui.add_enabled_ui(pinging, |ui| {
|
||||
let prog = ping.lock().unwrap().prog.round();
|
||||
let msg = RichText::new(format!(
|
||||
"{} ... {}%",
|
||||
ping.lock().unwrap().msg,
|
||||
prog
|
||||
));
|
||||
// let height = height / 1.25;
|
||||
// let size = vec2(size.x, height);
|
||||
ui.add_space(SPACE);
|
||||
ui.label(msg);
|
||||
ui.add_space(SPACE);
|
||||
if pinging {
|
||||
ui.spinner();
|
||||
} else {
|
||||
ui.label("...");
|
||||
}
|
||||
ui.add(ProgressBar::new(prog.round() / 100.0));
|
||||
ui.add_space(SPACE);
|
||||
});
|
||||
});
|
||||
|
||||
debug!("P2Pool Tab | Rendering [Auto-*] buttons");
|
||||
ui.group(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
let width =
|
||||
(((ui.available_width() - ui.spacing().item_spacing.x) / 3.0)
|
||||
- SPACE * 1.5)
|
||||
.max(ui.text_style_height(&TextStyle::Button) * 7.0);
|
||||
let size = vec2(
|
||||
width,
|
||||
height_txt_before_button(ui, &TextStyle::Button) * 2.0,
|
||||
);
|
||||
// [Auto-node]
|
||||
ui.add_sized(
|
||||
size,
|
||||
Checkbox::new(&mut self.auto_select, "Auto-select"),
|
||||
)
|
||||
// ui.checkbox(&mut self.auto_select, "Auto-select")
|
||||
.on_hover_text(P2POOL_AUTO_SELECT);
|
||||
ui.separator();
|
||||
// [Auto-node]
|
||||
ui.add_sized(size, Checkbox::new(&mut self.auto_ping, "Auto-ping"))
|
||||
// ui.checkbox(&mut self.auto_ping, "Auto-ping")
|
||||
.on_hover_text(P2POOL_AUTO_NODE);
|
||||
ui.separator();
|
||||
// [Backup host]
|
||||
ui.add_sized(
|
||||
size,
|
||||
Checkbox::new(&mut self.backup_host, "Backup host"),
|
||||
)
|
||||
// ui.checkbox(&mut self.backup_host, "Backup host")
|
||||
.on_hover_text(P2POOL_BACKUP_HOST_SIMPLE);
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
debug!("P2Pool Tab | Rendering [Auto-*] buttons");
|
||||
ui.group(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
let width = ((size.x / 3.0) - (SPACE * 1.75)).max(0.0);
|
||||
let size = vec2(width, height);
|
||||
// [Auto-node]
|
||||
ui.add_sized(size, Checkbox::new(&mut self.auto_select, "Auto-select"))
|
||||
.on_hover_text(P2POOL_AUTO_SELECT);
|
||||
ui.separator();
|
||||
// [Auto-node]
|
||||
ui.add_sized(size, Checkbox::new(&mut self.auto_ping, "Auto-ping"))
|
||||
.on_hover_text(P2POOL_AUTO_NODE);
|
||||
ui.separator();
|
||||
// [Backup host]
|
||||
ui.add_sized(size, Checkbox::new(&mut self.backup_host, "Backup host"))
|
||||
.on_hover_text(P2POOL_BACKUP_HOST_SIMPLE);
|
||||
})
|
||||
});
|
||||
|
||||
debug!("P2Pool Tab | Rendering warning text");
|
||||
ui.add_sized(
|
||||
[size.x, height / 2.0],
|
||||
Hyperlink::from_label_and_url(
|
||||
ui.add_space(SPACE);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.hyperlink_to(
|
||||
"WARNING: It is recommended to run/use your own Monero Node (hover for details)",
|
||||
"https://github.com/Cyrix126/gupaxx#running-a-local-monero-node",
|
||||
),
|
||||
)
|
||||
.on_hover_text(P2POOL_COMMUNITY_NODE_WARNING);
|
||||
)
|
||||
.on_hover_text(P2POOL_COMMUNITY_NODE_WARNING);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::{app::Benchmark, disk::state::Status, helper::xrig::xmrig::PubXmrigApi};
|
||||
use egui::{Hyperlink, ProgressBar, ScrollArea, Spinner, Vec2};
|
||||
use egui::{ProgressBar, ScrollArea, TextWrapMode};
|
||||
use egui_extras::{Column, TableBuilder};
|
||||
use readable::num::{Float, Percent, Unsigned};
|
||||
|
||||
|
@ -11,85 +11,56 @@ use log::*;
|
|||
impl Status {
|
||||
pub(super) fn benchmarks(
|
||||
&mut self,
|
||||
size: Vec2,
|
||||
ui: &mut egui::Ui,
|
||||
benchmarks: &[Benchmark],
|
||||
xmrig_alive: bool,
|
||||
xmrig_api: &Arc<Mutex<PubXmrigApi>>,
|
||||
) {
|
||||
debug!("Status Tab | Rendering [Benchmarks]");
|
||||
let text = size.y / 20.0;
|
||||
let text = ui.text_style_height(&egui::TextStyle::Body);
|
||||
let double = text * 2.0;
|
||||
let log = size.y / 3.0;
|
||||
|
||||
let width = size.x;
|
||||
let width = ui.available_width();
|
||||
// [0], The user's CPU (most likely).
|
||||
let cpu = &benchmarks[0];
|
||||
ui.horizontal(|ui| {
|
||||
let width = (width / 2.0) - (SPACE * 1.666);
|
||||
let min_height = log;
|
||||
ui.group(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
ui.set_min_height(min_height);
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(RichText::new("Your CPU").underline().color(BONE)),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_YOUR_CPU);
|
||||
ui.add_sized([width, text], Label::new(cpu.cpu.as_str()));
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(RichText::new("Total Benchmarks").underline().color(BONE)),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_YOUR_BENCHMARKS);
|
||||
ui.add_sized([width, text], Label::new(format!("{}", cpu.benchmarks)));
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(RichText::new("Rank").underline().color(BONE)),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_YOUR_RANK);
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(format!("{}/{}", cpu.rank, &benchmarks.len())),
|
||||
);
|
||||
ui.set_max_width(ui.available_width() / 2.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new("Your CPU").underline().color(BONE))
|
||||
.on_hover_text(STATUS_SUBMENU_YOUR_CPU);
|
||||
ui.label(cpu.cpu.as_str());
|
||||
ui.label(RichText::new("Total Banchmarks").underline().color(BONE))
|
||||
.on_hover_text(STATUS_SUBMENU_YOUR_BENCHMARKS);
|
||||
ui.label(format!("{}", cpu.benchmarks));
|
||||
// ui.add_sized([width, text], Label::new(format!("{}", cpu.benchmarks)));
|
||||
ui.label(RichText::new("Rank").underline().color(BONE))
|
||||
.on_hover_text(STATUS_SUBMENU_YOUR_RANK);
|
||||
ui.label(format!("{}/{}", cpu.rank, &benchmarks.len()));
|
||||
})
|
||||
});
|
||||
ui.group(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
ui.set_min_height(min_height);
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(RichText::new("High Hashrate").underline().color(BONE)),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_YOUR_HIGH);
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(format!("{} H/s", Float::from_0(cpu.high.into()))),
|
||||
);
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(RichText::new("Average Hashrate").underline().color(BONE)),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_YOUR_AVERAGE);
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(format!("{} H/s", Float::from_0(cpu.average.into()))),
|
||||
);
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(RichText::new("Low Hashrate").underline().color(BONE)),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_YOUR_LOW);
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(format!("{} H/s", Float::from_0(cpu.low.into()))),
|
||||
);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new("High Hashrate").underline().color(BONE))
|
||||
.on_hover_text(STATUS_SUBMENU_YOUR_HIGH);
|
||||
ui.label(format!("{} H/s", Float::from_0(cpu.high.into())));
|
||||
ui.label(RichText::new("Average Hashrate").underline().color(BONE))
|
||||
.on_hover_text(STATUS_SUBMENU_YOUR_AVERAGE);
|
||||
ui.label(format!("{} H/s", Float::from_0(cpu.average.into())));
|
||||
ui.label(RichText::new("Low Hashrate").underline().color(BONE))
|
||||
.on_hover_text(STATUS_SUBMENU_YOUR_LOW);
|
||||
ui.label(RichText::new(format!(
|
||||
"{} H/s",
|
||||
Float::from_0(cpu.low.into())
|
||||
)));
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
// User's CPU hashrate comparison (if XMRig is alive).
|
||||
ui.scope(|ui| {
|
||||
// User's CPU hashrate comparison (if XMRig is alive).
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.add_space(SPACE);
|
||||
if xmrig_alive {
|
||||
let api = xmrig_api.lock().unwrap();
|
||||
let percent = (api.hashrate_raw / cpu.high) * 100.0;
|
||||
|
@ -102,11 +73,11 @@ impl Status {
|
|||
human, api.hashrate
|
||||
)),
|
||||
);
|
||||
ui.add_sized([width, text], ProgressBar::new(1.0));
|
||||
ui.add(ProgressBar::new(1.0));
|
||||
} else if api.hashrate_raw == 0.0 {
|
||||
ui.add_sized([width, text], Label::new("Measuring hashrate..."));
|
||||
ui.add_sized([width, text], Spinner::new().size(text));
|
||||
ui.add_sized([width, text], ProgressBar::new(0.0));
|
||||
ui.label("Measuring hashrate...");
|
||||
ui.spinner();
|
||||
ui.add(ProgressBar::new(0.0));
|
||||
} else {
|
||||
ui.add_sized(
|
||||
[width, double],
|
||||
|
@ -115,7 +86,7 @@ impl Status {
|
|||
human, api.hashrate
|
||||
)),
|
||||
);
|
||||
ui.add_sized([width, text], ProgressBar::new(percent / 100.0));
|
||||
ui.add(ProgressBar::new(percent / 100.0));
|
||||
}
|
||||
} else {
|
||||
ui.add_enabled_ui(xmrig_alive, |ui| {
|
||||
|
@ -123,22 +94,21 @@ impl Status {
|
|||
[width, double],
|
||||
Label::new("XMRig is offline. Hashrate cannot be determined."),
|
||||
);
|
||||
ui.add_sized([width, text], ProgressBar::new(0.0));
|
||||
ui.add(ProgressBar::new(0.0));
|
||||
});
|
||||
}
|
||||
ui.add_space(SPACE);
|
||||
// Comparison
|
||||
ui.group(|ui| {
|
||||
ui.hyperlink_to("Other CPUs", "https://xmrig.com/benchmark")
|
||||
.on_hover_text(STATUS_SUBMENU_OTHER_CPUS);
|
||||
});
|
||||
});
|
||||
|
||||
// Comparison
|
||||
ui.group(|ui| {
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Hyperlink::from_label_and_url("Other CPUs", "https://xmrig.com/benchmark"),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_OTHER_CPUS);
|
||||
});
|
||||
let width_column = width / 20.0;
|
||||
let (cpu, bar, high, average, low, rank, bench) = (
|
||||
width_column * 10.0,
|
||||
width_column * 6.0,
|
||||
width_column * 3.0,
|
||||
width_column * 2.0,
|
||||
width_column * 2.0,
|
||||
|
@ -146,6 +116,7 @@ impl Status {
|
|||
width_column,
|
||||
width_column * 2.0,
|
||||
);
|
||||
ui.style_mut().wrap_mode = Some(TextWrapMode::Extend);
|
||||
ScrollArea::horizontal().show(ui, |ui| {
|
||||
TableBuilder::new(ui)
|
||||
.columns(Column::auto(), 7)
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use egui::Vec2;
|
||||
|
||||
use crate::{
|
||||
app::{Benchmark, eframe_impl::ProcessStatesGui},
|
||||
disk::{gupax_p2pool_api::GupaxP2poolApi, state::Status, status::*},
|
||||
|
@ -51,10 +49,9 @@ impl Status {
|
|||
p2pool_img: &Arc<Mutex<ImgP2pool>>,
|
||||
xmrig_img: &Arc<Mutex<ImgXmrig>>,
|
||||
states: &ProcessStatesGui,
|
||||
max_threads: usize,
|
||||
max_threads: u16,
|
||||
gupax_p2pool_api: &Arc<Mutex<GupaxP2poolApi>>,
|
||||
benchmarks: &[Benchmark],
|
||||
size: Vec2,
|
||||
_ctx: &egui::Context,
|
||||
ui: &mut egui::Ui,
|
||||
) {
|
||||
|
@ -62,7 +59,6 @@ impl Status {
|
|||
if self.submenu == Submenu::Processes {
|
||||
self.processes(
|
||||
sys,
|
||||
size,
|
||||
ui,
|
||||
node_api,
|
||||
p2pool_api,
|
||||
|
@ -77,7 +73,6 @@ impl Status {
|
|||
//---------------------------------------------------------------------------------------------------- [P2Pool]
|
||||
} else if self.submenu == Submenu::P2pool {
|
||||
self.p2pool(
|
||||
size,
|
||||
ui,
|
||||
gupax_p2pool_api,
|
||||
states.is_alive(ProcessName::P2pool),
|
||||
|
@ -86,7 +81,6 @@ impl Status {
|
|||
//---------------------------------------------------------------------------------------------------- [Benchmarks]
|
||||
} else if self.submenu == Submenu::Benchmarks {
|
||||
self.benchmarks(
|
||||
size,
|
||||
ui,
|
||||
benchmarks,
|
||||
states.is_alive(ProcessName::Xmrig),
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use egui::{Label, RichText, SelectableLabel, Slider, TextEdit, Vec2};
|
||||
use egui::{Label, RichText, ScrollArea, SelectableLabel, Separator, Slider, TextStyle};
|
||||
use readable::num::Unsigned;
|
||||
use strum::{EnumCount, IntoEnumIterator};
|
||||
|
||||
use crate::{
|
||||
disk::{
|
||||
|
@ -16,445 +17,345 @@ use crate::{
|
|||
impl Status {
|
||||
pub fn p2pool(
|
||||
&mut self,
|
||||
size: Vec2,
|
||||
ui: &mut egui::Ui,
|
||||
gupax_p2pool_api: &Arc<Mutex<GupaxP2poolApi>>,
|
||||
p2pool_alive: bool,
|
||||
p2pool_api: &Arc<Mutex<PubP2poolApi>>,
|
||||
) {
|
||||
let api = gupax_p2pool_api.lock().unwrap();
|
||||
let height = size.y;
|
||||
let width = size.x;
|
||||
let text = height / 25.0;
|
||||
let log = height / 2.8;
|
||||
// let height = size.y;
|
||||
// let width = size.x;
|
||||
// let text = height / 25.0;
|
||||
// let log = height / 2.8;
|
||||
// Payout Text + PayoutView buttons
|
||||
ui.group(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
let width = (width / 3.0) - (SPACE * 4.0);
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(
|
||||
RichText::new(format!("Total Payouts: {}", api.payout))
|
||||
.underline()
|
||||
.color(LIGHT_GRAY),
|
||||
),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_PAYOUT);
|
||||
ui.separator();
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(
|
||||
RichText::new(format!("Total XMR: {}", api.xmr))
|
||||
.underline()
|
||||
.color(LIGHT_GRAY),
|
||||
),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_XMR);
|
||||
let width = width / 4.0;
|
||||
ui.separator();
|
||||
if ui
|
||||
.add_sized(
|
||||
[width, text],
|
||||
SelectableLabel::new(self.payout_view == PayoutView::Latest, "Latest"),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_LATEST)
|
||||
.clicked()
|
||||
{
|
||||
self.payout_view = PayoutView::Latest;
|
||||
}
|
||||
ui.separator();
|
||||
if ui
|
||||
.add_sized(
|
||||
[width, text],
|
||||
SelectableLabel::new(self.payout_view == PayoutView::Oldest, "Oldest"),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_OLDEST)
|
||||
.clicked()
|
||||
{
|
||||
self.payout_view = PayoutView::Oldest;
|
||||
}
|
||||
ui.separator();
|
||||
if ui
|
||||
.add_sized(
|
||||
[width, text],
|
||||
SelectableLabel::new(self.payout_view == PayoutView::Biggest, "Biggest"),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_BIGGEST)
|
||||
.clicked()
|
||||
{
|
||||
self.payout_view = PayoutView::Biggest;
|
||||
}
|
||||
ui.separator();
|
||||
if ui
|
||||
.add_sized(
|
||||
[width, text],
|
||||
SelectableLabel::new(self.payout_view == PayoutView::Smallest, "Smallest"),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_SMALLEST)
|
||||
.clicked()
|
||||
{
|
||||
self.payout_view = PayoutView::Smallest;
|
||||
}
|
||||
});
|
||||
ui.separator();
|
||||
// Actual logs
|
||||
egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| {
|
||||
egui::ScrollArea::vertical()
|
||||
.stick_to_bottom(self.payout_view == PayoutView::Oldest)
|
||||
.max_width(width)
|
||||
.max_height(log)
|
||||
.auto_shrink([false; 2])
|
||||
.show_viewport(ui, |ui, _| {
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Body);
|
||||
match self.payout_view {
|
||||
PayoutView::Latest => ui.add_sized(
|
||||
[width, log],
|
||||
TextEdit::multiline(&mut api.log_rev.as_str()),
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||
ui.style_mut().override_text_style = Some(TextStyle::Body);
|
||||
let size_text = ui.text_style_height(&TextStyle::Body);
|
||||
let height = (ui.style().spacing.button_padding.y * 2.0) + size_text;
|
||||
ScrollArea::vertical().show(ui, |ui| {
|
||||
ui.group(|ui| {
|
||||
ScrollArea::horizontal().show(ui, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
let width =
|
||||
((ui.available_width() / 3.0) - (SPACE * 4.0)).max(size_text * 9.0);
|
||||
let width_button =
|
||||
((ui.available_width() / 3.0 / 4.0) - SPACE).max(size_text * 4.0);
|
||||
ui.add_sized(
|
||||
[width, height],
|
||||
Label::new(
|
||||
RichText::new(format!("Total Payouts: {}", api.payout))
|
||||
.underline()
|
||||
.color(LIGHT_GRAY),
|
||||
),
|
||||
PayoutView::Oldest => ui.add_sized(
|
||||
[width, log],
|
||||
TextEdit::multiline(&mut api.log.as_str()),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_PAYOUT);
|
||||
ui.add(Separator::default().vertical());
|
||||
ui.add_sized(
|
||||
[width, height],
|
||||
Label::new(
|
||||
RichText::new(format!("Total XMR: {}", api.xmr))
|
||||
.underline()
|
||||
.color(LIGHT_GRAY),
|
||||
),
|
||||
PayoutView::Biggest => ui.add_sized(
|
||||
[width, log],
|
||||
TextEdit::multiline(&mut api.payout_high.as_str()),
|
||||
),
|
||||
PayoutView::Smallest => ui.add_sized(
|
||||
[width, log],
|
||||
TextEdit::multiline(&mut api.payout_low.as_str()),
|
||||
),
|
||||
};
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_XMR);
|
||||
// });
|
||||
ui.add(Separator::default().vertical());
|
||||
PayoutView::iter().enumerate().for_each(|(count, p)| {
|
||||
if ui
|
||||
.add_sized(
|
||||
[width_button, height],
|
||||
SelectableLabel::new(self.payout_view == p, p.to_string()),
|
||||
)
|
||||
.on_hover_text(p.msg_help())
|
||||
.clicked()
|
||||
{
|
||||
self.payout_view = p;
|
||||
}
|
||||
if count + 1 < PayoutView::COUNT {
|
||||
ui.add(Separator::default().vertical());
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
drop(api);
|
||||
// Payout/Share Calculator
|
||||
let button = (width / 20.0) - (SPACE * 1.666);
|
||||
ui.group(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.set_min_width(width - SPACE);
|
||||
if ui
|
||||
.add_sized(
|
||||
[button * 2.0, text],
|
||||
SelectableLabel::new(!self.manual_hash, "Automatic"),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_AUTOMATIC)
|
||||
.clicked()
|
||||
{
|
||||
self.manual_hash = false;
|
||||
}
|
||||
ui.separator();
|
||||
if ui
|
||||
.add_sized(
|
||||
[button * 2.0, text],
|
||||
SelectableLabel::new(self.manual_hash, "Manual"),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_MANUAL)
|
||||
.clicked()
|
||||
{
|
||||
self.manual_hash = true;
|
||||
}
|
||||
ui.separator();
|
||||
ui.add_enabled_ui(self.manual_hash, |ui| {
|
||||
if ui
|
||||
.add_sized(
|
||||
[button, text],
|
||||
SelectableLabel::new(self.hash_metric == Hash::Hash, "Hash"),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_HASH)
|
||||
.clicked()
|
||||
{
|
||||
self.hash_metric = Hash::Hash;
|
||||
}
|
||||
ui.separator();
|
||||
if ui
|
||||
.add_sized(
|
||||
[button, text],
|
||||
SelectableLabel::new(self.hash_metric == Hash::Kilo, "Kilo"),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_KILO)
|
||||
.clicked()
|
||||
{
|
||||
self.hash_metric = Hash::Kilo;
|
||||
}
|
||||
ui.separator();
|
||||
if ui
|
||||
.add_sized(
|
||||
[button, text],
|
||||
SelectableLabel::new(self.hash_metric == Hash::Mega, "Mega"),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_MEGA)
|
||||
.clicked()
|
||||
{
|
||||
self.hash_metric = Hash::Mega;
|
||||
}
|
||||
ui.separator();
|
||||
if ui
|
||||
.add_sized(
|
||||
[button, text],
|
||||
SelectableLabel::new(self.hash_metric == Hash::Giga, "Giga"),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_GIGA)
|
||||
.clicked()
|
||||
{
|
||||
self.hash_metric = Hash::Giga;
|
||||
}
|
||||
ui.separator();
|
||||
ui.spacing_mut().slider_width = button * 11.5;
|
||||
ui.add_sized(
|
||||
[button * 14.0, text],
|
||||
Slider::new(&mut self.hashrate, 1.0..=1_000.0),
|
||||
);
|
||||
});
|
||||
})
|
||||
});
|
||||
// Actual stats
|
||||
ui.add_enabled_ui(p2pool_alive, |ui| {
|
||||
let text = height / 25.0;
|
||||
let width = (width / 3.0) - (SPACE * 1.666);
|
||||
let min_height = ui.available_height() / 1.3;
|
||||
let api = p2pool_api.lock().unwrap();
|
||||
ui.horizontal(|ui| {
|
||||
ui.group(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
ui.set_min_height(min_height);
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(RichText::new("Monero Difficulty").underline().color(BONE)),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_MONERO_DIFFICULTY);
|
||||
ui.add_sized([width, text], Label::new(api.monero_difficulty.as_str()));
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(RichText::new("Monero Hashrate").underline().color(BONE)),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_MONERO_HASHRATE);
|
||||
ui.add_sized([width, text], Label::new(api.monero_hashrate.as_str()));
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(RichText::new("P2Pool Difficulty").underline().color(BONE)),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_P2POOL_DIFFICULTY);
|
||||
ui.add_sized([width, text], Label::new(api.p2pool_difficulty.as_str()));
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(RichText::new("P2Pool Hashrate").underline().color(BONE)),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_P2POOL_HASHRATE);
|
||||
ui.add_sized([width, text], Label::new(api.p2pool_hashrate.as_str()));
|
||||
})
|
||||
});
|
||||
ui.group(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
ui.set_min_height(min_height);
|
||||
if self.manual_hash {
|
||||
let hashrate =
|
||||
Hash::convert_to_hash(self.hashrate, self.hash_metric) as u64;
|
||||
let p2pool_share_mean = PubP2poolApi::calculate_share_or_block_time(
|
||||
hashrate,
|
||||
api.p2pool_difficulty_u64,
|
||||
);
|
||||
let solo_block_mean = PubP2poolApi::calculate_share_or_block_time(
|
||||
hashrate,
|
||||
api.monero_difficulty_u64,
|
||||
);
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(
|
||||
RichText::new("Manually Inputted Hashrate")
|
||||
.underline()
|
||||
.color(BONE),
|
||||
),
|
||||
);
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(format!("{} H/s", Unsigned::from(hashrate))),
|
||||
);
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(
|
||||
RichText::new("P2Pool Block Mean").underline().color(BONE),
|
||||
),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_P2POOL_BLOCK_MEAN);
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(api.p2pool_block_mean.to_string()),
|
||||
);
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(
|
||||
RichText::new("Your P2Pool Share Mean")
|
||||
.underline()
|
||||
.color(BONE),
|
||||
),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_P2POOL_SHARE_MEAN);
|
||||
ui.add_sized([width, text], Label::new(p2pool_share_mean.to_string()));
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(
|
||||
RichText::new("Your Solo Block Mean")
|
||||
.underline()
|
||||
.color(BONE),
|
||||
),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_SOLO_BLOCK_MEAN);
|
||||
ui.add_sized([width, text], Label::new(solo_block_mean.to_string()));
|
||||
} else {
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(
|
||||
RichText::new("Your P2Pool Hashrate")
|
||||
.underline()
|
||||
.color(BONE),
|
||||
),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_YOUR_P2POOL_HASHRATE);
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(format!("{} H/s", api.hashrate_1h)),
|
||||
);
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(
|
||||
RichText::new("P2Pool Block Mean").underline().color(BONE),
|
||||
),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_P2POOL_BLOCK_MEAN);
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(api.p2pool_block_mean.to_string()),
|
||||
);
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(
|
||||
RichText::new("Your P2Pool Share Mean")
|
||||
.underline()
|
||||
.color(BONE),
|
||||
),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_P2POOL_SHARE_MEAN);
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(api.p2pool_share_mean.to_string()),
|
||||
);
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(
|
||||
RichText::new("Your Solo Block Mean")
|
||||
.underline()
|
||||
.color(BONE),
|
||||
),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_SOLO_BLOCK_MEAN);
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(api.solo_block_mean.to_string()),
|
||||
);
|
||||
}
|
||||
})
|
||||
});
|
||||
ui.group(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
ui.set_min_height(min_height);
|
||||
if self.manual_hash {
|
||||
let hashrate =
|
||||
Hash::convert_to_hash(self.hashrate, self.hash_metric) as u64;
|
||||
let user_p2pool_percent = PubP2poolApi::calculate_dominance(
|
||||
hashrate,
|
||||
api.p2pool_hashrate_u64,
|
||||
);
|
||||
let user_monero_percent = PubP2poolApi::calculate_dominance(
|
||||
hashrate,
|
||||
api.monero_hashrate_u64,
|
||||
);
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(RichText::new("P2Pool Miners").underline().color(BONE)),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_P2POOL_MINERS);
|
||||
ui.add_sized([width, text], Label::new(api.miners.as_str()));
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(
|
||||
RichText::new("P2Pool Dominance").underline().color(BONE),
|
||||
),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_P2POOL_DOMINANCE);
|
||||
ui.add_sized([width, text], Label::new(api.p2pool_percent.as_str()));
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(
|
||||
RichText::new("Your P2Pool Dominance")
|
||||
.underline()
|
||||
.color(BONE),
|
||||
),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_YOUR_P2POOL_DOMINANCE);
|
||||
ui.add_sized([width, text], Label::new(user_p2pool_percent.as_str()));
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(
|
||||
RichText::new("Your Monero Dominance")
|
||||
.underline()
|
||||
.color(BONE),
|
||||
),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_YOUR_MONERO_DOMINANCE);
|
||||
ui.add_sized([width, text], Label::new(user_monero_percent.as_str()));
|
||||
} else {
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(RichText::new("P2Pool Miners").underline().color(BONE)),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_P2POOL_MINERS);
|
||||
ui.add_sized([width, text], Label::new(api.miners.as_str()));
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(
|
||||
RichText::new("P2Pool Dominance").underline().color(BONE),
|
||||
),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_P2POOL_DOMINANCE);
|
||||
ui.add_sized([width, text], Label::new(api.p2pool_percent.as_str()));
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(
|
||||
RichText::new("Your P2Pool Dominance")
|
||||
.underline()
|
||||
.color(BONE),
|
||||
),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_YOUR_P2POOL_DOMINANCE);
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(api.user_p2pool_percent.as_str()),
|
||||
);
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(
|
||||
RichText::new("Your Monero Dominance")
|
||||
.underline()
|
||||
.color(BONE),
|
||||
),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_YOUR_MONERO_DOMINANCE);
|
||||
ui.add_sized(
|
||||
[width, text],
|
||||
Label::new(api.user_monero_percent.as_str()),
|
||||
);
|
||||
}
|
||||
})
|
||||
// ui.separator();
|
||||
// Actual logs
|
||||
egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| {
|
||||
egui::ScrollArea::vertical()
|
||||
.stick_to_bottom(self.payout_view == PayoutView::Oldest)
|
||||
.max_width(ui.available_width())
|
||||
.max_height(ui.available_height() / 2.8)
|
||||
.auto_shrink([false; 2])
|
||||
.show_viewport(ui, |ui, _| {
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Body);
|
||||
ui.style_mut().spacing.text_edit_width = ui.available_width();
|
||||
match self.payout_view {
|
||||
PayoutView::Latest => {
|
||||
ui.text_edit_multiline(&mut api.log_rev.as_str())
|
||||
}
|
||||
PayoutView::Oldest => ui.text_edit_multiline(&mut api.log.as_str()),
|
||||
PayoutView::Biggest => {
|
||||
ui.text_edit_multiline(&mut api.payout_high.as_str())
|
||||
}
|
||||
PayoutView::Smallest => {
|
||||
ui.text_edit_multiline(&mut api.payout_low.as_str())
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
// Tick bar
|
||||
ui.add_sized(
|
||||
[ui.available_width(), text],
|
||||
Label::new(api.calculate_tick_bar()),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_PROGRESS_BAR);
|
||||
// });
|
||||
drop(api);
|
||||
// Payout/Share Calculator
|
||||
// let button = (width / 20.0) - (SPACE * 1.666);
|
||||
ui.group(|ui| {
|
||||
ui.set_width(ui.available_width());
|
||||
ui.horizontal(|ui| {
|
||||
// ui.set_min_width(width - SPACE);
|
||||
let width = ui.available_width() / 10.0;
|
||||
if ui
|
||||
.add_sized(
|
||||
[width, height],
|
||||
SelectableLabel::new(!self.manual_hash, "Automatic"),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_AUTOMATIC)
|
||||
.clicked()
|
||||
{
|
||||
self.manual_hash = false;
|
||||
}
|
||||
ui.separator();
|
||||
if ui
|
||||
.add_sized(
|
||||
[width, height],
|
||||
SelectableLabel::new(self.manual_hash, "Manual"),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_MANUAL)
|
||||
.clicked()
|
||||
{
|
||||
self.manual_hash = true;
|
||||
}
|
||||
ui.separator();
|
||||
ui.add_enabled_ui(self.manual_hash, |ui| {
|
||||
if ui
|
||||
.selectable_label(self.hash_metric == Hash::Hash, "Hash")
|
||||
.on_hover_text(STATUS_SUBMENU_HASH)
|
||||
.clicked()
|
||||
{
|
||||
self.hash_metric = Hash::Hash;
|
||||
}
|
||||
ui.separator();
|
||||
if ui
|
||||
.selectable_label(self.hash_metric == Hash::Kilo, "Kilo")
|
||||
.on_hover_text(STATUS_SUBMENU_KILO)
|
||||
.clicked()
|
||||
{
|
||||
self.hash_metric = Hash::Kilo;
|
||||
}
|
||||
ui.separator();
|
||||
if ui
|
||||
.selectable_label(self.hash_metric == Hash::Mega, "Mega")
|
||||
.on_hover_text(STATUS_SUBMENU_MEGA)
|
||||
.clicked()
|
||||
{
|
||||
self.hash_metric = Hash::Mega;
|
||||
}
|
||||
ui.separator();
|
||||
if ui
|
||||
.selectable_label(self.hash_metric == Hash::Giga, "Giga")
|
||||
.on_hover_text(STATUS_SUBMENU_GIGA)
|
||||
.clicked()
|
||||
{
|
||||
self.hash_metric = Hash::Giga;
|
||||
}
|
||||
ui.separator();
|
||||
ui.spacing_mut().slider_width = (ui.available_width() / 1.2).max(0.0);
|
||||
ui.add_sized(
|
||||
[0.0, height],
|
||||
Slider::new(&mut self.hashrate, 1.0..=1_000.0)
|
||||
.suffix(format!(" {}", self.hash_metric)),
|
||||
);
|
||||
});
|
||||
})
|
||||
});
|
||||
// Actual stats
|
||||
ui.add_space(height / 2.0);
|
||||
ui.add_enabled_ui(p2pool_alive, |ui| {
|
||||
let min_height = ui.available_height() / 1.5;
|
||||
let api = p2pool_api.lock().unwrap();
|
||||
ui.horizontal(|ui| {
|
||||
ui.columns_const(|[col1, col2, col3]| {
|
||||
col1.group(|ui| {
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.add_space(height * 2.0);
|
||||
ui.set_min_height(min_height);
|
||||
ui.label(
|
||||
RichText::new("Monero Difficulty").underline().color(BONE),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_MONERO_DIFFICULTY);
|
||||
ui.label(api.monero_difficulty.as_str());
|
||||
ui.label(RichText::new("Monero Hashrate").underline().color(BONE))
|
||||
.on_hover_text(STATUS_SUBMENU_MONERO_HASHRATE);
|
||||
ui.label(api.monero_hashrate.as_str());
|
||||
ui.label(
|
||||
RichText::new("P2Pool Difficulty").underline().color(BONE),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_P2POOL_DIFFICULTY);
|
||||
ui.label(api.p2pool_difficulty.as_str());
|
||||
ui.label(RichText::new("P2Pool Hashrate").underline().color(BONE))
|
||||
.on_hover_text(STATUS_SUBMENU_P2POOL_HASHRATE);
|
||||
ui.label(api.p2pool_hashrate.as_str());
|
||||
})
|
||||
});
|
||||
col2.group(|ui| {
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.add_space(height * 2.0);
|
||||
ui.set_min_height(min_height);
|
||||
if self.manual_hash {
|
||||
let hashrate =
|
||||
Hash::convert_to_hash(self.hashrate, self.hash_metric)
|
||||
as u64;
|
||||
let p2pool_share_mean =
|
||||
PubP2poolApi::calculate_share_or_block_time(
|
||||
hashrate,
|
||||
api.p2pool_difficulty_u64,
|
||||
);
|
||||
let solo_block_mean =
|
||||
PubP2poolApi::calculate_share_or_block_time(
|
||||
hashrate,
|
||||
api.monero_difficulty_u64,
|
||||
);
|
||||
ui.label(
|
||||
RichText::new("Manually Inputted Hashrate")
|
||||
.underline()
|
||||
.color(BONE),
|
||||
);
|
||||
ui.label(format!("{} H/s", Unsigned::from(hashrate)));
|
||||
ui.label(
|
||||
RichText::new("P2Pool Block Mean").underline().color(BONE),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_P2POOL_BLOCK_MEAN);
|
||||
ui.label(api.p2pool_block_mean.to_string());
|
||||
ui.label(
|
||||
RichText::new("Your P2Pool Share Mean")
|
||||
.underline()
|
||||
.color(BONE),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_P2POOL_SHARE_MEAN);
|
||||
ui.label(p2pool_share_mean.to_string());
|
||||
ui.label(
|
||||
RichText::new("Your Solo Block Mean")
|
||||
.underline()
|
||||
.color(BONE),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_SOLO_BLOCK_MEAN);
|
||||
ui.label(solo_block_mean.to_string());
|
||||
} else {
|
||||
ui.label(
|
||||
RichText::new("Your P2Pool Hashrate")
|
||||
.underline()
|
||||
.color(BONE),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_YOUR_P2POOL_HASHRATE);
|
||||
ui.label(format!("{} H/s", api.hashrate_1h));
|
||||
ui.label(
|
||||
RichText::new("P2Pool Block Mean").underline().color(BONE),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_P2POOL_BLOCK_MEAN);
|
||||
ui.label(api.p2pool_block_mean.to_string());
|
||||
ui.label(
|
||||
RichText::new("Your P2Pool Share Mean")
|
||||
.underline()
|
||||
.color(BONE),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_P2POOL_SHARE_MEAN);
|
||||
ui.label(api.p2pool_share_mean.to_string());
|
||||
ui.label(
|
||||
RichText::new("Your Solo Block Mean")
|
||||
.underline()
|
||||
.color(BONE),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_SOLO_BLOCK_MEAN);
|
||||
ui.label(api.solo_block_mean.to_string());
|
||||
}
|
||||
})
|
||||
});
|
||||
col3.group(|ui| {
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.add_space(height * 2.0);
|
||||
ui.set_min_height(min_height);
|
||||
if self.manual_hash {
|
||||
let hashrate =
|
||||
Hash::convert_to_hash(self.hashrate, self.hash_metric)
|
||||
as u64;
|
||||
let user_p2pool_percent = PubP2poolApi::calculate_dominance(
|
||||
hashrate,
|
||||
api.p2pool_hashrate_u64,
|
||||
);
|
||||
let user_monero_percent = PubP2poolApi::calculate_dominance(
|
||||
hashrate,
|
||||
api.monero_hashrate_u64,
|
||||
);
|
||||
ui.label(
|
||||
RichText::new("P2Pool Miners").underline().color(BONE),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_P2POOL_MINERS);
|
||||
ui.label(api.miners.as_str());
|
||||
ui.label(
|
||||
RichText::new("P2Pool Dominance").underline().color(BONE),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_P2POOL_DOMINANCE);
|
||||
ui.label(api.p2pool_percent.as_str());
|
||||
ui.label(
|
||||
RichText::new("Your P2Pool Dominance")
|
||||
.underline()
|
||||
.color(BONE),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_YOUR_P2POOL_DOMINANCE);
|
||||
ui.label(user_p2pool_percent.as_str());
|
||||
ui.label(
|
||||
RichText::new("Your Monero Dominance")
|
||||
.underline()
|
||||
.color(BONE),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_YOUR_MONERO_DOMINANCE);
|
||||
ui.label(user_monero_percent.as_str());
|
||||
} else {
|
||||
ui.label(
|
||||
RichText::new("P2Pool Miners").underline().color(BONE),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_P2POOL_MINERS);
|
||||
ui.label(api.miners.as_str());
|
||||
ui.label(
|
||||
RichText::new("P2Pool Dominance").underline().color(BONE),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_P2POOL_DOMINANCE);
|
||||
ui.label(api.p2pool_percent.as_str());
|
||||
ui.label(
|
||||
RichText::new("Your P2Pool Dominance")
|
||||
.underline()
|
||||
.color(BONE),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_YOUR_P2POOL_DOMINANCE);
|
||||
ui.label(api.user_p2pool_percent.as_str());
|
||||
ui.label(
|
||||
RichText::new("Your Monero Dominance")
|
||||
.underline()
|
||||
.color(BONE),
|
||||
)
|
||||
.on_hover_text(STATUS_SUBMENU_YOUR_MONERO_DOMINANCE);
|
||||
ui.label(api.user_monero_percent.as_str());
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
// Tick bar
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(api.calculate_tick_bar())
|
||||
.on_hover_text(STATUS_SUBMENU_PROGRESS_BAR);
|
||||
});
|
||||
drop(api);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -15,481 +15,195 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::app::panels::middle::common::console::{console, input_args_field, start_options_field};
|
||||
use crate::app::panels::middle::common::list_poolnode::list_poolnode;
|
||||
use crate::app::panels::middle::common::state_edit_field::{
|
||||
monero_address_field, slider_state_field,
|
||||
};
|
||||
use crate::constants::*;
|
||||
use crate::disk::pool::Pool;
|
||||
use crate::disk::state::Xmrig;
|
||||
use crate::helper::Process;
|
||||
use crate::helper::xrig::xmrig::PubXmrigApi;
|
||||
use crate::regex::{REGEXES, num_lines};
|
||||
use crate::utils::regex::Regexes;
|
||||
use egui::{
|
||||
Button, Checkbox, ComboBox, Label, RichText, SelectableLabel, Slider, TextEdit, TextStyle,
|
||||
Vec2, vec2,
|
||||
};
|
||||
use crate::miscs::height_txt_before_button;
|
||||
use crate::regex::REGEXES;
|
||||
use egui::{Checkbox, Ui, vec2};
|
||||
use log::*;
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use super::common::list_poolnode::PoolNode;
|
||||
use super::common::state_edit_field::StateTextEdit;
|
||||
|
||||
impl Xmrig {
|
||||
#[inline(always)] // called once
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn show(
|
||||
&mut self,
|
||||
pool_vec: &mut Vec<(String, Pool)>,
|
||||
pool_vec: &mut Vec<(String, PoolNode)>,
|
||||
process: &Arc<Mutex<Process>>,
|
||||
api: &Arc<Mutex<PubXmrigApi>>,
|
||||
buffer: &mut String,
|
||||
size: Vec2,
|
||||
_ctx: &egui::Context,
|
||||
ui: &mut egui::Ui,
|
||||
) {
|
||||
let text_edit = size.y / 25.0;
|
||||
//---------------------------------------------------------------------------------------------------- [Simple] Console
|
||||
debug!("XMRig Tab | Rendering [Console]");
|
||||
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||
ui.group(|ui| {
|
||||
let text = &api.lock().unwrap().output;
|
||||
let nb_lines = num_lines(text);
|
||||
let (height, width) = if self.simple {
|
||||
(size.y / 1.5, size.x - SPACE)
|
||||
} else {
|
||||
(size.y / 2.8, size.x - SPACE)
|
||||
};
|
||||
egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| {
|
||||
ui.style_mut().override_text_style = Some(TextStyle::Small);
|
||||
egui::ScrollArea::vertical()
|
||||
.stick_to_bottom(true)
|
||||
.max_width(width)
|
||||
.max_height(height)
|
||||
.auto_shrink([false; 2])
|
||||
// .show_viewport(ui, |ui, _| {
|
||||
.show_rows(
|
||||
ui,
|
||||
ui.text_style_height(&TextStyle::Small),
|
||||
nb_lines,
|
||||
|ui, row_range| {
|
||||
for i in row_range {
|
||||
if let Some(line) = text.lines().nth(i) {
|
||||
ui.label(line);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
//---------------------------------------------------------------------------------------------------- [Advanced] Console
|
||||
if !self.simple {
|
||||
ui.separator();
|
||||
let response = ui
|
||||
.add_sized(
|
||||
[width, text_edit],
|
||||
TextEdit::hint_text(
|
||||
TextEdit::singleline(buffer),
|
||||
r#"Commands: [h]ashrate, [p]ause, [r]esume, re[s]ults, [c]onnection"#,
|
||||
),
|
||||
)
|
||||
.on_hover_text(XMRIG_INPUT);
|
||||
// If the user pressed enter, dump buffer contents into the process STDIN
|
||||
if response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) {
|
||||
response.request_focus(); // Get focus back
|
||||
let buffer = std::mem::take(buffer); // Take buffer
|
||||
let mut process = process.lock().unwrap(); // Lock
|
||||
if process.is_alive() {
|
||||
process.input.push(buffer);
|
||||
} // Push only if alive
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Arguments
|
||||
if !self.simple {
|
||||
debug!("XMRig Tab | Rendering [Arguments]");
|
||||
ui.group(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
let width = (size.x / 10.0) - SPACE;
|
||||
ui.add_sized([width, text_edit], Label::new("Command arguments:"));
|
||||
ui.add_sized(
|
||||
[ui.available_width(), text_edit],
|
||||
TextEdit::hint_text(
|
||||
TextEdit::singleline(&mut self.arguments),
|
||||
r#"--url <...> --user <...> --config <...>"#,
|
||||
),
|
||||
)
|
||||
.on_hover_text(XMRIG_ARGUMENTS);
|
||||
self.arguments.truncate(1024);
|
||||
})
|
||||
});
|
||||
ui.add_enabled_ui(self.arguments.is_empty(), |ui|{
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Address
|
||||
debug!("XMRig Tab | Rendering [Address]");
|
||||
ui.group(|ui| {
|
||||
let width = size.x - SPACE;
|
||||
ui.spacing_mut().text_edit_width = (width) - (SPACE * 3.0);
|
||||
let text;
|
||||
let color;
|
||||
let len = format!("{:02}", self.address.len());
|
||||
if self.address.is_empty() {
|
||||
text = format!("Monero Address [{}/95] ➖", len);
|
||||
color = LIGHT_GRAY;
|
||||
} else if Regexes::addr_ok(&self.address) {
|
||||
text = format!("Monero Address [{}/95] ✔", len);
|
||||
color = GREEN;
|
||||
} else {
|
||||
text = format!("Monero Address [{}/95] ❌", len);
|
||||
color = RED;
|
||||
}
|
||||
ui.add_sized(
|
||||
[width, text_edit],
|
||||
Label::new(RichText::new(text).color(color)),
|
||||
);
|
||||
ui.add_sized(
|
||||
[width, text_edit],
|
||||
TextEdit::hint_text(TextEdit::singleline(&mut self.address), "4..."),
|
||||
)
|
||||
.on_hover_text(XMRIG_ADDRESS);
|
||||
self.address.truncate(95);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Threads
|
||||
if self.simple {
|
||||
ui.add_space(SPACE);
|
||||
}
|
||||
debug!("XMRig Tab | Rendering [Threads]");
|
||||
ui.vertical(|ui| {
|
||||
let width = size.x / 10.0;
|
||||
let text_width = width * 2.4;
|
||||
ui.spacing_mut().slider_width = width * 6.5;
|
||||
ui.spacing_mut().icon_width = width / 25.0;
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_sized(
|
||||
[text_width, text_edit],
|
||||
Label::new(format!("Threads [1-{}]:", self.max_threads)),
|
||||
);
|
||||
ui.add_sized(
|
||||
[width, text_edit],
|
||||
Slider::new(&mut self.current_threads, 1..=self.max_threads),
|
||||
)
|
||||
.on_hover_text(XMRIG_THREADS);
|
||||
});
|
||||
#[cfg(not(target_os = "linux"))] // Pause on active isn't supported on Linux
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_sized(
|
||||
[text_width, text_edit],
|
||||
Label::new("Pause on active [0-255]:".to_string()),
|
||||
);
|
||||
ui.add_sized([width, text_edit], Slider::new(&mut self.pause, 0..=255))
|
||||
.on_hover_text(format!("{} [{}] seconds.", XMRIG_PAUSE, self.pause));
|
||||
});
|
||||
});
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Simple
|
||||
if !self.simple {
|
||||
debug!("XMRig Tab | Rendering [Pool List] elements");
|
||||
let width = ui.available_width() - 10.0;
|
||||
let mut incorrect_input = false; // This will disable [Add/Delete] on bad input
|
||||
// [Pool IP/Port]
|
||||
ui.horizontal(|ui| {
|
||||
ui.group(|ui| {
|
||||
let width = width/10.0;
|
||||
ui.vertical(|ui| {
|
||||
ui.spacing_mut().text_edit_width = width*3.32;
|
||||
ui.horizontal(|ui| {
|
||||
let text;
|
||||
let color;
|
||||
let len = format!("{:02}", self.name.len());
|
||||
if self.name.is_empty() {
|
||||
text = format!("Name [ {}/30 ]➖", len);
|
||||
color = LIGHT_GRAY;
|
||||
incorrect_input = true;
|
||||
} else if REGEXES.name.is_match(&self.name) {
|
||||
text = format!("Name [ {}/30 ]✔", len);
|
||||
color = GREEN;
|
||||
} else {
|
||||
text = format!("Name [ {}/30 ]❌", len);
|
||||
color = RED;
|
||||
incorrect_input = true;
|
||||
}
|
||||
ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color)));
|
||||
ui.text_edit_singleline(&mut self.name).on_hover_text(XMRIG_NAME);
|
||||
self.name.truncate(30);
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
let text;
|
||||
let color;
|
||||
let len = format!("{:03}", self.ip.len());
|
||||
if self.ip.is_empty() {
|
||||
text = format!(" IP [{}/255]➖", len);
|
||||
color = LIGHT_GRAY;
|
||||
incorrect_input = true;
|
||||
} else if self.ip == "localhost" || REGEXES.ipv4.is_match(&self.ip) || REGEXES.domain.is_match(&self.ip) {
|
||||
text = format!(" IP [{}/255]✔", len);
|
||||
color = GREEN;
|
||||
} else {
|
||||
text = format!(" IP [{}/255]❌", len);
|
||||
color = RED;
|
||||
incorrect_input = true;
|
||||
}
|
||||
ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color)));
|
||||
ui.text_edit_singleline(&mut self.ip).on_hover_text(XMRIG_IP);
|
||||
self.ip.truncate(255);
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
let text;
|
||||
let color;
|
||||
let len = self.port.len();
|
||||
if self.port.is_empty() {
|
||||
text = format!("Port [ {}/5 ]➖", len);
|
||||
color = LIGHT_GRAY;
|
||||
incorrect_input = true;
|
||||
} else if REGEXES.port.is_match(&self.port) {
|
||||
text = format!("Port [ {}/5 ]✔", len);
|
||||
color = GREEN;
|
||||
} else {
|
||||
text = format!("Port [ {}/5 ]❌", len);
|
||||
color = RED;
|
||||
incorrect_input = true;
|
||||
}
|
||||
ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color)));
|
||||
ui.text_edit_singleline(&mut self.port).on_hover_text(XMRIG_PORT);
|
||||
self.port.truncate(5);
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
let text;
|
||||
let color;
|
||||
let len = format!("{:02}", self.rig.len());
|
||||
if self.rig.is_empty() {
|
||||
text = format!(" Rig [ {}/30 ]➖", len);
|
||||
color = LIGHT_GRAY;
|
||||
} else if REGEXES.name.is_match(&self.rig) {
|
||||
text = format!(" Rig [ {}/30 ]✔", len);
|
||||
color = GREEN;
|
||||
} else {
|
||||
text = format!(" Rig [ {}/30 ]❌", len);
|
||||
color = RED;
|
||||
incorrect_input = true;
|
||||
}
|
||||
ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color)));
|
||||
ui.text_edit_singleline(&mut self.rig).on_hover_text(XMRIG_RIG);
|
||||
self.rig.truncate(30);
|
||||
});
|
||||
});
|
||||
|
||||
ui.vertical(|ui| {
|
||||
let width = ui.available_width();
|
||||
ui.add_space(1.0);
|
||||
// [Manual node selection]
|
||||
ui.spacing_mut().slider_width = width - 8.0;
|
||||
ui.spacing_mut().icon_width = width / 25.0;
|
||||
// [Node List]
|
||||
debug!("XMRig Tab | Rendering [Node List] ComboBox");
|
||||
let text = RichText::new(format!("{}. {}", self.selected_index+1, self.selected_name));
|
||||
ComboBox::from_id_salt("manual_pool").selected_text(text).width(width).show_ui(ui, |ui| {
|
||||
for (n, (name, pool)) in pool_vec.iter().enumerate() {
|
||||
let text = format!("{}. {}\n IP: {}\n Port: {}\n Rig: {}", n+1, name, pool.ip, pool.port, pool.rig);
|
||||
if ui.add(SelectableLabel::new(self.selected_name == *name, text)).clicked() {
|
||||
self.selected_index = n;
|
||||
let pool = pool.clone();
|
||||
self.selected_name.clone_from(name);
|
||||
self.selected_rig.clone_from(&pool.rig);
|
||||
self.selected_ip.clone_from(&pool.ip);
|
||||
self.selected_port.clone_from(&pool.port);
|
||||
self.name.clone_from(name);
|
||||
self.rig = pool.rig;
|
||||
self.ip = pool.ip;
|
||||
self.port = pool.port;
|
||||
}
|
||||
}
|
||||
});
|
||||
// [Add/Save]
|
||||
let pool_vec_len = pool_vec.len();
|
||||
let mut exists = false;
|
||||
let mut save_diff = true;
|
||||
let mut existing_index = 0;
|
||||
for (name, pool) in pool_vec.iter() {
|
||||
if *name == self.name {
|
||||
exists = true;
|
||||
if self.rig == pool.rig && self.ip == pool.ip && self.port == pool.port {
|
||||
save_diff = false;
|
||||
}
|
||||
break
|
||||
}
|
||||
existing_index += 1;
|
||||
}
|
||||
ui.horizontal(|ui| {
|
||||
let text = if exists { LIST_SAVE } else { LIST_ADD };
|
||||
let text = format!("{}\n Currently selected pool: {}. {}\n Current amount of pools: {}/1000", text, self.selected_index+1, self.selected_name, pool_vec_len);
|
||||
// If the pool already exists, show [Save] and mutate the already existing pool
|
||||
if exists {
|
||||
ui.add_enabled_ui(!incorrect_input && save_diff, |ui|{
|
||||
if ui.add_sized([width, text_edit], Button::new("Save")).on_hover_text(text).clicked() {
|
||||
let pool = Pool {
|
||||
rig: self.rig.clone(),
|
||||
ip: self.ip.clone(),
|
||||
port: self.port.clone(),
|
||||
};
|
||||
pool_vec[existing_index].1 = pool;
|
||||
self.selected_name.clone_from(&self.name);
|
||||
self.selected_rig.clone_from(&self.rig);
|
||||
self.selected_ip.clone_from(&self.ip);
|
||||
self.selected_port.clone_from(&self.port);
|
||||
info!("Node | S | [index: {}, name: \"{}\", ip: \"{}\", port: {}, rig: \"{}\"]", existing_index+1, self.name, self.ip, self.port, self.rig);
|
||||
}
|
||||
});
|
||||
// Else, add to the list
|
||||
} else {
|
||||
ui.add_enabled_ui(!incorrect_input && pool_vec_len < 1000, |ui|{
|
||||
if ui.add_sized([width, text_edit], Button::new("Add")).on_hover_text(text).clicked() {
|
||||
let pool = Pool {
|
||||
rig: self.rig.clone(),
|
||||
ip: self.ip.clone(),
|
||||
port: self.port.clone(),
|
||||
};
|
||||
pool_vec.push((self.name.clone(), pool));
|
||||
self.selected_index = pool_vec_len;
|
||||
self.selected_name.clone_from(&self.name);
|
||||
self.selected_rig.clone_from(&self.rig);
|
||||
self.selected_ip.clone_from(&self.ip);
|
||||
self.selected_port.clone_from(&self.port);
|
||||
info!("Node | A | [index: {}, name: \"{}\", ip: \"{}\", port: {}, rig: \"{}\"]", pool_vec_len, self.name, self.ip, self.port, self.rig);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// [Delete]
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_enabled_ui(pool_vec_len > 1, |ui|{
|
||||
let text = format!("{}\n Currently selected pool: {}. {}\n Current amount of pools: {}/1000", LIST_DELETE, self.selected_index+1, self.selected_name, pool_vec_len);
|
||||
if ui.add_sized([width, text_edit], Button::new("Delete")).on_hover_text(text).clicked() {
|
||||
let new_name;
|
||||
let new_pool;
|
||||
match self.selected_index {
|
||||
0 => {
|
||||
new_name = pool_vec[1].0.clone();
|
||||
new_pool = pool_vec[1].1.clone();
|
||||
pool_vec.remove(0);
|
||||
}
|
||||
_ => {
|
||||
pool_vec.remove(self.selected_index);
|
||||
self.selected_index -= 1;
|
||||
new_name = pool_vec[self.selected_index].0.clone();
|
||||
new_pool = pool_vec[self.selected_index].1.clone();
|
||||
}
|
||||
};
|
||||
self.selected_name.clone_from(&new_name);
|
||||
self.selected_rig.clone_from(&new_pool.rig);
|
||||
self.selected_ip.clone_from(&new_pool.ip);
|
||||
self.selected_port.clone_from(&new_pool.port);
|
||||
self.name = new_name;
|
||||
self.rig = new_pool.rig;
|
||||
self.ip = new_pool.ip;
|
||||
self.port = new_pool.port;
|
||||
info!("Node | D | [index: {}, name: \"{}\", ip: \"{}\", port: {}, rig\"{}\"]", self.selected_index, self.selected_name, self.selected_ip, self.selected_port, self.selected_rig);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_enabled_ui(!self.name.is_empty() || !self.ip.is_empty() || !self.port.is_empty(), |ui|{
|
||||
if ui.add_sized([width, text_edit], Button::new("Clear")).on_hover_text(LIST_CLEAR).clicked() {
|
||||
self.name.clear();
|
||||
self.rig.clear();
|
||||
self.ip.clear();
|
||||
self.port.clear();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.add_space(5.0);
|
||||
|
||||
debug!("XMRig Tab | Rendering [API] TextEdits");
|
||||
// [HTTP API IP/Port]
|
||||
ui.group(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
let width = width / 10.0;
|
||||
ui.spacing_mut().text_edit_width = width * 2.39;
|
||||
// HTTP API
|
||||
ui.horizontal(|ui| {
|
||||
let text;
|
||||
let color;
|
||||
let len = format!("{:03}", self.api_ip.len());
|
||||
if self.api_ip.is_empty() {
|
||||
text = format!("HTTP API IP [{}/255]➖", len);
|
||||
color = LIGHT_GRAY;
|
||||
incorrect_input = true;
|
||||
} else if self.api_ip == "localhost"
|
||||
|| REGEXES.ipv4.is_match(&self.api_ip)
|
||||
|| REGEXES.domain.is_match(&self.api_ip)
|
||||
{
|
||||
text = format!("HTTP API IP [{}/255]✔", len);
|
||||
color = GREEN;
|
||||
} else {
|
||||
text = format!("HTTP API IP [{}/255]❌", len);
|
||||
color = RED;
|
||||
incorrect_input = true;
|
||||
}
|
||||
ui.add_sized(
|
||||
[width, text_edit],
|
||||
Label::new(RichText::new(text).color(color)),
|
||||
);
|
||||
ui.text_edit_singleline(&mut self.api_ip)
|
||||
.on_hover_text(XMRIG_API_IP);
|
||||
self.api_ip.truncate(255);
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
let text;
|
||||
let color;
|
||||
let len = self.api_port.len();
|
||||
if self.api_port.is_empty() {
|
||||
text = format!("HTTP API Port [ {}/5 ]➖", len);
|
||||
color = LIGHT_GRAY;
|
||||
incorrect_input = true;
|
||||
} else if REGEXES.port.is_match(&self.api_port) {
|
||||
text = format!("HTTP API Port [ {}/5 ]✔", len);
|
||||
color = GREEN;
|
||||
} else {
|
||||
text = format!("HTTP API Port [ {}/5 ]❌", len);
|
||||
color = RED;
|
||||
incorrect_input = true;
|
||||
}
|
||||
ui.add_sized(
|
||||
[width, text_edit],
|
||||
Label::new(RichText::new(text).color(color)),
|
||||
);
|
||||
ui.text_edit_singleline(&mut self.api_port)
|
||||
.on_hover_text(XMRIG_API_PORT);
|
||||
self.api_port.truncate(5);
|
||||
});
|
||||
});
|
||||
|
||||
let text = &api.lock().unwrap().output;
|
||||
console(ui, text);
|
||||
if !self.simple {
|
||||
ui.separator();
|
||||
|
||||
debug!("XMRig Tab | Rendering [TLS/Keepalive] buttons");
|
||||
ui.vertical(|ui| {
|
||||
// TLS/Keepalive
|
||||
ui.horizontal(|ui| {
|
||||
let width = (ui.available_width() / 2.0) - 11.0;
|
||||
let height = text_edit * 2.0;
|
||||
let size = vec2(width, height);
|
||||
// let mut style = (*ctx.style()).clone();
|
||||
// style.spacing.icon_width_inner = width / 8.0;
|
||||
// style.spacing.icon_width = width / 6.0;
|
||||
// style.spacing.icon_spacing = 20.0;
|
||||
// ctx.set_style(style);
|
||||
ui.add_sized(size, Checkbox::new(&mut self.tls, "TLS Connection"))
|
||||
.on_hover_text(XMRIG_TLS);
|
||||
ui.separator();
|
||||
ui.add_sized(size, Checkbox::new(&mut self.keepalive, "Keepalive"))
|
||||
.on_hover_text(XMRIG_KEEPALIVE);
|
||||
input_args_field(
|
||||
ui,
|
||||
buffer,
|
||||
process,
|
||||
r#"Commands: [h]ashrate, [p]ause, [r]esume, re[s]ults, [c]onnection"#,
|
||||
XMRIG_INPUT,
|
||||
);
|
||||
}
|
||||
});
|
||||
if !self.simple {
|
||||
debug!("XMRig Tab | Rendering [Arguments]");
|
||||
ui.horizontal(|ui| {
|
||||
start_options_field(
|
||||
ui,
|
||||
&mut self.arguments,
|
||||
r#"--url <...> --user <...> --config <...>"#,
|
||||
XMRIG_ARGUMENTS,
|
||||
);
|
||||
});
|
||||
ui.add_enabled_ui(self.arguments.is_empty(), |ui| {
|
||||
debug!("XMRig Tab | Rendering [Address]");
|
||||
monero_address_field(&mut self.address, ui, XMRIG_ADDRESS);
|
||||
});
|
||||
}
|
||||
if self.simple {
|
||||
ui.add_space(SPACE);
|
||||
}
|
||||
debug!("XMRig Tab | Rendering [Threads]");
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.set_max_width(ui.available_width() * 0.75);
|
||||
slider_state_field(
|
||||
ui,
|
||||
&format!("Threads [1-{}]:", self.max_threads),
|
||||
XMRIG_THREADS,
|
||||
&mut self.current_threads,
|
||||
1..=self.max_threads,
|
||||
);
|
||||
#[cfg(not(target_os = "linux"))] // Pause on active isn't supported on Linux
|
||||
slider_state_field(
|
||||
ui,
|
||||
"Pause on active [0-255]:",
|
||||
&format!("{} [{}] seconds.", XMRIG_PAUSE, self.pause),
|
||||
&mut self.pause,
|
||||
0..=255,
|
||||
);
|
||||
});
|
||||
if !self.simple {
|
||||
debug!("XMRig Tab | Rendering [Pool List] elements");
|
||||
let mut incorrect_input = false; // This will disable [Add/Delete] on bad input
|
||||
ui.horizontal(|ui| {
|
||||
ui.group(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
if !self.name_field(ui) {
|
||||
incorrect_input = false;
|
||||
}
|
||||
if !self.ip_field(ui) {
|
||||
incorrect_input = false;
|
||||
}
|
||||
if !self.rpc_port_field(ui) {
|
||||
incorrect_input = false;
|
||||
}
|
||||
if !self.rig_field(ui) {
|
||||
incorrect_input = false;
|
||||
}
|
||||
});
|
||||
ui.vertical(|ui| {
|
||||
list_poolnode(
|
||||
ui,
|
||||
&mut (&mut self.name, &mut self.ip, &mut self.port, &mut self.rig),
|
||||
&mut self.selected_pool,
|
||||
pool_vec,
|
||||
incorrect_input,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
ui.add_space(5.0);
|
||||
debug!("XMRig Tab | Rendering [API] TextEdits");
|
||||
// [HTTP API IP/Port]
|
||||
ui.group(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
self.api_ip_field(ui);
|
||||
self.api_port_field(ui);
|
||||
});
|
||||
ui.separator();
|
||||
debug!("XMRig Tab | Rendering [TLS/Keepalive] buttons");
|
||||
ui.vertical(|ui| {
|
||||
// TLS/Keepalive
|
||||
ui.horizontal(|ui| {
|
||||
let width = (ui.available_width() / 2.0) - 11.0;
|
||||
let height =
|
||||
height_txt_before_button(ui, &egui::TextStyle::Button) * 2.0;
|
||||
let size = vec2(width, height);
|
||||
ui.add_sized(size, Checkbox::new(&mut self.tls, "TLS Connection"))
|
||||
.on_hover_text(XMRIG_TLS);
|
||||
ui.separator();
|
||||
ui.add_sized(size, Checkbox::new(&mut self.keepalive, "Keepalive"))
|
||||
.on_hover_text(XMRIG_KEEPALIVE);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
fn name_field(&mut self, ui: &mut Ui) -> bool {
|
||||
StateTextEdit::new(ui)
|
||||
.description(" Name ")
|
||||
.max_ch(30)
|
||||
.help_msg(XMRIG_NAME)
|
||||
.validations(&[|x| REGEXES.name.is_match(x)])
|
||||
.build(ui, &mut self.name)
|
||||
}
|
||||
fn rpc_port_field(&mut self, ui: &mut Ui) -> bool {
|
||||
StateTextEdit::new(ui)
|
||||
.description(" RPC PORT ")
|
||||
.max_ch(5)
|
||||
.help_msg(XMRIG_PORT)
|
||||
.validations(&[|x| REGEXES.port.is_match(x)])
|
||||
.build(ui, &mut self.port)
|
||||
}
|
||||
fn ip_field(&mut self, ui: &mut Ui) -> bool {
|
||||
StateTextEdit::new(ui)
|
||||
.description(" IP ")
|
||||
.max_ch(255)
|
||||
.help_msg(XMRIG_IP)
|
||||
.validations(&[|x| REGEXES.ipv4.is_match(x) || REGEXES.domain.is_match(x)])
|
||||
.build(ui, &mut self.ip)
|
||||
}
|
||||
fn rig_field(&mut self, ui: &mut Ui) -> bool {
|
||||
StateTextEdit::new(ui)
|
||||
.description(" Name ")
|
||||
.max_ch(30)
|
||||
.help_msg(XMRIG_RIG)
|
||||
.build(ui, &mut self.rig)
|
||||
}
|
||||
fn api_ip_field(&mut self, ui: &mut Ui) -> bool {
|
||||
StateTextEdit::new(ui)
|
||||
.description(" API IP ")
|
||||
.max_ch(255)
|
||||
.help_msg(XMRIG_API_IP)
|
||||
.validations(&[|x| REGEXES.ipv4.is_match(x) || REGEXES.domain.is_match(x)])
|
||||
.build(ui, &mut self.api_ip)
|
||||
}
|
||||
fn api_port_field(&mut self, ui: &mut Ui) -> bool {
|
||||
StateTextEdit::new(ui)
|
||||
.description(" API PORT ")
|
||||
.max_ch(5)
|
||||
.help_msg(XMRIG_API_PORT)
|
||||
.validations(&[|x| REGEXES.port.is_match(x)])
|
||||
.build(ui, &mut self.api_port)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,439 +1,201 @@
|
|||
use egui::{
|
||||
Button, Checkbox, ComboBox, Label, RichText, SelectableLabel, TextEdit, TextStyle, Vec2, vec2,
|
||||
};
|
||||
use egui::{Checkbox, Label, TextStyle, Ui, vec2};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use log::{debug, info};
|
||||
use log::debug;
|
||||
|
||||
use crate::disk::pool::Pool;
|
||||
use crate::app::panels::middle::common::console::{console, input_args_field, start_options_field};
|
||||
use crate::app::panels::middle::common::list_poolnode::list_poolnode;
|
||||
use crate::disk::state::XmrigProxy;
|
||||
use crate::helper::Process;
|
||||
use crate::helper::xrig::xmrig_proxy::PubXmrigProxyApi;
|
||||
use crate::regex::{REGEXES, num_lines};
|
||||
use crate::utils::constants::DARK_GRAY;
|
||||
use crate::miscs::height_txt_before_button;
|
||||
use crate::regex::REGEXES;
|
||||
use crate::{
|
||||
GREEN, LIGHT_GRAY, LIST_ADD, LIST_CLEAR, LIST_DELETE, LIST_SAVE, RED, SPACE, XMRIG_API_IP,
|
||||
XMRIG_API_PORT, XMRIG_IP, XMRIG_KEEPALIVE, XMRIG_NAME, XMRIG_PORT, XMRIG_PROXY_ARGUMENTS,
|
||||
XMRIG_PROXY_INPUT, XMRIG_PROXY_REDIRECT, XMRIG_PROXY_URL, XMRIG_RIG, XMRIG_TLS,
|
||||
SPACE, XMRIG_API_IP, XMRIG_API_PORT, XMRIG_IP, XMRIG_KEEPALIVE, XMRIG_NAME, XMRIG_PORT,
|
||||
XMRIG_PROXY_ARGUMENTS, XMRIG_PROXY_INPUT, XMRIG_PROXY_REDIRECT, XMRIG_PROXY_URL, XMRIG_RIG,
|
||||
XMRIG_TLS,
|
||||
};
|
||||
|
||||
use super::common::list_poolnode::PoolNode;
|
||||
use super::common::state_edit_field::StateTextEdit;
|
||||
|
||||
impl XmrigProxy {
|
||||
#[inline(always)] // called once
|
||||
pub fn show(
|
||||
&mut self,
|
||||
process: &Arc<Mutex<Process>>,
|
||||
pool_vec: &mut Vec<(String, Pool)>,
|
||||
pool_vec: &mut Vec<(String, PoolNode)>,
|
||||
api: &Arc<Mutex<PubXmrigProxyApi>>,
|
||||
buffer: &mut String,
|
||||
size: Vec2,
|
||||
ui: &mut egui::Ui,
|
||||
) {
|
||||
let width = size.x;
|
||||
let height = size.y;
|
||||
let space_h = height / 48.0;
|
||||
let text_edit = size.y / 25.0;
|
||||
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.add_space(space_h);
|
||||
ui.add_space(SPACE);
|
||||
ui.style_mut().override_text_style = Some(TextStyle::Heading);
|
||||
ui.hyperlink_to("XMRig-Proxy", XMRIG_PROXY_URL);
|
||||
ui.style_mut().override_text_style = Some(TextStyle::Body);
|
||||
ui.add(Label::new("High performant proxy for your miners"));
|
||||
ui.add_space(space_h);
|
||||
ui.add_space(SPACE);
|
||||
});
|
||||
// console output for log
|
||||
debug!("Xmrig-Proxy Tab | Rendering [Console]");
|
||||
ui.group(|ui| {
|
||||
let text = &api.lock().unwrap().output;
|
||||
let nb_lines = num_lines(text);
|
||||
let height = size.y / 2.8;
|
||||
let width = size.x - (space_h / 2.0);
|
||||
egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| {
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Small
|
||||
);
|
||||
egui::ScrollArea::vertical()
|
||||
.stick_to_bottom(true)
|
||||
.max_width(width)
|
||||
.max_height(height)
|
||||
.auto_shrink([false; 2])
|
||||
// .show_viewport(ui, |ui, _| {
|
||||
.show_rows(
|
||||
ui,
|
||||
ui.text_style_height(&TextStyle::Small),
|
||||
nb_lines,
|
||||
|ui, row_range| {
|
||||
for i in row_range {
|
||||
if let Some(line) = text.lines().nth(i) {
|
||||
ui.label(line);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
//---------------------------------------------------------------------------------------------------- [Advanced] Console
|
||||
if !self.simple {
|
||||
ui.separator();
|
||||
let response = ui
|
||||
.add_sized(
|
||||
[width, text_edit],
|
||||
TextEdit::hint_text(
|
||||
TextEdit::singleline(buffer),
|
||||
r#"Commands: [h]ashrate, [c]onnections, [v]erbose, [w]orkers"#,
|
||||
),
|
||||
)
|
||||
.on_hover_text(XMRIG_PROXY_INPUT);
|
||||
// If the user pressed enter, dump buffer contents into the process STDIN
|
||||
if response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) {
|
||||
response.request_focus(); // Get focus back
|
||||
let buffer = std::mem::take(buffer); // Take buffer
|
||||
let mut process = process.lock().unwrap(); // Lock
|
||||
if process.is_alive() {
|
||||
process.input.push(buffer);
|
||||
} // Push only if alive
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Arguments
|
||||
debug!("XMRig-Proxy Tab | Rendering [Arguments]");
|
||||
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||
ui.group(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
let width = (size.x / 10.0) - SPACE;
|
||||
ui.add_sized([width, text_edit], Label::new("Command arguments:"));
|
||||
ui.add_sized(
|
||||
[ui.available_width(), text_edit],
|
||||
TextEdit::hint_text(
|
||||
TextEdit::singleline(&mut self.arguments),
|
||||
r#"--url <...> --user <...> --config <...>"#,
|
||||
),
|
||||
)
|
||||
.on_hover_text(XMRIG_PROXY_ARGUMENTS);
|
||||
self.arguments.truncate(1024);
|
||||
})
|
||||
});
|
||||
if !self.arguments.is_empty() {
|
||||
ui.disable();
|
||||
}
|
||||
ui.add_space(space_h);
|
||||
ui.style_mut().spacing.icon_width_inner = width / 45.0;
|
||||
ui.style_mut().spacing.icon_width = width / 35.0;
|
||||
ui.style_mut().spacing.icon_spacing = space_h;
|
||||
ui.checkbox(
|
||||
&mut self.redirect_local_xmrig,
|
||||
"Auto Redirect local Xmrig to Xmrig-Proxy",
|
||||
)
|
||||
.on_hover_text(XMRIG_PROXY_REDIRECT);
|
||||
|
||||
// idea
|
||||
// need to warn the user if local firewall is blocking port
|
||||
// need to warn the user if NAT is blocking port
|
||||
// need to show local ip address
|
||||
// need to show public ip
|
||||
|
||||
debug!("XMRig-Proxy Tab | Rendering [Pool List] elements");
|
||||
let width = ui.available_width() - 10.0;
|
||||
let mut incorrect_input = false; // This will disable [Add/Delete] on bad input
|
||||
// [Pool IP/Port]
|
||||
ui.horizontal(|ui| {
|
||||
ui.group(|ui| {
|
||||
let width = width/10.0;
|
||||
ui.vertical(|ui| {
|
||||
ui.spacing_mut().text_edit_width = width*3.32;
|
||||
ui.horizontal(|ui| {
|
||||
let text;
|
||||
let color;
|
||||
let len = format!("{:02}", self.name.len());
|
||||
if self.name.is_empty() {
|
||||
text = format!("Name [ {}/30 ]➖", len);
|
||||
color = LIGHT_GRAY;
|
||||
incorrect_input = true;
|
||||
} else if REGEXES.name.is_match(&self.name) {
|
||||
text = format!("Name [ {}/30 ]✔", len);
|
||||
color = GREEN;
|
||||
} else {
|
||||
text = format!("Name [ {}/30 ]❌", len);
|
||||
color = RED;
|
||||
incorrect_input = true;
|
||||
}
|
||||
ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color)));
|
||||
ui.text_edit_singleline(&mut self.name).on_hover_text(XMRIG_NAME);
|
||||
self.name.truncate(30);
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
let text;
|
||||
let color;
|
||||
let len = format!("{:03}", self.p2pool_ip.len());
|
||||
if self.p2pool_ip.is_empty() {
|
||||
text = format!(" IP [{}/255]➖", len);
|
||||
color = LIGHT_GRAY;
|
||||
incorrect_input = true;
|
||||
} else if self.p2pool_ip == "localhost" || REGEXES.ipv4.is_match(&self.p2pool_ip) || REGEXES.domain.is_match(&self.p2pool_ip) {
|
||||
text = format!(" IP [{}/255]✔", len);
|
||||
color = GREEN;
|
||||
} else {
|
||||
text = format!(" IP [{}/255]❌", len);
|
||||
color = RED;
|
||||
incorrect_input = true;
|
||||
}
|
||||
ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color)));
|
||||
ui.text_edit_singleline(&mut self.p2pool_ip).on_hover_text(XMRIG_IP);
|
||||
self.p2pool_ip.truncate(255);
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
let text;
|
||||
let color;
|
||||
let len = self.p2pool_port.len();
|
||||
if self.p2pool_port.is_empty() {
|
||||
text = format!("Port [ {}/5 ]➖", len);
|
||||
color = LIGHT_GRAY;
|
||||
incorrect_input = true;
|
||||
} else if REGEXES.port.is_match(&self.p2pool_port) {
|
||||
text = format!("Port [ {}/5 ]✔", len);
|
||||
color = GREEN;
|
||||
} else {
|
||||
text = format!("Port [ {}/5 ]❌", len);
|
||||
color = RED;
|
||||
incorrect_input = true;
|
||||
}
|
||||
ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color)));
|
||||
ui.text_edit_singleline(&mut self.p2pool_port).on_hover_text(XMRIG_PORT);
|
||||
self.p2pool_port.truncate(5);
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
let text;
|
||||
let color;
|
||||
let len = format!("{:02}", self.rig.len());
|
||||
if self.rig.is_empty() {
|
||||
text = format!(" Rig [ {}/30 ]➖", len);
|
||||
color = LIGHT_GRAY;
|
||||
} else if REGEXES.name.is_match(&self.rig) {
|
||||
text = format!(" Rig [ {}/30 ]✔", len);
|
||||
color = GREEN;
|
||||
} else {
|
||||
text = format!(" Rig [ {}/30 ]❌", len);
|
||||
color = RED;
|
||||
incorrect_input = true;
|
||||
}
|
||||
ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color)));
|
||||
ui.text_edit_singleline(&mut self.rig).on_hover_text(XMRIG_RIG);
|
||||
self.rig.truncate(30);
|
||||
});
|
||||
});
|
||||
|
||||
ui.vertical(|ui| {
|
||||
let width = ui.available_width();
|
||||
ui.add_space(1.0);
|
||||
// [Manual node selection]
|
||||
ui.spacing_mut().slider_width = width - 8.0;
|
||||
ui.spacing_mut().icon_width = width / 25.0;
|
||||
// [Node List]
|
||||
debug!("XMRig-Proxy Tab | Rendering [Node List] ComboBox");
|
||||
let text = RichText::new(format!("{}. {}", self.selected_index+1, self.selected_name));
|
||||
ComboBox::from_id_salt("manual_pool").selected_text(text).width(width).show_ui(ui, |ui| {
|
||||
for (n, (name, pool)) in pool_vec.iter().enumerate() {
|
||||
let text = format!("{}. {}\n IP: {}\n Port: {}\n Rig: {}", n+1, name, pool.ip, pool.port, pool.rig);
|
||||
if ui.add(SelectableLabel::new(self.selected_name == *name, text)).clicked() {
|
||||
self.selected_index = n;
|
||||
let pool = pool.clone();
|
||||
self.selected_name.clone_from(name);
|
||||
self.selected_rig.clone_from(&pool.rig);
|
||||
self.selected_ip.clone_from(&pool.ip);
|
||||
self.selected_port.clone_from(&pool.port);
|
||||
self.name.clone_from(name);
|
||||
self.rig = pool.rig;
|
||||
self.p2pool_ip = pool.ip;
|
||||
self.p2pool_port = pool.port;
|
||||
}
|
||||
}
|
||||
});
|
||||
// [Add/Save]
|
||||
let pool_vec_len = pool_vec.len();
|
||||
let mut exists = false;
|
||||
let mut save_diff = true;
|
||||
let mut existing_index = 0;
|
||||
for (name, pool) in pool_vec.iter() {
|
||||
if *name == self.name {
|
||||
exists = true;
|
||||
if self.rig == pool.rig && self.p2pool_ip == pool.ip && self.p2pool_port == pool.port {
|
||||
save_diff = false;
|
||||
}
|
||||
break
|
||||
}
|
||||
existing_index += 1;
|
||||
}
|
||||
ui.horizontal(|ui| {
|
||||
let text = if exists { LIST_SAVE } else { LIST_ADD };
|
||||
let text = format!("{}\n Currently selected pool: {}. {}\n Current amount of pools: {}/1000", text, self.selected_index+1, self.selected_name, pool_vec_len);
|
||||
// If the pool already exists, show [Save] and mutate the already existing pool
|
||||
if exists {
|
||||
ui.add_enabled_ui(!incorrect_input && save_diff, |ui|{
|
||||
if ui.add_sized([width, text_edit], Button::new("Save")).on_hover_text(text).clicked() {
|
||||
let pool = Pool {
|
||||
rig: self.rig.clone(),
|
||||
ip: self.p2pool_ip.clone(),
|
||||
port: self.p2pool_port.clone(),
|
||||
};
|
||||
pool_vec[existing_index].1 = pool;
|
||||
self.selected_name.clone_from(&self.name);
|
||||
self.selected_rig.clone_from(&self.rig);
|
||||
self.selected_ip.clone_from(&self.p2pool_ip);
|
||||
self.selected_port.clone_from(&self.p2pool_port);
|
||||
info!("Node | S | [index: {}, name: \"{}\", ip: \"{}\", port: {}, rig: \"{}\"]", existing_index+1, self.name, self.p2pool_ip, self.p2pool_port, self.rig);
|
||||
}
|
||||
|
||||
});
|
||||
// Else, add to the list
|
||||
} else {
|
||||
ui.add_enabled_ui(!incorrect_input && pool_vec_len < 1000, |ui|{
|
||||
if ui.add_sized([width, text_edit], Button::new("Add")).on_hover_text(text).clicked() {
|
||||
let pool = Pool {
|
||||
rig: self.rig.clone(),
|
||||
ip: self.p2pool_ip.clone(),
|
||||
port: self.p2pool_port.clone(),
|
||||
};
|
||||
pool_vec.push((self.name.clone(), pool));
|
||||
self.selected_index = pool_vec_len;
|
||||
self.selected_name.clone_from(&self.name);
|
||||
self.selected_rig.clone_from(&self.rig);
|
||||
self.selected_ip.clone_from(&self.p2pool_ip);
|
||||
self.selected_port.clone_from(&self.p2pool_port);
|
||||
info!("Node | A | [index: {}, name: \"{}\", ip: \"{}\", port: {}, rig: \"{}\"]", pool_vec_len, self.name, self.p2pool_ip, self.p2pool_port, self.rig);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
// [Delete]
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_enabled_ui(pool_vec_len > 1, |ui|{
|
||||
let text = format!("{}\n Currently selected pool: {}. {}\n Current amount of pools: {}/1000", LIST_DELETE, self.selected_index+1, self.selected_name, pool_vec_len);
|
||||
if ui.add_sized([width, text_edit], Button::new("Delete")).on_hover_text(text).clicked() {
|
||||
let new_name;
|
||||
let new_pool;
|
||||
match self.selected_index {
|
||||
0 => {
|
||||
new_name = pool_vec[1].0.clone();
|
||||
new_pool = pool_vec[1].1.clone();
|
||||
pool_vec.remove(0);
|
||||
}
|
||||
_ => {
|
||||
pool_vec.remove(self.selected_index);
|
||||
self.selected_index -= 1;
|
||||
new_name = pool_vec[self.selected_index].0.clone();
|
||||
new_pool = pool_vec[self.selected_index].1.clone();
|
||||
}
|
||||
};
|
||||
self.selected_name.clone_from(&new_name);
|
||||
self.selected_rig.clone_from(&new_pool.rig);
|
||||
self.selected_ip.clone_from(&new_pool.ip);
|
||||
self.selected_port.clone_from(&new_pool.port);
|
||||
self.name = new_name;
|
||||
self.rig = new_pool.rig;
|
||||
self.p2pool_ip = new_pool.ip;
|
||||
self.p2pool_port = new_pool.port;
|
||||
info!("Node | D | [index: {}, name: \"{}\", ip: \"{}\", port: {}, rig\"{}\"]", self.selected_index, self.selected_name, self.selected_ip, self.selected_port, self.selected_rig);
|
||||
}
|
||||
});
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_enabled_ui(!self.name.is_empty() || !self.p2pool_ip.is_empty() || !self.p2pool_port.is_empty(), |ui|{
|
||||
if ui.add_sized([width, text_edit], Button::new("Clear")).on_hover_text(LIST_CLEAR).clicked() {
|
||||
self.name.clear();
|
||||
self.rig.clear();
|
||||
self.p2pool_ip.clear();
|
||||
self.p2pool_port.clear();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.add_space(5.0);
|
||||
|
||||
debug!("XMRig-Proxy Tab | Rendering [API] TextEdits");
|
||||
// [HTTP API IP/Port]
|
||||
ui.group(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
let width = width / 10.0;
|
||||
ui.spacing_mut().text_edit_width = width * 2.39;
|
||||
// HTTP API
|
||||
ui.horizontal(|ui| {
|
||||
let text;
|
||||
let color;
|
||||
let len = format!("{:03}", self.api_ip.len());
|
||||
if self.api_ip.is_empty() {
|
||||
text = format!("HTTP API IP [{}/255]➖", len);
|
||||
color = LIGHT_GRAY;
|
||||
incorrect_input = true;
|
||||
} else if self.api_ip == "localhost"
|
||||
|| REGEXES.ipv4.is_match(&self.api_ip)
|
||||
|| REGEXES.domain.is_match(&self.api_ip)
|
||||
{
|
||||
text = format!("HTTP API IP [{}/255]✔", len);
|
||||
color = GREEN;
|
||||
} else {
|
||||
text = format!("HTTP API IP [{}/255]❌", len);
|
||||
color = RED;
|
||||
incorrect_input = true;
|
||||
}
|
||||
ui.add_sized(
|
||||
[width, text_edit],
|
||||
Label::new(RichText::new(text).color(color)),
|
||||
);
|
||||
ui.text_edit_singleline(&mut self.api_ip)
|
||||
.on_hover_text(XMRIG_API_IP);
|
||||
self.api_ip.truncate(255);
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
let text;
|
||||
let color;
|
||||
let len = self.api_port.len();
|
||||
if self.api_port.is_empty() {
|
||||
text = format!("HTTP API Port [ {}/5 ]➖", len);
|
||||
color = LIGHT_GRAY;
|
||||
incorrect_input = true;
|
||||
} else if REGEXES.port.is_match(&self.api_port) {
|
||||
text = format!("HTTP API Port [ {}/5 ]✔", len);
|
||||
color = GREEN;
|
||||
} else {
|
||||
text = format!("HTTP API Port [ {}/5 ]❌", len);
|
||||
color = RED;
|
||||
incorrect_input = true;
|
||||
}
|
||||
ui.add_sized(
|
||||
[width, text_edit],
|
||||
Label::new(RichText::new(text).color(color)),
|
||||
);
|
||||
ui.text_edit_singleline(&mut self.api_port)
|
||||
.on_hover_text(XMRIG_API_PORT);
|
||||
self.api_port.truncate(5);
|
||||
});
|
||||
});
|
||||
|
||||
let text = &api.lock().unwrap().output;
|
||||
console(ui, text);
|
||||
//---------------------------------------------------------------------------------------------------- [Advanced] Console
|
||||
if !self.simple {
|
||||
ui.separator();
|
||||
input_args_field(
|
||||
ui,
|
||||
buffer,
|
||||
process,
|
||||
r#"Commands: [h]ashrate, [c]onnections, [v]erbose, [w]orkers"#,
|
||||
XMRIG_PROXY_INPUT,
|
||||
);
|
||||
}
|
||||
});
|
||||
if !self.simple {
|
||||
//---------------------------------------------------------------------------------------------------- Arguments
|
||||
debug!("XMRig-Proxy Tab | Rendering [Arguments]");
|
||||
ui.horizontal(|ui| {
|
||||
start_options_field(
|
||||
ui,
|
||||
&mut self.arguments,
|
||||
r#"--url <...> --user <...> --config <...>"#,
|
||||
XMRIG_PROXY_ARGUMENTS,
|
||||
);
|
||||
});
|
||||
if !self.arguments.is_empty() {
|
||||
ui.disable();
|
||||
}
|
||||
ui.add_space(SPACE);
|
||||
// ui.style_mut().spacing.icon_width_inner = width / 45.0;
|
||||
// ui.style_mut().spacing.icon_width = width / 35.0;
|
||||
// ui.style_mut().spacing.icon_spacing = space_h;
|
||||
ui.checkbox(
|
||||
&mut self.redirect_local_xmrig,
|
||||
"Auto Redirect local Xmrig to Xmrig-Proxy",
|
||||
)
|
||||
.on_hover_text(XMRIG_PROXY_REDIRECT);
|
||||
|
||||
debug!("XMRig-Proxy Tab | Rendering [TLS/Keepalive] buttons");
|
||||
ui.vertical(|ui| {
|
||||
// TLS/Keepalive
|
||||
ui.horizontal(|ui| {
|
||||
let width = (ui.available_width() / 2.0) - 11.0;
|
||||
let height = text_edit * 2.0;
|
||||
let size = vec2(width, height);
|
||||
// let mut style = (*ctx.style()).clone();
|
||||
// style.spacing.icon_width_inner = width / 8.0;
|
||||
// style.spacing.icon_width = width / 6.0;
|
||||
// style.spacing.icon_spacing = 20.0;
|
||||
// ctx.set_style(style);
|
||||
ui.add_sized(size, Checkbox::new(&mut self.tls, "TLS Connection"))
|
||||
.on_hover_text(XMRIG_TLS);
|
||||
ui.separator();
|
||||
ui.add_sized(size, Checkbox::new(&mut self.keepalive, "Keepalive"))
|
||||
.on_hover_text(XMRIG_KEEPALIVE);
|
||||
// idea
|
||||
// need to warn the user if local firewall is blocking port
|
||||
// need to warn the user if NAT is blocking port
|
||||
// need to show local ip address
|
||||
// need to show public ip
|
||||
|
||||
debug!("XMRig-Proxy Tab | Rendering [Pool List] elements");
|
||||
// let width = ui.available_width() - 10.0;
|
||||
let mut incorrect_input = false; // This will disable [Add/Delete] on bad input
|
||||
// [Pool IP/Port]
|
||||
ui.horizontal(|ui| {
|
||||
ui.group(|ui| {
|
||||
// let width = width / 10.0;
|
||||
ui.vertical(|ui| {
|
||||
if !self.name_field(ui) {
|
||||
incorrect_input = false;
|
||||
}
|
||||
if !self.ip_field(ui) {
|
||||
incorrect_input = false;
|
||||
}
|
||||
if !self.rpc_port_field(ui) {
|
||||
incorrect_input = false;
|
||||
}
|
||||
if !self.rig_field(ui) {
|
||||
incorrect_input = false;
|
||||
}
|
||||
});
|
||||
|
||||
ui.vertical(|ui| {
|
||||
list_poolnode(
|
||||
ui,
|
||||
&mut (&mut self.name, &mut self.ip, &mut self.port, &mut self.rig),
|
||||
&mut self.selected_pool,
|
||||
pool_vec,
|
||||
incorrect_input,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
ui.add_space(5.0);
|
||||
|
||||
debug!("XMRig-Proxy Tab | Rendering [API] TextEdits");
|
||||
// [HTTP API IP/Port]
|
||||
ui.group(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
// HTTP API
|
||||
self.api_ip_field(ui);
|
||||
self.api_port_field(ui);
|
||||
});
|
||||
|
||||
ui.separator();
|
||||
|
||||
debug!("XMRig-Proxy Tab | Rendering [TLS/Keepalive] buttons");
|
||||
ui.vertical(|ui| {
|
||||
// TLS/Keepalive
|
||||
ui.horizontal(|ui| {
|
||||
let width = (ui.available_width() / 2.0) - 11.0;
|
||||
let height = height_txt_before_button(ui, &TextStyle::Button) * 2.0;
|
||||
let size = vec2(width, height);
|
||||
ui.add_sized(size, Checkbox::new(&mut self.tls, "TLS Connection"))
|
||||
.on_hover_text(XMRIG_TLS);
|
||||
ui.separator();
|
||||
ui.add_sized(size, Checkbox::new(&mut self.keepalive, "Keepalive"))
|
||||
.on_hover_text(XMRIG_KEEPALIVE);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
fn name_field(&mut self, ui: &mut Ui) -> bool {
|
||||
StateTextEdit::new(ui)
|
||||
.description(" Name ")
|
||||
.max_ch(30)
|
||||
.help_msg(XMRIG_NAME)
|
||||
.validations(&[|x| REGEXES.name.is_match(x)])
|
||||
.build(ui, &mut self.name)
|
||||
}
|
||||
fn rpc_port_field(&mut self, ui: &mut Ui) -> bool {
|
||||
StateTextEdit::new(ui)
|
||||
.description(" RPC PORT ")
|
||||
.max_ch(5)
|
||||
.help_msg(XMRIG_PORT)
|
||||
.validations(&[|x| REGEXES.port.is_match(x)])
|
||||
.build(ui, &mut self.port)
|
||||
}
|
||||
fn ip_field(&mut self, ui: &mut Ui) -> bool {
|
||||
StateTextEdit::new(ui)
|
||||
.description(" IP ")
|
||||
.max_ch(255)
|
||||
.help_msg(XMRIG_IP)
|
||||
.validations(&[|x| REGEXES.ipv4.is_match(x) || REGEXES.domain.is_match(x)])
|
||||
.build(ui, &mut self.ip)
|
||||
}
|
||||
fn rig_field(&mut self, ui: &mut Ui) -> bool {
|
||||
StateTextEdit::new(ui)
|
||||
.description(" Name ")
|
||||
.max_ch(30)
|
||||
.help_msg(XMRIG_RIG)
|
||||
.build(ui, &mut self.rig)
|
||||
}
|
||||
fn api_ip_field(&mut self, ui: &mut Ui) -> bool {
|
||||
StateTextEdit::new(ui)
|
||||
.description(" API IP ")
|
||||
.max_ch(255)
|
||||
.help_msg(XMRIG_API_IP)
|
||||
.validations(&[|x| REGEXES.ipv4.is_match(x) || REGEXES.domain.is_match(x)])
|
||||
.build(ui, &mut self.api_ip)
|
||||
}
|
||||
fn api_port_field(&mut self, ui: &mut Ui) -> bool {
|
||||
StateTextEdit::new(ui)
|
||||
.description(" API PORT ")
|
||||
.max_ch(5)
|
||||
.help_msg(XMRIG_API_PORT)
|
||||
.validations(&[|x| REGEXES.port.is_match(x)])
|
||||
.build(ui, &mut self.api_port)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,30 +1,33 @@
|
|||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use egui::{Image, RichText, TextEdit, TextStyle, Ui, Vec2, vec2};
|
||||
use egui::{Align, Image, RichText, ScrollArea, TextStyle, Ui};
|
||||
use log::debug;
|
||||
use readable::num::Float;
|
||||
use readable::up::Uptime;
|
||||
use strum::EnumCount;
|
||||
|
||||
use crate::XVB_MINING_ON_FIELD;
|
||||
use crate::app::panels::middle::common::console::console;
|
||||
use crate::app::panels::middle::common::header_tab::header_tab;
|
||||
use crate::app::panels::middle::common::state_edit_field::StateTextEdit;
|
||||
use crate::disk::state::{ManualDonationLevel, ManualDonationMetric, XvbMode};
|
||||
use crate::helper::xrig::xmrig::PubXmrigApi;
|
||||
use crate::helper::xrig::xmrig_proxy::PubXmrigProxyApi;
|
||||
use crate::helper::xvb::PubXvbApi;
|
||||
use crate::helper::xvb::priv_stats::RuntimeMode;
|
||||
use crate::regex::num_lines;
|
||||
use crate::miscs::height_txt_before_button;
|
||||
use crate::utils::constants::{
|
||||
GREEN, LIGHT_GRAY, ORANGE, RED, XVB_DONATED_1H_FIELD, XVB_DONATED_24H_FIELD,
|
||||
XVB_DONATION_LEVEL_DONOR_HELP, XVB_DONATION_LEVEL_MEGA_DONOR_HELP,
|
||||
XVB_DONATION_LEVEL_VIP_DONOR_HELP, XVB_DONATION_LEVEL_WHALE_DONOR_HELP, XVB_FAILURE_FIELD,
|
||||
XVB_HELP, XVB_HERO_SELECT, XVB_MANUAL_SLIDER_MANUAL_P2POOL_HELP,
|
||||
XVB_MANUAL_SLIDER_MANUAL_XVB_HELP, XVB_MODE_MANUAL_DONATION_LEVEL_HELP,
|
||||
XVB_MODE_MANUAL_P2POOL_HELP, XVB_MODE_MANUAL_XVB_HELP, XVB_ROUND_TYPE_FIELD, XVB_TOKEN_FIELD,
|
||||
XVB_TOKEN_LEN, XVB_URL_RULES, XVB_WINNER_FIELD,
|
||||
ORANGE, XVB_DONATED_1H_FIELD, XVB_DONATED_24H_FIELD, XVB_DONATION_LEVEL_DONOR_HELP,
|
||||
XVB_DONATION_LEVEL_MEGA_DONOR_HELP, XVB_DONATION_LEVEL_VIP_DONOR_HELP,
|
||||
XVB_DONATION_LEVEL_WHALE_DONOR_HELP, XVB_FAILURE_FIELD, XVB_HELP, XVB_HERO_SELECT,
|
||||
XVB_MANUAL_SLIDER_MANUAL_P2POOL_HELP, XVB_MANUAL_SLIDER_MANUAL_XVB_HELP,
|
||||
XVB_MODE_MANUAL_DONATION_LEVEL_HELP, XVB_MODE_MANUAL_P2POOL_HELP, XVB_MODE_MANUAL_XVB_HELP,
|
||||
XVB_ROUND_TYPE_FIELD, XVB_TOKEN_LEN, XVB_URL_RULES, XVB_WINNER_FIELD,
|
||||
};
|
||||
use crate::utils::regex::Regexes;
|
||||
use crate::{
|
||||
constants::{BYTES_XVB, SPACE},
|
||||
utils::constants::{DARK_GRAY, XVB_URL},
|
||||
utils::constants::XVB_URL,
|
||||
};
|
||||
|
||||
impl crate::disk::state::Xvb {
|
||||
|
@ -32,7 +35,6 @@ impl crate::disk::state::Xvb {
|
|||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn show(
|
||||
&mut self,
|
||||
size: Vec2,
|
||||
address: &str,
|
||||
_ctx: &egui::Context,
|
||||
ui: &mut egui::Ui,
|
||||
|
@ -41,96 +43,47 @@ impl crate::disk::state::Xvb {
|
|||
gui_api_xp: &Arc<Mutex<PubXmrigProxyApi>>,
|
||||
is_alive: bool,
|
||||
) {
|
||||
// let text_edit = ui.available_height() / 25.0;
|
||||
// let website_height = ui.available_height() / 10.0;
|
||||
|
||||
// logo and website link
|
||||
let logo = Some(Image::from_bytes("bytes:/xvb.png", BYTES_XVB));
|
||||
header_tab(
|
||||
ui,
|
||||
logo,
|
||||
&[
|
||||
("XMRvsBEAST", XVB_URL, ""),
|
||||
(
|
||||
"Rules",
|
||||
XVB_URL_RULES,
|
||||
"Click here to read the rules and understand how the raffle works.",
|
||||
),
|
||||
("FAQ", "https://xmrvsbeast.com/p2pool/faq.html", ""),
|
||||
],
|
||||
None,
|
||||
true,
|
||||
);
|
||||
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||
|
||||
let text_edit = size.y / 25.0;
|
||||
let website_height = size.y / 10.0;
|
||||
let width = size.x;
|
||||
let height = size.y;
|
||||
let space_h = height / 48.0;
|
||||
|
||||
// logo and website link
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.add_sized(
|
||||
[width, website_height],
|
||||
Image::from_bytes("bytes:/xvb.png", BYTES_XVB),
|
||||
);
|
||||
ui.style_mut().override_text_style = Some(TextStyle::Heading);
|
||||
ui.add_space(space_h);
|
||||
ui.hyperlink_to("XMRvsBeast", XVB_URL);
|
||||
ui.add_space(space_h);
|
||||
});
|
||||
|
||||
// console output for log
|
||||
debug!("XvB Tab | Rendering [Console]");
|
||||
ui.group(|ui| {
|
||||
let text = &api.lock().unwrap().output;
|
||||
let nb_lines = num_lines(text);
|
||||
let height = size.y / 2.8;
|
||||
let width = size.x - (space_h / 2.0);
|
||||
egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| {
|
||||
ui.style_mut().override_text_style = Some(TextStyle::Small);
|
||||
egui::ScrollArea::vertical()
|
||||
.stick_to_bottom(true)
|
||||
.max_width(width)
|
||||
.max_height(height)
|
||||
.auto_shrink([false; 2])
|
||||
// .show_viewport(ui, |ui, _| {
|
||||
.show_rows(
|
||||
ui,
|
||||
ui.text_style_height(&TextStyle::Small),
|
||||
nb_lines,
|
||||
|ui, row_range| {
|
||||
for i in row_range {
|
||||
if let Some(line) = text.lines().nth(i) {
|
||||
ui.label(line);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
// let nb_lines = num_lines(text);
|
||||
console(ui, text);
|
||||
});
|
||||
// input token
|
||||
let len_token = format!("{}", self.token.len());
|
||||
let (text, color) = if self.token.is_empty() {
|
||||
(
|
||||
format!("{} [{}/{}] ➖", XVB_TOKEN_FIELD, len_token, XVB_TOKEN_LEN),
|
||||
LIGHT_GRAY,
|
||||
)
|
||||
} else if self.token.parse::<u32>().is_ok() && self.token.len() < XVB_TOKEN_LEN {
|
||||
(
|
||||
format!("{} [{}/{}]", XVB_TOKEN_FIELD, len_token, XVB_TOKEN_LEN),
|
||||
GREEN,
|
||||
)
|
||||
} else if self.token.parse::<u32>().is_ok() && self.token.len() == XVB_TOKEN_LEN {
|
||||
(format!("{} ✔", XVB_TOKEN_FIELD), GREEN)
|
||||
} else {
|
||||
(
|
||||
format!("{} [{}/{}] ❌", XVB_TOKEN_FIELD, len_token, XVB_TOKEN_LEN),
|
||||
RED,
|
||||
)
|
||||
};
|
||||
ui.add_space(space_h);
|
||||
ui.add_space(SPACE);
|
||||
ui.horizontal(|ui| {
|
||||
// hovering text is difficult because egui doesn't hover over inner widget. But on disabled does.
|
||||
ui.group(|ui| {
|
||||
ui.colored_label(color, text)
|
||||
.on_hover_text(XVB_HELP);
|
||||
ui.add(
|
||||
TextEdit::singleline(&mut self.token)
|
||||
.char_limit(9)
|
||||
.desired_width(ui.text_style_height(&TextStyle::Body) * 9.0)
|
||||
.vertical_align(egui::Align::Center),
|
||||
).on_hover_text(XVB_HELP)
|
||||
ui.group(|ui|{
|
||||
ui.style_mut().override_text_valign = Some(Align::Center);
|
||||
// ui.set_height(height_txt_before_button(ui, &TextStyle::Body));
|
||||
self.field_token(ui);
|
||||
});
|
||||
// .on_hover_text(XVB_HELP);
|
||||
ui.add_space(height / 48.0);
|
||||
ui.style_mut().spacing.icon_width_inner = width / 45.0;
|
||||
ui.style_mut().spacing.icon_width = width / 35.0;
|
||||
ui.style_mut().spacing.icon_spacing = space_h;
|
||||
|
||||
// --------------------------- XVB Simple -------------------------------------------
|
||||
if self.simple {
|
||||
ui.add_space(SPACE);
|
||||
ui.checkbox(&mut self.simple_hero_mode, "Hero Mode").on_hover_text(XVB_HERO_SELECT);
|
||||
// set runtime mode immediately if we are on simple mode.
|
||||
if self.simple_hero_mode {
|
||||
|
@ -140,14 +93,19 @@ impl crate::disk::state::Xvb {
|
|||
}
|
||||
}
|
||||
});
|
||||
ui.add_space(space_h);
|
||||
ui.add_space(SPACE);
|
||||
// --------------------------- XVB Advanced -----------------------------------------
|
||||
if !self.simple {
|
||||
|
||||
ui.group(|ui| {
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
egui::ComboBox::from_label("")
|
||||
ui.style_mut().override_text_valign = Some(Align::Center);
|
||||
ui.set_height(0.0);
|
||||
// ui.horizontal_centered(|ui| {
|
||||
ui.set_height(0.0);
|
||||
let text_height = height_txt_before_button(ui, &TextStyle::Heading) * 1.4;
|
||||
// let text_height = 0.0;
|
||||
egui::ComboBox::from_label("").height(XvbMode::COUNT as f32 * (ui.text_style_height(&TextStyle::Button) + (ui.spacing().button_padding.y * 2.0) + ui.spacing().item_spacing.y))
|
||||
.selected_text(self.mode.to_string())
|
||||
.show_ui(ui, |ui| {
|
||||
ui.selectable_value(&mut self.mode, XvbMode::Auto,
|
||||
|
@ -166,7 +124,7 @@ impl crate::disk::state::Xvb {
|
|||
});
|
||||
if self.mode == XvbMode::ManualXvb || self.mode == XvbMode::ManualP2pool {
|
||||
|
||||
ui.add_space(space_h);
|
||||
ui.add_space(SPACE);
|
||||
|
||||
let default_xmrig_hashrate = match self.manual_donation_metric {
|
||||
ManualDonationMetric::Hash => 1_000.0,
|
||||
|
@ -203,22 +161,22 @@ impl crate::disk::state::Xvb {
|
|||
|
||||
ui.horizontal(|ui| {
|
||||
|
||||
if ui.add(egui::SelectableLabel::new(self.manual_donation_metric == ManualDonationMetric::Hash, "Hash")).clicked() {
|
||||
if ui.add_sized([0.0, text_height],egui::SelectableLabel::new(self.manual_donation_metric == ManualDonationMetric::Hash, "Hash")).clicked() {
|
||||
self.manual_donation_metric = ManualDonationMetric::Hash;
|
||||
self.manual_slider_amount = self.manual_amount_raw;
|
||||
}
|
||||
if ui.add(egui::SelectableLabel::new(self.manual_donation_metric == ManualDonationMetric::Kilo, "Kilo")).clicked() {
|
||||
if ui.add_sized([0.0, text_height],egui::SelectableLabel::new(self.manual_donation_metric == ManualDonationMetric::Kilo, "Kilo")).clicked() {
|
||||
self.manual_donation_metric = ManualDonationMetric::Kilo;
|
||||
self.manual_slider_amount = self.manual_amount_raw / 1000.0;
|
||||
};
|
||||
if ui.add(egui::SelectableLabel::new(self.manual_donation_metric == ManualDonationMetric::Mega, "Mega")).clicked() {
|
||||
if ui.add_sized([0.0, text_height],egui::SelectableLabel::new(self.manual_donation_metric == ManualDonationMetric::Mega, "Mega")).clicked() {
|
||||
self.manual_donation_metric = ManualDonationMetric::Mega;
|
||||
self.manual_slider_amount = self.manual_amount_raw / 1_000_000.0;
|
||||
};
|
||||
|
||||
ui.spacing_mut().slider_width = width * 0.5;
|
||||
// less menu, less metrics buttons,less space, less metrics.
|
||||
ui.spacing_mut().slider_width = ui.available_width() * 0.3;
|
||||
ui.add_sized(
|
||||
[width, text_edit],
|
||||
[ui.available_width(), text_height],
|
||||
egui::Slider::new(&mut self.manual_slider_amount, 0.0..=(hashrate_xmrig as f64))
|
||||
.text(self.manual_donation_metric.to_string())
|
||||
.max_decimals(3)
|
||||
|
@ -228,6 +186,8 @@ impl crate::disk::state::Xvb {
|
|||
}
|
||||
|
||||
if self.mode == XvbMode::ManualDonationLevel {
|
||||
ui.add_space(SPACE);
|
||||
ui.horizontal(|ui| {
|
||||
ui.radio_value(&mut self.manual_donation_level, ManualDonationLevel::Donor,
|
||||
ManualDonationLevel::Donor.to_string())
|
||||
.on_hover_text(XVB_DONATION_LEVEL_DONOR_HELP);
|
||||
|
@ -242,10 +202,12 @@ impl crate::disk::state::Xvb {
|
|||
.on_hover_text(XVB_DONATION_LEVEL_MEGA_DONOR_HELP);
|
||||
|
||||
api.lock().unwrap().stats_priv.runtime_manual_donation_level = self.manual_donation_level.clone().into();
|
||||
});
|
||||
}
|
||||
ui.add_space(SPACE);
|
||||
});
|
||||
});
|
||||
});
|
||||
// });
|
||||
|
||||
// Update manual_amount_raw based on slider
|
||||
match self.manual_donation_metric {
|
||||
|
@ -263,18 +225,18 @@ impl crate::disk::state::Xvb {
|
|||
// Set runtime_mode & runtime_manual_amount
|
||||
api.lock().unwrap().stats_priv.runtime_mode = self.mode.clone().into();
|
||||
api.lock().unwrap().stats_priv.runtime_manual_amount = self.manual_amount_raw;
|
||||
ui.add_space(space_h);
|
||||
ui.add_space(SPACE);
|
||||
|
||||
// allow user to modify the buffer for p2pool
|
||||
// button
|
||||
ui.add_sized(
|
||||
[width, text_edit],
|
||||
[ui.available_width() * 0.8, height_txt_before_button(ui, &TextStyle::Button)],
|
||||
egui::Slider::new(&mut self.p2pool_buffer, -100..=100)
|
||||
.text("% P2Pool Buffer" )
|
||||
).on_hover_text("Set the % amount of additional HR to send to p2pool. Will reduce (if positive) or augment (if negative) the chances to miss the p2pool window");
|
||||
}
|
||||
|
||||
ui.add_space(space_h);
|
||||
ui.add_space(SPACE);
|
||||
// need to warn the user if no address is set in p2pool tab
|
||||
if !Regexes::addr_ok(address) {
|
||||
debug!("XvB Tab | Rendering warning text");
|
||||
|
@ -284,100 +246,62 @@ impl crate::disk::state::Xvb {
|
|||
});
|
||||
}
|
||||
// private stats
|
||||
ui.add_space(space_h);
|
||||
ui.add_space(SPACE);
|
||||
// ui.add_enabled_ui(is_alive, |ui| {
|
||||
ui.add_enabled_ui(is_alive, |ui| {
|
||||
let api = &api.lock().unwrap();
|
||||
let priv_stats = &api.stats_priv;
|
||||
let current_node = &api.current_node;
|
||||
let width_stat = (ui.available_width() - SPACE * 4.0) / 5.0;
|
||||
let height_stat = 0.0;
|
||||
let size_stat = vec2(width_stat, height_stat);
|
||||
ui.horizontal(|ui| {
|
||||
let round = match &priv_stats.round_participate {
|
||||
Some(r) => r.to_string(),
|
||||
None => "None".to_string(),
|
||||
};
|
||||
ui.add_sized(size_stat, |ui: &mut Ui| {
|
||||
ui.group(|ui| {
|
||||
let size_stat = vec2(
|
||||
ui.available_width(),
|
||||
0.0, // + ui.spacing().item_spacing.y,
|
||||
);
|
||||
ui.add_sized(size_stat, |ui: &mut Ui| {
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(XVB_FAILURE_FIELD);
|
||||
ui.label(priv_stats.fails.to_string());
|
||||
})
|
||||
.response
|
||||
});
|
||||
ui.separator();
|
||||
ui.add_sized(size_stat, |ui: &mut Ui| {
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(XVB_DONATED_1H_FIELD);
|
||||
ui.label(
|
||||
[
|
||||
let style_height = ui.text_style_height(&TextStyle::Body);
|
||||
ui.spacing_mut().item_spacing = [style_height * 2.0, style_height * 2.0].into();
|
||||
|
||||
// let width_stat = (ui.available_width() - SPACE * 4.0) / 5.0;
|
||||
let width_column = ui.text_style_height(&TextStyle::Body) * 16.0;
|
||||
let height_column = 0.0;
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||
ScrollArea::horizontal().id_salt("horizontal").show(ui, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
// Failures
|
||||
stat_box(ui, XVB_FAILURE_FIELD, &priv_stats.fails.to_string(), width_column, height_column, style_height);
|
||||
stat_box(ui, XVB_DONATED_1H_FIELD,
|
||||
&[
|
||||
Float::from_3(priv_stats.donor_1hr_avg as f64).to_string(),
|
||||
" kH/s".to_string(),
|
||||
]
|
||||
.concat(),
|
||||
);
|
||||
})
|
||||
.response
|
||||
});
|
||||
ui.separator();
|
||||
ui.add_sized(size_stat, |ui: &mut Ui| {
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(XVB_DONATED_24H_FIELD);
|
||||
ui.label(
|
||||
[
|
||||
.concat()
|
||||
, width_column, height_column, style_height);
|
||||
stat_box(ui, XVB_DONATED_24H_FIELD,
|
||||
&[
|
||||
Float::from_3(priv_stats.donor_24hr_avg as f64).to_string(),
|
||||
" kH/s".to_string(),
|
||||
]
|
||||
.concat(),
|
||||
);
|
||||
})
|
||||
.response
|
||||
});
|
||||
ui.separator();
|
||||
.concat()
|
||||
, width_column, height_column, style_height);
|
||||
ui.add_enabled_ui(priv_stats.round_participate.is_some(), |ui| {
|
||||
ui.add_sized(size_stat, |ui: &mut Ui| {
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(XVB_ROUND_TYPE_FIELD);
|
||||
ui.label(round);
|
||||
})
|
||||
.response
|
||||
})
|
||||
let round = match &priv_stats.round_participate {
|
||||
Some(r) => r.to_string(),
|
||||
None => "None".to_string(),
|
||||
};
|
||||
stat_box(ui, XVB_ROUND_TYPE_FIELD, &round, width_column, height_column, style_height);
|
||||
}).response
|
||||
.on_disabled_hover_text(
|
||||
"You do not yet have a share in the PPLNS Window.",
|
||||
);
|
||||
});
|
||||
ui.separator();
|
||||
ui.add_sized(size_stat, |ui: &mut Ui| {
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(XVB_WINNER_FIELD);
|
||||
ui.label(if priv_stats.win_current {
|
||||
stat_box(ui, XVB_WINNER_FIELD,
|
||||
if priv_stats.win_current {
|
||||
"You are Winning the round !"
|
||||
} else {
|
||||
"You are not the winner"
|
||||
});
|
||||
})
|
||||
.response
|
||||
});
|
||||
})
|
||||
.response
|
||||
});
|
||||
}
|
||||
, width_column, height_column, style_height);
|
||||
});
|
||||
// indicators
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_sized(size_stat, |ui: &mut Ui| {
|
||||
});
|
||||
ui.vertical(|ui| {
|
||||
ui.group(|ui| {
|
||||
let size_stat = vec2(
|
||||
ui.available_width(),
|
||||
0.0, // + ui.spacing().item_spacing.y,
|
||||
);
|
||||
ui.add_sized(size_stat, |ui: &mut Ui| {
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.set_width(width_column);
|
||||
ui.set_height(height_column);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.add_space(SPACE);
|
||||
ui.label(XVB_MINING_ON_FIELD)
|
||||
.on_hover_text_at_pointer(&priv_stats.msg_indicator);
|
||||
ui.label(
|
||||
|
@ -389,24 +313,44 @@ impl crate::disk::state::Xvb {
|
|||
ui.label(Uptime::from(priv_stats.time_switch_node).to_string())
|
||||
.on_hover_text_at_pointer(&priv_stats.msg_indicator)
|
||||
})
|
||||
.response
|
||||
})
|
||||
})
|
||||
});
|
||||
})
|
||||
.response
|
||||
.on_disabled_hover_text("Algorithm is not running.")
|
||||
.on_disabled_hover_text("Algorithm is not running.");
|
||||
// indicators
|
||||
})
|
||||
// currently mining on
|
||||
});
|
||||
});
|
||||
// Rules link help
|
||||
ui.horizontal_centered(|ui| {
|
||||
// can't have horizontal and vertical centering work together so fix by this.
|
||||
ui.add_space((width / 2.0) - (ui.text_style_height(&TextStyle::Heading) * 1.5));
|
||||
ui.style_mut().override_text_style = Some(TextStyle::Heading);
|
||||
ui.hyperlink_to("Rules", XVB_URL_RULES)
|
||||
.on_hover_text("Click here to read the rules and understand how the raffle works.");
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
fn field_token(&mut self, ui: &mut Ui) {
|
||||
StateTextEdit::new(ui)
|
||||
.help_msg(XVB_HELP)
|
||||
.max_ch(XVB_TOKEN_LEN as u8)
|
||||
.text_edit_width_same_as_max_ch(ui)
|
||||
.description(" Token ")
|
||||
.validations(&[|x| x.parse::<u32>().is_ok() && x.len() == XVB_TOKEN_LEN])
|
||||
.build(ui, &mut self.token);
|
||||
}
|
||||
}
|
||||
fn stat_box(
|
||||
ui: &mut Ui,
|
||||
title: &str,
|
||||
value: &str,
|
||||
column_width: f32,
|
||||
column_height: f32,
|
||||
style_height: f32,
|
||||
) {
|
||||
ui.vertical(|ui| {
|
||||
ui.group(|ui| {
|
||||
ui.set_width(column_width);
|
||||
ui.set_height(column_height);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.spacing_mut().item_spacing = [style_height / 2.0, style_height / 2.0].into();
|
||||
ui.add_space(SPACE * 3.0);
|
||||
ui.label(title);
|
||||
ui.label(value);
|
||||
ui.add_space(SPACE);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -189,7 +189,7 @@ impl crate::app::App {
|
|||
.add_sized([width, height / 2.0], Button::new("Quit"))
|
||||
.clicked()
|
||||
{
|
||||
if self.state.gupax.save_before_quit {
|
||||
if self.state.gupax.auto.save_before_quit {
|
||||
self.save_before_quit();
|
||||
}
|
||||
exit(0);
|
||||
|
|
|
@ -12,8 +12,6 @@ impl crate::app::App {
|
|||
ui.style_mut().spacing.item_spacing.x = 4.0;
|
||||
// spacing of separator, will reduce width size of the button. Low value so that tabs can be selected easily.
|
||||
let spacing_separator = 2.0;
|
||||
// TODO if screen smaller, go on two lines.
|
||||
// TODO if screen really to small, go on tab per line.
|
||||
ui.with_layout(egui::Layout::left_to_right(egui::Align::Min), |ui| {
|
||||
ui.style_mut().override_text_style = Some(TextStyle::Heading);
|
||||
let height = ui
|
||||
|
|
|
@ -16,11 +16,11 @@ impl App {
|
|||
return None;
|
||||
}
|
||||
info!("quit");
|
||||
if self.state.gupax.ask_before_quit {
|
||||
if self.state.gupax.auto.ask_before_quit {
|
||||
// If we're already on the [ask_before_quit] screen and
|
||||
// the user tried to exit again, exit.
|
||||
if self.error_state.quit_twice {
|
||||
if self.state.gupax.save_before_quit {
|
||||
if self.state.gupax.auto.save_before_quit {
|
||||
self.save_before_quit();
|
||||
}
|
||||
return Some(ViewportCommand::Close);
|
||||
|
@ -32,7 +32,7 @@ impl App {
|
|||
Some(ViewportCommand::CancelClose)
|
||||
// Else, just quit.
|
||||
} else {
|
||||
if self.state.gupax.save_before_quit {
|
||||
if self.state.gupax.auto.save_before_quit {
|
||||
self.save_before_quit();
|
||||
}
|
||||
Some(ViewportCommand::Close)
|
||||
|
|
|
@ -241,7 +241,7 @@ impl Update {
|
|||
#[cfg(feature = "distro")]
|
||||
return;
|
||||
// verify validity of absolute path for p2pool, xmrig and xmrig-proxy only if we want to update them.
|
||||
if og.lock().unwrap().gupax.bundled {
|
||||
if og.lock().unwrap().gupax.auto.bundled {
|
||||
// Check P2Pool path for safety
|
||||
// Attempt relative to absolute path
|
||||
// it's ok if file doesn't exist. User could enable bundled version for the first time.
|
||||
|
@ -465,7 +465,7 @@ impl Update {
|
|||
// arch
|
||||
// standalone or bundled
|
||||
// archive extension
|
||||
let bundle = if og.lock().unwrap().gupax.bundled {
|
||||
let bundle = if og.lock().unwrap().gupax.auto.bundled {
|
||||
"bundle"
|
||||
} else {
|
||||
"standalone"
|
||||
|
@ -577,7 +577,7 @@ impl Update {
|
|||
path.display()
|
||||
);
|
||||
// if bundled, create directory for p2pool, xmrig and xmrig-proxy if not present
|
||||
if og.lock().unwrap().gupax.bundled
|
||||
if og.lock().unwrap().gupax.auto.bundled
|
||||
&& (name == P2POOL_BINARY
|
||||
|| name == XMRIG_BINARY
|
||||
|| name == XMRIG_PROXY_BINARY
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
use crate::disk::*;
|
||||
use crate::{app::panels::middle::common::list_poolnode::PoolNode, disk::*};
|
||||
use serde::{Deserialize, Serialize};
|
||||
//---------------------------------------------------------------------------------------------------- [Node] Impl
|
||||
impl Node {
|
||||
pub fn localhost() -> Self {
|
||||
Self {
|
||||
pub fn localhost() -> PoolNode {
|
||||
PoolNode::Node(Self {
|
||||
ip: "localhost".to_string(),
|
||||
rpc: "18081".to_string(),
|
||||
zmq: "18083".to_string(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_vec() -> Vec<(String, Self)> {
|
||||
pub fn new_vec() -> Vec<(String, PoolNode)> {
|
||||
vec![("Local Monero Node".to_string(), Self::localhost())]
|
||||
}
|
||||
|
||||
pub fn new_tuple() -> (String, Self) {
|
||||
pub fn new_tuple() -> (String, PoolNode) {
|
||||
("Local Monero Node".to_string(), Self::localhost())
|
||||
}
|
||||
|
||||
// Convert [String] to [Node] Vec
|
||||
pub fn from_str_to_vec(string: &str) -> Result<Vec<(String, Self)>, TomlError> {
|
||||
pub fn from_str_to_vec(string: &str) -> Result<Vec<(String, PoolNode)>, TomlError> {
|
||||
let nodes: toml::map::Map<String, toml::Value> = match toml::de::from_str(string) {
|
||||
Ok(map) => {
|
||||
info!("Node | Parse ... OK");
|
||||
|
@ -73,20 +73,23 @@ impl Node {
|
|||
}
|
||||
};
|
||||
let node = Node { ip, rpc, zmq };
|
||||
vec.push((key.clone(), node));
|
||||
vec.push((key.clone(), PoolNode::Node(node)));
|
||||
}
|
||||
Ok(vec)
|
||||
}
|
||||
|
||||
// Convert [Vec<(String, Self)>] into [String]
|
||||
// that can be written as a proper TOML file
|
||||
pub fn to_string(vec: &[(String, Self)]) -> Result<String, TomlError> {
|
||||
pub fn to_string(vec: &[(String, PoolNode)]) -> Result<String, TomlError> {
|
||||
let mut toml = String::new();
|
||||
for (key, value) in vec.iter() {
|
||||
write!(
|
||||
toml,
|
||||
"[\'{}\']\nip = {:#?}\nrpc = {:#?}\nzmq = {:#?}\n\n",
|
||||
key, value.ip, value.rpc, value.zmq,
|
||||
key,
|
||||
value.ip(),
|
||||
value.port(),
|
||||
value.custom(),
|
||||
)?;
|
||||
}
|
||||
Ok(toml)
|
||||
|
@ -97,7 +100,7 @@ impl Node {
|
|||
// |_ Create a default file if not found
|
||||
// 2. Deserialize [String] into a proper [Struct]
|
||||
// |_ Attempt to merge if deserialization fails
|
||||
pub fn get(path: &PathBuf) -> Result<Vec<(String, Self)>, TomlError> {
|
||||
pub fn get(path: &PathBuf) -> Result<Vec<(String, PoolNode)>, TomlError> {
|
||||
// Read
|
||||
let file = File::Node;
|
||||
let string = match read_to_string(file, path) {
|
||||
|
@ -114,7 +117,7 @@ impl Node {
|
|||
|
||||
// Completely overwrite current [node.toml]
|
||||
// with a new default version, and return [Vec<String, Self>].
|
||||
pub fn create_new(path: &PathBuf) -> Result<Vec<(String, Self)>, TomlError> {
|
||||
pub fn create_new(path: &PathBuf) -> Result<Vec<(String, PoolNode)>, TomlError> {
|
||||
info!("Node | Creating new default...");
|
||||
let new = Self::new_vec();
|
||||
let string = Self::to_string(&Self::new_vec())?;
|
||||
|
@ -124,7 +127,7 @@ impl Node {
|
|||
}
|
||||
|
||||
// Save [Node] onto disk file [node.toml]
|
||||
pub fn save(vec: &[(String, Self)], path: &PathBuf) -> Result<(), TomlError> {
|
||||
pub fn save(vec: &[(String, PoolNode)], path: &PathBuf) -> Result<(), TomlError> {
|
||||
info!("Node | Saving to disk ... [{}]", path.display());
|
||||
let string = Self::to_string(vec)?;
|
||||
match fs::write(path, string) {
|
||||
|
|
|
@ -1,23 +1,25 @@
|
|||
use crate::app::panels::middle::common::list_poolnode::PoolNode;
|
||||
|
||||
use super::*;
|
||||
//---------------------------------------------------------------------------------------------------- [Pool] impl
|
||||
impl Pool {
|
||||
pub fn p2pool() -> Self {
|
||||
Self {
|
||||
pub fn p2pool() -> PoolNode {
|
||||
PoolNode::Pool(Self {
|
||||
rig: GUPAX_VERSION_UNDERSCORE.to_string(),
|
||||
ip: "localhost".to_string(),
|
||||
port: "3333".to_string(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_vec() -> Vec<(String, Self)> {
|
||||
pub fn new_vec() -> Vec<(String, PoolNode)> {
|
||||
vec![("Local P2Pool".to_string(), Self::p2pool())]
|
||||
}
|
||||
|
||||
pub fn new_tuple() -> (String, Self) {
|
||||
pub fn new_tuple() -> (String, PoolNode) {
|
||||
("Local P2Pool".to_string(), Self::p2pool())
|
||||
}
|
||||
|
||||
pub fn from_str_to_vec(string: &str) -> Result<Vec<(String, Self)>, TomlError> {
|
||||
pub fn from_str_to_vec(string: &str) -> Result<Vec<(String, PoolNode)>, TomlError> {
|
||||
let pools: toml::map::Map<String, toml::Value> = match toml::de::from_str(string) {
|
||||
Ok(map) => {
|
||||
info!("Pool | Parse ... OK");
|
||||
|
@ -72,24 +74,27 @@ impl Pool {
|
|||
}
|
||||
};
|
||||
let pool = Pool { rig, ip, port };
|
||||
vec.push((key.clone(), pool));
|
||||
vec.push((key.clone(), PoolNode::Pool(pool)));
|
||||
}
|
||||
Ok(vec)
|
||||
}
|
||||
|
||||
pub fn to_string(vec: &[(String, Self)]) -> Result<String, TomlError> {
|
||||
pub fn to_string(vec: &[(String, PoolNode)]) -> Result<String, TomlError> {
|
||||
let mut toml = String::new();
|
||||
for (key, value) in vec.iter() {
|
||||
write!(
|
||||
toml,
|
||||
"[\'{}\']\nrig = {:#?}\nip = {:#?}\nport = {:#?}\n\n",
|
||||
key, value.rig, value.ip, value.port,
|
||||
key,
|
||||
value.custom(),
|
||||
value.ip(),
|
||||
value.port(),
|
||||
)?;
|
||||
}
|
||||
Ok(toml)
|
||||
}
|
||||
|
||||
pub fn get(path: &PathBuf) -> Result<Vec<(String, Self)>, TomlError> {
|
||||
pub fn get(path: &PathBuf) -> Result<Vec<(String, PoolNode)>, TomlError> {
|
||||
// Read
|
||||
let file = File::Pool;
|
||||
let string = match read_to_string(file, path) {
|
||||
|
@ -104,7 +109,7 @@ impl Pool {
|
|||
Self::from_str_to_vec(&string)
|
||||
}
|
||||
|
||||
pub fn create_new(path: &PathBuf) -> Result<Vec<(String, Self)>, TomlError> {
|
||||
pub fn create_new(path: &PathBuf) -> Result<Vec<(String, PoolNode)>, TomlError> {
|
||||
info!("Pool | Creating new default...");
|
||||
let new = Self::new_vec();
|
||||
let string = Self::to_string(&Self::new_vec())?;
|
||||
|
@ -113,7 +118,7 @@ impl Pool {
|
|||
Ok(new)
|
||||
}
|
||||
|
||||
pub fn save(vec: &[(String, Self)], path: &PathBuf) -> Result<(), TomlError> {
|
||||
pub fn save(vec: &[(String, PoolNode)], path: &PathBuf) -> Result<(), TomlError> {
|
||||
info!("Pool | Saving to disk ... [{}]", path.display());
|
||||
let string = Self::to_string(vec)?;
|
||||
match fs::write(path, string) {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use anyhow::Result;
|
||||
use rand::{Rng, distributions::Alphanumeric, thread_rng};
|
||||
use strum::{EnumCount, EnumIter};
|
||||
|
||||
use super::*;
|
||||
use crate::{components::node::RemoteNode, disk::status::*};
|
||||
use crate::{components::node::RemoteNode, disk::status::*, helper::ProcessName};
|
||||
//---------------------------------------------------------------------------------------------------- [State] Impl
|
||||
impl Default for State {
|
||||
fn default() -> Self {
|
||||
|
@ -12,7 +13,7 @@ impl Default for State {
|
|||
|
||||
impl State {
|
||||
pub fn new() -> Self {
|
||||
let max_threads = benri::threads!();
|
||||
let max_threads = benri::threads!() as u16;
|
||||
let current_threads = if max_threads == 1 { 1 } else { max_threads / 2 };
|
||||
Self {
|
||||
status: Status::default(),
|
||||
|
@ -132,7 +133,6 @@ impl State {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Take [String] as input, merge it with whatever the current [default] is,
|
||||
// leaving behind old keys+values and updating [default] with old valid ones.
|
||||
pub fn merge(old: &str) -> Result<Self, TomlError> {
|
||||
|
@ -179,14 +179,7 @@ pub struct Status {
|
|||
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
|
||||
pub struct Gupax {
|
||||
pub simple: bool,
|
||||
pub auto_update: bool,
|
||||
pub auto_p2pool: bool,
|
||||
pub auto_node: bool,
|
||||
pub auto_xmrig: bool,
|
||||
pub auto_xp: bool,
|
||||
pub auto_xvb: bool,
|
||||
pub ask_before_quit: bool,
|
||||
pub save_before_quit: bool,
|
||||
pub auto: AutoEnabled,
|
||||
pub p2pool_path: String,
|
||||
pub node_path: String,
|
||||
pub xmrig_path: String,
|
||||
|
@ -200,9 +193,94 @@ pub struct Gupax {
|
|||
pub selected_scale: f32,
|
||||
pub tab: Tab,
|
||||
pub ratio: Ratio,
|
||||
pub bundled: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
|
||||
pub struct AutoEnabled {
|
||||
pub update: bool,
|
||||
pub bundled: bool,
|
||||
pub ask_before_quit: bool,
|
||||
pub save_before_quit: bool,
|
||||
pub processes: Vec<ProcessName>,
|
||||
}
|
||||
impl AutoEnabled {
|
||||
pub fn enable(&mut self, auto: &AutoStart, enable: bool) {
|
||||
match auto {
|
||||
AutoStart::Update => self.update = enable,
|
||||
AutoStart::Bundle => self.bundled = enable,
|
||||
AutoStart::AskBeforeQuit => self.ask_before_quit = enable,
|
||||
AutoStart::SaveBeforequit => self.save_before_quit = enable,
|
||||
AutoStart::Process(p) => {
|
||||
let processes = &mut self.processes;
|
||||
if !processes.iter().any(|a| a == p) && enable {
|
||||
self.processes.push(*p);
|
||||
} else if let Some(i) = processes.iter().position(|a| a == p) {
|
||||
if !enable {
|
||||
processes.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn is_enabled(&self, auto: &AutoStart) -> bool {
|
||||
match auto {
|
||||
AutoStart::Update => self.update,
|
||||
AutoStart::Bundle => self.bundled,
|
||||
AutoStart::AskBeforeQuit => self.ask_before_quit,
|
||||
AutoStart::SaveBeforequit => self.save_before_quit,
|
||||
AutoStart::Process(p) => self.processes.iter().any(|a| a == p),
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(PartialEq, strum::Display, EnumCount, EnumIter)]
|
||||
pub enum AutoStart {
|
||||
#[strum(to_string = "Auto-Update")]
|
||||
Update,
|
||||
Bundle,
|
||||
#[strum(to_string = "Confirm quit")]
|
||||
AskBeforeQuit,
|
||||
#[strum(to_string = "Save on exit")]
|
||||
SaveBeforequit,
|
||||
#[strum(to_string = "Auto-{0}")]
|
||||
Process(ProcessName),
|
||||
}
|
||||
impl AutoStart {
|
||||
pub const fn help_msg(&self) -> &str {
|
||||
match self {
|
||||
AutoStart::Update => GUPAX_AUTO_UPDATE,
|
||||
AutoStart::Bundle => GUPAX_BUNDLED_UPDATE,
|
||||
AutoStart::AskBeforeQuit => GUPAX_ASK_BEFORE_QUIT,
|
||||
AutoStart::SaveBeforequit => GUPAX_SAVE_BEFORE_QUIT,
|
||||
AutoStart::Process(p) => p.msg_auto_help(),
|
||||
}
|
||||
}
|
||||
// todo: generate as const with all process in middle ?
|
||||
// Would necessities unstable feature https://github.com/rust-lang/rust/issues/87575
|
||||
pub const ALL: &[AutoStart] = &[
|
||||
AutoStart::Update,
|
||||
AutoStart::Bundle,
|
||||
AutoStart::Process(ProcessName::Node),
|
||||
AutoStart::Process(ProcessName::P2pool),
|
||||
AutoStart::Process(ProcessName::Xmrig),
|
||||
AutoStart::Process(ProcessName::XmrigProxy),
|
||||
AutoStart::Process(ProcessName::Xvb),
|
||||
AutoStart::AskBeforeQuit,
|
||||
AutoStart::SaveBeforequit,
|
||||
];
|
||||
// non const:
|
||||
// let mut autos = AutoStart::iter().collect::<Vec<_>>();
|
||||
// // remove ProcessName default
|
||||
// autos.remove(AutoStart::COUNT - 1);
|
||||
// // insert ProcessName before AskBeforeQuit
|
||||
// let before_quit_index = autos
|
||||
// .iter()
|
||||
// .position(|a| *a == AutoStart::AskBeforeQuit)
|
||||
// .expect("Before quit should be in iter");
|
||||
// ProcessName::iter()
|
||||
// .rev()
|
||||
// .for_each(|p| autos.insert(before_quit_index, AutoStart::Process(p)));
|
||||
// autos
|
||||
}
|
||||
#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)]
|
||||
pub struct P2pool {
|
||||
pub simple: bool,
|
||||
|
@ -213,7 +291,7 @@ pub struct P2pool {
|
|||
pub backup_host: bool,
|
||||
pub out_peers: u16,
|
||||
pub in_peers: u16,
|
||||
pub log_level: u8,
|
||||
pub log_level: u16,
|
||||
pub node: String,
|
||||
pub arguments: String,
|
||||
pub address: String,
|
||||
|
@ -221,11 +299,17 @@ pub struct P2pool {
|
|||
pub ip: String,
|
||||
pub rpc: String,
|
||||
pub zmq: String,
|
||||
pub selected_index: usize,
|
||||
pub selected_name: String,
|
||||
pub selected_ip: String,
|
||||
pub selected_rpc: String,
|
||||
pub selected_zmq: String,
|
||||
pub selected_node: SelectedPoolNode,
|
||||
}
|
||||
|
||||
// compatible for P2Pool and Xmrig/Proxy
|
||||
#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)]
|
||||
pub struct SelectedPoolNode {
|
||||
pub index: usize,
|
||||
pub name: String,
|
||||
pub ip: String,
|
||||
pub rpc: String,
|
||||
pub zmq_rig: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)]
|
||||
|
@ -235,7 +319,7 @@ pub struct Node {
|
|||
pub api_port: String,
|
||||
pub out_peers: u16,
|
||||
pub in_peers: u16,
|
||||
pub log_level: u8,
|
||||
pub log_level: u16,
|
||||
pub arguments: String,
|
||||
pub zmq_ip: String,
|
||||
pub zmq_port: String,
|
||||
|
@ -268,13 +352,13 @@ impl Default for Node {
|
|||
#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)]
|
||||
pub struct Xmrig {
|
||||
pub simple: bool,
|
||||
pub pause: u8,
|
||||
pub pause: u16,
|
||||
pub simple_rig: String,
|
||||
pub arguments: String,
|
||||
pub tls: bool,
|
||||
pub keepalive: bool,
|
||||
pub max_threads: usize,
|
||||
pub current_threads: usize,
|
||||
pub max_threads: u16,
|
||||
pub current_threads: u16,
|
||||
pub address: String,
|
||||
pub api_ip: String,
|
||||
pub api_port: String,
|
||||
|
@ -282,11 +366,7 @@ pub struct Xmrig {
|
|||
pub rig: String,
|
||||
pub ip: String,
|
||||
pub port: String,
|
||||
pub selected_index: usize,
|
||||
pub selected_name: String,
|
||||
pub selected_rig: String,
|
||||
pub selected_ip: String,
|
||||
pub selected_port: String,
|
||||
pub selected_pool: SelectedPoolNode,
|
||||
pub token: String,
|
||||
}
|
||||
|
||||
|
@ -307,15 +387,41 @@ pub struct XmrigProxy {
|
|||
pub api_port: String,
|
||||
pub p2pool_ip: String,
|
||||
pub p2pool_port: String,
|
||||
pub selected_index: usize,
|
||||
pub selected_name: String,
|
||||
pub selected_rig: String,
|
||||
pub selected_ip: String,
|
||||
pub selected_port: String,
|
||||
pub selected_pool: SelectedPoolNode,
|
||||
pub token: String,
|
||||
pub redirect_local_xmrig: bool,
|
||||
}
|
||||
|
||||
impl Gupax {
|
||||
pub fn path_binary(&mut self, process: &BundledProcess) -> &mut String {
|
||||
match process {
|
||||
BundledProcess::Node => &mut self.node_path,
|
||||
BundledProcess::P2Pool => &mut self.p2pool_path,
|
||||
BundledProcess::Xmrig => &mut self.xmrig_path,
|
||||
BundledProcess::XmrigProxy => &mut self.xmrig_proxy_path,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// do not include process that are from Gupaxx
|
||||
#[derive(EnumIter)]
|
||||
pub enum BundledProcess {
|
||||
Node,
|
||||
P2Pool,
|
||||
Xmrig,
|
||||
XmrigProxy,
|
||||
}
|
||||
impl BundledProcess {
|
||||
pub fn process_name(&self) -> ProcessName {
|
||||
match self {
|
||||
BundledProcess::Node => ProcessName::Node,
|
||||
BundledProcess::P2Pool => ProcessName::P2pool,
|
||||
BundledProcess::Xmrig => ProcessName::Xmrig,
|
||||
BundledProcess::XmrigProxy => ProcessName::XmrigProxy,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for XmrigProxy {
|
||||
fn default() -> Self {
|
||||
XmrigProxy {
|
||||
|
@ -335,11 +441,13 @@ impl Default for XmrigProxy {
|
|||
port: "3355".to_string(),
|
||||
p2pool_ip: "localhost".to_string(),
|
||||
p2pool_port: "3333".to_string(),
|
||||
selected_index: 0,
|
||||
selected_name: "Local P2Pool".to_string(),
|
||||
selected_ip: "localhost".to_string(),
|
||||
selected_rig: GUPAX_VERSION_UNDERSCORE.to_string(),
|
||||
selected_port: "3333".to_string(),
|
||||
selected_pool: SelectedPoolNode {
|
||||
index: 0,
|
||||
name: "Local P2Pool".to_string(),
|
||||
ip: "localhost".to_string(),
|
||||
rpc: "3333".to_string(),
|
||||
zmq_rig: GUPAX_VERSION_UNDERSCORE.to_string(),
|
||||
},
|
||||
api_ip: "localhost".to_string(),
|
||||
api_port: "18089".to_string(),
|
||||
tls: false,
|
||||
|
@ -361,7 +469,7 @@ pub struct Xvb {
|
|||
pub p2pool_buffer: i8,
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize, Default)]
|
||||
#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize, Default, EnumCount, EnumIter)]
|
||||
pub enum XvbMode {
|
||||
#[default]
|
||||
Auto,
|
||||
|
@ -435,6 +543,20 @@ pub struct Version {
|
|||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- [State] Defaults
|
||||
impl Default for AutoEnabled {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
update: false,
|
||||
#[cfg(feature = "bundle")]
|
||||
bundled: true,
|
||||
#[cfg(not(feature = "bundle"))]
|
||||
bundled: false,
|
||||
ask_before_quit: true,
|
||||
save_before_quit: true,
|
||||
processes: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Default for Status {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
|
@ -452,14 +574,7 @@ impl Default for Gupax {
|
|||
fn default() -> Self {
|
||||
Self {
|
||||
simple: true,
|
||||
auto_update: false,
|
||||
auto_p2pool: false,
|
||||
auto_node: false,
|
||||
auto_xmrig: false,
|
||||
auto_xp: false,
|
||||
auto_xvb: false,
|
||||
ask_before_quit: true,
|
||||
save_before_quit: true,
|
||||
auto: AutoEnabled::default(),
|
||||
p2pool_path: DEFAULT_P2POOL_PATH.to_string(),
|
||||
xmrig_path: DEFAULT_XMRIG_PATH.to_string(),
|
||||
node_path: DEFAULT_NODE_PATH.to_string(),
|
||||
|
@ -473,10 +588,6 @@ impl Default for Gupax {
|
|||
selected_scale: APP_DEFAULT_SCALE,
|
||||
ratio: Ratio::Width,
|
||||
tab: Tab::Xvb,
|
||||
#[cfg(feature = "bundle")]
|
||||
bundled: true,
|
||||
#[cfg(not(feature = "bundle"))]
|
||||
bundled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -500,17 +611,19 @@ impl Default for P2pool {
|
|||
ip: "localhost".to_string(),
|
||||
rpc: "18081".to_string(),
|
||||
zmq: "18083".to_string(),
|
||||
selected_index: 0,
|
||||
selected_name: "Local Monero Node".to_string(),
|
||||
selected_ip: "localhost".to_string(),
|
||||
selected_rpc: "18081".to_string(),
|
||||
selected_zmq: "18083".to_string(),
|
||||
selected_node: SelectedPoolNode {
|
||||
index: 0,
|
||||
name: "Local Monero Node".to_string(),
|
||||
ip: "localhost".to_string(),
|
||||
rpc: "18081".to_string(),
|
||||
zmq_rig: "18083".to_string(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Xmrig {
|
||||
fn with_threads(max_threads: usize, current_threads: usize) -> Self {
|
||||
fn with_threads(max_threads: u16, current_threads: u16) -> Self {
|
||||
let xmrig = Self::default();
|
||||
Self {
|
||||
max_threads,
|
||||
|
@ -531,17 +644,19 @@ impl Default for Xmrig {
|
|||
rig: GUPAX_VERSION_UNDERSCORE.to_string(),
|
||||
ip: "localhost".to_string(),
|
||||
port: "3333".to_string(),
|
||||
selected_index: 0,
|
||||
selected_name: "Local P2Pool".to_string(),
|
||||
selected_ip: "localhost".to_string(),
|
||||
selected_rig: GUPAX_VERSION_UNDERSCORE.to_string(),
|
||||
selected_port: "3333".to_string(),
|
||||
api_ip: "localhost".to_string(),
|
||||
api_port: "18088".to_string(),
|
||||
tls: false,
|
||||
keepalive: false,
|
||||
current_threads: 1,
|
||||
max_threads: 1,
|
||||
selected_pool: SelectedPoolNode {
|
||||
index: 0,
|
||||
name: "Local Monero Node".to_string(),
|
||||
ip: "localhost".to_string(),
|
||||
rpc: "18081".to_string(),
|
||||
zmq_rig: "18083".to_string(),
|
||||
},
|
||||
token: thread_rng()
|
||||
.sample_iter(Alphanumeric)
|
||||
.take(16)
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
use derive_more::derive::Display;
|
||||
use strum::{EnumCount, EnumIter};
|
||||
|
||||
use super::*;
|
||||
//---------------------------------------------------------------------------------------------------- [Submenu] enum for [Status] tab
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug, Deserialize, Serialize)]
|
||||
|
@ -25,7 +28,9 @@ impl Display for Submenu {
|
|||
|
||||
//---------------------------------------------------------------------------------------------------- [PayoutView] enum for [Status/P2Pool] tab
|
||||
// The enum buttons for selecting which "view" to sort the payout log in.
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug, Deserialize, Serialize)]
|
||||
#[derive(
|
||||
Clone, Copy, Eq, PartialEq, Debug, Deserialize, Serialize, Display, EnumIter, EnumCount,
|
||||
)]
|
||||
pub enum PayoutView {
|
||||
Latest, // Shows the most recent logs first
|
||||
Oldest, // Shows the oldest logs first
|
||||
|
@ -33,6 +38,17 @@ pub enum PayoutView {
|
|||
Smallest, // Shows lowest to highest payouts
|
||||
}
|
||||
|
||||
impl PayoutView {
|
||||
pub const fn msg_help(&self) -> &str {
|
||||
match self {
|
||||
Self::Latest => STATUS_SUBMENU_LATEST,
|
||||
Self::Oldest => STATUS_SUBMENU_OLDEST,
|
||||
Self::Biggest => STATUS_SUBMENU_SMALLEST,
|
||||
Self::Smallest => STATUS_SUBMENU_BIGGEST,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PayoutView {
|
||||
fn new() -> Self {
|
||||
Self::Latest
|
||||
|
@ -45,12 +61,6 @@ impl Default for PayoutView {
|
|||
}
|
||||
}
|
||||
|
||||
impl Display for PayoutView {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- [Hash] enum for [Status/P2Pool]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug, Deserialize, Serialize)]
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
|
@ -110,8 +120,10 @@ impl Hash {
|
|||
impl Display for Hash {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Hash::Hash => write!(f, "Hash"),
|
||||
_ => write!(f, "{:?}hash", self),
|
||||
Hash::Hash => write!(f, "H/s"),
|
||||
Hash::Kilo => write!(f, "KH/s"),
|
||||
Hash::Mega => write!(f, "MH/s"),
|
||||
Hash::Giga => write!(f, "GH/s"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,13 @@ mod test {
|
|||
ratio = "Width"
|
||||
bundled = false
|
||||
|
||||
[gupax.auto]
|
||||
update = false
|
||||
bundled = false
|
||||
ask_before_quit = false
|
||||
save_before_quit = true
|
||||
processes = []
|
||||
|
||||
[status]
|
||||
submenu = "P2pool"
|
||||
payout_view = "Oldest"
|
||||
|
@ -76,11 +83,13 @@ mod test {
|
|||
ip = "192.168.1.123"
|
||||
rpc = "18089"
|
||||
zmq = "18083"
|
||||
selected_index = 0
|
||||
selected_name = "Local Monero Node"
|
||||
selected_ip = "192.168.1.123"
|
||||
selected_rpc = "18089"
|
||||
selected_zmq = "18083"
|
||||
|
||||
[p2pool.selected_node]
|
||||
index = 0
|
||||
name = "Local Monero Node"
|
||||
ip = "localhost"
|
||||
rpc = "18081"
|
||||
zmq_rig = "18083"
|
||||
|
||||
[xmrig]
|
||||
simple = true
|
||||
|
@ -98,13 +107,17 @@ mod test {
|
|||
rig = "Gupaxx"
|
||||
ip = "192.168.1.122"
|
||||
port = "3333"
|
||||
selected_index = 1
|
||||
selected_name = "linux"
|
||||
selected_rig = "Gupaxx"
|
||||
selected_ip = "192.168.1.122"
|
||||
selected_port = "3333"
|
||||
token = "testtoken"
|
||||
|
||||
|
||||
[xmrig.selected_pool]
|
||||
index = 0
|
||||
name = "Local Monero Node"
|
||||
ip = "localhost"
|
||||
rpc = "18081"
|
||||
zmq_rig = "18083"
|
||||
|
||||
|
||||
[xmrig_proxy]
|
||||
simple = true
|
||||
arguments = ""
|
||||
|
@ -121,13 +134,15 @@ mod test {
|
|||
p2pool_ip = "localhost"
|
||||
p2pool_port = "18088"
|
||||
token = "testtoken"
|
||||
selected_index = 1
|
||||
selected_name = "linux"
|
||||
selected_rig = "Gupaxx"
|
||||
selected_ip = "192.168.1.122"
|
||||
selected_port = "3333"
|
||||
redirect_local_xmrig = true
|
||||
|
||||
[xmrig_proxy.selected_pool]
|
||||
index = 0
|
||||
name = "Local Monero Node"
|
||||
ip = "localhost"
|
||||
rpc = "18081"
|
||||
zmq_rig = "18083"
|
||||
|
||||
[xvb]
|
||||
simple = true
|
||||
simple_hero_mode = true
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
// This also includes all things related to handling the child processes (P2Pool/XMRig):
|
||||
// piping their stdout/stderr/stdin, accessing their APIs (HTTP + disk files), etc.
|
||||
|
||||
use crate::components::gupax::FileType;
|
||||
use crate::components::update::{NODE_BINARY, P2POOL_BINARY, XMRIG_BINARY, XMRIG_PROXY_BINARY};
|
||||
//---------------------------------------------------------------------------------------------------- Import
|
||||
use crate::helper::xrig::xmrig_proxy::PubXmrigProxyApi;
|
||||
|
@ -46,6 +47,7 @@ use log::*;
|
|||
use node::PubNodeApi;
|
||||
use portable_pty::Child;
|
||||
use readable::up::Uptime;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Write;
|
||||
use std::path::Path;
|
||||
use std::{
|
||||
|
@ -54,7 +56,7 @@ use std::{
|
|||
thread,
|
||||
time::*,
|
||||
};
|
||||
use strum::EnumIter;
|
||||
use strum::{EnumCount, EnumIter};
|
||||
|
||||
use self::xvb::{PubXvbApi, nodes::XvbNode};
|
||||
pub mod node;
|
||||
|
@ -250,18 +252,21 @@ impl Default for ProcessSignal {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Display, EnumIter)]
|
||||
#[derive(
|
||||
Copy, Clone, Eq, PartialEq, Debug, Display, EnumIter, EnumCount, Serialize, Deserialize, Default,
|
||||
)]
|
||||
pub enum ProcessName {
|
||||
Node,
|
||||
P2pool,
|
||||
Xmrig,
|
||||
#[display("Proxy")]
|
||||
XmrigProxy,
|
||||
#[default]
|
||||
Xvb,
|
||||
}
|
||||
|
||||
impl ProcessName {
|
||||
pub fn binary_name(&self) -> &str {
|
||||
pub const fn binary_name(&self) -> &str {
|
||||
match self {
|
||||
ProcessName::Node => NODE_BINARY,
|
||||
ProcessName::P2pool => P2POOL_BINARY,
|
||||
|
@ -270,6 +275,69 @@ impl ProcessName {
|
|||
ProcessName::Xvb => "",
|
||||
}
|
||||
}
|
||||
pub const fn msg_binary_path_empty(&self) -> &str {
|
||||
match self {
|
||||
ProcessName::Node => NODE_PATH_EMPTY,
|
||||
ProcessName::P2pool => P2POOL_PATH_EMPTY,
|
||||
ProcessName::Xmrig => XMRIG_PATH_EMPTY,
|
||||
ProcessName::XmrigProxy => XMRIG_PROXY_PATH_EMPTY,
|
||||
ProcessName::Xvb => "",
|
||||
}
|
||||
}
|
||||
pub const fn msg_binary_path_not_file(&self) -> &str {
|
||||
match self {
|
||||
ProcessName::Node => NODE_PATH_NOT_FILE,
|
||||
ProcessName::P2pool => P2POOL_PATH_NOT_FILE,
|
||||
ProcessName::Xmrig => XMRIG_PATH_NOT_FILE,
|
||||
ProcessName::XmrigProxy => XMRIG_PROXY_PATH_NOT_FILE,
|
||||
ProcessName::Xvb => "",
|
||||
}
|
||||
}
|
||||
pub const fn msg_binary_path_invalid(&self) -> &str {
|
||||
match self {
|
||||
ProcessName::Node => NODE_PATH_NOT_VALID,
|
||||
ProcessName::P2pool => P2POOL_PATH_NOT_VALID,
|
||||
ProcessName::Xmrig => XMRIG_PATH_NOT_VALID,
|
||||
ProcessName::XmrigProxy => XMRIG_PROXY_PATH_NOT_VALID,
|
||||
ProcessName::Xvb => "",
|
||||
}
|
||||
}
|
||||
pub const fn msg_binary_path_ok(&self) -> &str {
|
||||
match self {
|
||||
ProcessName::Node => NODE_PATH_OK,
|
||||
ProcessName::P2pool => P2POOL_PATH_OK,
|
||||
ProcessName::Xmrig => XMRIG_PATH_OK,
|
||||
ProcessName::XmrigProxy => XMRIG_PROXY_PATH_OK,
|
||||
ProcessName::Xvb => "",
|
||||
}
|
||||
}
|
||||
pub const fn msg_path_edit(&self) -> &str {
|
||||
match self {
|
||||
ProcessName::Node => GUPAX_PATH_NODE,
|
||||
ProcessName::P2pool => GUPAX_PATH_P2POOL,
|
||||
ProcessName::Xmrig => GUPAX_PATH_XMRIG,
|
||||
ProcessName::XmrigProxy => GUPAX_PATH_XMRIG_PROXY,
|
||||
ProcessName::Xvb => "",
|
||||
}
|
||||
}
|
||||
pub const fn msg_auto_help(&self) -> &str {
|
||||
match self {
|
||||
ProcessName::Node => GUPAX_AUTO_NODE,
|
||||
ProcessName::P2pool => GUPAX_AUTO_P2POOL,
|
||||
ProcessName::Xmrig => GUPAX_AUTO_XMRIG,
|
||||
ProcessName::XmrigProxy => GUPAX_AUTO_XMRIG_PROXY,
|
||||
ProcessName::Xvb => GUPAX_AUTO_XVB,
|
||||
}
|
||||
}
|
||||
pub const fn file_type(&self) -> Option<FileType> {
|
||||
match self {
|
||||
ProcessName::Node => Some(FileType::Node),
|
||||
ProcessName::P2pool => Some(FileType::P2pool),
|
||||
ProcessName::Xmrig => Some(FileType::Xmrig),
|
||||
ProcessName::XmrigProxy => Some(FileType::XmrigProxy),
|
||||
ProcessName::Xvb => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ProcessState {
|
||||
|
@ -374,7 +442,7 @@ impl Helper {
|
|||
pub_sys: &mut Sys,
|
||||
pid: &sysinfo::Pid,
|
||||
helper: &Helper,
|
||||
max_threads: usize,
|
||||
max_threads: u16,
|
||||
) {
|
||||
let gupax_uptime = helper.uptime.to_string();
|
||||
let cpu = &sysinfo.cpus()[0];
|
||||
|
@ -416,7 +484,7 @@ impl Helper {
|
|||
helper: &Arc<Mutex<Self>>,
|
||||
mut sysinfo: sysinfo::System,
|
||||
pid: sysinfo::Pid,
|
||||
max_threads: usize,
|
||||
max_threads: u16,
|
||||
) {
|
||||
// The ordering of these locks is _very_ important. They MUST be in sync with how the main GUI thread locks stuff
|
||||
// or a deadlock will occur given enough time. They will eventually both want to lock the [Arc<Mutex>] the other
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use super::Helper;
|
||||
use super::Process;
|
||||
use crate::app::panels::middle::common::list_poolnode::PoolNode;
|
||||
use crate::components::node::RemoteNode;
|
||||
use crate::disk::state::P2pool;
|
||||
use crate::helper::ProcessName;
|
||||
|
@ -18,7 +19,7 @@ use crate::regex::estimated_hr;
|
|||
use crate::regex::nb_current_shares;
|
||||
use crate::{
|
||||
constants::*,
|
||||
disk::{gupax_p2pool_api::GupaxP2poolApi, node::Node},
|
||||
disk::gupax_p2pool_api::GupaxP2poolApi,
|
||||
helper::{MONERO_BLOCK_TIME_IN_SECONDS, P2POOL_BLOCK_TIME_IN_SECONDS},
|
||||
human::*,
|
||||
macros::*,
|
||||
|
@ -171,7 +172,7 @@ impl Helper {
|
|||
helper: &Arc<Mutex<Self>>,
|
||||
state: &P2pool,
|
||||
path: &Path,
|
||||
backup_hosts: Option<Vec<Node>>,
|
||||
backup_hosts: Option<Vec<PoolNode>>,
|
||||
) {
|
||||
info!("P2Pool | Attempting to restart...");
|
||||
helper.lock().unwrap().p2pool.lock().unwrap().signal = ProcessSignal::Restart;
|
||||
|
@ -200,7 +201,7 @@ impl Helper {
|
|||
helper: &Arc<Mutex<Self>>,
|
||||
state: &P2pool,
|
||||
path: &Path,
|
||||
backup_hosts: Option<Vec<Node>>,
|
||||
backup_hosts: Option<Vec<PoolNode>>,
|
||||
) {
|
||||
helper.lock().unwrap().p2pool.lock().unwrap().state = ProcessState::Middle;
|
||||
|
||||
|
@ -253,7 +254,7 @@ impl Helper {
|
|||
helper: &Arc<Mutex<Self>>,
|
||||
state: &P2pool,
|
||||
path: &Path,
|
||||
backup_hosts: Option<Vec<Node>>,
|
||||
backup_hosts: Option<Vec<PoolNode>>,
|
||||
) -> (Vec<String>, PathBuf, PathBuf, PathBuf) {
|
||||
let mut args = Vec::with_capacity(500);
|
||||
let path = path.to_path_buf();
|
||||
|
@ -282,13 +283,13 @@ impl Helper {
|
|||
// Push other nodes if `backup_host`.
|
||||
if let Some(nodes) = backup_hosts {
|
||||
for node in nodes {
|
||||
if (node.ip.as_str(), node.rpc.as_str(), node.zmq.as_str()) != (ip, rpc, zmq) {
|
||||
if (node.ip(), node.port(), node.custom()) != (ip, rpc, zmq) {
|
||||
args.push("--host".to_string());
|
||||
args.push(node.ip.to_string());
|
||||
args.push(node.ip().to_string());
|
||||
args.push("--rpc-port".to_string());
|
||||
args.push(node.rpc.to_string());
|
||||
args.push(node.port().to_string());
|
||||
args.push("--zmq-port".to_string());
|
||||
args.push(node.zmq.to_string());
|
||||
args.push(node.custom().to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -395,20 +396,18 @@ impl Helper {
|
|||
// Push other nodes if `backup_host`.
|
||||
if let Some(nodes) = backup_hosts {
|
||||
for node in nodes {
|
||||
let ip = if node.ip == "localhost" {
|
||||
let ip = if node.ip() == "localhost" {
|
||||
"127.0.0.1"
|
||||
} else {
|
||||
&node.ip
|
||||
node.ip()
|
||||
};
|
||||
if (node.ip.as_str(), node.rpc.as_str(), node.zmq.as_str())
|
||||
!= (ip, &state.rpc, &state.zmq)
|
||||
{
|
||||
if (node.ip(), node.port(), node.custom()) != (ip, &state.rpc, &state.zmq) {
|
||||
args.push("--host".to_string());
|
||||
args.push(node.ip.to_string());
|
||||
args.push(node.ip().to_string());
|
||||
args.push("--rpc-port".to_string());
|
||||
args.push(node.rpc.to_string());
|
||||
args.push(node.port().to_string());
|
||||
args.push("--zmq-port".to_string());
|
||||
args.push(node.zmq.to_string());
|
||||
args.push(node.custom().to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -420,9 +419,9 @@ impl Helper {
|
|||
"P2Pool Main".to_string()
|
||||
},
|
||||
address: Self::head_tail_of_monero_address(&state.address),
|
||||
host: state.selected_ip.to_string(),
|
||||
rpc: state.selected_rpc.to_string(),
|
||||
zmq: state.selected_zmq.to_string(),
|
||||
host: state.selected_node.ip.to_string(),
|
||||
rpc: state.selected_node.rpc.to_string(),
|
||||
zmq: state.selected_node.zmq_rig.to_string(),
|
||||
out_peers: state.out_peers.to_string(),
|
||||
in_peers: state.in_peers.to_string(),
|
||||
};
|
||||
|
|
|
@ -298,13 +298,13 @@ Uptime = 0h 2m 4s
|
|||
assert_eq!(p.miners.to_string(), "1,000");
|
||||
assert_eq!(
|
||||
p.solo_block_mean.to_string(),
|
||||
"5 months, 21 days, 9 hours, 52 minutes"
|
||||
"5 months\n21 days\n9 hours\n52 minutes"
|
||||
);
|
||||
assert_eq!(
|
||||
p.p2pool_block_mean.to_string(),
|
||||
"3 days, 11 hours, 20 minutes"
|
||||
"3 days\n11 hours\n20 minutes"
|
||||
);
|
||||
assert_eq!(p.p2pool_share_mean.to_string(), "8 minutes, 20 seconds");
|
||||
assert_eq!(p.p2pool_share_mean.to_string(), "8 minutes\n20 seconds");
|
||||
assert_eq!(p.p2pool_percent.to_string(), "0.040000%");
|
||||
assert_eq!(p.user_p2pool_percent.to_string(), "2.000000%");
|
||||
assert_eq!(p.user_monero_percent.to_string(), "0.000800%");
|
||||
|
|
54
src/inits.rs
54
src/inits.rs
|
@ -24,6 +24,7 @@ use std::time::Instant;
|
|||
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
// everything is resized from here with the scale.
|
||||
pub fn init_text_styles(ctx: &egui::Context, pixels_per_point: f32) {
|
||||
ctx.all_styles_mut(|style| {
|
||||
style.text_styles = [
|
||||
|
@ -34,13 +35,15 @@ pub fn init_text_styles(ctx: &egui::Context, pixels_per_point: f32) {
|
|||
(Heading, FontId::new(22.0, egui::FontFamily::Monospace)),
|
||||
]
|
||||
.into();
|
||||
style.spacing.icon_width_inner = 32.0;
|
||||
style.spacing.icon_width = 64.0;
|
||||
style.spacing.icon_spacing = 20.0;
|
||||
style.spacing.scroll = egui::style::ScrollStyle {
|
||||
bar_width: 8.0,
|
||||
..egui::style::ScrollStyle::solid()
|
||||
};
|
||||
style.spacing.icon_width_inner = 24.0;
|
||||
style.spacing.icon_width = 48.0;
|
||||
style.spacing.icon_spacing = 16.0;
|
||||
style.spacing.button_padding = [8.0, 8.0].into();
|
||||
style.spacing.item_spacing = [8.0, 8.0].into();
|
||||
// style.spacing.scroll = egui::style::ScrollStyle {
|
||||
// bar_width: 8.0,
|
||||
// ..egui::style::ScrollStyle::solid()
|
||||
// };
|
||||
});
|
||||
// Make sure scale f32 is a regular number.
|
||||
let pixels_per_point = clamp_scale(pixels_per_point);
|
||||
|
@ -134,7 +137,7 @@ pub fn init_auto(app: &mut App) {
|
|||
|
||||
// [Auto-Update]
|
||||
#[cfg(not(feature = "distro"))]
|
||||
if app.state.gupax.auto_update {
|
||||
if app.state.gupax.auto.is_enabled(&AutoStart::Update) {
|
||||
Update::spawn_thread(
|
||||
&app.og,
|
||||
&app.state.gupax,
|
||||
|
@ -155,7 +158,12 @@ pub fn init_auto(app: &mut App) {
|
|||
}
|
||||
|
||||
// [Auto-Node]
|
||||
if app.state.gupax.auto_node {
|
||||
if app
|
||||
.state
|
||||
.gupax
|
||||
.auto
|
||||
.is_enabled(&AutoStart::Process(ProcessName::Node))
|
||||
{
|
||||
if !Gupax::path_is_file(&app.state.gupax.node_path) {
|
||||
warn!("Gupaxx | Node path is not a file! Skipping auto-node...");
|
||||
} else if !check_binary_path(&app.state.gupax.node_path, ProcessName::Node) {
|
||||
|
@ -177,7 +185,12 @@ pub fn init_auto(app: &mut App) {
|
|||
info!("Skipping auto-node...");
|
||||
}
|
||||
// [Auto-P2Pool]
|
||||
if app.state.gupax.auto_p2pool {
|
||||
if app
|
||||
.state
|
||||
.gupax
|
||||
.auto
|
||||
.is_enabled(&AutoStart::Process(ProcessName::P2pool))
|
||||
{
|
||||
if !Regexes::addr_ok(&app.state.p2pool.address) {
|
||||
warn!("Gupaxx | P2Pool address is not valid! Skipping auto-p2pool...");
|
||||
} else if !Gupax::path_is_file(&app.state.gupax.p2pool_path) {
|
||||
|
@ -202,7 +215,12 @@ pub fn init_auto(app: &mut App) {
|
|||
}
|
||||
|
||||
// [Auto-XMRig]
|
||||
if app.state.gupax.auto_xmrig {
|
||||
if app
|
||||
.state
|
||||
.gupax
|
||||
.auto
|
||||
.is_enabled(&AutoStart::Process(ProcessName::Xmrig))
|
||||
{
|
||||
if !Gupax::path_is_file(&app.state.gupax.xmrig_path) {
|
||||
warn!("Gupaxx | XMRig path is not an executable! Skipping auto-xmrig...");
|
||||
} else if !check_binary_path(&app.state.gupax.xmrig_path, ProcessName::Xmrig) {
|
||||
|
@ -226,7 +244,12 @@ pub fn init_auto(app: &mut App) {
|
|||
info!("Skipping auto-xmrig...");
|
||||
}
|
||||
// [Auto-XMRig-Proxy]
|
||||
if app.state.gupax.auto_xp {
|
||||
if app
|
||||
.state
|
||||
.gupax
|
||||
.auto
|
||||
.is_enabled(&AutoStart::Process(ProcessName::XmrigProxy))
|
||||
{
|
||||
if !Gupax::path_is_file(&app.state.gupax.xmrig_proxy_path) {
|
||||
warn!("Gupaxx | Xmrig-Proxy path is not a file! Skipping auto-xmrig_proxy...");
|
||||
} else if !check_binary_path(&app.state.gupax.xmrig_proxy_path, ProcessName::XmrigProxy) {
|
||||
|
@ -247,7 +270,12 @@ pub fn init_auto(app: &mut App) {
|
|||
info!("Skipping auto-XMRig-Proxy...");
|
||||
}
|
||||
// [Auto-XvB]
|
||||
if app.state.gupax.auto_xvb {
|
||||
if app
|
||||
.state
|
||||
.gupax
|
||||
.auto
|
||||
.is_enabled(&AutoStart::Process(ProcessName::Xvb))
|
||||
{
|
||||
Helper::start_xvb(
|
||||
&app.helper,
|
||||
&app.state.xvb,
|
||||
|
|
|
@ -132,6 +132,8 @@ pub fn cmp_f64(a: f64, b: f64) -> std::cmp::Ordering {
|
|||
use crate::disk::gupax_p2pool_api::GupaxP2poolApi;
|
||||
use crate::helper::ProcessName;
|
||||
use chrono::Local;
|
||||
use egui::TextStyle;
|
||||
use egui::Ui;
|
||||
use log::error;
|
||||
use log::warn;
|
||||
use regex::Regex;
|
||||
|
@ -182,3 +184,7 @@ pub fn client() -> ClientWithMiddleware {
|
|||
))
|
||||
.build()
|
||||
}
|
||||
/// to get the right height that a text must take before a button to be aligned in the center correctly.
|
||||
pub fn height_txt_before_button(ui: &Ui, style: &TextStyle) -> f32 {
|
||||
ui.style().spacing.button_padding.y * 2.0 + ui.text_style_height(style)
|
||||
}
|
||||
|
|
|
@ -348,6 +348,7 @@ pub const GUPAX_TAB_STATUS: &str = "Set the tab Gupaxx starts on to: Status";
|
|||
pub const GUPAX_TAB_GUPAX: &str = "Set the tab Gupaxx starts on to: Gupaxx";
|
||||
pub const GUPAX_TAB_P2POOL: &str = "Set the tab Gupaxx starts on to: P2Pool";
|
||||
pub const GUPAX_TAB_XMRIG: &str = "Set the tab Gupaxx starts on to: XMRig";
|
||||
pub const GUPAX_TAB_XMRIG_PROXY: &str = "Set the tab Gupaxx starts on to: Proxy";
|
||||
pub const GUPAX_TAB_XVB: &str = "Set the tab Gupaxx starts on to: XvB";
|
||||
pub const GUPAX_TAB_NODE: &str = "Set the default tab Gupaxx starts on to: Node";
|
||||
|
||||
|
@ -526,7 +527,6 @@ pub const XVB_TIME_ALGO: u32 = 600;
|
|||
pub const XVB_MIN_TIME_SEND: u32 = (XVB_TIME_ALGO as f32 * 0.01) as u32;
|
||||
pub const XVB_TOKEN_LEN: usize = 9;
|
||||
pub const XVB_HERO_SELECT: &str = "Donate as much as possible while keeping a share on p2pool, increases the odds of your round winning\nWhen modified, the algorithm will use the new choice at the next decision.";
|
||||
pub const XVB_TOKEN_FIELD: &str = "Token";
|
||||
pub const XVB_FAILURE_FIELD: &str = "Failures";
|
||||
pub const XVB_DONATED_1H_FIELD: &str = "Donated last hour";
|
||||
pub const XVB_DONATED_24H_FIELD: &str = "Donated last 24 hours";
|
||||
|
|
|
@ -59,7 +59,7 @@ impl HumanTime {
|
|||
) -> std::fmt::Result {
|
||||
if value > 0 {
|
||||
if *started {
|
||||
f.write_str(", ")?;
|
||||
f.write_str("\n")?;
|
||||
}
|
||||
write!(f, "{} {}", value, name)?;
|
||||
if value > 1 {
|
||||
|
@ -332,64 +332,64 @@ mod test {
|
|||
assert!(HumanTime::into_human(Duration::from_secs(2)).to_string() == "2 seconds");
|
||||
assert!(HumanTime::into_human(Duration::from_secs(59)).to_string() == "59 seconds");
|
||||
assert!(HumanTime::into_human(Duration::from_secs(60)).to_string() == "1 minute");
|
||||
assert!(HumanTime::into_human(Duration::from_secs(61)).to_string() == "1 minute, 1 second");
|
||||
assert!(HumanTime::into_human(Duration::from_secs(61)).to_string() == "1 minute\n1 second");
|
||||
assert!(
|
||||
HumanTime::into_human(Duration::from_secs(62)).to_string() == "1 minute, 2 seconds"
|
||||
HumanTime::into_human(Duration::from_secs(62)).to_string() == "1 minute\n2 seconds"
|
||||
);
|
||||
assert!(HumanTime::into_human(Duration::from_secs(120)).to_string() == "2 minutes");
|
||||
assert!(
|
||||
HumanTime::into_human(Duration::from_secs(121)).to_string() == "2 minutes, 1 second"
|
||||
HumanTime::into_human(Duration::from_secs(121)).to_string() == "2 minutes\n1 second"
|
||||
);
|
||||
assert!(
|
||||
HumanTime::into_human(Duration::from_secs(122)).to_string() == "2 minutes, 2 seconds"
|
||||
HumanTime::into_human(Duration::from_secs(122)).to_string() == "2 minutes\n2 seconds"
|
||||
);
|
||||
assert!(
|
||||
HumanTime::into_human(Duration::from_secs(179)).to_string() == "2 minutes, 59 seconds"
|
||||
HumanTime::into_human(Duration::from_secs(179)).to_string() == "2 minutes\n59 seconds"
|
||||
);
|
||||
assert!(
|
||||
HumanTime::into_human(Duration::from_secs(3599)).to_string()
|
||||
== "59 minutes, 59 seconds"
|
||||
== "59 minutes\n59 seconds"
|
||||
);
|
||||
assert!(HumanTime::into_human(Duration::from_secs(3600)).to_string() == "1 hour");
|
||||
assert!(HumanTime::into_human(Duration::from_secs(3601)).to_string() == "1 hour, 1 second");
|
||||
assert!(HumanTime::into_human(Duration::from_secs(3601)).to_string() == "1 hour\n1 second");
|
||||
assert!(
|
||||
HumanTime::into_human(Duration::from_secs(3602)).to_string() == "1 hour, 2 seconds"
|
||||
HumanTime::into_human(Duration::from_secs(3602)).to_string() == "1 hour\n2 seconds"
|
||||
);
|
||||
assert!(HumanTime::into_human(Duration::from_secs(3660)).to_string() == "1 hour, 1 minute");
|
||||
assert!(HumanTime::into_human(Duration::from_secs(3660)).to_string() == "1 hour\n1 minute");
|
||||
assert!(
|
||||
HumanTime::into_human(Duration::from_secs(3720)).to_string() == "1 hour, 2 minutes"
|
||||
HumanTime::into_human(Duration::from_secs(3720)).to_string() == "1 hour\n2 minutes"
|
||||
);
|
||||
assert!(
|
||||
HumanTime::into_human(Duration::from_secs(86399)).to_string()
|
||||
== "23 hours, 59 minutes, 59 seconds"
|
||||
== "23 hours\n59 minutes\n59 seconds"
|
||||
);
|
||||
assert!(HumanTime::into_human(Duration::from_secs(86400)).to_string() == "1 day");
|
||||
assert!(HumanTime::into_human(Duration::from_secs(86401)).to_string() == "1 day, 1 second");
|
||||
assert!(HumanTime::into_human(Duration::from_secs(86401)).to_string() == "1 day\n1 second");
|
||||
assert!(
|
||||
HumanTime::into_human(Duration::from_secs(86402)).to_string() == "1 day, 2 seconds"
|
||||
HumanTime::into_human(Duration::from_secs(86402)).to_string() == "1 day\n2 seconds"
|
||||
);
|
||||
assert!(HumanTime::into_human(Duration::from_secs(86460)).to_string() == "1 day, 1 minute");
|
||||
assert!(HumanTime::into_human(Duration::from_secs(86460)).to_string() == "1 day\n1 minute");
|
||||
assert!(
|
||||
HumanTime::into_human(Duration::from_secs(86520)).to_string() == "1 day, 2 minutes"
|
||||
HumanTime::into_human(Duration::from_secs(86520)).to_string() == "1 day\n2 minutes"
|
||||
);
|
||||
assert!(HumanTime::into_human(Duration::from_secs(90000)).to_string() == "1 day, 1 hour");
|
||||
assert!(HumanTime::into_human(Duration::from_secs(93600)).to_string() == "1 day, 2 hours");
|
||||
assert!(HumanTime::into_human(Duration::from_secs(90000)).to_string() == "1 day\n1 hour");
|
||||
assert!(HumanTime::into_human(Duration::from_secs(93600)).to_string() == "1 day\n2 hours");
|
||||
assert!(
|
||||
HumanTime::into_human(Duration::from_secs(604799)).to_string()
|
||||
== "6 days, 23 hours, 59 minutes, 59 seconds"
|
||||
== "6 days\n23 hours\n59 minutes\n59 seconds"
|
||||
);
|
||||
assert!(HumanTime::into_human(Duration::from_secs(604800)).to_string() == "7 days");
|
||||
assert!(HumanTime::into_human(Duration::from_secs(2630016)).to_string() == "1 month");
|
||||
assert!(
|
||||
HumanTime::into_human(Duration::from_secs(3234815)).to_string()
|
||||
== "1 month, 6 days, 23 hours, 59 minutes, 59 seconds"
|
||||
== "1 month\n6 days\n23 hours\n59 minutes\n59 seconds"
|
||||
);
|
||||
assert!(HumanTime::into_human(Duration::from_secs(5260032)).to_string() == "2 months");
|
||||
assert!(HumanTime::into_human(Duration::from_secs(31557600)).to_string() == "1 year");
|
||||
assert!(HumanTime::into_human(Duration::from_secs(63115200)).to_string() == "2 years");
|
||||
assert_eq!(
|
||||
HumanTime::into_human(Duration::from_secs(18446744073709551615)).to_string(),
|
||||
"584542046090 years, 7 months, 15 days, 17 hours, 5 minutes, 3 seconds",
|
||||
"584542046090 years\n7 months\n15 days\n17 hours\n5 minutes\n3 seconds",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue