gui: refactor navigator, add back button handling on android
This commit is contained in:
parent
22c5b945c6
commit
1a7de809c5
10 changed files with 142 additions and 111 deletions
|
@ -4,6 +4,8 @@ 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 com.google.androidgamesdk.GameActivity;
|
||||
|
||||
public class MainActivity extends GameActivity {
|
||||
|
@ -45,4 +47,15 @@ public class MainActivity extends GameActivity {
|
|||
BackgroundService.stop(getApplicationContext());
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
onBackButtonPress();
|
||||
return true;
|
||||
}
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
public native void onBackButtonPress();
|
||||
}
|
|
@ -35,7 +35,6 @@ impl super::Screen for Account {
|
|||
fn ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
frame: &mut eframe::Frame,
|
||||
nav: &mut Navigator,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
|
||||
}
|
||||
|
|
|
@ -31,25 +31,19 @@ impl Screen for Accounts {
|
|||
fn ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
frame: &mut eframe::Frame,
|
||||
nav: &mut Navigator,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
let mut panel: TitlePanel = TitlePanel::new(nav)
|
||||
.title(t!("accounts"))
|
||||
.right_action(TitlePanelAction {
|
||||
icon: GEAR_SIX.into(),
|
||||
on_click: Box::new(|nav| {
|
||||
//TODO: open settings
|
||||
}),
|
||||
});
|
||||
if !is_dual_panel_mode(frame) {
|
||||
panel = panel.left_action(TitlePanelAction {
|
||||
icon: GLOBE.into(),
|
||||
on_click: Box::new(|nav|{
|
||||
nav.toggle_left_panel();
|
||||
}),
|
||||
});
|
||||
}
|
||||
panel.ui(ui);
|
||||
TitlePanel::new(t!("accounts"))
|
||||
.left_action(
|
||||
if !is_dual_panel_mode(frame) {
|
||||
TitlePanelAction::new(GLOBE.into(), || {
|
||||
Navigator::toggle_side_panel();
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
).right_action(TitlePanelAction::new(GEAR_SIX.into(), || {
|
||||
//TODO: settings
|
||||
})).ui(ui);
|
||||
|
||||
egui::CentralPanel::default().frame(Frame {
|
||||
stroke: View::DEFAULT_STROKE,
|
||||
|
@ -57,10 +51,10 @@ impl Screen for Accounts {
|
|||
}).show_inside(ui, |ui| {
|
||||
ui.label(format!("{}Here we go 10000 ツ", ARROW_CIRCLE_LEFT));
|
||||
if ui.button("TEST").clicked() {
|
||||
nav.to(ScreenId::Account)
|
||||
Navigator::to(ScreenId::Account)
|
||||
};
|
||||
if ui.button(format!("{}BACK ", ARROW_CIRCLE_LEFT)).clicked() {
|
||||
nav.to(ScreenId::Account)
|
||||
Navigator::to(ScreenId::Account)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
|
@ -35,6 +35,5 @@ pub trait Screen {
|
|||
fn ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
frame: &mut eframe::Frame,
|
||||
navigator: &mut Navigator,
|
||||
cb: &dyn PlatformCallbacks);
|
||||
}
|
||||
|
|
|
@ -13,39 +13,66 @@
|
|||
// limitations under the License.
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::RwLock;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use crate::gui::screens::ScreenId;
|
||||
|
||||
lazy_static! {
|
||||
static ref NAVIGATOR_STATE: RwLock<Navigator> = RwLock::new(Navigator::default());
|
||||
}
|
||||
|
||||
pub struct Navigator {
|
||||
stack: BTreeSet<ScreenId>,
|
||||
pub(crate) left_panel_open: bool,
|
||||
screens_stack: BTreeSet<ScreenId>,
|
||||
side_panel_open: AtomicBool,
|
||||
}
|
||||
|
||||
impl Default for Navigator {
|
||||
fn default() -> Self {
|
||||
let mut stack = BTreeSet::new();
|
||||
stack.insert(ScreenId::Accounts);
|
||||
Self {
|
||||
stack,
|
||||
left_panel_open: false
|
||||
screens_stack: BTreeSet::new(),
|
||||
side_panel_open: AtomicBool::new(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Navigator {
|
||||
pub fn current(&mut self) -> &ScreenId {
|
||||
self.stack.last().unwrap()
|
||||
pub fn init(from: ScreenId) {
|
||||
let mut w_nav = NAVIGATOR_STATE.write().unwrap();
|
||||
w_nav.screens_stack.clear();
|
||||
w_nav.screens_stack.insert(from);
|
||||
}
|
||||
|
||||
pub fn to(&mut self, id: ScreenId) {
|
||||
self.stack.insert(id);
|
||||
pub fn is_current(id: &ScreenId) -> bool {
|
||||
let r_nav = NAVIGATOR_STATE.read().unwrap();
|
||||
r_nav.screens_stack.last().unwrap() == id
|
||||
}
|
||||
|
||||
pub fn back(&mut self) {
|
||||
self.stack.pop_last();
|
||||
pub fn to(id: ScreenId) {
|
||||
NAVIGATOR_STATE.write().unwrap().screens_stack.insert(id);
|
||||
}
|
||||
|
||||
pub fn toggle_left_panel(&mut self) {
|
||||
self.left_panel_open = !self.left_panel_open;
|
||||
pub fn back() {
|
||||
let mut w_nav = NAVIGATOR_STATE.write().unwrap();
|
||||
if w_nav.screens_stack.len() > 1 {
|
||||
w_nav.screens_stack.pop_last();
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toggle_side_panel() {
|
||||
let w_nav = NAVIGATOR_STATE.write().unwrap();
|
||||
w_nav.side_panel_open.store(
|
||||
!w_nav.side_panel_open.load(Ordering::Relaxed),
|
||||
Ordering::Relaxed
|
||||
);
|
||||
}
|
||||
|
||||
pub fn is_side_panel_open() -> bool {
|
||||
let r_nav = NAVIGATOR_STATE.read().unwrap();
|
||||
r_nav.side_panel_open.load(Ordering::Relaxed)
|
||||
}
|
||||
}
|
|
@ -20,15 +20,15 @@ use crate::gui::screens::{Account, Accounts, Navigator, Screen, ScreenId};
|
|||
use crate::gui::views::Network;
|
||||
|
||||
pub struct Root {
|
||||
navigator: Navigator,
|
||||
screens: Vec<Box<dyn Screen>>,
|
||||
network: Network
|
||||
}
|
||||
|
||||
impl Default for Root {
|
||||
fn default() -> Self {
|
||||
Navigator::init_from(ScreenId::Accounts);
|
||||
|
||||
Self {
|
||||
navigator: Navigator::default(),
|
||||
screens: (vec![
|
||||
Box::new(Accounts::default()),
|
||||
Box::new(Account::default())
|
||||
|
@ -40,7 +40,7 @@ impl Default for Root {
|
|||
|
||||
impl Root {
|
||||
pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) {
|
||||
let is_network_panel_open = self.navigator.left_panel_open || is_dual_panel_mode(frame);
|
||||
let is_network_panel_open = Navigator::is_side_panel_open() || is_dual_panel_mode(frame);
|
||||
|
||||
egui::SidePanel::left("network_panel")
|
||||
.resizable(false)
|
||||
|
@ -53,7 +53,7 @@ impl Root {
|
|||
.. Default::default()
|
||||
})
|
||||
.show_animated_inside(ui, is_network_panel_open, |ui| {
|
||||
self.network.ui(ui, frame, &mut self.navigator, cb);
|
||||
self.network.ui(ui, frame, cb);
|
||||
});
|
||||
|
||||
egui::CentralPanel::default().frame(egui::Frame {
|
||||
|
@ -68,13 +68,24 @@ impl Root {
|
|||
ui: &mut egui::Ui,
|
||||
frame: &mut eframe::Frame,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
let Self { navigator, screens, .. } = self;
|
||||
let current = navigator.current();
|
||||
let Self { screens, .. } = self;
|
||||
for screen in screens.iter_mut() {
|
||||
if screen.id() == *current {
|
||||
screen.ui(ui, frame, navigator, cb);
|
||||
if Navigator::is_current(&screen.id()) {
|
||||
screen.ui(ui, frame, cb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "android")]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn Java_mw_gri_android_MainActivity_onBackButtonPress(
|
||||
_env: jni::JNIEnv,
|
||||
_class: jni::objects::JObject,
|
||||
_activity: jni::objects::JObject,
|
||||
) {
|
||||
Navigator::back();
|
||||
}
|
|
@ -63,7 +63,6 @@ impl Network {
|
|||
pub fn ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
frame: &mut eframe::Frame,
|
||||
nav: &mut Navigator,
|
||||
_: &dyn PlatformCallbacks) {
|
||||
|
||||
egui::TopBottomPanel::top("network_title")
|
||||
|
@ -76,7 +75,7 @@ impl Network {
|
|||
..Default::default()
|
||||
})
|
||||
.show_inside(ui, |ui| {
|
||||
self.draw_title(ui, frame, nav);
|
||||
self.draw_title(ui, frame);
|
||||
});
|
||||
|
||||
egui::TopBottomPanel::bottom("network_tabs")
|
||||
|
@ -145,10 +144,7 @@ impl Network {
|
|||
}
|
||||
}
|
||||
|
||||
fn draw_title(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, nav: &mut Navigator) {
|
||||
// Disable stroke around title buttons on hover
|
||||
ui.style_mut().visuals.widgets.active.bg_stroke = Stroke::NONE;
|
||||
|
||||
fn draw_title(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
|
||||
StripBuilder::new(ui)
|
||||
.size(Size::exact(52.0))
|
||||
.vertical(|mut strip| {
|
||||
|
@ -172,7 +168,7 @@ impl Network {
|
|||
if !is_dual_panel_mode(frame) {
|
||||
ui.centered_and_justified(|ui| {
|
||||
View::title_button(ui, CARDHOLDER, || {
|
||||
nav.toggle_left_panel();
|
||||
Navigator::toggle_side_panel();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -163,7 +163,6 @@ impl NetworkTab for NetworkNode {
|
|||
[false, false, true, false]);
|
||||
});
|
||||
columns[1].vertical_centered(|ui| {
|
||||
let ts = stats.chain_stats.latest_timestamp;
|
||||
View::rounded_box(ui,
|
||||
stats.peer_count.to_string(),
|
||||
t!("peers"),
|
||||
|
|
|
@ -23,7 +23,13 @@ use crate::gui::views::View;
|
|||
|
||||
pub struct TitlePanelAction {
|
||||
pub(crate) icon: Box<str>,
|
||||
pub(crate) on_click: Box<dyn Fn(&mut Navigator)>,
|
||||
pub(crate) on_click: Box<dyn Fn()>,
|
||||
}
|
||||
|
||||
impl TitlePanelAction {
|
||||
pub fn new(icon: Box<str>, on_click: fn()) -> Option<Self> {
|
||||
Option::from(Self { icon, on_click: Box::new(on_click) })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -32,41 +38,31 @@ pub struct TitlePanelActions {
|
|||
right: Option<TitlePanelAction>
|
||||
}
|
||||
|
||||
pub struct TitlePanel<'nav> {
|
||||
title: Option<String>,
|
||||
pub struct TitlePanel {
|
||||
title: String,
|
||||
actions: TitlePanelActions,
|
||||
nav: &'nav mut Navigator
|
||||
}
|
||||
|
||||
impl<'nav> TitlePanel<'nav> {
|
||||
pub fn new(nav: &'nav mut Navigator) -> TitlePanel<'nav> {
|
||||
impl TitlePanel {
|
||||
pub fn new(title: String) -> Self {
|
||||
Self {
|
||||
title: None,
|
||||
actions: Default::default(),
|
||||
nav,
|
||||
title,
|
||||
actions: TitlePanelActions::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn title(mut self, title: String) -> Self {
|
||||
self.title = Some(title);
|
||||
pub fn left_action(mut self, action: Option<TitlePanelAction>) -> Self {
|
||||
self.actions.left = action;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn left_action(mut self, action: TitlePanelAction) -> Self {
|
||||
self.actions.left = Some(action);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn right_action(mut self, action: TitlePanelAction) -> Self {
|
||||
self.actions.right = Some(action);
|
||||
pub fn right_action(mut self, action: Option<TitlePanelAction>) -> Self {
|
||||
self.actions.right = action;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn ui(&mut self, ui: &mut egui::Ui) {
|
||||
// Disable stroke around panel buttons on hover
|
||||
ui.style_mut().visuals.widgets.active.bg_stroke = Stroke::NONE;
|
||||
|
||||
let Self { actions, title, nav } = self;
|
||||
let Self { actions, title } = self;
|
||||
|
||||
egui::TopBottomPanel::top("title_panel")
|
||||
.resizable(false)
|
||||
|
@ -88,19 +84,19 @@ impl<'nav> TitlePanel<'nav> {
|
|||
.size(Size::exact(52.0))
|
||||
.horizontal(|mut strip| {
|
||||
strip.cell(|ui| {
|
||||
show_action(ui, actions.left.as_ref(), nav);
|
||||
show_action(ui, actions.left.as_ref());
|
||||
});
|
||||
strip.strip(|builder| {
|
||||
builder
|
||||
.size(Size::remainder())
|
||||
.vertical(|mut strip| {
|
||||
strip.cell(|ui| {
|
||||
show_title(&*title, ui);
|
||||
show_title(title, ui);
|
||||
});
|
||||
});
|
||||
});
|
||||
strip.cell(|ui| {
|
||||
show_action(ui, actions.right.as_ref(), nav);
|
||||
show_action(ui, actions.right.as_ref());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -109,35 +105,32 @@ impl<'nav> TitlePanel<'nav> {
|
|||
}
|
||||
}
|
||||
|
||||
fn show_action(ui: &mut egui::Ui, action: Option<&TitlePanelAction>, navigator: &mut Navigator) {
|
||||
fn show_action(ui: &mut egui::Ui, action: Option<&TitlePanelAction>) {
|
||||
if action.is_some() {
|
||||
let action = action.unwrap();
|
||||
ui.centered_and_justified(|ui| {
|
||||
View::title_button(ui, &action.icon, || {
|
||||
(action.on_click)(navigator);
|
||||
(action.on_click)();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn show_title(title: &Option<String>, ui: &mut egui::Ui) {
|
||||
if title.is_some() {
|
||||
ui.centered_and_justified(|ui| {
|
||||
let title_text = title.as_ref().unwrap().to_uppercase();
|
||||
let mut job = LayoutJob::single_section(title_text, TextFormat {
|
||||
font_id: FontId::proportional(20.0),
|
||||
color: COLOR_DARK,
|
||||
.. Default::default()
|
||||
});
|
||||
job.wrap = TextWrapping {
|
||||
max_rows: 1,
|
||||
break_anywhere: false,
|
||||
overflow_character: Option::from('﹍'),
|
||||
..Default::default()
|
||||
};
|
||||
ui.label(job);
|
||||
|
||||
fn show_title(title: &String, ui: &mut egui::Ui) {
|
||||
ui.centered_and_justified(|ui| {
|
||||
let mut job = LayoutJob::single_section(title.to_uppercase(), TextFormat {
|
||||
font_id: FontId::proportional(20.0),
|
||||
color: COLOR_DARK,
|
||||
.. Default::default()
|
||||
});
|
||||
}
|
||||
job.wrap = TextWrapping {
|
||||
max_rows: 1,
|
||||
break_anywhere: false,
|
||||
overflow_character: Option::from('﹍'),
|
||||
..Default::default()
|
||||
};
|
||||
ui.label(job);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -24,15 +24,20 @@ 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());
|
||||
ui.scope(|ui| {
|
||||
// Disable stroke around title buttons on hover
|
||||
ui.style_mut().visuals.widgets.active.bg_stroke = Stroke::NONE;
|
||||
|
||||
// Click optimization for touch screens
|
||||
if b.drag_released() || b.clicked() {
|
||||
(action)();
|
||||
};
|
||||
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()) {
|
||||
|
@ -53,11 +58,6 @@ impl View {
|
|||
.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)();
|
||||
|
|
Loading…
Reference in a new issue