gui: fix button touch, add icons, optimize screen panels (in progress)

This commit is contained in:
ardocrat
2023-04-21 03:45:59 +03:00
parent a5ba9660b3
commit c572c3926e
55 changed files with 1083 additions and 448 deletions
+1 -3
View File
@@ -1,4 +1,4 @@
// Copyright 2023 The Grin Developers
// 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.
@@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//! Build hooks to spit out version+build time info
use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;
+16 -1
View File
@@ -1,4 +1,4 @@
// Copyright 2023 The Grin Developers
// 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.
@@ -59,10 +59,25 @@ fn main() {
}
fn start(mut options: NativeOptions, app_creator: AppCreator) {
setup_i18n();
options.renderer = Renderer::Wgpu;
eframe::run_native("Grim", options, app_creator);
}
fn setup_i18n() {
const DEFAULT_LOCALE: &str = "en";
let locale = sys_locale::get_locale().unwrap_or(String::from(DEFAULT_LOCALE));
let locale_str = if locale.contains("-") {
locale.split("-").next().unwrap_or(DEFAULT_LOCALE)
} else {
DEFAULT_LOCALE
};
if crate::available_locales().contains(&locale_str) {
rust_i18n::set_locale(locale_str);
}
}
mod built_info {
include!(concat!(env!("OUT_DIR"), "/built.rs"));
}
+135 -10
View File
@@ -13,8 +13,14 @@
// limitations under the License.
use std::any::Any;
use std::collections::BTreeSet;
use eframe::emath::Align;
use eframe::Frame;
use egui::Context;
use egui::{Color32, Context, Id, Layout, RichText, Sense, Separator, Stroke, Ui, Widget};
use egui::epaint::Shadow;
use egui::panel::PanelState;
use egui::style::Margin;
use wgpu::Color;
use crate::gui::PlatformCallbacks;
use crate::gui::screens::{Screen};
@@ -25,7 +31,8 @@ pub struct PlatformApp<Platform> {
pub struct Screens {
screens: Vec<Box<dyn Screen>>,
current: String,
navigation: BTreeSet<String>,
menu_open: bool,
}
impl Default for Screens {
@@ -37,15 +44,133 @@ impl Default for Screens {
}
impl Screens {
pub fn from_screens(screens: Vec<Box<dyn Screen>>) -> Self {
let current = screens[0].name().to_string();
Self {
screens,
current,
fn from_screens(screens: Vec<Box<dyn Screen>>) -> Self {
let current = screens[0].name().to_owned();
let mut navigation = BTreeSet::new();
navigation.insert(current);
Self { screens, navigation, menu_open: false }
}
fn show_screens(&mut self, ui: &mut Ui, frame: &mut Frame, cb: &dyn PlatformCallbacks) {
let Self { screens, navigation, .. } = self;
for screen in screens {
let id = screen.name();
let show = navigation.contains(id.as_str());
if show {
screen.show(ui, frame, cb);
}
Self::set_show_screen(navigation, id, show);
}
}
pub fn ui(&mut self, ctx: &Context, frame: &mut Frame, cb: &dyn PlatformCallbacks) {
fn set_show_screen(navigation: &mut BTreeSet<String>, key: String, show: bool) {
if show {
if !navigation.contains(key.as_str()) {
navigation.insert(key.to_owned());
}
} else {
navigation.remove(key.as_str());
}
}
}
fn current_screen_title(&self) -> &String {
self.navigation.last().unwrap()
}
fn menu_is_open(&mut self, frame: &mut Frame) -> bool {
return self.menu_open || is_landscape(frame);
}
pub fn ui(&mut self, ui: &mut Ui, frame: &mut Frame, cb: &dyn PlatformCallbacks) {
let menu_open = self.menu_is_open(frame);
egui::TopBottomPanel::top("title_panel")
.resizable(false)
// .default_height(120.0)
.frame(egui::Frame {
inner_margin: Margin::same(0.0),
outer_margin: Margin::same(0.0),
rounding: Default::default(),
..Default::default()
})
.show_inside(ui, |ui| {
ui.with_layout(Layout::right_to_left(Align::Min), |ui| {
let b = egui::widgets::Button::new(
RichText::new(" + ").size(38.0)
).fill(Color32::TRANSPARENT).ui(ui).interact(Sense::click_and_drag());
if b.drag_released() || b.clicked() {
//TODO: Add wallet
//self.menu_open = !menu_open
};
});
ui.with_layout(Layout::top_down(Align::Center), |ui| {
ui.heading(self.current_screen_title())
});
ui.with_layout(Layout::left_to_right(Align::Min), |ui| {
let b = egui::widgets::Button::new(
RichText::new(" = ").size(38.0)
).fill(Color32::TRANSPARENT).ui(ui).interact(Sense::click_and_drag());
if b.drag_released() || b.clicked() {
self.menu_open = !menu_open
};
});
});
egui::CentralPanel::default().frame(egui::containers::Frame{
outer_margin: Margin {
left: 0.0,
right: 0.0,
// top: 120.0,
top: 0.0,
bottom: 0.0,
},
inner_margin: Margin::same(3.0),
fill: ui.ctx().style().visuals.panel_fill,
..Default::default()
})
.show_inside(ui, |ui| {
self.show_screens(ui, frame, cb);
});
ui.style_mut().visuals.widgets.noninteractive.bg_stroke = Stroke::NONE;
// egui::SidePanel::right("screens_content")
// .resizable(false)
// .min_width(frame.info().window_info.size.x)
// .frame(egui::Frame {
// inner_margin: Margin::same(3.0),
// outer_margin: Margin::same(0.0),
// rounding: Default::default(),
// shadow: Shadow::NONE,
// fill: Color32::KHAKI,
// stroke: Stroke::NONE,
// })
// .show_inside(ui, |ui| {
// self.show_screens(ui, frame, cb);
// });
egui::SidePanel::left("menu_panel")
.resizable(false)
.frame(egui::Frame {
inner_margin: Margin::same(0.0),
outer_margin: Margin::same(0.0),
rounding: Default::default(),
shadow: Shadow::NONE,
fill: Color32::YELLOW,
stroke: Stroke::NONE,
})
.show_animated_inside(ui, menu_open, |ui| {
//TODO: Menu content
ui.vertical_centered(|ui| {
ui.heading("💻 Backend");
});
ui.separator();
});
}
}
pub fn is_landscape(frame: &mut Frame) -> bool {
return frame.info().window_info.size.x > frame.info().window_info.size.y
}
+3 -5
View File
@@ -14,13 +14,11 @@
mod app;
pub use app::PlatformApp;
pub use app::is_landscape;
pub mod screens;
pub mod platform;
pub trait Ui {
fn ui(&mut self, ui: &mut egui::Ui);
}
pub mod screens;
pub mod views;
pub trait PlatformCallbacks {
fn show_keyboard(&mut self);
+126 -96
View File
@@ -12,10 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use egui::{Color32, FontTweak, Visuals};
use egui::epaint::Shadow;
use jni::objects::{JObject, JPrimitiveArray};
use winit::platform::android::activity::AndroidApp;
use crate::gui::{PlatformApp, PlatformCallbacks};
@@ -23,9 +19,9 @@ use crate::gui::app::Screens;
#[derive(Clone)]
pub struct Android {
pub android_app: AndroidApp,
pub cutouts: [i32; 4],
pub window_size: [f32; 2]
android_app: AndroidApp,
cutouts: [i32; 4],
window_size: [f32; 2]
}
impl Android {
@@ -58,32 +54,80 @@ impl PlatformCallbacks for Android {
impl PlatformApp<Android> {
pub fn new(cc: &eframe::CreationContext<'_>, platform: Android) -> Self {
setup_fonts(&cc.egui_ctx);
Self::setup_visuals(&cc.egui_ctx);
Self::setup_fonts(&cc.egui_ctx);
Self {
screens: Screens::default(),
platform,
}
}
}
fn setup_fonts(ctx: &egui::Context) {
let mut fonts = egui::FontDefinitions::default();
fonts.font_data.insert(
"roboto".to_owned(),
egui::FontData::from_static(include_bytes!(
"../../../../fonts/roboto.ttf"
)).tweak(FontTweak {
scale: 1.0,
y_offset_factor: -0.20,
y_offset: 0.0
}),
);
fonts
.families
.entry(egui::FontFamily::Proportional)
.or_default()
.insert(0, "roboto".to_owned());
ctx.set_fonts(fonts);
fn setup_visuals(ctx: &egui::Context) {
ctx.set_visuals(egui::Visuals::light());
}
fn setup_fonts(ctx: &egui::Context) {
use egui::FontFamily::Proportional;
let mut fonts = egui::FontDefinitions::default();
fonts.font_data.insert(
"roboto".to_owned(),
egui::FontData::from_static(include_bytes!(
"../../../../fonts/roboto.ttf"
)).tweak(egui::FontTweak {
scale: 1.0,
y_offset_factor: -0.20,
y_offset: 0.0
}),
);
fonts
.families
.entry(Proportional)
.or_default()
.insert(0, "roboto".to_owned());
ctx.set_fonts(fonts);
use egui::FontId;
use egui::TextStyle::*;
let mut style = (*ctx.style()).clone();
style.text_styles = [
(Heading, FontId::new(24.0, Proportional)),
(Body, FontId::new(18.0, Proportional)),
(Monospace, FontId::new(18.0, Proportional)),
(Button, FontId::new(18.0, Proportional)),
(Small, FontId::new(12.0, Proportional)),
].into();
ctx.set_style(style);
}
fn get_display_cutouts(app: &AndroidApp) -> [i32; 4] {
use jni::objects::{JObject, JPrimitiveArray};
let vm = unsafe { jni::JavaVM::from_raw(app.vm_as_ptr() as _) }.unwrap();
let mut env = vm.attach_current_thread().unwrap();
let activity = unsafe {
JObject::from_raw(app.activity_as_ptr() as jni::sys::jobject)
};
let _res = env
.call_method(
activity,
"getDisplayCutouts",
"()[I",
&[],
)
.unwrap();
let mut array: [i32; 4] = [0; 4];
let object: jni::sys::jobject = unsafe { _res.as_jni().l };
unsafe {
env.get_int_array_region(JPrimitiveArray::from(
JObject::from_raw(object)), 0, array.as_mut()
).unwrap();
}
array
}
}
impl eframe::App for PlatformApp<Android> {
@@ -93,80 +137,66 @@ impl eframe::App for PlatformApp<Android> {
if _x != self.platform.window_size[0] || _y != self.platform.window_size[1] {
self.platform.window_size[0] = _x;
self.platform.window_size[1] = _y;
self.platform.cutouts = get_display_cutouts(&self.platform.android_app);
self.platform.cutouts = Self::get_display_cutouts(&self.platform.android_app);
}
let is_dark = ctx.style().visuals.dark_mode;
egui::TopBottomPanel::top("top_padding_panel")
.frame(egui::Frame {
shadow: Shadow::NONE,
fill: if is_dark {Color32::BLACK} else {Color32::WHITE},
..Default::default()
})
.show_separator_line(false)
.resizable(false)
.exact_height(self.platform.cutouts[0] as f32)
.show(ctx, |ui| {});
padding_panels(self, ctx);
egui::TopBottomPanel::bottom("bottom_padding_panel")
egui::CentralPanel::default()
.frame(egui::Frame {
shadow: Shadow::NONE,
fill: if is_dark {Color32::BLACK} else {Color32::WHITE},
..Default::default()
inner_margin: egui::style::Margin::same(0.0),
outer_margin: egui::style::Margin::same(0.0),
fill: ctx.style().visuals.panel_fill,
.. Default::default()
})
.show_separator_line(false)
.resizable(false)
.exact_height(self.platform.cutouts[2] as f32)
.show(ctx, |ui| {});
egui::SidePanel::right("right_padding_panel")
.frame(egui::Frame {
shadow: Shadow::NONE,
fill: if is_dark {Color32::BLACK} else {Color32::WHITE},
..Default::default()
})
.show_separator_line(false)
.resizable(false)
.default_width(self.platform.cutouts[1] as f32)
.show(ctx, |ui| {});
egui::SidePanel::left("left_padding_panel")
.frame(egui::Frame {
shadow: Shadow::NONE,
fill: if is_dark {Color32::BLACK} else {Color32::WHITE},
..Default::default()
})
.show_separator_line(false)
.resizable(false)
.default_width(self.platform.cutouts[3] as f32)
.show(ctx, |ui| {});
egui::CentralPanel::default().show(ctx, |ui| {
self.screens.ui(ctx, frame, &self.platform);
});
.show(ctx, |ui| {
self.screens.ui(ui, frame, &self.platform);
});
}
}
fn get_display_cutouts(app: &AndroidApp) -> [i32; 4] {
let vm = unsafe { jni::JavaVM::from_raw(app.vm_as_ptr() as _) }.unwrap();
let mut env = vm.attach_current_thread().unwrap();
let activity = unsafe {
JObject::from_raw(app.activity_as_ptr() as jni::sys::jobject)
};
let _res = env
.call_method(
activity,
"getDisplayCutouts",
"()[I",
&[],
)
.unwrap();
let mut array: [i32; 4] = [0; 4];
let object: jni::sys::jobject = unsafe { _res.as_jni().l };
unsafe {
env.get_int_array_region(JPrimitiveArray::from(
JObject::from_raw(object)), 0, array.as_mut()
).unwrap();
}
array
}
fn padding_panels(app: &PlatformApp<Android>, ctx: &egui::Context) {
egui::TopBottomPanel::top("top_padding_panel")
.frame(egui::Frame {
inner_margin: egui::style::Margin::same(0.0),
fill: ctx.style().visuals.panel_fill,
..Default::default()
})
.show_separator_line(false)
.resizable(false)
.exact_height(app.platform.cutouts[0] as f32)
.show(ctx, |ui| {});
egui::TopBottomPanel::bottom("bottom_padding_panel")
.frame(egui::Frame {
inner_margin: egui::style::Margin::same(0.0),
fill: ctx.style().visuals.panel_fill,
..Default::default()
})
.show_separator_line(false)
.resizable(false)
.exact_height(app.platform.cutouts[2] as f32)
.show(ctx, |ui| {});
egui::SidePanel::right("right_padding_panel")
.frame(egui::Frame {
inner_margin: egui::style::Margin::same(0.0),
fill: ctx.style().visuals.panel_fill,
..Default::default()
})
.show_separator_line(false)
.resizable(false)
.max_width(app.platform.cutouts[1] as f32)
.show(ctx, |ui| {});
egui::SidePanel::left("left_padding_panel")
.frame(egui::Frame {
inner_margin: egui::style::Margin::same(0.0),
fill: ctx.style().visuals.panel_fill,
..Default::default()
})
.show_separator_line(false)
.resizable(false)
.max_width(app.platform.cutouts[3] as f32)
.show(ctx, |ui| {});
}
+6 -1
View File
@@ -13,8 +13,13 @@
// limitations under the License.
mod wallets;
pub use wallets::Wallets;
use crate::gui::PlatformCallbacks;
pub trait Screen {
fn name(&self) -> &'static str;
fn name(&self) -> String;
fn show(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks);
// fn option_on_panel(&mut self, ui: &mut egui::Ui);
}
+15 -13
View File
@@ -12,34 +12,36 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use egui::ScrollArea;
use crate::gui::Ui;
use std::ops::Deref;
use eframe::Frame;
use egui::{ScrollArea, Ui};
use crate::gui::app::Screens;
use crate::gui::{PlatformCallbacks};
pub struct Wallets {
}
impl Default for Wallets {
fn default() -> Self {
Self {
}
}
}
impl Ui for Wallets {
fn ui(&mut self, ui: &mut egui::Ui) {
impl super::Screen for Wallets {
fn name(&self) -> String {
t!("wallets")
}
fn show(&mut self, ui: &mut Ui, frame: &mut Frame, cb: &dyn PlatformCallbacks) {
ScrollArea::vertical()
.auto_shrink([false; 2])
.show(ui, |ui| {
for item in 1..=50 {
ui.heading(format!("This is future Wallet {}", item));
for item in 1..=55 {
ui.heading(format!("This is longest future Wallet #{}", item));
}
});
}
}
impl super::Screen for Wallets {
fn name(&self) -> &'static str {
"Wallets"
}
}
+14
View File
@@ -0,0 +1,14 @@
// 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.
+22
View File
@@ -0,0 +1,22 @@
// 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 crate::gui::PlatformCallbacks;
mod navigation;
mod menu;
pub trait View {
fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks);
}
+32
View File
@@ -0,0 +1,32 @@
// 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::Frame;
use egui::Ui;
use crate::gui::PlatformCallbacks;
use crate::gui::views::View;
struct NavigationPanel {
title: str
}
impl View for NavigationPanel {
fn ui(&mut self, ui: &mut Ui, frame: &mut Frame, cb: &dyn PlatformCallbacks) {
}
}
impl NavigationPanel {
}
+6 -2
View File
@@ -1,4 +1,4 @@
// Copyright 2023 The Grin Developers
// 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.
@@ -12,8 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#[macro_use]
extern crate rust_i18n;
i18n!("locales");
mod node;
mod wallet;
mod gui;
pub mod grim;
pub mod grim;
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright 2023 The Grin Developers
// 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.
+13 -13
View File
@@ -1,16 +1,16 @@
// // Copyright 2023 The Grin 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.
// 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 std::sync::mpsc;
use grin_config::{config, GlobalConfig};
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright 2023 The Grin Developers
// 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.
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright 2023 The Grin Developers
// 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.