ui: move global modal to root screen, refactor rounded box background painting, optimize center content
This commit is contained in:
parent
5143a39700
commit
f95645ea81
8 changed files with 134 additions and 126 deletions
|
@ -12,15 +12,12 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use egui::{Color32, Context, RichText, Spinner, Stroke, Widget};
|
||||
use egui::{Context, Stroke};
|
||||
use egui::os::OperatingSystem;
|
||||
use egui::style::Margin;
|
||||
|
||||
use crate::gui::{Colors, Navigator};
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::screens::Root;
|
||||
use crate::gui::views::{Modal, ModalId, ModalLocation, View};
|
||||
use crate::node::Node;
|
||||
|
||||
pub struct PlatformApp<Platform> {
|
||||
pub(crate) app: App,
|
||||
|
@ -30,7 +27,6 @@ pub struct PlatformApp<Platform> {
|
|||
#[derive(Default)]
|
||||
pub struct App {
|
||||
root: Root,
|
||||
show_exit_progress: bool
|
||||
}
|
||||
|
||||
impl App {
|
||||
|
@ -41,71 +37,11 @@ impl App {
|
|||
.. Default::default()
|
||||
})
|
||||
.show(ctx, |ui| {
|
||||
if Navigator::is_modal_open(ModalLocation::Global) {
|
||||
self.show_global_modal(ui, frame, cb);
|
||||
}
|
||||
self.root.ui(ui, frame, cb);
|
||||
});
|
||||
}
|
||||
|
||||
fn show_global_modal(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
frame: &mut eframe::Frame,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
let location = ModalLocation::Global;
|
||||
Navigator::modal_ui(ui, location, |ui, modal| {
|
||||
match modal.id {
|
||||
ModalId::Exit => {
|
||||
if self.show_exit_progress {
|
||||
if !Node::is_running() {
|
||||
Self::exit(frame, cb);
|
||||
modal.close();
|
||||
}
|
||||
ui.add_space(16.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
Spinner::new().size(42.0).color(Colors::GRAY).ui(ui);
|
||||
ui.add_space(10.0);
|
||||
ui.label(RichText::new(Node::get_sync_status_text())
|
||||
.size(18.0)
|
||||
.color(Colors::INACTIVE_TEXT)
|
||||
);
|
||||
});
|
||||
ui.add_space(12.0);
|
||||
} else {
|
||||
ui.add_space(8.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(t!("modal_exit.description"));
|
||||
});
|
||||
ui.add_space(10.0);
|
||||
// Setup spacing between buttons
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0);
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal_exit.exit"), Colors::WHITE, || {
|
||||
if !Node::is_running() {
|
||||
Self::exit(frame, cb);
|
||||
modal.close();
|
||||
} else {
|
||||
Node::stop();
|
||||
modal.disable_closing();
|
||||
self.show_exit_progress = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.cancel"), Colors::WHITE, || {
|
||||
modal.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn exit(frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) {
|
||||
pub fn exit(frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) {
|
||||
match OperatingSystem::from_target_os() {
|
||||
OperatingSystem::Android => {
|
||||
cb.exit();
|
||||
|
|
|
@ -25,7 +25,7 @@ impl Colors {
|
|||
pub const GOLD: Color32 = Color32::from_rgb(255, 215, 0);
|
||||
pub const FILL: Color32 = Color32::from_gray(240);
|
||||
pub const TITLE: Color32 = Color32::from_gray(60);
|
||||
pub const SUB_TITLE: Color32 = Color32::from_gray(80);
|
||||
pub const TEXT: Color32 = Color32::from_gray(80);
|
||||
pub const BUTTON: Color32 = Color32::from_gray(70);
|
||||
pub const GRAY: Color32 = Color32::from_gray(120);
|
||||
pub const STROKE: Color32 = Color32::from_gray(190);
|
||||
|
|
|
@ -14,14 +14,18 @@
|
|||
|
||||
use std::cmp::min;
|
||||
|
||||
use crate::gui::Navigator;
|
||||
use egui::{RichText, Spinner, Widget};
|
||||
|
||||
use crate::gui::{App, Colors, Navigator};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::screens::{Account, Accounts, Screen, ScreenId};
|
||||
use crate::gui::views::{Network, View};
|
||||
use crate::gui::views::{ModalId, ModalLocation, Network, View};
|
||||
use crate::node::Node;
|
||||
|
||||
pub struct Root {
|
||||
screens: Vec<Box<dyn Screen>>,
|
||||
network: Network
|
||||
network: Network,
|
||||
show_exit_progress: bool
|
||||
}
|
||||
|
||||
impl Default for Root {
|
||||
|
@ -33,14 +37,19 @@ impl Default for Root {
|
|||
Box::new(Accounts::default()),
|
||||
Box::new(Account::default())
|
||||
]),
|
||||
network: Network::default()
|
||||
network: Network::default(),
|
||||
show_exit_progress: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Root {
|
||||
pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) {
|
||||
let (is_panel_open, panel_width) = dual_panel_state_width(frame);
|
||||
if Navigator::is_modal_open(ModalLocation::Global) {
|
||||
self.show_global_modal(ui, frame, cb);
|
||||
}
|
||||
|
||||
let (is_panel_open, panel_width) = self.dual_panel_state_width(frame);
|
||||
egui::SidePanel::left("network_panel")
|
||||
.resizable(false)
|
||||
.exact_width(panel_width)
|
||||
|
@ -56,6 +65,63 @@ impl Root {
|
|||
});
|
||||
}
|
||||
|
||||
fn show_global_modal(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
frame: &mut eframe::Frame,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
let location = ModalLocation::Global;
|
||||
Navigator::modal_ui(ui, location, |ui, modal| {
|
||||
match modal.id {
|
||||
ModalId::Exit => {
|
||||
if self.show_exit_progress {
|
||||
if !Node::is_running() {
|
||||
App::exit(frame, cb);
|
||||
modal.close();
|
||||
}
|
||||
ui.add_space(16.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
Spinner::new().size(48.0).color(Colors::GRAY).ui(ui);
|
||||
ui.add_space(12.0);
|
||||
ui.label(RichText::new(t!("sync_status.shutdown"))
|
||||
.size(17.0)
|
||||
.color(Colors::TEXT)
|
||||
);
|
||||
});
|
||||
ui.add_space(10.0);
|
||||
} else {
|
||||
ui.add_space(8.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(t!("modal_exit.description"));
|
||||
});
|
||||
ui.add_space(10.0);
|
||||
// Setup spacing between buttons
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0);
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal_exit.exit"), Colors::WHITE, || {
|
||||
if !Node::is_running() {
|
||||
App::exit(frame, cb);
|
||||
modal.close();
|
||||
} else {
|
||||
Node::stop();
|
||||
modal.disable_closing();
|
||||
self.show_exit_progress = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.cancel"), Colors::WHITE, || {
|
||||
modal.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn show_current_screen(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
frame: &mut eframe::Frame,
|
||||
|
@ -68,18 +134,18 @@ impl Root {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get dual panel state and width
|
||||
fn dual_panel_state_width(frame: &mut eframe::Frame) -> (bool, f32) {
|
||||
let dual_panel_mode = View::is_dual_panel_mode(frame);
|
||||
let is_panel_open = dual_panel_mode || Navigator::is_side_panel_open();
|
||||
let panel_width = if dual_panel_mode {
|
||||
min(frame.info().window_info.size.x as i64, View::SIDE_PANEL_MIN_WIDTH) as f32
|
||||
} else {
|
||||
frame.info().window_info.size.x
|
||||
};
|
||||
(is_panel_open, panel_width)
|
||||
/// Get dual panel state and width
|
||||
fn dual_panel_state_width(&self, frame: &mut eframe::Frame) -> (bool, f32) {
|
||||
let dual_panel_mode = View::is_dual_panel_mode(frame);
|
||||
let is_panel_open = dual_panel_mode || Navigator::is_side_panel_open();
|
||||
let panel_width = if dual_panel_mode {
|
||||
min(frame.info().window_info.size.x as i64, View::SIDE_PANEL_MIN_WIDTH) as f32
|
||||
} else {
|
||||
frame.info().window_info.size.x
|
||||
};
|
||||
(is_panel_open, panel_width)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
|
|
@ -76,7 +76,6 @@ impl Network {
|
|||
outer_margin: Margin::same(5.0),
|
||||
.. Default::default()
|
||||
})
|
||||
.resizable(false)
|
||||
.show_inside(ui, |ui| {
|
||||
self.draw_tabs(ui);
|
||||
});
|
||||
|
@ -219,7 +218,7 @@ impl Network {
|
|||
};
|
||||
|
||||
// Draw sync text
|
||||
let status_color_rgba = Rgba::from(Colors::SUB_TITLE) * color_factor;
|
||||
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);
|
||||
|
||||
|
@ -234,9 +233,9 @@ impl Network {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn server_off_content(ui: &mut egui::Ui) {
|
||||
View::center_content(ui, [ui.available_width() - 48.0, 160.0], |ui| {
|
||||
let text = t!("network.inactive_message","dots" => DOTS_THREE_OUTLINE_VERTICAL);
|
||||
pub fn disabled_server_content(ui: &mut egui::Ui) {
|
||||
View::center_content(ui, 142.0, |ui| {
|
||||
let text = t!("network.disabled_server", "dots" => DOTS_THREE_OUTLINE_VERTICAL);
|
||||
ui.label(RichText::new(text)
|
||||
.size(16.0)
|
||||
.color(Colors::INACTIVE_TEXT)
|
||||
|
|
|
@ -39,14 +39,14 @@ impl NetworkTab for NetworkMetrics {
|
|||
let server_stats = Node::get_stats();
|
||||
if server_stats.is_none() || server_stats.as_ref().unwrap().diff_stats.height == 0 {
|
||||
if !Node::is_running() {
|
||||
Network::server_off_content(ui);
|
||||
Network::disabled_server_content(ui);
|
||||
} else {
|
||||
View::center_content(ui, [280.0, 160.0], |ui| {
|
||||
View::center_content(ui, 160.0, |ui| {
|
||||
Spinner::new().size(104.0).color(Colors::GOLD).ui(ui);
|
||||
ui.add_space(18.0);
|
||||
ui.label(RichText::new(t!("network_metrics.loading"))
|
||||
.size(16.0)
|
||||
.color(Colors::INACTIVE_TEXT)
|
||||
.color(Colors::TEXT)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ impl NetworkTab for NetworkMining {
|
|||
}
|
||||
|
||||
fn ui(&mut self, ui: &mut Ui) {
|
||||
todo!()
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -35,7 +35,7 @@ impl NetworkTab for NetworkNode {
|
|||
let server_stats = Node::get_stats();
|
||||
if !server_stats.is_some() {
|
||||
if !Node::is_running() {
|
||||
Network::server_off_content(ui);
|
||||
Network::disabled_server_content(ui);
|
||||
} else {
|
||||
ui.centered_and_justified(|ui| {
|
||||
Spinner::new().size(104.0).color(Colors::GOLD).ui(ui);
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
// limitations under the License.
|
||||
|
||||
use egui::{Button, PointerState, Response, RichText, Sense, Vec2, Widget};
|
||||
use egui::epaint::{Color32, FontId, Rounding, Stroke};
|
||||
use egui::epaint::{Color32, FontId, RectShape, Rounding, Stroke};
|
||||
use egui::epaint::text::TextWrapping;
|
||||
use egui::text::{LayoutJob, TextFormat};
|
||||
use egui_extras::{Size, StripBuilder};
|
||||
|
@ -56,7 +56,7 @@ impl View {
|
|||
|
||||
/// Sub-header with uppercase characters and more lighter color.
|
||||
pub fn sub_header(ui: &mut egui::Ui, text: String) {
|
||||
ui.label(RichText::new(text.to_uppercase()).size(16.0).color(Colors::SUB_TITLE));
|
||||
ui.label(RichText::new(text.to_uppercase()).size(16.0).color(Colors::TEXT));
|
||||
}
|
||||
|
||||
/// Temporary button click optimization for touch screens.
|
||||
|
@ -90,7 +90,7 @@ impl View {
|
|||
pub fn tab_button(ui: &mut egui::Ui, icon: &str, active: bool, action: impl FnOnce()) {
|
||||
let text_color = match active {
|
||||
true => { Colors::TITLE }
|
||||
false => { Colors::SUB_TITLE }
|
||||
false => { Colors::TEXT }
|
||||
};
|
||||
let wt = RichText::new(icon.to_string()).size(24.0).color(text_color);
|
||||
|
||||
|
@ -128,52 +128,58 @@ impl View {
|
|||
/// | 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(
|
||||
// Create background shape.
|
||||
let mut bg_shape = RectShape {
|
||||
rect,
|
||||
Rounding {
|
||||
rounding: 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 },
|
||||
},
|
||||
Colors::WHITE,
|
||||
Stroke { width: 1.0, color: Colors::ITEM_STROKE },
|
||||
);
|
||||
fill: Colors::WHITE,
|
||||
stroke: Stroke { width: 1.0, color: Colors::ITEM_STROKE },
|
||||
};
|
||||
let bg_idx = ui.painter().add(bg_shape);
|
||||
|
||||
ui.vertical_centered_justified(|ui| {
|
||||
// Correct vertical spacing between items
|
||||
ui.style_mut().spacing.item_spacing.y = -4.0;
|
||||
// Draw box content.
|
||||
let content_resp = ui.allocate_ui_at_rect(rect, |ui| {
|
||||
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: Colors::BLACK,
|
||||
.. Default::default()
|
||||
// Draw box value.
|
||||
let mut job = LayoutJob::single_section(value, TextFormat {
|
||||
font_id: FontId::proportional(18.0),
|
||||
color: Colors::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(Colors::GRAY).size(15.0));
|
||||
});
|
||||
job.wrap = TextWrapping {
|
||||
max_rows: 1,
|
||||
break_anywhere: false,
|
||||
overflow_character: Option::from('﹍'),
|
||||
..Default::default()
|
||||
};
|
||||
ui.label(job);
|
||||
}).response;
|
||||
|
||||
// Draw box label
|
||||
ui.label(RichText::new(label).color(Colors::GRAY).size(15.0));
|
||||
});
|
||||
// Setup background shape to be painted behind box content.
|
||||
bg_shape.rect = content_resp.rect;
|
||||
ui.painter().set(bg_idx, bg_shape);
|
||||
}
|
||||
|
||||
/// 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)) {
|
||||
pub fn center_content(ui: &mut egui::Ui, height: f32, 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);
|
||||
let side_margin = 24.0;
|
||||
rect.min += egui::emath::vec2(side_margin, ui.available_height() / 2.0 - height / 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);
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue