gui: added node metrics, refactor views
This commit is contained in:
parent
59990f9ee2
commit
f4ec1b9ab0
12 changed files with 453 additions and 257 deletions
|
@ -31,3 +31,9 @@ sync_status:
|
||||||
body_sync: Downloading blocks
|
body_sync: Downloading blocks
|
||||||
body_sync_percent: 'Downloading blocks: %{percent}%'
|
body_sync_percent: 'Downloading blocks: %{percent}%'
|
||||||
shutdown: Shutting down
|
shutdown: Shutting down
|
||||||
|
emission: Emission
|
||||||
|
inflation: Inflation
|
||||||
|
supply: Supply
|
||||||
|
block_time: Block time
|
||||||
|
reward: Reward
|
||||||
|
difficulty_at_blocks: 'Difficulty on %{size} blocks'
|
|
@ -31,3 +31,9 @@ sync_status:
|
||||||
body_sync: Загрузка блоков
|
body_sync: Загрузка блоков
|
||||||
body_sync_percent: 'Загрузка блоков: %{percent}%'
|
body_sync_percent: 'Загрузка блоков: %{percent}%'
|
||||||
shutdown: Выключение
|
shutdown: Выключение
|
||||||
|
emission: Эмиссия
|
||||||
|
inflation: Инфляция
|
||||||
|
supply: Предложение
|
||||||
|
block_time: Время блока
|
||||||
|
reward: Награда
|
||||||
|
difficulty_at_blocks: 'Сложность на %{size} блоках'
|
|
@ -16,3 +16,4 @@ pub const COLOR_YELLOW: egui::Color32 = egui::Color32::from_rgb(254, 241, 2);
|
||||||
pub const COLOR_LIGHT: egui::Color32 = egui::Color32::from_gray(240);
|
pub const COLOR_LIGHT: egui::Color32 = egui::Color32::from_gray(240);
|
||||||
pub const COLOR_DARK: egui::Color32 = egui::Color32::from_gray(60);
|
pub const COLOR_DARK: egui::Color32 = egui::Color32::from_gray(60);
|
||||||
pub const COLOR_GRAY: egui::Color32 = egui::Color32::from_gray(120);
|
pub const COLOR_GRAY: egui::Color32 = egui::Color32::from_gray(120);
|
||||||
|
// pub const COLOR_LIGHT_GRAY: egui::Color32 = egui::Color32::from_gray(230);
|
|
@ -85,7 +85,18 @@ impl PlatformApp<Android> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_visuals(ctx: &egui::Context) {
|
fn setup_visuals(ctx: &egui::Context) {
|
||||||
|
// Setup style
|
||||||
|
let mut style = (*ctx.style()).clone();
|
||||||
|
|
||||||
|
// Make scroll-bar thinner
|
||||||
|
style.spacing.scroll_bar_width = 4.0;
|
||||||
|
//
|
||||||
|
style.spacing.item_spacing = egui::vec2(0.0, 0.0);
|
||||||
|
ctx.set_style(style);
|
||||||
|
|
||||||
|
// Setup visuals
|
||||||
let mut visuals = egui::Visuals::light();
|
let mut visuals = egui::Visuals::light();
|
||||||
|
|
||||||
// Disable stroke around panels by default
|
// Disable stroke around panels by default
|
||||||
visuals.widgets.noninteractive.bg_stroke = Stroke::NONE;
|
visuals.widgets.noninteractive.bg_stroke = Stroke::NONE;
|
||||||
ctx.set_visuals(visuals);
|
ctx.set_visuals(visuals);
|
||||||
|
|
|
@ -18,7 +18,7 @@ use crate::gui::app::is_dual_panel_mode;
|
||||||
use crate::gui::icons::{ARROW_CIRCLE_LEFT, GEAR_SIX, GLOBE};
|
use crate::gui::icons::{ARROW_CIRCLE_LEFT, GEAR_SIX, GLOBE};
|
||||||
use crate::gui::platform::PlatformCallbacks;
|
use crate::gui::platform::PlatformCallbacks;
|
||||||
use crate::gui::screens::{Navigator, Screen, ScreenId};
|
use crate::gui::screens::{Navigator, Screen, ScreenId};
|
||||||
use crate::gui::views::{DEFAULT_STROKE, TitlePanel, TitlePanelAction};
|
use crate::gui::views::{TitlePanel, TitlePanelAction, View};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Accounts {}
|
pub struct Accounts {}
|
||||||
|
@ -52,7 +52,7 @@ impl Screen for Accounts {
|
||||||
panel.ui(ui);
|
panel.ui(ui);
|
||||||
|
|
||||||
egui::CentralPanel::default().frame(Frame {
|
egui::CentralPanel::default().frame(Frame {
|
||||||
stroke: DEFAULT_STROKE,
|
stroke: View::DEFAULT_STROKE,
|
||||||
.. Default::default()
|
.. Default::default()
|
||||||
}).show_inside(ui, |ui| {
|
}).show_inside(ui, |ui| {
|
||||||
ui.label(format!("{}Here we go 10000 ツ", ARROW_CIRCLE_LEFT));
|
ui.label(format!("{}Here we go 10000 ツ", ARROW_CIRCLE_LEFT));
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
// Copyright 2023 The Grim Developers
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
use eframe::epaint::{Color32, Stroke};
|
|
||||||
use egui::{RichText, Sense, Widget};
|
|
||||||
|
|
||||||
use crate::gui::colors::{COLOR_DARK, COLOR_LIGHT};
|
|
||||||
use crate::gui::views::DEFAULT_STROKE;
|
|
||||||
|
|
||||||
pub fn title_button(ui: &mut egui::Ui, icon: &str, action: impl FnOnce()) {
|
|
||||||
let b = egui::widgets::Button::new(
|
|
||||||
RichText::new(icon.to_string()).size(24.0).color(COLOR_DARK)
|
|
||||||
).fill(Color32::TRANSPARENT)
|
|
||||||
.ui(ui).interact(Sense::click_and_drag());
|
|
||||||
|
|
||||||
// Click optimization for touch screens
|
|
||||||
if b.drag_released() || b.clicked() {
|
|
||||||
(action)();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tab_button(ui: &mut egui::Ui, icon: &str, active: bool, mut action: impl FnMut()) {
|
|
||||||
let stroke = match active {
|
|
||||||
true => { Stroke::NONE }
|
|
||||||
false => { DEFAULT_STROKE }
|
|
||||||
};
|
|
||||||
|
|
||||||
let color = match active {
|
|
||||||
true => { COLOR_LIGHT }
|
|
||||||
false => { Color32::WHITE }
|
|
||||||
};
|
|
||||||
|
|
||||||
let b = egui::widgets::Button::new(
|
|
||||||
RichText::new(icon.to_string()).size(24.0).color(COLOR_DARK)
|
|
||||||
).min_size(ui.available_size_before_wrap())
|
|
||||||
.stroke(stroke)
|
|
||||||
.fill(color)
|
|
||||||
.ui(ui).interact(Sense::click_and_drag());
|
|
||||||
|
|
||||||
// Click optimization for touch screens
|
|
||||||
if b.drag_released() || b.clicked() {
|
|
||||||
(action)();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sub_title(ui: &mut egui::Ui, text: String, color: Color32) {
|
|
||||||
ui.label(RichText::new(text).size(17.0).color(color));
|
|
||||||
}
|
|
|
@ -14,22 +14,21 @@
|
||||||
|
|
||||||
use eframe::epaint::{Color32, Stroke};
|
use eframe::epaint::{Color32, Stroke};
|
||||||
|
|
||||||
pub use crate::gui::views::network::Network;
|
mod views;
|
||||||
pub use crate::gui::views::title_panel::{TitlePanel, TitlePanelAction, TitlePanelActions};
|
pub use self::views::View;
|
||||||
|
|
||||||
pub mod common;
|
|
||||||
|
|
||||||
mod title_panel;
|
mod title_panel;
|
||||||
|
pub use self::title_panel::{TitlePanel, TitlePanelAction};
|
||||||
|
|
||||||
mod network;
|
mod network;
|
||||||
mod network_node;
|
mod network_node;
|
||||||
mod network_tuning;
|
mod network_tuning;
|
||||||
mod network_metrics;
|
mod network_metrics;
|
||||||
|
pub use self::network::Network;
|
||||||
|
|
||||||
pub trait NetworkTab {
|
pub trait NetworkTab {
|
||||||
fn ui(&mut self, ui: &mut egui::Ui, node: &mut crate::node::Node);
|
|
||||||
fn name(&self) -> &String;
|
fn name(&self) -> &String;
|
||||||
|
fn ui(&mut self, ui: &mut egui::Ui, node: &mut crate::node::Node);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const DEFAULT_STROKE: Stroke = Stroke { width: 1.0, color: Color32::from_gray(190) };
|
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,8 @@ use crate::gui::colors::{COLOR_DARK, COLOR_YELLOW};
|
||||||
use crate::gui::icons::{CARDHOLDER, DATABASE, DOTS_THREE_OUTLINE_VERTICAL, FACTORY, FADERS, GAUGE};
|
use crate::gui::icons::{CARDHOLDER, DATABASE, DOTS_THREE_OUTLINE_VERTICAL, FACTORY, FADERS, GAUGE};
|
||||||
use crate::gui::platform::PlatformCallbacks;
|
use crate::gui::platform::PlatformCallbacks;
|
||||||
use crate::gui::screens::Navigator;
|
use crate::gui::screens::Navigator;
|
||||||
use crate::gui::views::{DEFAULT_STROKE, NetworkTab};
|
use crate::gui::views::{NetworkTab, View};
|
||||||
use crate::gui::views::common::{tab_button, title_button};
|
use crate::gui::views::network_metrics::NetworkMetrics;
|
||||||
use crate::gui::views::network_node::NetworkNode;
|
use crate::gui::views::network_node::NetworkNode;
|
||||||
use crate::node::Node;
|
use crate::node::Node;
|
||||||
|
|
||||||
|
@ -45,7 +45,9 @@ pub struct Network {
|
||||||
node: Node,
|
node: Node,
|
||||||
|
|
||||||
current_mode: Mode,
|
current_mode: Mode,
|
||||||
|
|
||||||
node_view: NetworkNode,
|
node_view: NetworkNode,
|
||||||
|
metrics_view: NetworkMetrics,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Network {
|
impl Default for Network {
|
||||||
|
@ -54,7 +56,8 @@ impl Default for Network {
|
||||||
Self {
|
Self {
|
||||||
node,
|
node,
|
||||||
current_mode: Mode::Node,
|
current_mode: Mode::Node,
|
||||||
node_view: NetworkNode::default()
|
node_view: NetworkNode::default(),
|
||||||
|
metrics_view: NetworkMetrics::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,7 +93,7 @@ impl Network {
|
||||||
});
|
});
|
||||||
|
|
||||||
egui::CentralPanel::default().frame(egui::Frame {
|
egui::CentralPanel::default().frame(egui::Frame {
|
||||||
stroke: DEFAULT_STROKE,
|
stroke: View::DEFAULT_STROKE,
|
||||||
inner_margin: Margin::same(4.0),
|
inner_margin: Margin::same(4.0),
|
||||||
fill: Color32::WHITE,
|
fill: Color32::WHITE,
|
||||||
.. Default::default()
|
.. Default::default()
|
||||||
|
@ -102,24 +105,27 @@ impl Network {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_tabs(&mut self, ui: &mut egui::Ui) {
|
fn draw_tabs(&mut self, ui: &mut egui::Ui) {
|
||||||
|
//Setup spacing between tabs
|
||||||
|
ui.style_mut().spacing.item_spacing = egui::vec2(6.0, 0.0);
|
||||||
|
|
||||||
ui.columns(4, |columns| {
|
ui.columns(4, |columns| {
|
||||||
columns[0].vertical_centered(|ui| {
|
columns[0].vertical_centered(|ui| {
|
||||||
tab_button(ui, DATABASE, self.current_mode == Mode::Node, || {
|
View::tab_button(ui, DATABASE, self.current_mode == Mode::Node, || {
|
||||||
self.current_mode = Mode::Node;
|
self.current_mode = Mode::Node;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
columns[1].vertical_centered(|ui| {
|
columns[1].vertical_centered(|ui| {
|
||||||
tab_button(ui, GAUGE, self.current_mode == Mode::Metrics, || {
|
View::tab_button(ui, GAUGE, self.current_mode == Mode::Metrics, || {
|
||||||
self.current_mode = Mode::Metrics;
|
self.current_mode = Mode::Metrics;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
columns[2].vertical_centered(|ui| {
|
columns[2].vertical_centered(|ui| {
|
||||||
tab_button(ui, FACTORY, self.current_mode == Mode::Miner, || {
|
View::tab_button(ui, FACTORY, self.current_mode == Mode::Miner, || {
|
||||||
self.current_mode = Mode::Miner;
|
self.current_mode = Mode::Miner;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
columns[3].vertical_centered(|ui| {
|
columns[3].vertical_centered(|ui| {
|
||||||
tab_button(ui, FADERS, self.current_mode == Mode::Tuning, || {
|
View::tab_button(ui, FADERS, self.current_mode == Mode::Tuning, || {
|
||||||
self.current_mode = Mode::Tuning;
|
self.current_mode = Mode::Tuning;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -132,8 +138,12 @@ impl Network {
|
||||||
Mode::Node => {
|
Mode::Node => {
|
||||||
self.node_view.ui(ui, &mut self.node);
|
self.node_view.ui(ui, &mut self.node);
|
||||||
}
|
}
|
||||||
Mode::Metrics => {}
|
Mode::Metrics => {
|
||||||
Mode::Tuning => {}
|
self.metrics_view.ui(ui, &mut self.node);
|
||||||
|
}
|
||||||
|
Mode::Tuning => {
|
||||||
|
self.node_view.ui(ui, &mut self.node);
|
||||||
|
}
|
||||||
Mode::Miner => {}
|
Mode::Miner => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -153,7 +163,7 @@ impl Network {
|
||||||
.horizontal(|mut strip| {
|
.horizontal(|mut strip| {
|
||||||
strip.cell(|ui| {
|
strip.cell(|ui| {
|
||||||
ui.centered_and_justified(|ui| {
|
ui.centered_and_justified(|ui| {
|
||||||
title_button(ui, DOTS_THREE_OUTLINE_VERTICAL, || {
|
View::title_button(ui, DOTS_THREE_OUTLINE_VERTICAL, || {
|
||||||
//TODO: Actions for node
|
//TODO: Actions for node
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -164,7 +174,7 @@ impl Network {
|
||||||
strip.cell(|ui| {
|
strip.cell(|ui| {
|
||||||
if !is_dual_panel_mode(frame) {
|
if !is_dual_panel_mode(frame) {
|
||||||
ui.centered_and_justified(|ui| {
|
ui.centered_and_justified(|ui| {
|
||||||
title_button(ui, CARDHOLDER, || {
|
View::title_button(ui, CARDHOLDER, || {
|
||||||
nav.toggle_left_panel();
|
nav.toggle_left_panel();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -181,7 +191,7 @@ impl Network {
|
||||||
self.node_view.name()
|
self.node_view.name()
|
||||||
}
|
}
|
||||||
Mode::Metrics => {
|
Mode::Metrics => {
|
||||||
self.node_view.name()
|
self.metrics_view.name()
|
||||||
}
|
}
|
||||||
Mode::Miner => {
|
Mode::Miner => {
|
||||||
self.node_view.name()
|
self.node_view.name()
|
||||||
|
|
|
@ -12,8 +12,14 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use egui::Ui;
|
use chrono::{DateTime, NaiveDateTime, Utc};
|
||||||
use crate::gui::views::NetworkTab;
|
use eframe::epaint::{Color32, Rounding, Stroke};
|
||||||
|
use egui::{RichText, ScrollArea, Spinner, Widget};
|
||||||
|
use grin_servers::DiffBlock;
|
||||||
|
|
||||||
|
use crate::gui::colors::{COLOR_DARK, COLOR_GRAY};
|
||||||
|
use crate::gui::icons::{AT, CALENDAR_PLUS, COINS, CUBE, CUBE_TRANSPARENT, HASH, HOURGLASS_LOW, HOURGLASS_MEDIUM, TIMER};
|
||||||
|
use crate::gui::views::{NetworkTab, View};
|
||||||
use crate::node::Node;
|
use crate::node::Node;
|
||||||
|
|
||||||
pub struct NetworkMetrics {
|
pub struct NetworkMetrics {
|
||||||
|
@ -28,12 +34,185 @@ impl Default for NetworkMetrics {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const BLOCK_REWARD: f64 = 60.0;
|
||||||
|
// 1 year is calculated as 365 days and 6 hours (31557600).
|
||||||
|
const YEARLY_SUPPLY: f64 = ((60 * 60 * 24 * 365) + 6 * 60 * 60) as f64;
|
||||||
|
|
||||||
impl NetworkTab for NetworkMetrics {
|
impl NetworkTab for NetworkMetrics {
|
||||||
fn ui(&mut self, ui: &mut Ui, node: &mut Node) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fn name(&self) -> &String {
|
fn name(&self) -> &String {
|
||||||
&self.title
|
&self.title
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ui(&mut self, ui: &mut egui::Ui, node: &mut Node) {
|
||||||
|
let server_stats = node.state.get_stats();
|
||||||
|
if !server_stats.is_some() {
|
||||||
|
ui.centered_and_justified(|ui| {
|
||||||
|
Spinner::new().size(42.0).color(COLOR_GRAY).ui(ui);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let stats = server_stats.as_ref().unwrap();
|
||||||
|
|
||||||
|
// Show emission info
|
||||||
|
ui.vertical_centered_justified(|ui| {
|
||||||
|
View::sub_title(ui, format!("{} {}", COINS, t!("emission")), COLOR_DARK);
|
||||||
|
});
|
||||||
|
ui.add_space(4.0);
|
||||||
|
|
||||||
|
let supply = stats.header_stats.height as f64 * BLOCK_REWARD;
|
||||||
|
let rate = (YEARLY_SUPPLY * 100.0) / supply;
|
||||||
|
|
||||||
|
ui.columns(3, |columns| {
|
||||||
|
columns[0].vertical_centered(|ui| {
|
||||||
|
View::rounded_box(ui,
|
||||||
|
format!("{}ツ", BLOCK_REWARD),
|
||||||
|
t!("reward"),
|
||||||
|
[true, false, true, false]);
|
||||||
|
});
|
||||||
|
columns[1].vertical_centered(|ui| {
|
||||||
|
View::rounded_box(ui,
|
||||||
|
format!("{:.2}%", rate),
|
||||||
|
t!("inflation"),
|
||||||
|
[false, false, false, false]);
|
||||||
|
});
|
||||||
|
columns[2].vertical_centered(|ui| {
|
||||||
|
View::rounded_box(ui,
|
||||||
|
supply.to_string(),
|
||||||
|
t!("supply"),
|
||||||
|
[false, true, false, true]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
ui.add_space(4.0);
|
||||||
|
|
||||||
|
// Show difficulty window info
|
||||||
|
ui.vertical_centered_justified(|ui| {
|
||||||
|
let title = t!("difficulty_at_blocks", "size" => stats.diff_stats.window_size);
|
||||||
|
View::sub_title(ui, format!("{} {}", HOURGLASS_MEDIUM, title), COLOR_DARK);
|
||||||
|
});
|
||||||
|
ui.add_space(4.0);
|
||||||
|
ui.columns(3, |columns| {
|
||||||
|
columns[0].vertical_centered(|ui| {
|
||||||
|
View::rounded_box(ui,
|
||||||
|
stats.diff_stats.height.to_string(),
|
||||||
|
t!("height"),
|
||||||
|
[true, false, true, false]);
|
||||||
|
});
|
||||||
|
columns[1].vertical_centered(|ui| {
|
||||||
|
View::rounded_box(ui,
|
||||||
|
format!("{}s", stats.diff_stats.average_block_time),
|
||||||
|
t!("block_time"),
|
||||||
|
[false, false, false, false]);
|
||||||
|
});
|
||||||
|
columns[2].vertical_centered(|ui| {
|
||||||
|
View::rounded_box(ui,
|
||||||
|
stats.diff_stats.average_difficulty.to_string(),
|
||||||
|
t!("difficulty"),
|
||||||
|
[false, true, false, true]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
ui.add_space(6.0);
|
||||||
|
|
||||||
|
// Draw difficulty window blocks
|
||||||
|
let blocks_size = stats.diff_stats.last_blocks.len();
|
||||||
|
ScrollArea::vertical().auto_shrink([false; 2]).stick_to_bottom(true).show_rows(
|
||||||
|
ui,
|
||||||
|
DIFF_BLOCK_HEIGHT,
|
||||||
|
blocks_size,
|
||||||
|
|ui, row_range| {
|
||||||
|
for (index, db) in stats.diff_stats.last_blocks.iter().enumerate() {
|
||||||
|
let rounding = if blocks_size == 1 {
|
||||||
|
[true, true]
|
||||||
|
} else if index == 0 {
|
||||||
|
[true, false]
|
||||||
|
} else if index == blocks_size - 1 {
|
||||||
|
[false, true]
|
||||||
|
} else {
|
||||||
|
[false, false]
|
||||||
|
};
|
||||||
|
draw_diff_block(ui, db, rounding)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const DIFF_BLOCK_HEIGHT: f32 = 77.0;
|
||||||
|
|
||||||
|
fn draw_diff_block(ui: &mut egui::Ui, db: &DiffBlock, rounding: [bool; 2]) {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.add_space(6.0);
|
||||||
|
ui.vertical(|ui| {
|
||||||
|
let mut rect = ui.available_rect_before_wrap();
|
||||||
|
rect.set_height(DIFF_BLOCK_HEIGHT);
|
||||||
|
|
||||||
|
ui.painter().rect(
|
||||||
|
rect,
|
||||||
|
Rounding {
|
||||||
|
nw: if rounding[0] { 8.0 } else { 0.0 },
|
||||||
|
ne: if rounding[0] { 8.0 } else { 0.0 },
|
||||||
|
sw: if rounding[1] { 8.0 } else { 0.0 },
|
||||||
|
se: if rounding[1] { 8.0 } else { 0.0 },
|
||||||
|
},
|
||||||
|
Color32::WHITE,
|
||||||
|
Stroke { width: 1.0, color: Color32::from_gray(230) }
|
||||||
|
);
|
||||||
|
|
||||||
|
ui.add_space(2.0);
|
||||||
|
ui.horizontal_top(|ui| {
|
||||||
|
ui.add_space(6.0);
|
||||||
|
ui.heading(RichText::new(HASH)
|
||||||
|
.color(Color32::BLACK)
|
||||||
|
.size(16.0));
|
||||||
|
ui.add_space(3.0);
|
||||||
|
|
||||||
|
// Draw block hash
|
||||||
|
ui.heading(RichText::new(db.block_hash.to_string())
|
||||||
|
.color(Color32::BLACK)
|
||||||
|
.size(16.0));
|
||||||
|
});
|
||||||
|
ui.horizontal_top(|ui| {
|
||||||
|
ui.add_space(6.0);
|
||||||
|
ui.heading(RichText::new(CUBE_TRANSPARENT)
|
||||||
|
.color(COLOR_DARK)
|
||||||
|
.size(16.0));
|
||||||
|
ui.add_space(4.0);
|
||||||
|
|
||||||
|
// Draw block difficulty and height
|
||||||
|
ui.heading(RichText::new(db.difficulty.to_string())
|
||||||
|
.color(COLOR_DARK)
|
||||||
|
.size(16.0));
|
||||||
|
ui.add_space(2.0);
|
||||||
|
ui.heading(RichText::new(AT).color(COLOR_DARK).size(16.0));
|
||||||
|
ui.add_space(2.0);
|
||||||
|
ui.heading(RichText::new(db.block_height.to_string())
|
||||||
|
.color(COLOR_DARK)
|
||||||
|
.size(16.0));
|
||||||
|
});
|
||||||
|
ui.horizontal_top(|ui| {
|
||||||
|
ui.add_space(6.0);
|
||||||
|
ui.heading(RichText::new(TIMER)
|
||||||
|
.color(COLOR_GRAY)
|
||||||
|
.size(16.0));
|
||||||
|
ui.add_space(4.0);
|
||||||
|
|
||||||
|
// Draw block time
|
||||||
|
ui.heading(RichText::new(format!("{}s", db.duration))
|
||||||
|
.color(COLOR_GRAY)
|
||||||
|
.size(16.0));
|
||||||
|
ui.add_space(2.0);
|
||||||
|
ui.heading(RichText::new(HOURGLASS_LOW).color(COLOR_GRAY).size(16.0));
|
||||||
|
ui.add_space(2.0);
|
||||||
|
|
||||||
|
let naive_datetime = NaiveDateTime::from_timestamp_opt(db.time as i64, 0);
|
||||||
|
if naive_datetime.is_some() {
|
||||||
|
let datetime: DateTime<Utc> = DateTime::from_utc(naive_datetime.unwrap(), Utc);
|
||||||
|
ui.heading(RichText::new(datetime.to_string())
|
||||||
|
.color(COLOR_GRAY)
|
||||||
|
.size(16.0));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ui.add_space(4.0);
|
||||||
|
})
|
||||||
|
});
|
||||||
}
|
}
|
|
@ -12,22 +12,14 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use std::time::SystemTime;
|
use eframe::epaint::Stroke;
|
||||||
|
|
||||||
use chrono::{DateTime, Local, Offset, TimeZone, Utc};
|
|
||||||
use chrono::format::DelayedFormat;
|
|
||||||
use eframe::emath::Vec2;
|
|
||||||
use eframe::epaint::{FontId, Stroke};
|
|
||||||
use eframe::epaint::text::{LayoutJob, TextFormat, TextWrapping};
|
|
||||||
use egui::{Color32, RichText, Rounding, ScrollArea, Spinner, Widget};
|
use egui::{Color32, RichText, Rounding, ScrollArea, Spinner, Widget};
|
||||||
use egui_extras::{Size, StripBuilder};
|
|
||||||
use grin_servers::common::stats::TxStats;
|
use grin_servers::common::stats::TxStats;
|
||||||
use grin_servers::PeerStats;
|
use grin_servers::PeerStats;
|
||||||
|
|
||||||
use crate::gui::colors::{COLOR_DARK, COLOR_GRAY, COLOR_LIGHT, COLOR_YELLOW};
|
use crate::gui::colors::{COLOR_DARK, COLOR_GRAY};
|
||||||
use crate::gui::icons::{AT, CUBE, DEVICES, DOWNLOAD_SIMPLE, FLOW_ARROW, HANDSHAKE, PACKAGE, PLUGS_CONNECTED, SHARE_NETWORK};
|
use crate::gui::icons::{AT, CUBE, DEVICES, FLOW_ARROW, HANDSHAKE, PACKAGE, PLUGS_CONNECTED, SHARE_NETWORK};
|
||||||
use crate::gui::views::{DEFAULT_STROKE, NetworkTab};
|
use crate::gui::views::{NetworkTab, View};
|
||||||
use crate::gui::views::common::sub_title;
|
|
||||||
use crate::node::Node;
|
use crate::node::Node;
|
||||||
|
|
||||||
pub struct NetworkNode {
|
pub struct NetworkNode {
|
||||||
|
@ -43,6 +35,10 @@ impl Default for NetworkNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NetworkTab for NetworkNode {
|
impl NetworkTab for NetworkNode {
|
||||||
|
fn name(&self) -> &String {
|
||||||
|
&self.title
|
||||||
|
}
|
||||||
|
|
||||||
fn ui(&mut self, ui: &mut egui::Ui, node: &mut Node) {
|
fn ui(&mut self, ui: &mut egui::Ui, node: &mut Node) {
|
||||||
let server_stats = node.state.get_stats();
|
let server_stats = node.state.get_stats();
|
||||||
if !server_stats.is_some() {
|
if !server_stats.is_some() {
|
||||||
|
@ -54,97 +50,86 @@ impl NetworkTab for NetworkNode {
|
||||||
|
|
||||||
let stats = server_stats.as_ref().unwrap();
|
let stats = server_stats.as_ref().unwrap();
|
||||||
|
|
||||||
// Make scroll bar thinner
|
|
||||||
ui.style_mut().spacing.scroll_bar_width = 4.0;
|
|
||||||
|
|
||||||
ScrollArea::vertical()
|
ScrollArea::vertical()
|
||||||
.auto_shrink([false; 2])
|
.auto_shrink([false; 2])
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
// Disable item spacing
|
|
||||||
ui.style_mut().spacing.item_spacing = Vec2::new(0.0, 0.0);
|
|
||||||
|
|
||||||
// Show header stats
|
// Show header stats
|
||||||
ui.vertical_centered_justified(|ui| {
|
ui.vertical_centered_justified(|ui| {
|
||||||
sub_title(ui, format!("{} {}", FLOW_ARROW, t!("header")), COLOR_DARK);
|
View::sub_title(ui, format!("{} {}", FLOW_ARROW, t!("header")), COLOR_DARK);
|
||||||
});
|
});
|
||||||
ui.add_space(4.0);
|
ui.add_space(4.0);
|
||||||
|
|
||||||
ui.columns(2, |columns| {
|
ui.columns(2, |columns| {
|
||||||
columns[0].vertical_centered(|ui| {
|
columns[0].vertical_centered(|ui| {
|
||||||
draw_stat_box(ui,
|
View::rounded_box(ui,
|
||||||
stats.header_stats.last_block_h.to_string(),
|
stats.header_stats.last_block_h.to_string(),
|
||||||
t!("hash"),
|
t!("hash"),
|
||||||
StatBoxRounding::TopLeft);
|
[true, false, false, false]);
|
||||||
});
|
});
|
||||||
columns[1].vertical_centered(|ui| {
|
columns[1].vertical_centered(|ui| {
|
||||||
draw_stat_box(ui,
|
View::rounded_box(ui,
|
||||||
stats.header_stats.height.to_string(),
|
stats.header_stats.height.to_string(),
|
||||||
t!("height"),
|
t!("height"),
|
||||||
StatBoxRounding::TopRight);
|
[false, true, false, false]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.columns(2, |columns| {
|
ui.columns(2, |columns| {
|
||||||
columns[0].vertical_centered(|ui| {
|
columns[0].vertical_centered(|ui| {
|
||||||
draw_stat_box(ui,
|
View::rounded_box(ui,
|
||||||
stats.header_stats.total_difficulty.to_string(),
|
stats.header_stats.total_difficulty.to_string(),
|
||||||
t!("difficulty"),
|
t!("difficulty"),
|
||||||
StatBoxRounding::BottomLeft);
|
[false, false, true, false]);
|
||||||
});
|
});
|
||||||
columns[1].vertical_centered(|ui| {
|
columns[1].vertical_centered(|ui| {
|
||||||
let ts = stats.header_stats.latest_timestamp;
|
let ts = stats.header_stats.latest_timestamp;
|
||||||
draw_stat_box(ui,
|
View::rounded_box(ui,
|
||||||
format!("{}", ts.format("%d/%m/%Y %H:%M")),
|
format!("{}", ts.format("%d/%m/%Y %H:%M")),
|
||||||
t!("time_utc"),
|
t!("time_utc"),
|
||||||
StatBoxRounding::BottomRight);
|
[false, false, false, true]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Show block stats
|
// Show block stats
|
||||||
ui.add_space(5.0);
|
ui.add_space(5.0);
|
||||||
ui.vertical_centered_justified(|ui| {
|
ui.vertical_centered_justified(|ui| {
|
||||||
sub_title(ui, format!("{} {}", CUBE, t!("block")), COLOR_DARK);
|
View::sub_title(ui, format!("{} {}", CUBE, t!("block")), COLOR_DARK);
|
||||||
});
|
});
|
||||||
ui.add_space(4.0);
|
ui.add_space(4.0);
|
||||||
|
|
||||||
ui.columns(2, |columns| {
|
ui.columns(2, |columns| {
|
||||||
columns[0].vertical_centered(|ui| {
|
columns[0].vertical_centered(|ui| {
|
||||||
draw_stat_box(ui,
|
View::rounded_box(ui,
|
||||||
stats.chain_stats.last_block_h.to_string(),
|
stats.chain_stats.last_block_h.to_string(),
|
||||||
t!("hash"),
|
t!("hash"),
|
||||||
StatBoxRounding::TopLeft);
|
[true, false, false, false]);
|
||||||
});
|
});
|
||||||
columns[1].vertical_centered(|ui| {
|
columns[1].vertical_centered(|ui| {
|
||||||
draw_stat_box(ui,
|
View::rounded_box(ui,
|
||||||
stats.chain_stats.height.to_string(),
|
stats.chain_stats.height.to_string(),
|
||||||
t!("height"),
|
t!("height"),
|
||||||
StatBoxRounding::TopRight);
|
[false, true, false, false]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.columns(2, |columns| {
|
ui.columns(2, |columns| {
|
||||||
columns[0].vertical_centered(|ui| {
|
columns[0].vertical_centered(|ui| {
|
||||||
draw_stat_box(ui,
|
View::rounded_box(ui,
|
||||||
stats.chain_stats.total_difficulty.to_string(),
|
stats.chain_stats.total_difficulty.to_string(),
|
||||||
t!("difficulty"),
|
t!("difficulty"),
|
||||||
StatBoxRounding::BottomLeft);
|
[false, false, true, false]);
|
||||||
});
|
});
|
||||||
columns[1].vertical_centered(|ui| {
|
columns[1].vertical_centered(|ui| {
|
||||||
let ts = stats.chain_stats.latest_timestamp;
|
let ts = stats.chain_stats.latest_timestamp;
|
||||||
draw_stat_box(ui,
|
View::rounded_box(ui,
|
||||||
format!("{}", ts.format("%d/%m/%Y %H:%M")),
|
format!("{}", ts.format("%d/%m/%Y %H:%M")),
|
||||||
t!("time_utc"),
|
t!("time_utc"),
|
||||||
StatBoxRounding::BottomRight);
|
[false, false, false, true]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Show data stats
|
// Show data stats
|
||||||
ui.add_space(5.0);
|
ui.add_space(5.0);
|
||||||
ui.vertical_centered_justified(|ui| {
|
ui.vertical_centered_justified(|ui| {
|
||||||
sub_title(ui, format!("{} {}", SHARE_NETWORK, t!("data")), COLOR_DARK);
|
View::sub_title(ui, format!("{} {}", SHARE_NETWORK, t!("data")), COLOR_DARK);
|
||||||
});
|
});
|
||||||
ui.add_space(4.0);
|
ui.add_space(4.0);
|
||||||
|
|
||||||
ui.columns(2, |columns| {
|
ui.columns(2, |columns| {
|
||||||
columns[0].vertical_centered(|ui| {
|
columns[0].vertical_centered(|ui| {
|
||||||
let tx_stat = match &stats.tx_stats {
|
let tx_stat = match &stats.tx_stats {
|
||||||
|
@ -153,7 +138,10 @@ impl NetworkTab for NetworkNode {
|
||||||
format!("{} ({})", tx.tx_pool_size, tx.tx_pool_kernels)
|
format!("{} ({})", tx.tx_pool_size, tx.tx_pool_kernels)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
draw_stat_box(ui, tx_stat, t!("main_pool"), StatBoxRounding::TopLeft);
|
View::rounded_box(ui,
|
||||||
|
tx_stat,
|
||||||
|
t!("main_pool"),
|
||||||
|
[true, false, false, false]);
|
||||||
});
|
});
|
||||||
columns[1].vertical_centered(|ui| {
|
columns[1].vertical_centered(|ui| {
|
||||||
let stem_tx_stat = match &stats.tx_stats {
|
let stem_tx_stat = match &stats.tx_stats {
|
||||||
|
@ -162,23 +150,25 @@ impl NetworkTab for NetworkNode {
|
||||||
format!("{} ({})", stx.stem_pool_size, stx.stem_pool_kernels)
|
format!("{} ({})", stx.stem_pool_size, stx.stem_pool_kernels)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
draw_stat_box(ui, stem_tx_stat, t!("stem_pool"), StatBoxRounding::TopRight);
|
View::rounded_box(ui,
|
||||||
|
stem_tx_stat,
|
||||||
|
t!("stem_pool"),
|
||||||
|
[false, true, false, false]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.columns(2, |columns| {
|
ui.columns(2, |columns| {
|
||||||
columns[0].vertical_centered(|ui| {
|
columns[0].vertical_centered(|ui| {
|
||||||
draw_stat_box(ui,
|
View::rounded_box(ui,
|
||||||
stats.disk_usage_gb.to_string(),
|
stats.disk_usage_gb.to_string(),
|
||||||
t!("size"),
|
t!("size"),
|
||||||
StatBoxRounding::BottomLeft);
|
[false, false, true, false]);
|
||||||
});
|
});
|
||||||
columns[1].vertical_centered(|ui| {
|
columns[1].vertical_centered(|ui| {
|
||||||
let ts = stats.chain_stats.latest_timestamp;
|
let ts = stats.chain_stats.latest_timestamp;
|
||||||
draw_stat_box(ui,
|
View::rounded_box(ui,
|
||||||
stats.peer_count.to_string(),
|
stats.peer_count.to_string(),
|
||||||
t!("peers"),
|
t!("peers"),
|
||||||
StatBoxRounding::BottomRight);
|
[false, false, false, true]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -186,15 +176,14 @@ impl NetworkTab for NetworkNode {
|
||||||
if stats.peer_count > 0 {
|
if stats.peer_count > 0 {
|
||||||
ui.add_space(5.0);
|
ui.add_space(5.0);
|
||||||
ui.vertical_centered_justified(|ui| {
|
ui.vertical_centered_justified(|ui| {
|
||||||
sub_title(ui, format!("{} {}", HANDSHAKE, t!("peers")), COLOR_DARK);
|
View::sub_title(ui,format!("{} {}", HANDSHAKE, t!("peers")), COLOR_DARK);
|
||||||
});
|
});
|
||||||
ui.add_space(4.0);
|
ui.add_space(4.0);
|
||||||
|
|
||||||
for (index, ps) in stats.peer_stats.iter().enumerate() {
|
for (index, ps) in stats.peer_stats.iter().enumerate() {
|
||||||
let rounding = if index == 0 {
|
let rounding = if stats.peer_count == 1 {
|
||||||
if stats.peer_count == 1 {
|
[true, true]
|
||||||
[true, true];
|
} else if index == 0 {
|
||||||
}
|
|
||||||
[true, false]
|
[true, false]
|
||||||
} else if index == &stats.peer_stats.len() - 1 {
|
} else if index == &stats.peer_stats.len() - 1 {
|
||||||
[false, true]
|
[false, true]
|
||||||
|
@ -208,10 +197,6 @@ impl NetworkTab for NetworkNode {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name(&self) -> &String {
|
|
||||||
&self.title
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_peer_stats(ui: &mut egui::Ui, peer: &PeerStats, rounding: [bool; 2]) {
|
fn draw_peer_stats(ui: &mut egui::Ui, peer: &PeerStats, rounding: [bool; 2]) {
|
||||||
|
@ -231,14 +216,11 @@ fn draw_peer_stats(ui: &mut egui::Ui, peer: &PeerStats, rounding: [bool; 2]) {
|
||||||
);
|
);
|
||||||
|
|
||||||
ui.add_space(2.0);
|
ui.add_space(2.0);
|
||||||
|
|
||||||
ui.horizontal_top(|ui| {
|
ui.horizontal_top(|ui| {
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
|
||||||
ui.heading(RichText::new(PLUGS_CONNECTED)
|
ui.heading(RichText::new(PLUGS_CONNECTED)
|
||||||
.color(Color32::BLACK)
|
.color(Color32::BLACK)
|
||||||
.size(18.0));
|
.size(18.0));
|
||||||
|
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
|
||||||
// Draw peer address
|
// Draw peer address
|
||||||
|
@ -246,14 +228,11 @@ fn draw_peer_stats(ui: &mut egui::Ui, peer: &PeerStats, rounding: [bool; 2]) {
|
||||||
.color(Color32::BLACK)
|
.color(Color32::BLACK)
|
||||||
.size(18.0));
|
.size(18.0));
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.horizontal_top(|ui| {
|
ui.horizontal_top(|ui| {
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
|
||||||
ui.heading(RichText::new(PACKAGE)
|
ui.heading(RichText::new(PACKAGE)
|
||||||
.color(COLOR_DARK)
|
.color(COLOR_DARK)
|
||||||
.size(16.0));
|
.size(16.0));
|
||||||
|
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
|
||||||
// Draw peer difficulty and height
|
// Draw peer difficulty and height
|
||||||
|
@ -270,11 +249,9 @@ fn draw_peer_stats(ui: &mut egui::Ui, peer: &PeerStats, rounding: [bool; 2]) {
|
||||||
|
|
||||||
ui.horizontal_top(|ui| {
|
ui.horizontal_top(|ui| {
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
|
||||||
ui.heading(RichText::new(DEVICES)
|
ui.heading(RichText::new(DEVICES)
|
||||||
.color(COLOR_GRAY)
|
.color(COLOR_GRAY)
|
||||||
.size(16.0));
|
.size(16.0));
|
||||||
|
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
|
||||||
// Draw peer user-agent
|
// Draw peer user-agent
|
||||||
|
@ -282,51 +259,5 @@ fn draw_peer_stats(ui: &mut egui::Ui, peer: &PeerStats, rounding: [bool; 2]) {
|
||||||
.color(COLOR_GRAY)
|
.color(COLOR_GRAY)
|
||||||
.size(16.0));
|
.size(16.0));
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.add_space(2.0);
|
ui.add_space(2.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
|
||||||
enum StatBoxRounding {
|
|
||||||
TopLeft, TopRight, BottomRight, BottomLeft
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_stat_box(ui: &mut egui::Ui, value: String, label: String, rounding: StatBoxRounding) {
|
|
||||||
let mut rect = ui.available_rect_before_wrap();
|
|
||||||
rect.set_height(46.0);
|
|
||||||
|
|
||||||
// Draw box background
|
|
||||||
ui.painter().rect(
|
|
||||||
rect,
|
|
||||||
Rounding {
|
|
||||||
nw: if rounding == StatBoxRounding::TopLeft { 8.0 } else { 0.0 },
|
|
||||||
ne: if rounding == StatBoxRounding::TopRight { 8.0 } else { 0.0 },
|
|
||||||
sw: if rounding == StatBoxRounding::BottomLeft { 8.0 } else { 0.0 },
|
|
||||||
se: if rounding == StatBoxRounding::BottomRight { 8.0 } else { 0.0 },
|
|
||||||
},
|
|
||||||
Color32::WHITE,
|
|
||||||
Stroke { width: 1.0, color: Color32::from_gray(230) },
|
|
||||||
);
|
|
||||||
|
|
||||||
ui.vertical_centered_justified(|ui| {
|
|
||||||
// Correct vertical spacing between items
|
|
||||||
ui.style_mut().spacing.item_spacing.y = -4.0;
|
|
||||||
|
|
||||||
// Draw box value
|
|
||||||
let mut job = LayoutJob::single_section(value, TextFormat {
|
|
||||||
font_id: FontId::proportional(18.0),
|
|
||||||
color: Color32::BLACK,
|
|
||||||
.. Default::default()
|
|
||||||
});
|
|
||||||
job.wrap = TextWrapping {
|
|
||||||
max_rows: 1,
|
|
||||||
break_anywhere: false,
|
|
||||||
overflow_character: Option::from('﹍'),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
ui.label(job);
|
|
||||||
|
|
||||||
// Draw box label
|
|
||||||
ui.label(RichText::new(label).color(COLOR_GRAY).size(15.0));
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -16,10 +16,10 @@ use eframe::epaint::{FontId, Stroke};
|
||||||
use eframe::epaint::text::{LayoutJob, TextFormat, TextWrapping};
|
use eframe::epaint::text::{LayoutJob, TextFormat, TextWrapping};
|
||||||
use egui::style::Margin;
|
use egui::style::Margin;
|
||||||
use egui_extras::{Size, StripBuilder};
|
use egui_extras::{Size, StripBuilder};
|
||||||
use crate::gui::colors::{COLOR_DARK, COLOR_YELLOW};
|
|
||||||
|
|
||||||
|
use crate::gui::colors::{COLOR_DARK, COLOR_YELLOW};
|
||||||
use crate::gui::screens::Navigator;
|
use crate::gui::screens::Navigator;
|
||||||
use crate::gui::views::common::title_button;
|
use crate::gui::views::View;
|
||||||
|
|
||||||
pub struct TitlePanelAction {
|
pub struct TitlePanelAction {
|
||||||
pub(crate) icon: Box<str>,
|
pub(crate) icon: Box<str>,
|
||||||
|
@ -88,33 +88,32 @@ impl<'nav> TitlePanel<'nav> {
|
||||||
.size(Size::exact(52.0))
|
.size(Size::exact(52.0))
|
||||||
.horizontal(|mut strip| {
|
.horizontal(|mut strip| {
|
||||||
strip.cell(|ui| {
|
strip.cell(|ui| {
|
||||||
Self::show_action(ui, actions.left.as_ref(), nav);
|
show_action(ui, actions.left.as_ref(), nav);
|
||||||
});
|
});
|
||||||
strip.strip(|builder| {
|
strip.strip(|builder| {
|
||||||
builder
|
builder
|
||||||
.size(Size::remainder())
|
.size(Size::remainder())
|
||||||
.vertical(|mut strip| {
|
.vertical(|mut strip| {
|
||||||
strip.cell(|ui| {
|
strip.cell(|ui| {
|
||||||
Self::show_title(&*title, ui);
|
show_title(&*title, ui);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
strip.cell(|ui| {
|
strip.cell(|ui| {
|
||||||
Self::show_action(ui, actions.right.as_ref(), nav);
|
show_action(ui, actions.right.as_ref(), nav);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn show_action(ui: &mut egui::Ui,
|
fn show_action(ui: &mut egui::Ui, action: Option<&TitlePanelAction>, navigator: &mut Navigator) {
|
||||||
action: Option<&TitlePanelAction>,
|
|
||||||
navigator: &mut Navigator) {
|
|
||||||
if action.is_some() {
|
if action.is_some() {
|
||||||
let action = action.unwrap();
|
let action = action.unwrap();
|
||||||
ui.centered_and_justified(|ui| {
|
ui.centered_and_justified(|ui| {
|
||||||
title_button(ui, &action.icon, || {
|
View::title_button(ui, &action.icon, || {
|
||||||
(action.on_click)(navigator);
|
(action.on_click)(navigator);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -141,5 +140,4 @@ impl<'nav> TitlePanel<'nav> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
114
src/gui/views/views.rs
Normal file
114
src/gui/views/views.rs
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
// Copyright 2023 The Grim Developers
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
use eframe::epaint::{Color32, FontId, Rounding, Stroke};
|
||||||
|
use eframe::epaint::text::{LayoutJob, TextFormat, TextWrapping};
|
||||||
|
use egui::{RichText, Sense, Widget};
|
||||||
|
|
||||||
|
use crate::gui::colors::{COLOR_DARK, COLOR_GRAY, COLOR_LIGHT};
|
||||||
|
|
||||||
|
pub struct View;
|
||||||
|
|
||||||
|
impl View {
|
||||||
|
pub const DEFAULT_STROKE: Stroke = Stroke { width: 1.0, color: Color32::from_gray(190) };
|
||||||
|
|
||||||
|
pub fn title_button(ui: &mut egui::Ui, icon: &str, action: impl FnOnce()) {
|
||||||
|
let b = egui::widgets::Button::new(
|
||||||
|
RichText::new(icon.to_string()).size(24.0).color(COLOR_DARK)
|
||||||
|
).fill(Color32::TRANSPARENT)
|
||||||
|
.ui(ui).interact(Sense::click_and_drag());
|
||||||
|
|
||||||
|
// Click optimization for touch screens
|
||||||
|
if b.drag_released() || b.clicked() {
|
||||||
|
(action)();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tab_button(ui: &mut egui::Ui, icon: &str, active: bool, mut action: impl FnMut()) {
|
||||||
|
let stroke = match active {
|
||||||
|
true => { Stroke::NONE }
|
||||||
|
false => { Self::DEFAULT_STROKE }
|
||||||
|
};
|
||||||
|
|
||||||
|
let color = match active {
|
||||||
|
true => { COLOR_LIGHT }
|
||||||
|
false => { Color32::WHITE }
|
||||||
|
};
|
||||||
|
|
||||||
|
let b = egui::widgets::Button::new(
|
||||||
|
RichText::new(icon.to_string()).size(24.0).color(COLOR_DARK)
|
||||||
|
).min_size(ui.available_size_before_wrap())
|
||||||
|
.stroke(stroke)
|
||||||
|
.fill(color)
|
||||||
|
.ui(ui).interact(Sense::click_and_drag());
|
||||||
|
|
||||||
|
|
||||||
|
let vel_y = ui.ctx().input().pointer.delta().y;
|
||||||
|
let vel_x = ui.ctx().input().pointer.delta().x;
|
||||||
|
println!("12345, vel {}, {}", vel_y, vel_x);
|
||||||
|
|
||||||
|
// Click optimization for touch screens
|
||||||
|
if b.drag_released() || b.clicked() {
|
||||||
|
(action)();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sub_title(ui: &mut egui::Ui, text: String, color: Color32) {
|
||||||
|
ui.label(RichText::new(text).size(17.0).color(color));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draw rounded box with some value and label in the middle
|
||||||
|
/// where is r = [top_left, top_right, bottom_left, bottom_right]
|
||||||
|
/// | VALUE |
|
||||||
|
/// | label |
|
||||||
|
pub fn rounded_box(ui: &mut egui::Ui, value: String, label: String, r: [bool; 4]) {
|
||||||
|
let mut rect = ui.available_rect_before_wrap();
|
||||||
|
rect.set_height(46.0);
|
||||||
|
|
||||||
|
// Draw box background
|
||||||
|
ui.painter().rect(
|
||||||
|
rect,
|
||||||
|
Rounding {
|
||||||
|
nw: if r[0] { 8.0 } else { 0.0 },
|
||||||
|
ne: if r[1] { 8.0 } else { 0.0 },
|
||||||
|
sw: if r[2] { 8.0 } else { 0.0 },
|
||||||
|
se: if r[3] { 8.0 } else { 0.0 },
|
||||||
|
},
|
||||||
|
Color32::WHITE,
|
||||||
|
Stroke { width: 1.0, color: Color32::from_gray(230) },
|
||||||
|
);
|
||||||
|
|
||||||
|
ui.vertical_centered_justified(|ui| {
|
||||||
|
// Correct vertical spacing between items
|
||||||
|
ui.style_mut().spacing.item_spacing.y = -4.0;
|
||||||
|
|
||||||
|
// Draw box value
|
||||||
|
let mut job = LayoutJob::single_section(value, TextFormat {
|
||||||
|
font_id: FontId::proportional(18.0),
|
||||||
|
color: Color32::BLACK,
|
||||||
|
.. Default::default()
|
||||||
|
});
|
||||||
|
job.wrap = TextWrapping {
|
||||||
|
max_rows: 1,
|
||||||
|
break_anywhere: false,
|
||||||
|
overflow_character: Option::from('﹍'),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
ui.label(job);
|
||||||
|
|
||||||
|
// Draw box label
|
||||||
|
ui.label(RichText::new(label).color(COLOR_GRAY).size(15.0));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue