ui + build: title panel refactoring, check node idle state for network title animation, optimize font sizes, run build on several devices for Android

This commit is contained in:
ardocrat 2023-07-14 03:51:06 +03:00
parent dbe178f792
commit c20ee3e629
23 changed files with 185 additions and 228 deletions

View file

@ -37,8 +37,11 @@ then
yes | cp -f target/${platform_path}/${type}/libgrim.so app/src/main/jniLibs/${platform_param}
./gradlew clean
./gradlew build
#./gradlew installDebug
adb install app/build/outputs/apk/debug/app-debug.apk
sleep 1s
adb shell am start -n mw.gri.android/.MainActivity
# Install on several devices
for SERIAL in $(adb devices | grep -v List | cut -f 1);
do
adb -s $SERIAL install app/build/outputs/apk/debug/app-debug.apk
sleep 1s
adb -s $SERIAL shell am start -n mw.gri.android/.MainActivity;
done
fi

View file

@ -9,8 +9,6 @@ network:
mining: Mining
settings: Node settings
enable_node: Enable node
disable: Disable
restart: Restart
autorun: Autorun
disabled_server: 'Enable integrated node or add another connection method by pressing %{dots} in the top-left corner of the screen.'
no_ips: There are no available IP addresses on your system, server cannot be started, check your network connectivity.

View file

@ -9,8 +9,6 @@ network:
mining: Майнинг
settings: Настройки узла
enable_node: Включить узел
disable: Выключить
restart: Перезапустить
autorun: Автозапуск
disabled_server: 'Включите встроенный узел или добавьте другой способ подключения, нажав %{dots} в левом-верхнем углу экрана.'
no_ips: В вашей системе отсутствуют доступные IP адреса, запуск сервера невозможен, проверьте ваше подключение к сети.
@ -79,7 +77,7 @@ network_settings:
restart_node_required: Для применения изменений требуется перезапуск узла.
enable: Включить
disable: Выключить
restart: Перезапустить
restart: Перезапуск
server: Сервер
api_ip: API IP Адрес
api_port: API Порт

View file

@ -43,7 +43,7 @@ impl<Platform: PlatformCallbacks> eframe::App for PlatformApp<Platform> {
// Show main content.
egui::CentralPanel::default()
.frame(egui::Frame {
fill: Colors::FILL,
fill: Colors::YELLOW,
..Default::default()
})
.show(ctx, |ui| {

View file

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::gui::{App, PlatformApp};
use crate::gui::PlatformApp;
use crate::gui::platform::PlatformCallbacks;
#[derive(Default)]

View file

@ -15,7 +15,7 @@
use crate::gui::Colors;
use crate::gui::icons::{GLOBE, PLUS};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Root, TitleAction, TitlePanel, View};
use crate::gui::views::{Root, TitleAction, TitleType, TitlePanel, View};
/// Accounts content.
pub struct Accounts {
@ -33,7 +33,8 @@ impl Default for Accounts {
impl Accounts {
pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) {
TitlePanel::ui(t!("accounts.title"), if !Root::is_dual_panel_mode(frame) {
let title_content = TitleType::Single(t!("accounts.title").to_uppercase());
TitlePanel::ui(title_content, if !Root::is_dual_panel_mode(frame) {
TitleAction::new(GLOBE, || {
Root::toggle_side_panel();
})

View file

@ -291,7 +291,7 @@ impl Modal {
ui.vertical_centered_justified(|ui| {
ui.add_space(8.0);
ui.label(RichText::new(self.title.as_ref().unwrap())
.size(20.0)
.size(19.0)
.color(Colors::TITLE)
);
ui.add_space(8.0);

View file

@ -187,7 +187,7 @@ fn block_item_ui(ui: &mut egui::Ui, db: &DiffBlock, rounding: [bool; 2]) {
// Draw block hash.
ui.heading(RichText::new(format!("{} {}", HASH, db.block_hash))
.color(Colors::BLACK)
.size(18.0));
.size(17.0));
});
ui.horizontal(|ui| {
ui.add_space(6.0);

View file

@ -246,7 +246,7 @@ fn worker_item_ui(ui: &mut egui::Ui, ws: &WorkerStats, rounding: [bool; 2]) {
let status_line_text = format!("{} {} {}", status_icon, ws.id, status_text);
ui.heading(RichText::new(status_line_text)
.color(status_color)
.size(18.0));
.size(17.0));
ui.add_space(2.0);
});
ui.horizontal(|ui| {

View file

@ -21,7 +21,7 @@ use crate::AppConfig;
use crate::gui::Colors;
use crate::gui::icons::{CARDHOLDER, DATABASE, DOTS_THREE_OUTLINE_VERTICAL, FACTORY, FADERS, GAUGE, POWER};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Modal, ModalContainer, NetworkMetrics, NetworkMining, NetworkNode, NetworkSettings, Root, TitleAction, TitleContent, TitlePanel, View};
use crate::gui::views::{Modal, ModalContainer, NetworkMetrics, NetworkMining, NetworkNode, NetworkSettings, Root, TitleAction, TitleType, TitlePanel, View};
use crate::gui::views::network::setup::{DandelionSetup, NodeSetup, P2PSetup, PoolSetup, StratumSetup};
use crate::node::Node;
@ -155,6 +155,11 @@ impl Network {
.show_inside(ui, |ui| {
self.current_tab.ui(ui, cb);
});
// Redraw content after delay if node is not syncing to update stats.
if Node::not_syncing() {
ui.ctx().request_repaint_after(Node::STATS_UPDATE_DELAY);
}
}
/// Draw tab buttons in the bottom of the screen.
@ -194,9 +199,13 @@ impl Network {
/// Draw title content.
fn title_ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
let title_content = TitleContent::Custom("network_title".to_string(), Box::new(|ui| {
}));
TitlePanel::test_ui(title_content, TitleAction::new(DOTS_THREE_OUTLINE_VERTICAL, || {
// Setup values for title panel.
let title_text = self.current_tab.get_type().title().to_uppercase();
let subtitle_text = Node::get_sync_status_text();
let not_syncing = Node::not_syncing();
let title_content = TitleType::WithSubTitle(title_text, subtitle_text, !not_syncing);
// Draw title panel.
TitlePanel::ui(title_content, TitleAction::new(DOTS_THREE_OUTLINE_VERTICAL, || {
//TODO: Show connections
}), if !Root::is_dual_panel_mode(frame) {
TitleAction::new(CARDHOLDER, || {
@ -205,78 +214,6 @@ impl Network {
} else {
None
}, ui);
// StripBuilder::new(ui)
// .size(Size::exact(52.0))
// .size(Size::remainder())
// .size(Size::exact(52.0))
// .horizontal(|mut strip| {
// strip.cell(|ui| {
// ui.centered_and_justified(|ui| {
// View::title_button(ui, DOTS_THREE_OUTLINE_VERTICAL, || {
// //TODO: Show connections
// });
// });
// });
// strip.strip(|builder| {
// self.title_text_ui(builder);
// });
// strip.cell(|ui| {
// if !Root::is_dual_panel_mode(frame) {
// ui.centered_and_justified(|ui| {
// View::title_button(ui, CARDHOLDER, || {
// Root::toggle_side_panel();
// });
// });
// }
// });
// });
}
/// Draw title text.
fn title_text_ui(&self, builder: StripBuilder) {
builder
.size(Size::remainder())
.size(Size::exact(28.0))
.vertical(|mut strip| {
strip.cell(|ui| {
ui.add_space(4.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(self.current_tab.get_type().title().to_uppercase())
.size(19.0)
.color(Colors::TITLE));
});
});
strip.cell(|ui| {
ui.centered_and_justified(|ui| {
let sync_status = Node::get_sync_status();
// Setup text color animation based on sync status
let idle = match sync_status {
None => !Node::is_starting(),
Some(ss) => ss == SyncStatus::NoSync
};
let (dark, bright) = (0.3, 1.0);
let color_factor = if !idle {
lerp(dark..=bright, ui.input(|i| i.time).cos().abs()) as f32
} else {
bright as f32
};
// Draw sync status text.
let status_color_rgba = Rgba::from(Colors::TEXT) * color_factor;
let status_color = Color32::from(status_color_rgba);
View::ellipsize_text(ui, Node::get_sync_status_text(), 15.0, status_color);
// Repaint delay based on sync status.
if idle {
ui.ctx().request_repaint_after(Node::STATS_UPDATE_DELAY);
} else {
ui.ctx().request_repaint();
}
});
});
});
}
/// Content to draw when node is disabled.

View file

@ -210,7 +210,7 @@ fn peer_item_ui(ui: &mut egui::Ui, peer: &PeerStats, rounding: [bool; 2]) {
ui.horizontal(|ui| {
ui.add_space(5.0);
let addr_text = format!("{} {}", PLUGS_CONNECTED, &peer.addr);
ui.label(RichText::new(addr_text).color(Colors::BLACK).size(18.0));
ui.label(RichText::new(addr_text).color(Colors::BLACK).size(17.0));
});
// Draw peer difficulty and height
ui.horizontal(|ui| {

View file

@ -158,7 +158,7 @@ impl NetworkSettings {
ui.add_space(6.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_settings.reset_settings_desc"))
.size(18.0)
.size(17.0)
.color(Colors::TEXT));
ui.add_space(8.0);
});
@ -212,7 +212,7 @@ impl NetworkSettings {
ui.add_space(6.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_settings.restart_node_required"))
.size(18.0)
.size(17.0)
.color(Colors::GRAY));
ui.add_space(8.0);
});

View file

@ -127,7 +127,7 @@ impl DandelionSetup {
ui.add_space(6.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_settings.epoch_duration"))
.size(18.0)
.size(17.0)
.color(Colors::GRAY));
ui.add_space(8.0);
@ -147,7 +147,7 @@ impl DandelionSetup {
if self.epoch_edit.parse::<u16>().is_err() {
ui.add_space(12.0);
ui.label(RichText::new(t!("network_settings.not_valid_value"))
.size(18.0)
.size(17.0)
.color(Colors::RED));
} else {
NetworkSettings::node_restart_required_ui(ui);
@ -212,7 +212,7 @@ impl DandelionSetup {
ui.add_space(6.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_settings.embargo_timer"))
.size(18.0)
.size(17.0)
.color(Colors::GRAY));
ui.add_space(8.0);
@ -232,7 +232,7 @@ impl DandelionSetup {
if self.embargo_edit.parse::<u16>().is_err() {
ui.add_space(12.0);
ui.label(RichText::new(t!("network_settings.not_valid_value"))
.size(18.0)
.size(17.0)
.color(Colors::RED));
} else {
NetworkSettings::node_restart_required_ui(ui);
@ -297,7 +297,7 @@ impl DandelionSetup {
ui.add_space(6.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_settings.aggregation_period"))
.size(18.0)
.size(17.0)
.color(Colors::GRAY));
ui.add_space(8.0);
@ -317,7 +317,7 @@ impl DandelionSetup {
if self.aggregation_edit.parse::<u16>().is_err() {
ui.add_space(12.0);
ui.label(RichText::new(t!("network_settings.not_valid_value"))
.size(18.0)
.size(17.0)
.color(Colors::RED));
} else {
NetworkSettings::node_restart_required_ui(ui);
@ -382,7 +382,7 @@ impl DandelionSetup {
ui.add_space(6.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_settings.stem_probability"))
.size(18.0)
.size(17.0)
.color(Colors::GRAY));
ui.add_space(8.0);
@ -402,7 +402,7 @@ impl DandelionSetup {
if self.stem_prob_edit.parse::<u8>().is_err() {
ui.add_space(12.0);
ui.label(RichText::new(t!("network_settings.not_valid_value"))
.size(18.0)
.size(17.0)
.color(Colors::RED));
} else {
NetworkSettings::node_restart_required_ui(ui);

View file

@ -245,7 +245,7 @@ impl NodeSetup {
ui.add_space(6.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_settings.api_port"))
.size(18.0)
.size(17.0)
.color(Colors::GRAY));
ui.add_space(6.0);
@ -353,7 +353,7 @@ impl NodeSetup {
_ => t!("network_settings.foreign_api_secret")
};
ui.label(RichText::new(description)
.size(18.0)
.size(17.0)
.color(Colors::GRAY));
ui.add_space(6.0);
@ -376,19 +376,17 @@ impl NodeSetup {
ui.spacing_mut().item_spacing = egui::Vec2::new(12.0, 0.0);
let mut buttons_rect = ui.available_rect_before_wrap();
buttons_rect.set_height(46.0);
buttons_rect.set_height(42.0);
ui.allocate_ui_at_rect(buttons_rect, |ui| {
ui.columns(2, |columns| {
columns[0].with_layout(Layout::right_to_left(Align::Center), |ui| {
let copy_title = format!("{} {}", COPY, t!("copy"));
View::button(ui, copy_title, Colors::WHITE, || {
View::button(ui, COPY.to_string(), Colors::WHITE, || {
cb.copy_string_to_buffer(self.secret_edit.clone());
});
});
columns[1].with_layout(Layout::left_to_right(Align::Center), |ui| {
let paste_title = format!("{} {}", CLIPBOARD_TEXT, t!("paste"));
View::button(ui, paste_title, Colors::WHITE, || {
View::button(ui, CLIPBOARD_TEXT.to_string(), Colors::WHITE, || {
self.secret_edit = cb.get_string_from_buffer();
});
});
@ -469,7 +467,7 @@ impl NodeSetup {
ui.add_space(6.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_settings.ftl"))
.size(18.0)
.size(17.0)
.color(Colors::GRAY));
ui.add_space(8.0);
@ -489,7 +487,7 @@ impl NodeSetup {
if self.ftl_edit.parse::<u64>().is_err() {
ui.add_space(12.0);
ui.label(RichText::new(t!("network_settings.not_valid_value"))
.size(18.0)
.size(17.0)
.color(Colors::RED));
} else {
NetworkSettings::node_restart_required_ui(ui);

View file

@ -235,7 +235,7 @@ impl P2PSetup {
ui.add_space(6.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_settings.p2p_port"))
.size(18.0)
.size(17.0)
.color(Colors::GRAY));
ui.add_space(8.0);
@ -255,7 +255,7 @@ impl P2PSetup {
if !self.port_available_edit {
ui.add_space(12.0);
ui.label(RichText::new(t!("network_settings.port_unavailable"))
.size(18.0)
.size(17.0)
.color(Colors::RED));
}
@ -389,7 +389,7 @@ impl P2PSetup {
Self::CUSTOM_SEED_MODAL => t!("network_settings.add_seed"),
&_ => t!("network_settings.add_peer")
};
ui.label(RichText::new(label_text).size(18.0).color(Colors::GRAY));
ui.label(RichText::new(label_text).size(17.0).color(Colors::GRAY));
ui.add_space(8.0);
// Draw peer address text edit.
@ -577,7 +577,7 @@ impl P2PSetup {
ui.add_space(6.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_settings.ban_window"))
.size(18.0)
.size(17.0)
.color(Colors::GRAY));
ui.add_space(8.0);
@ -597,7 +597,7 @@ impl P2PSetup {
if self.ban_window_edit.parse::<i64>().is_err() {
ui.add_space(12.0);
ui.label(RichText::new(t!("network_settings.not_valid_value"))
.size(18.0)
.size(17.0)
.color(Colors::RED));
} else {
NetworkSettings::node_restart_required_ui(ui);
@ -663,7 +663,7 @@ impl P2PSetup {
ui.add_space(6.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_settings.max_inbound_count"))
.size(18.0)
.size(17.0)
.color(Colors::GRAY));
ui.add_space(8.0);
@ -683,7 +683,7 @@ impl P2PSetup {
if self.max_inbound_count.parse::<u32>().is_err() {
ui.add_space(12.0);
ui.label(RichText::new(t!("network_settings.not_valid_value"))
.size(18.0)
.size(17.0)
.color(Colors::RED));
} else {
NetworkSettings::node_restart_required_ui(ui);
@ -749,7 +749,7 @@ impl P2PSetup {
ui.add_space(6.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_settings.max_outbound_count"))
.size(18.0)
.size(17.0)
.color(Colors::GRAY));
ui.add_space(8.0);
@ -769,7 +769,7 @@ impl P2PSetup {
if self.max_outbound_count.parse::<u32>().is_err() {
ui.add_space(12.0);
ui.label(RichText::new(t!("network_settings.not_valid_value"))
.size(18.0)
.size(17.0)
.color(Colors::RED));
} else {
NetworkSettings::node_restart_required_ui(ui);
@ -840,7 +840,7 @@ impl P2PSetup {
ui.add_space(6.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_settings.min_outbound_count"))
.size(18.0)
.size(17.0)
.color(Colors::GRAY));
ui.add_space(8.0);
@ -860,7 +860,7 @@ impl P2PSetup {
if self.min_outbound_count.parse::<u32>().is_err() {
ui.add_space(12.0);
ui.label(RichText::new(t!("network_settings.not_valid_value"))
.size(18.0)
.size(17.0)
.color(Colors::RED));
} else {
NetworkSettings::node_restart_required_ui(ui);

View file

@ -129,7 +129,7 @@ impl PoolSetup {
ui.add_space(6.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_settings.pool_fee"))
.size(18.0)
.size(17.0)
.color(Colors::GRAY));
ui.add_space(8.0);
@ -149,7 +149,7 @@ impl PoolSetup {
if self.fee_base_edit.parse::<u64>().is_err() {
ui.add_space(12.0);
ui.label(RichText::new(t!("network_settings.not_valid_value"))
.size(18.0)
.size(17.0)
.color(Colors::RED));
} else {
NetworkSettings::node_restart_required_ui(ui);
@ -214,7 +214,7 @@ impl PoolSetup {
ui.add_space(6.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_settings.reorg_period"))
.size(18.0)
.size(17.0)
.color(Colors::GRAY));
ui.add_space(8.0);
@ -233,8 +233,8 @@ impl PoolSetup {
// Show error when specified value is not valid or reminder to restart enabled node.
if self.reorg_period_edit.parse::<u32>().is_err() {
ui.add_space(12.0);
ui.label(RichText::new(t!("network_settings.reorg_period"))
.size(18.0)
ui.label(RichText::new(t!("network_settings.not_valid_value"))
.size(17.0)
.color(Colors::RED));
} else {
NetworkSettings::node_restart_required_ui(ui);
@ -299,7 +299,7 @@ impl PoolSetup {
ui.add_space(6.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_settings.max_tx_pool"))
.size(18.0)
.size(17.0)
.color(Colors::GRAY));
ui.add_space(8.0);
@ -319,7 +319,7 @@ impl PoolSetup {
if self.pool_size_edit.parse::<usize>().is_err() {
ui.add_space(12.0);
ui.label(RichText::new(t!("network_settings.not_valid_value"))
.size(18.0)
.size(17.0)
.color(Colors::RED));
} else {
NetworkSettings::node_restart_required_ui(ui);
@ -384,7 +384,7 @@ impl PoolSetup {
ui.add_space(6.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_settings.max_tx_stempool"))
.size(18.0)
.size(17.0)
.color(Colors::GRAY));
ui.add_space(8.0);
@ -404,7 +404,7 @@ impl PoolSetup {
if self.stempool_size_edit.parse::<usize>().is_err() {
ui.add_space(12.0);
ui.label(RichText::new(t!("network_settings.not_valid_value"))
.size(18.0)
.size(17.0)
.color(Colors::RED));
} else {
NetworkSettings::node_restart_required_ui(ui);
@ -469,7 +469,7 @@ impl PoolSetup {
ui.add_space(6.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_settings.max_tx_weight"))
.size(18.0)
.size(17.0)
.color(Colors::GRAY));
ui.add_space(8.0);
@ -489,7 +489,7 @@ impl PoolSetup {
if self.max_weight_edit.parse::<u64>().is_err() {
ui.add_space(12.0);
ui.label(RichText::new(t!("network_settings.not_valid_value"))
.size(18.0)
.size(17.0)
.color(Colors::RED));
} else {
NetworkSettings::node_restart_required_ui(ui);

View file

@ -186,7 +186,7 @@ impl StratumSetup {
ui.add_space(6.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_settings.stratum_port"))
.size(18.0)
.size(17.0)
.color(Colors::GRAY));
ui.add_space(8.0);
@ -206,7 +206,7 @@ impl StratumSetup {
if !self.stratum_port_available_edit {
ui.add_space(12.0);
ui.label(RichText::new(t!("network_settings.port_unavailable"))
.size(18.0)
.size(17.0)
.color(Colors::RED));
} else {
server_restart_required_ui(ui);
@ -284,7 +284,7 @@ impl StratumSetup {
ui.add_space(6.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_settings.attempt_time"))
.size(18.0)
.size(17.0)
.color(Colors::GRAY));
ui.add_space(8.0);
@ -304,7 +304,7 @@ impl StratumSetup {
if self.attempt_time_edit.parse::<u32>().is_err() {
ui.add_space(12.0);
ui.label(RichText::new(t!("network_settings.not_valid_value"))
.size(18.0)
.size(17.0)
.color(Colors::RED));
} else {
server_restart_required_ui(ui);
@ -370,7 +370,7 @@ impl StratumSetup {
ui.add_space(6.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("network_settings.min_share_diff"))
.size(18.0)
.size(17.0)
.color(Colors::GRAY));
ui.add_space(8.0);
@ -390,7 +390,7 @@ impl StratumSetup {
if self.min_share_diff_edit.parse::<u64>().is_err() {
ui.add_space(12.0);
ui.label(RichText::new(t!("network_settings.not_valid_value"))
.size(18.0)
.size(17.0)
.color(Colors::RED));
} else {
server_restart_required_ui(ui);

View file

@ -140,7 +140,7 @@ impl Root {
/// Show exit confirmation modal.
pub fn show_exit_modal() {
let exit_modal = Modal::new(Self::EXIT_MODAL_ID).title(t!("modal_exit.exit"));
let exit_modal = Modal::new(Self::EXIT_MODAL_ID).title(t!("modal.confirmation"));
Modal::show(exit_modal);
}
@ -160,7 +160,7 @@ impl Root {
View::small_loading_spinner(ui);
ui.add_space(12.0);
ui.label(RichText::new(t!("sync_status.shutdown"))
.size(18.0)
.size(17.0)
.color(Colors::TEXT));
});
ui.add_space(10.0);
@ -168,7 +168,7 @@ impl Root {
ui.add_space(8.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("modal_exit.description"))
.size(18.0)
.size(17.0)
.color(Colors::TEXT));
});
ui.add_space(10.0);

View file

@ -12,13 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use egui::Id;
use egui::{Color32, Id, lerp, Rgba, RichText};
use egui::style::Margin;
use egui_extras::{Size, StripBuilder};
use crate::gui::Colors;
use crate::gui::views::View;
/// Title action button.
pub struct TitleAction {
pub(crate) icon: Box<&'static str>,
pub(crate) on_click: Box<dyn Fn()>,
@ -30,22 +31,22 @@ impl TitleAction {
}
}
/// Represents title content, can be text or callback to draw custom title.
pub enum TitleContent {
Text(String),
/// First argument is identifier for panel.
Custom(String, Box<dyn Fn(&mut egui::Ui)>)
/// Represents title content, can be single title or with animated sub-title.
pub enum TitleType {
Single(String),
WithSubTitle(String, String, bool)
}
/// Title panel with left/right action buttons and text in the middle.
pub struct TitlePanel;
impl TitlePanel {
pub const DEFAULT_HEIGHT: f32 = 52.0;
pub fn test_ui(title: TitleContent, l: Option<TitleAction>, r: Option<TitleAction>, ui: &mut egui::Ui) {
pub fn ui(title: TitleType, l: Option<TitleAction>, r: Option<TitleAction>, ui: &mut egui::Ui) {
let id = match &title {
TitleContent::Text(text) => Id::from(text.clone()),
TitleContent::Custom(text, _) => Id::from(text.clone())
TitleType::Single(text) => Id::from(text.clone()),
TitleType::WithSubTitle(text, _, _) => Id::from(text.clone())
};
egui::TopBottomPanel::top(id)
.resizable(false)
@ -64,44 +65,21 @@ impl TitlePanel {
strip.cell(|ui| {
Self::draw_action(ui, l);
});
strip.cell(|ui| {
match title {
TitleContent::Text(text) => {
Self::draw_title(ui, text);
}
TitleContent::Custom(_, cb) => {
(cb)(ui);
}
match title {
TitleType::Single(text) => {
strip.cell(|ui| {
ui.add_space(2.0);
ui.centered_and_justified(|ui| {
View::ellipsize_text(ui, text, 19.0, Colors::TITLE);
});
});
}
});
strip.cell(|ui| {
Self::draw_action(ui, r);
});
});
});
}
pub fn ui(title: String, l: Option<TitleAction>, r: Option<TitleAction>, ui: &mut egui::Ui) {
egui::TopBottomPanel::top(Id::from(title.clone()))
.resizable(false)
.exact_height(Self::DEFAULT_HEIGHT)
.frame(egui::Frame {
outer_margin: Margin::same(-1.0),
fill: Colors::YELLOW,
..Default::default()
})
.show_inside(ui, |ui| {
StripBuilder::new(ui)
.size(Size::exact(Self::DEFAULT_HEIGHT))
.size(Size::remainder())
.size(Size::exact(Self::DEFAULT_HEIGHT))
.horizontal(|mut strip| {
strip.cell(|ui| {
Self::draw_action(ui, l);
});
strip.cell(|ui| {
Self::draw_title(ui, title);
});
TitleType::WithSubTitle(text, subtitle_text, animate_sub) => {
strip.strip(|builder| {
Self::with_sub_title(builder, text, subtitle_text, animate_sub);
});
}
}
strip.cell(|ui| {
Self::draw_action(ui, r);
});
@ -109,6 +87,7 @@ impl TitlePanel {
});
}
/// Draw panel [`TitleAction`].
fn draw_action(ui: &mut egui::Ui, action: Option<TitleAction>) {
if action.is_some() {
let action = action.unwrap();
@ -120,10 +99,45 @@ impl TitlePanel {
}
}
fn draw_title(ui: &mut egui::Ui, title: String) {
ui.add_space(2.0);
ui.centered_and_justified(|ui| {
View::ellipsize_text(ui, title.to_uppercase(), 20.0, Colors::TITLE);
});
/// Draw title text for [`TitleType::Single`] type.
fn single(ui: &mut egui::Ui, title: String) {
}
/// Draw title text for [`TitleType::WithSubTitle`] type.
fn with_sub_title(builder: StripBuilder, title: String, subtitle: String, animate_sub: bool) {
builder
.size(Size::remainder())
.size(Size::exact(30.0))
.vertical(|mut strip| {
strip.cell(|ui| {
ui.add_space(4.0);
ui.centered_and_justified(|ui| {
View::ellipsize_text(ui, title, 18.0, Colors::TITLE);
});
});
strip.cell(|ui| {
ui.centered_and_justified(|ui| {
// Setup text color animation if needed.
let (dark, bright) = (0.3, 1.0);
let color_factor = if animate_sub {
lerp(dark..=bright, ui.input(|i| i.time).cos().abs()) as f32
} else {
bright as f32
};
// Draw subtitle text.
let sub_color_rgba = Rgba::from(Colors::TEXT) * color_factor;
let sub_color = Color32::from(sub_color_rgba);
View::ellipsize_text(ui, subtitle, 15.0, sub_color);
// Repaint delay based on animation status.
if animate_sub {
ui.ctx().request_repaint();
}
});
ui.add_space(2.0);
});
});
}
}

View file

@ -76,7 +76,7 @@ impl View {
// Disable stroke color on hover.
ui.style_mut().visuals.widgets.hovered.bg_stroke = Self::DEFAULT_STROKE;
// Setup text.
let wt = RichText::new(icon.to_string()).size(24.0).color(Colors::TITLE);
let wt = RichText::new(icon.to_string()).size(22.0).color(Colors::TITLE);
// Draw button.
let br = Button::new(wt)
.fill(Colors::TRANSPARENT)
@ -101,7 +101,7 @@ impl View {
true => Colors::FILL,
false => Colors::WHITE
};
let br = Button::new(RichText::new(icon.to_string()).size(24.0).color(text_color))
let br = Button::new(RichText::new(icon.to_string()).size(22.0).color(text_color))
.stroke(stroke)
.fill(color)
.ui(ui);
@ -112,7 +112,7 @@ impl View {
/// Draw [`Button`] with specified background fill color.
pub fn button(ui: &mut egui::Ui, text: String, fill_color: Color32, action: impl FnOnce()) {
let button_text = Self::ellipsize(text.to_uppercase(), 18.0, Colors::TEXT_BUTTON);
let button_text = Self::ellipsize(text.to_uppercase(), 17.0, Colors::TEXT_BUTTON);
let br = Button::new(button_text)
.stroke(Self::DEFAULT_STROKE)
.fill(fill_color)
@ -154,7 +154,7 @@ impl View {
// Draw box value.
let mut job = LayoutJob::single_section(value, TextFormat {
font_id: FontId::proportional(18.0),
font_id: FontId::proportional(17.0),
color: Colors::BLACK,
..Default::default()
});
@ -209,7 +209,7 @@ impl View {
false => (format!("{} {}", SQUARE, text), Colors::CHECKBOX)
};
let br = Button::new(RichText::new(text_value).size(18.0).color(color))
let br = Button::new(RichText::new(text_value).size(17.0).color(color))
.frame(false)
.stroke(Stroke::NONE)
.fill(Colors::TRANSPARENT)

View file

@ -173,10 +173,10 @@ pub fn setup_fonts(ctx: &Context) {
let mut style = (*ctx.style()).clone();
style.text_styles = [
(Heading, FontId::new(20.0, Proportional)),
(Heading, FontId::new(19.0, Proportional)),
(Body, FontId::new(16.0, Proportional)),
(Button, FontId::new(18.0, Proportional)),
(Small, FontId::new(12.0, Proportional)),
(Button, FontId::new(17.0, Proportional)),
(Small, FontId::new(15.0, Proportional)),
(Monospace, FontId::new(16.0, Proportional)),
].into();

View file

@ -29,31 +29,31 @@ use crate::node::NodeConfig;
use crate::node::stratum::{StratumStopState, StratumServer};
lazy_static! {
/// Static thread-aware state of [`Node`] to be updated from another thread.
/// Static thread-aware state of [`Node`] to be updated from separate thread.
static ref NODE_STATE: Arc<Node> = Arc::new(Node::default());
}
/// Provides [`Server`] control, holds current status and statistics.
pub struct Node {
/// The node [`Server`] statistics for UI.
/// Node [`Server`] statistics for UI.
stats: Arc<RwLock<Option<ServerStats>>>,
/// Stratum server statistics.
/// [`StratumServer`] statistics.
stratum_stats: Arc<grin_util::RwLock<StratumStats>>,
/// Stratum server statistics.
/// State to stop [`StratumServer`] from outside.
stratum_stop_state: Arc<StratumStopState>,
/// Running API server address.
/// Running API [`Server`] address.
api_addr: Arc<RwLock<Option<String>>>,
/// Running P2P server port.
/// Running P2P [`grin_p2p::Server`] port.
p2p_port: Arc<RwLock<Option<u16>>>,
/// Indicator if server is starting.
/// Indicator if node [`Server`] is starting.
starting: AtomicBool,
/// Thread flag to stop the server and start it again.
/// Thread flag to stop the [`Server`] and start it again.
restart_needed: AtomicBool,
/// Thread flag to stop the server.
/// Thread flag to stop the [`Server`].
stop_needed: AtomicBool,
/// Flag to check if app exit is needed after server stop.
/// Flag to check if app exit is needed after [`Server`] stop.
exit_after_stop: AtomicBool,
/// Thread flag to start stratum server.
/// Thread flag to start [`StratumServer`].
start_stratum_needed: AtomicBool,
/// Error on [`Server`] start.
init_error: Option<Error>
@ -78,7 +78,7 @@ impl Default for Node {
}
impl Node {
/// Delay for server thread to update the stats.
/// Delay for thread to update the stats.
pub const STATS_UPDATE_DELAY: Duration = Duration::from_millis(250);
/// Stop the [`Server`] and setup exit flag after if needed.
@ -87,14 +87,14 @@ impl Node {
NODE_STATE.exit_after_stop.store(exit_after_stop, Ordering::Relaxed);
}
/// Start the node.
/// Request to start the [`Node`].
pub fn start() {
if !Self::is_running() {
Self::start_server_thread();
}
}
/// Restart the node.
/// Request to restart the [`Node`].
pub fn restart() {
if Self::is_running() {
NODE_STATE.restart_needed.store(true, Ordering::Relaxed);
@ -103,7 +103,7 @@ impl Node {
}
}
/// Get API server address if node is running.
/// Get API [`Server`] address if [`Node`] is running.
pub fn get_api_addr() -> Option<String> {
let r_api_addr = NODE_STATE.api_addr.read().unwrap();
if r_api_addr.is_some() {
@ -113,7 +113,7 @@ impl Node {
}
}
/// Get P2P server port if node is running.
/// Get P2P [`grin_p2p::Server`] port if node is running.
pub fn get_p2p_port() -> Option<u16> {
let r_p2p_port = NODE_STATE.p2p_port.read().unwrap();
if r_p2p_port.is_some() {
@ -123,47 +123,47 @@ impl Node {
}
}
/// Request to start stratum server.
/// Request to start [`StratumServer`].
pub fn start_stratum() {
NODE_STATE.start_stratum_needed.store(true, Ordering::Relaxed);
}
/// Check if stratum server is starting.
/// Check if [`StratumServer`] is starting.
pub fn is_stratum_starting() -> bool {
NODE_STATE.start_stratum_needed.load(Ordering::Relaxed)
}
/// Get stratum server statistics.
/// Get [`StratumServer`] statistics.
pub fn get_stratum_stats() -> grin_util::RwLockReadGuard<'static, StratumStats> {
NODE_STATE.stratum_stats.read()
}
/// Stop stratum server.
/// Stop [`StratumServer`].
pub fn stop_stratum() {
NODE_STATE.stratum_stop_state.stop()
}
/// Check if stratum server is stopping.
/// Check if [`StratumServer`] is stopping.
pub fn is_stratum_stopping() -> bool {
NODE_STATE.stratum_stop_state.is_stopped()
}
/// Check if node is starting.
/// Check if [`Node`] is starting.
pub fn is_starting() -> bool {
NODE_STATE.starting.load(Ordering::Relaxed)
}
/// Check if node is running.
/// Check if [`Node`] is running.
pub fn is_running() -> bool {
Self::get_sync_status().is_some()
}
/// Check if node is stopping.
/// Check if [`Node`] is stopping.
pub fn is_stopping() -> bool {
NODE_STATE.stop_needed.load(Ordering::Relaxed)
}
/// Check if node is restarting.
/// Check if [`Node`] is restarting.
pub fn is_restarting() -> bool {
NODE_STATE.restart_needed.load(Ordering::Relaxed)
}
@ -173,6 +173,14 @@ impl Node {
NODE_STATE.stats.read().unwrap()
}
/// Check if [`Server`] is not syncing (disabled or just running after synchronization).
pub fn not_syncing() -> bool {
return match Node::get_sync_status() {
None => true,
Some(ss) => ss == SyncStatus::NoSync
};
}
/// Get synchronization status, empty when [`Server`] is not running.
pub fn get_sync_status() -> Option<SyncStatus> {
// Return Shutdown status when node is stopping.

View file

@ -205,7 +205,7 @@ impl State {
}
}
/// Stratum server stop state shared between to stop stratum from node thread.
/// Stratum server stop state to stop it from node thread.
pub struct StratumStopState {
stopping: AtomicBool,
}