ui: setup buttons style, add server loading progress for node and metrics, optimize node status
This commit is contained in:
parent
532a79fc47
commit
ffc68bc6ae
14 changed files with 203 additions and 135 deletions
|
@ -5,6 +5,7 @@ import android.os.Bundle;
|
|||
import android.os.Process;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.OrientationEventListener;
|
||||
import com.google.androidgamesdk.GameActivity;
|
||||
|
@ -29,7 +30,7 @@ public class MainActivity extends GameActivity {
|
|||
|
||||
// Callback to update display cutouts at native code.
|
||||
OrientationEventListener orientationEventListener = new OrientationEventListener(this,
|
||||
SensorManager.SENSOR_DELAY_GAME) {
|
||||
SensorManager.SENSOR_DELAY_FASTEST) {
|
||||
@Override
|
||||
public void onOrientationChanged(int orientation) {
|
||||
onDisplayCutoutsChanged(Utils.getDisplayCutouts(MainActivity.this));
|
||||
|
@ -82,6 +83,7 @@ public class MainActivity extends GameActivity {
|
|||
|
||||
// Called from native code
|
||||
public void onExit() {
|
||||
Log.d("12345", "onExit");
|
||||
mManualExit = true;
|
||||
BackgroundService.stop(this);
|
||||
finish();
|
||||
|
|
|
@ -5,6 +5,9 @@ network:
|
|||
metrics: Metrics
|
||||
mining: Mining
|
||||
settings: Server settings
|
||||
enable: Enable
|
||||
disable: Disable
|
||||
restart: Restart
|
||||
sync_status:
|
||||
server_restarting: Server is restarting
|
||||
server_down: Server is down
|
||||
|
@ -36,6 +39,7 @@ network_node:
|
|||
size: Size (GB)
|
||||
peers: Peers
|
||||
network_metrics:
|
||||
loading: Metrics will be available after the synchronization
|
||||
emission: Emission
|
||||
inflation: Inflation
|
||||
supply: Supply
|
||||
|
|
|
@ -5,6 +5,9 @@ network:
|
|||
metrics: Метрики
|
||||
mining: Майнинг
|
||||
settings: Настройки сервера
|
||||
enable: Включить
|
||||
disable: Выключить
|
||||
restart: Перезапустить
|
||||
sync_status:
|
||||
server_restarting: Сервер перезапускается
|
||||
server_down: Сервер выключен
|
||||
|
@ -36,6 +39,7 @@ network_node:
|
|||
size: Размер (ГБ)
|
||||
peers: Пиры
|
||||
network_metrics:
|
||||
loading: Метрики будут доступны после синхронизации
|
||||
emission: Эмиссия
|
||||
inflation: Инфляция
|
||||
supply: Предложение
|
||||
|
|
|
@ -12,15 +12,15 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use egui::{Context, Stroke, Widget};
|
||||
use egui::{Color32, Context, RichText, Spinner, Stroke, Widget};
|
||||
use egui::os::OperatingSystem;
|
||||
use egui::style::Margin;
|
||||
|
||||
use crate::gui::colors::COLOR_LIGHT;
|
||||
use crate::gui::colors::{COLOR_DARK, COLOR_LIGHT, COLOR_YELLOW};
|
||||
use crate::gui::Navigator;
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::screens::Root;
|
||||
use crate::gui::views::{Modal, ModalId, ModalLocation, ProgressLoading, View};
|
||||
use crate::gui::views::{Modal, ModalId, ModalLocation, View};
|
||||
use crate::node::Node;
|
||||
|
||||
pub struct PlatformApp<Platform> {
|
||||
|
@ -36,18 +36,17 @@ pub struct App {
|
|||
|
||||
impl App {
|
||||
pub fn ui(&mut self, ctx: &Context, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) {
|
||||
let modal_open = Navigator::is_modal_open(ModalLocation::Global);
|
||||
egui::CentralPanel::default()
|
||||
.frame(egui::Frame {
|
||||
fill: COLOR_LIGHT,
|
||||
.. Default::default()
|
||||
})
|
||||
.show(ctx, |ui| {
|
||||
if modal_open {
|
||||
if Navigator::is_modal_open(ModalLocation::Global) {
|
||||
self.show_global_modal(ui, frame, cb);
|
||||
}
|
||||
self.root.ui(ui, frame, cb);
|
||||
}).response.enabled = !modal_open;
|
||||
});
|
||||
}
|
||||
|
||||
fn show_global_modal(&mut self,
|
||||
|
@ -60,12 +59,18 @@ impl App {
|
|||
ModalId::Exit => {
|
||||
if self.show_exit_progress {
|
||||
if !Node::is_running() {
|
||||
modal.close();
|
||||
Self::exit(frame, cb);
|
||||
modal.close();
|
||||
}
|
||||
ui.add_space(12.0);
|
||||
let text = Node::get_sync_status_text(Node::get_sync_status());
|
||||
ProgressLoading::new(text).ui(ui);
|
||||
ui.add_space(16.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
Spinner::new().size(42.0).color(COLOR_YELLOW).ui(ui);
|
||||
ui.add_space(10.0);
|
||||
ui.label(RichText::new(Node::get_sync_status_text())
|
||||
.size(18.0)
|
||||
.color(COLOR_DARK)
|
||||
);
|
||||
});
|
||||
ui.add_space(12.0);
|
||||
} else {
|
||||
ui.add_space(8.0);
|
||||
|
@ -77,19 +82,19 @@ impl App {
|
|||
ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0);
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::modal_button(ui, t!("modal_exit.exit"), || {
|
||||
View::button(ui, t!("modal_exit.exit"), Color32::WHITE, || {
|
||||
if !Node::is_running() {
|
||||
Self::exit(frame, cb);
|
||||
modal.close();
|
||||
} else {
|
||||
modal.disable_closing();
|
||||
Node::stop();
|
||||
modal.disable_closing();
|
||||
self.show_exit_progress = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::modal_button(ui, t!("modal.cancel"), || {
|
||||
View::button(ui, t!("modal.cancel"), Color32::WHITE, || {
|
||||
modal.close();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -96,11 +96,13 @@ impl PlatformApp<Android> {
|
|||
fn setup_visuals(ctx: &egui::Context) {
|
||||
// Setup style
|
||||
let mut style = (*ctx.style()).clone();
|
||||
|
||||
// Make scroll-bar thinner
|
||||
// Setup spacing for buttons.
|
||||
style.spacing.button_padding = egui::vec2(12.0, 8.0);
|
||||
// Make scroll-bar thinner.
|
||||
style.spacing.scroll_bar_width = 4.0;
|
||||
//
|
||||
// Disable spacing between items.
|
||||
style.spacing.item_spacing = egui::vec2(0.0, 0.0);
|
||||
|
||||
ctx.set_style(style);
|
||||
|
||||
// Setup visuals
|
||||
|
@ -183,7 +185,7 @@ fn padding_panels(ctx: &egui::Context) {
|
|||
.show_separator_line(false)
|
||||
.resizable(false)
|
||||
.exact_height(DISPLAY_CUTOUT_TOP.load(Ordering::Relaxed) as f32)
|
||||
.show(ctx, |ui| {});
|
||||
.show(ctx, |_ui| {});
|
||||
|
||||
egui::TopBottomPanel::bottom("bottom_padding_panel")
|
||||
.frame(egui::Frame {
|
||||
|
@ -194,7 +196,7 @@ fn padding_panels(ctx: &egui::Context) {
|
|||
.show_separator_line(false)
|
||||
.resizable(false)
|
||||
.exact_height(DISPLAY_CUTOUT_BOTTOM.load(Ordering::Relaxed) as f32)
|
||||
.show(ctx, |ui| {});
|
||||
.show(ctx, |_ui| {});
|
||||
|
||||
egui::SidePanel::right("right_padding_panel")
|
||||
.frame(egui::Frame {
|
||||
|
@ -205,7 +207,7 @@ fn padding_panels(ctx: &egui::Context) {
|
|||
.show_separator_line(false)
|
||||
.resizable(false)
|
||||
.max_width(DISPLAY_CUTOUT_RIGHT.load(Ordering::Relaxed) as f32)
|
||||
.show(ctx, |ui| {});
|
||||
.show(ctx, |_ui| {});
|
||||
|
||||
egui::SidePanel::left("left_padding_panel")
|
||||
.frame(egui::Frame {
|
||||
|
@ -216,7 +218,7 @@ fn padding_panels(ctx: &egui::Context) {
|
|||
.show_separator_line(false)
|
||||
.resizable(false)
|
||||
.max_width(DISPLAY_CUTOUT_LEFT.load(Ordering::Relaxed) as f32)
|
||||
.show(ctx, |ui| {});
|
||||
.show(ctx, |_ui| {});
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
|
|
|
@ -22,14 +22,12 @@ mod modal;
|
|||
pub use modal::*;
|
||||
|
||||
mod network;
|
||||
pub use network::Network;
|
||||
|
||||
mod network_node;
|
||||
mod network_settings;
|
||||
mod network_metrics;
|
||||
mod network_mining;
|
||||
pub use network::Network;
|
||||
|
||||
mod progress_loading;
|
||||
pub use progress_loading::ProgressLoading;
|
||||
|
||||
pub trait NetworkTab {
|
||||
fn name(&self) -> &String;
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
use std::cmp::min;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use egui::{Align2, Color32, RichText, Rounding, Sense, Stroke, Vec2};
|
||||
use egui::{Align2, Color32, RichText, Rounding, Sense, Separator, Stroke, Vec2, Widget};
|
||||
use egui::epaint::RectShape;
|
||||
use egui::style::Margin;
|
||||
use egui_extras::{Size, StripBuilder};
|
||||
|
@ -29,7 +29,6 @@ pub enum ModalId {
|
|||
}
|
||||
|
||||
/// Location for [`Modal`] at application UI.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ModalLocation {
|
||||
/// To draw globally above side panel and screen.
|
||||
Global,
|
||||
|
@ -204,7 +203,7 @@ impl Modal {
|
|||
rect,
|
||||
rounding,
|
||||
fill: COLOR_LIGHT,
|
||||
stroke: View::DEFAULT_STROKE,
|
||||
stroke: Stroke::NONE,
|
||||
};
|
||||
let bg_idx = ui.painter().add(bg_shape);
|
||||
|
||||
|
@ -250,5 +249,11 @@ impl Modal {
|
|||
// Setup background shape to be painted behind title content.
|
||||
bg_shape.rect = title_resp.rect;
|
||||
ui.painter().set(bg_idx, bg_shape);
|
||||
|
||||
let (rect, _) = ui.allocate_exact_size(Vec2::new(ui.available_width(), 1.0),Sense::hover());
|
||||
let painter = ui.painter();
|
||||
painter.hline(rect.x_range(),
|
||||
painter.round_to_pixel(rect.center().y),
|
||||
View::DEFAULT_STROKE);
|
||||
}
|
||||
}
|
|
@ -20,8 +20,8 @@ use egui_extras::{Size, StripBuilder};
|
|||
use grin_chain::SyncStatus;
|
||||
use grin_core::global::ChainTypes;
|
||||
|
||||
use crate::gui::colors::{COLOR_DARK, COLOR_GRAY_DARK, COLOR_YELLOW};
|
||||
use crate::gui::icons::{CARDHOLDER, DATABASE, DOTS_THREE_OUTLINE_VERTICAL, FACTORY, FADERS, GAUGE};
|
||||
use crate::gui::colors::{COLOR_DARK, COLOR_GRAY, COLOR_GRAY_DARK, COLOR_YELLOW};
|
||||
use crate::gui::icons::{CARDHOLDER, DATABASE, DOTS_THREE_OUTLINE_VERTICAL, FACTORY, FADERS, GAUGE, PLUGS, POWER};
|
||||
use crate::gui::Navigator;
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{NetworkTab, View};
|
||||
|
@ -71,7 +71,7 @@ impl Network {
|
|||
|
||||
egui::TopBottomPanel::bottom("network_tabs")
|
||||
.frame(egui::Frame {
|
||||
outer_margin: Margin::same(6.0),
|
||||
outer_margin: Margin::same(5.0),
|
||||
.. Default::default()
|
||||
})
|
||||
.resizable(false)
|
||||
|
@ -93,26 +93,28 @@ impl Network {
|
|||
|
||||
fn draw_tabs(&mut self, ui: &mut egui::Ui) {
|
||||
ui.scope(|ui| {
|
||||
//Setup spacing between tabs
|
||||
ui.style_mut().spacing.item_spacing = egui::vec2(6.0, 0.0);
|
||||
// Setup spacing between tabs.
|
||||
ui.style_mut().spacing.item_spacing = egui::vec2(5.0, 0.0);
|
||||
// Setup vertical padding inside tab button.
|
||||
ui.style_mut().spacing.button_padding = egui::vec2(0.0, 3.0);
|
||||
|
||||
ui.columns(4, |columns| {
|
||||
columns[0].vertical_centered(|ui| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::tab_button(ui, DATABASE, self.current_mode == Mode::Node, || {
|
||||
self.current_mode = Mode::Node;
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered(|ui| {
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::tab_button(ui, GAUGE, self.current_mode == Mode::Metrics, || {
|
||||
self.current_mode = Mode::Metrics;
|
||||
});
|
||||
});
|
||||
columns[2].vertical_centered(|ui| {
|
||||
columns[2].vertical_centered_justified(|ui| {
|
||||
View::tab_button(ui, FACTORY, self.current_mode == Mode::Miner, || {
|
||||
self.current_mode = Mode::Miner;
|
||||
});
|
||||
});
|
||||
columns[3].vertical_centered(|ui| {
|
||||
columns[3].vertical_centered_justified(|ui| {
|
||||
View::tab_button(ui, FADERS, self.current_mode == Mode::Tuning, || {
|
||||
self.current_mode = Mode::Tuning;
|
||||
});
|
||||
|
@ -200,9 +202,7 @@ impl Network {
|
|||
});
|
||||
strip.cell(|ui| {
|
||||
ui.centered_and_justified(|ui| {
|
||||
// Select sync status text
|
||||
let sync_status = Node::get_sync_status();
|
||||
let status_text = Node::get_sync_status_text(sync_status);
|
||||
|
||||
// Setup text color animation based on sync status
|
||||
let idle = match sync_status {
|
||||
|
@ -219,7 +219,7 @@ impl Network {
|
|||
// Draw sync text
|
||||
let status_color_rgba = Rgba::from(COLOR_GRAY_DARK) * color_factor as f32;
|
||||
let status_color = Color32::from(status_color_rgba);
|
||||
View::ellipsize_text(ui, status_text, 15.0, status_color);
|
||||
View::ellipsize_text(ui, Node::get_sync_status_text(), 15.0, status_color);
|
||||
|
||||
// Repaint based on sync status
|
||||
if idle {
|
||||
|
@ -231,5 +231,23 @@ impl Network {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
pub fn server_off_content(ui: &mut egui::Ui) {
|
||||
View::center_content(ui, [240.0, 214.0], |ui| {
|
||||
ui.label(RichText::new(PLUGS)
|
||||
.size(84.0)
|
||||
.color(Color32::from_gray(200))
|
||||
);
|
||||
ui.add_space(-16.0);
|
||||
ui.label(RichText::new(Node::get_sync_status_text())
|
||||
.size(19.0)
|
||||
.color(Color32::from_gray(150))
|
||||
);
|
||||
ui.add_space(12.0);
|
||||
View::button(ui, format!("{} {}", POWER, t!("network.enable")), COLOR_YELLOW, || {
|
||||
Node::start(ChainTypes::Mainnet);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,11 +15,12 @@
|
|||
use chrono::{DateTime, NaiveDateTime, Utc};
|
||||
use eframe::epaint::{Color32, Rounding, Stroke};
|
||||
use egui::{RichText, ScrollArea, Spinner, Widget};
|
||||
use grin_core::global::ChainTypes;
|
||||
use grin_servers::DiffBlock;
|
||||
|
||||
use crate::gui::colors::{COLOR_DARK, COLOR_GRAY, COLOR_GRAY_LIGHT};
|
||||
use crate::gui::icons::{AT, COINS, CUBE_TRANSPARENT, HASH, HOURGLASS_LOW, HOURGLASS_MEDIUM, TIMER};
|
||||
use crate::gui::views::{NetworkTab, View};
|
||||
use crate::gui::colors::{COLOR_DARK, COLOR_GRAY, COLOR_GRAY_LIGHT, COLOR_YELLOW};
|
||||
use crate::gui::icons::{AT, COINS, CUBE_TRANSPARENT, HASH, HOURGLASS_LOW, HOURGLASS_MEDIUM, PLUGS, POWER, TIMER};
|
||||
use crate::gui::views::{Network, NetworkTab, View};
|
||||
use crate::node::Node;
|
||||
|
||||
pub struct NetworkMetrics {
|
||||
|
@ -45,11 +46,19 @@ impl NetworkTab for NetworkMetrics {
|
|||
|
||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||
let server_stats = Node::get_stats();
|
||||
// Show loading widget if server is not working or difficulty height is zero.
|
||||
if !server_stats.is_some() || server_stats.as_ref().unwrap().diff_stats.height == 0 {
|
||||
ui.centered_and_justified(|ui| {
|
||||
Spinner::new().size(42.0).color(COLOR_GRAY).ui(ui);
|
||||
if server_stats.is_none() || server_stats.as_ref().unwrap().diff_stats.height == 0 {
|
||||
if !Node::is_running() {
|
||||
Network::server_off_content(ui);
|
||||
} else {
|
||||
View::center_content(ui, [280.0, 160.0], |ui| {
|
||||
Spinner::new().size(104.0).color(COLOR_YELLOW).ui(ui);
|
||||
ui.add_space(18.0);
|
||||
ui.label(RichText::new(t!("network_metrics.loading"))
|
||||
.size(16.0)
|
||||
.color(Color32::from_gray(150))
|
||||
);
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,3 +11,28 @@
|
|||
// 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 egui::Ui;
|
||||
use crate::gui::views::NetworkTab;
|
||||
|
||||
pub struct NetworkMining {
|
||||
title: String
|
||||
}
|
||||
|
||||
impl Default for NetworkMining {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
title: t!("network.mining").to_uppercase(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NetworkTab for NetworkMining {
|
||||
fn name(&self) -> &String {
|
||||
&self.title
|
||||
}
|
||||
|
||||
fn ui(&mut self, ui: &mut Ui) {
|
||||
todo!()
|
||||
}
|
||||
}
|
|
@ -14,11 +14,13 @@
|
|||
|
||||
use eframe::epaint::Stroke;
|
||||
use egui::{Color32, RichText, Rounding, ScrollArea, Spinner, Widget};
|
||||
use egui_extras::{Size, StripBuilder};
|
||||
use grin_core::global::ChainTypes;
|
||||
use grin_servers::PeerStats;
|
||||
|
||||
use crate::gui::colors::{COLOR_DARK, COLOR_GRAY, COLOR_GRAY_LIGHT};
|
||||
use crate::gui::icons::{AT, CUBE, DEVICES, FLOW_ARROW, HANDSHAKE, PACKAGE, PLUGS_CONNECTED, SHARE_NETWORK};
|
||||
use crate::gui::views::{NetworkTab, View};
|
||||
use crate::gui::colors::{COLOR_DARK, COLOR_GRAY, COLOR_GRAY_LIGHT, COLOR_YELLOW};
|
||||
use crate::gui::icons::{AT, CUBE, DEVICES, FLOW_ARROW, HANDSHAKE, PACKAGE, PLUGS, PLUGS_CONNECTED, POWER, SHARE_NETWORK};
|
||||
use crate::gui::views::{Network, NetworkTab, View};
|
||||
use crate::node::Node;
|
||||
|
||||
pub struct NetworkNode {
|
||||
|
@ -41,9 +43,13 @@ impl NetworkTab for NetworkNode {
|
|||
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||
let server_stats = Node::get_stats();
|
||||
if !server_stats.is_some() {
|
||||
if !Node::is_running() {
|
||||
Network::server_off_content(ui);
|
||||
} else {
|
||||
ui.centered_and_justified(|ui| {
|
||||
Spinner::new().size(42.0).color(COLOR_GRAY).ui(ui);
|
||||
Spinner::new().size(104.0).color(COLOR_YELLOW).ui(ui);
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,40 +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 egui::{Response, RichText, Spinner, Ui, Widget};
|
||||
|
||||
use crate::gui::colors::COLOR_DARK;
|
||||
|
||||
pub struct ProgressLoading {
|
||||
text: String
|
||||
}
|
||||
|
||||
impl ProgressLoading {
|
||||
pub fn new(text: String) -> Self {
|
||||
Self {
|
||||
text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for ProgressLoading {
|
||||
fn ui(self, ui: &mut Ui) -> Response {
|
||||
ui.vertical_centered_justified(|ui| {
|
||||
ui.add_space(10.0);
|
||||
Spinner::new().size(36.0).color(COLOR_DARK).ui(ui);
|
||||
ui.add_space(10.0);
|
||||
ui.label(RichText::new(self.text).size(18.0).color(COLOR_DARK));
|
||||
}).response
|
||||
}
|
||||
}
|
|
@ -14,10 +14,11 @@
|
|||
|
||||
use egui::epaint::{Color32, FontId, Rounding, Stroke};
|
||||
use egui::text::{LayoutJob, TextFormat};
|
||||
use egui::{Button, PointerState, Response, RichText, Sense, Widget};
|
||||
use egui::{Button, PointerState, Response, RichText, Sense, Vec2, Widget};
|
||||
use egui::epaint::text::TextWrapping;
|
||||
use egui_extras::{Size, StripBuilder};
|
||||
|
||||
use crate::gui::colors::{COLOR_DARK, COLOR_GRAY, COLOR_LIGHT, COLOR_GRAY_LIGHT, COLOR_GRAY_DARK};
|
||||
use crate::gui::colors::{COLOR_DARK, COLOR_GRAY, COLOR_LIGHT, COLOR_GRAY_LIGHT, COLOR_GRAY_DARK, COLOR_YELLOW};
|
||||
|
||||
pub struct View;
|
||||
|
||||
|
@ -103,7 +104,6 @@ impl View {
|
|||
false => { Color32::WHITE }
|
||||
};
|
||||
let br = Button::new(wt)
|
||||
.min_size(ui.available_size_before_wrap())
|
||||
.stroke(stroke)
|
||||
.fill(color)
|
||||
.ui(ui).interact(Sense::click_and_drag());
|
||||
|
@ -111,16 +111,12 @@ impl View {
|
|||
Self::on_button_click(ui, br, action);
|
||||
}
|
||||
|
||||
/// Modal button with white background fill color, contains text.
|
||||
pub fn modal_button(ui: &mut egui::Ui, text: String, action: impl FnOnce()) {
|
||||
let mut size = ui.available_size_before_wrap();
|
||||
size.y = 36.0;
|
||||
|
||||
/// Draw [`Button`] with specified background fill color.
|
||||
pub fn button(ui: &mut egui::Ui, text: String, fill_color: Color32, action: impl FnOnce()) {
|
||||
let wt = RichText::new(text.to_uppercase()).size(18.0).color(COLOR_GRAY_DARK);
|
||||
let br = Button::new(wt)
|
||||
.stroke(Self::DEFAULT_STROKE)
|
||||
.min_size(size)
|
||||
.fill(Color32::WHITE)
|
||||
.fill(fill_color)
|
||||
.ui(ui).interact(Sense::click_and_drag());
|
||||
|
||||
Self::on_button_click(ui, br, action);
|
||||
|
@ -169,4 +165,18 @@ impl View {
|
|||
ui.label(RichText::new(label).color(COLOR_GRAY).size(15.0));
|
||||
});
|
||||
}
|
||||
|
||||
/// Draw content in the center of current layout with specified width and height.
|
||||
pub fn center_content(ui: &mut egui::Ui, w_h: [f32; 2], content: impl FnOnce(&mut egui::Ui)) {
|
||||
ui.vertical_centered(|ui| {
|
||||
let mut rect = ui.available_rect_before_wrap();
|
||||
let side_margin = (ui.available_width() - w_h[0]) / 2.0;
|
||||
rect.min += egui::emath::vec2(side_margin, ui.available_height() / 2.0 - w_h[1] / 2.0);
|
||||
rect.max -= egui::emath::vec2(side_margin, 0.0);
|
||||
// rect.set_width(w_h[0]);
|
||||
ui.allocate_ui_at_rect(rect, |ui| {
|
||||
(content)(ui);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -30,11 +30,11 @@ use lazy_static::lazy_static;
|
|||
use log::info;
|
||||
|
||||
lazy_static! {
|
||||
/// Static thread-aware state of [Node] to be updated from another thread.
|
||||
/// Static thread-aware state of [`Node`] to be updated from another thread.
|
||||
static ref NODE_STATE: Arc<Node> = Arc::new(Node::default());
|
||||
}
|
||||
|
||||
/// Provides [Server] control, holds current status and statistics.
|
||||
/// Provides [`Server`] control, holds current status and statistics.
|
||||
pub struct Node {
|
||||
/// Statistics data for UI.
|
||||
stats: Arc<RwLock<Option<ServerStats>>>,
|
||||
|
@ -61,12 +61,12 @@ impl Default for Node {
|
|||
}
|
||||
|
||||
impl Node {
|
||||
/// Stop [Server].
|
||||
/// Stop the [`Server`].
|
||||
pub fn stop() {
|
||||
NODE_STATE.stop_needed.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Start [Server] with provided chain type.
|
||||
/// Start [`Server`] with provided chain type.
|
||||
pub fn start(chain_type: ChainTypes) {
|
||||
if !Self::is_running() {
|
||||
let mut w_chain_type = NODE_STATE.chain_type.write().unwrap();
|
||||
|
@ -75,7 +75,7 @@ impl Node {
|
|||
}
|
||||
}
|
||||
|
||||
/// Restart [Server] with provided chain type.
|
||||
/// Restart [`Server`] with provided chain type.
|
||||
pub fn restart(chain_type: ChainTypes) {
|
||||
if Self::is_running() {
|
||||
let mut w_chain_type = NODE_STATE.chain_type.write().unwrap();
|
||||
|
@ -86,40 +86,40 @@ impl Node {
|
|||
}
|
||||
}
|
||||
|
||||
/// Check if [Server] is starting.
|
||||
/// Check if [`Server`] is starting.
|
||||
pub fn is_starting() -> bool {
|
||||
NODE_STATE.starting.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Check if [Server] is running.
|
||||
/// Check if [`Server`] is running.
|
||||
pub fn is_running() -> bool {
|
||||
Self::get_stats().is_some() || Self::is_starting()
|
||||
Self::get_sync_status().is_some()
|
||||
}
|
||||
|
||||
/// Check if [Server] is stopping.
|
||||
/// Check if [`Server`] is stopping.
|
||||
pub fn is_stopping() -> bool {
|
||||
NODE_STATE.stop_needed.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Check if [Server] is restarting.
|
||||
/// Check if [`Server`] is restarting.
|
||||
pub fn is_restarting() -> bool {
|
||||
NODE_STATE.restart_needed.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Get [Server] statistics.
|
||||
/// Get [`Server`] statistics.
|
||||
pub fn get_stats() -> RwLockReadGuard<'static, Option<ServerStats>> {
|
||||
NODE_STATE.stats.read().unwrap()
|
||||
}
|
||||
|
||||
/// Get [Server] synchronization status, empty when it is not running.
|
||||
/// Get [`Server`] synchronization status, empty when Server is not running.
|
||||
pub fn get_sync_status() -> Option<SyncStatus> {
|
||||
// Return Shutdown status when node is stopping.
|
||||
if Self::is_stopping() {
|
||||
return Some(SyncStatus::Shutdown)
|
||||
}
|
||||
|
||||
// Return Initial status when node is starting.
|
||||
if Self::is_starting() {
|
||||
// Return Initial status when node is starting or restarting.
|
||||
if Self::is_starting() || Self::is_restarting() {
|
||||
return Some(SyncStatus::Initial)
|
||||
}
|
||||
|
||||
|
@ -131,16 +131,24 @@ impl Node {
|
|||
None
|
||||
}
|
||||
|
||||
/// Start a thread to launch [Server] and update [NODE_STATE] with server statistics.
|
||||
fn start_server_thread() -> JoinHandle<()> {
|
||||
/// Start a thread to launch [`Server`] and update [`NODE_STATE`] with server statistics.
|
||||
fn start_server_thread() {
|
||||
thread::spawn(move || {
|
||||
NODE_STATE.starting.store(true, Ordering::Relaxed);
|
||||
|
||||
// Start the server.
|
||||
let mut server = start_server(&NODE_STATE.chain_type.read().unwrap());
|
||||
let mut first_start = true;
|
||||
|
||||
loop {
|
||||
if Self::is_restarting() {
|
||||
// Clean server stats.
|
||||
{
|
||||
let mut w_stats = NODE_STATE.stats.write().unwrap();
|
||||
*w_stats = None;
|
||||
}
|
||||
|
||||
// Stop the server.
|
||||
server.stop();
|
||||
|
||||
// Create new server with current chain type.
|
||||
|
@ -148,10 +156,14 @@ impl Node {
|
|||
|
||||
NODE_STATE.restart_needed.store(false, Ordering::Relaxed);
|
||||
} else if Self::is_stopping() {
|
||||
server.stop();
|
||||
|
||||
// Clean server stats.
|
||||
{
|
||||
let mut w_stats = NODE_STATE.stats.write().unwrap();
|
||||
*w_stats = None;
|
||||
}
|
||||
|
||||
// Stop the server.
|
||||
server.stop();
|
||||
|
||||
NODE_STATE.starting.store(false, Ordering::Relaxed);
|
||||
NODE_STATE.stop_needed.store(false, Ordering::Relaxed);
|
||||
|
@ -159,8 +171,11 @@ impl Node {
|
|||
} else {
|
||||
let stats = server.get_server_stats();
|
||||
if stats.is_ok() {
|
||||
// Update server stats.
|
||||
{
|
||||
let mut w_stats = NODE_STATE.stats.write().unwrap();
|
||||
*w_stats = Some(stats.as_ref().ok().unwrap().clone());
|
||||
}
|
||||
|
||||
if first_start {
|
||||
NODE_STATE.starting.store(false, Ordering::Relaxed);
|
||||
|
@ -170,18 +185,24 @@ impl Node {
|
|||
}
|
||||
thread::sleep(Duration::from_millis(250));
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/// Get synchronization status i18n text.
|
||||
pub fn get_sync_status_text(sync_status: Option<SyncStatus>) -> String {
|
||||
pub fn get_sync_status_text() -> String {
|
||||
if Node::is_starting() {
|
||||
return t!("sync_status.initial")
|
||||
};
|
||||
|
||||
if Node::is_stopping() {
|
||||
return t!("sync_status.shutdown")
|
||||
};
|
||||
|
||||
if Node::is_restarting() {
|
||||
return t!("sync_status.server_restarting")
|
||||
}
|
||||
|
||||
if Node::is_stopping() {
|
||||
return t!("sync_status.shutdown")
|
||||
}
|
||||
let sync_status = Self::get_sync_status();
|
||||
|
||||
if sync_status.is_none() {
|
||||
return t!("sync_status.server_down")
|
||||
|
@ -256,7 +277,7 @@ impl Node {
|
|||
|
||||
}
|
||||
|
||||
/// Start [Server] with provided chain type.
|
||||
/// Start the [`Server`] with provided chain type.
|
||||
fn start_server(chain_type: &ChainTypes) -> Server {
|
||||
// Initialize config
|
||||
let mut node_config_result = config::initial_setup_server(chain_type);
|
||||
|
@ -358,14 +379,13 @@ fn start_server(chain_type: &ChainTypes) -> Server {
|
|||
#[cfg(target_os = "android")]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
/// Get sync status text for Android notification from [NODE_STATE] in Java string format.
|
||||
/// Get sync status text for Android notification from [`NODE_STATE`] in Java string format.
|
||||
pub extern "C" fn Java_mw_gri_android_BackgroundService_getSyncStatusText(
|
||||
_env: jni::JNIEnv,
|
||||
_class: jni::objects::JObject,
|
||||
_activity: jni::objects::JObject,
|
||||
) -> jstring {
|
||||
let sync_status = Node::get_sync_status();
|
||||
let status_text = Node::get_sync_status_text(sync_status);
|
||||
let status_text = Node::get_sync_status_text();
|
||||
let j_text = _env.new_string(status_text);
|
||||
return j_text.unwrap().into_raw();
|
||||
}
|
||||
|
@ -388,7 +408,7 @@ pub extern "C" fn Java_mw_gri_android_BackgroundService_getSyncTitle(
|
|||
#[cfg(target_os = "android")]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
/// Calling on unexpected application termination (removal from recent apps)
|
||||
/// Calling on unexpected Android application termination (removal from recent apps).
|
||||
pub extern "C" fn Java_mw_gri_android_MainActivity_onTermination(
|
||||
_env: jni::JNIEnv,
|
||||
_class: jni::objects::JObject,
|
||||
|
|
Loading…
Reference in a new issue