ui: refactor colors, add disabled server message, optimize Android sensor delay.

This commit is contained in:
ardocrat 2023-06-03 11:22:51 +03:00
parent de7be791a9
commit c4a87ad755
15 changed files with 158 additions and 174 deletions

View file

@ -5,7 +5,6 @@ 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;
@ -30,7 +29,7 @@ public class MainActivity extends GameActivity {
// Callback to update display cutouts at native code.
OrientationEventListener orientationEventListener = new OrientationEventListener(this,
SensorManager.SENSOR_DELAY_FASTEST) {
SensorManager.SENSOR_DELAY_NORMAL) {
@Override
public void onOrientationChanged(int orientation) {
onDisplayCutoutsChanged(Utils.getDisplayCutouts(MainActivity.this));
@ -83,7 +82,6 @@ public class MainActivity extends GameActivity {
// Called from native code
public void onExit() {
Log.d("12345", "onExit");
mManualExit = true;
BackgroundService.stop(this);
finish();

View file

@ -8,6 +8,7 @@ network:
enable: Enable
disable: Disable
restart: Restart
inactive_message: 'Enable server or choose/add another connection method by pressing %{dots} on top left corner of the screen.'
sync_status:
server_restarting: Server is restarting
server_down: Server is down
@ -22,7 +23,7 @@ sync_status:
tx_hashset_range_proofs_validation: 'Validating state - range proofs: %{percent}%'
tx_hashset_kernels_validation: 'Validating state - kernels: %{percent}%'
tx_hashset_save: Finalizing chain state
body_sync: Downloading blocks
body_sync: Downloading blockchain
body_sync_percent: 'Downloading blocks: %{percent}%'
shutdown: Server is shutting down
network_node:

View file

@ -8,6 +8,7 @@ network:
enable: Включить
disable: Выключить
restart: Перезапустить
inactive_message: 'Включите сервер или выберите/добавьте другой способ подключения, нажав %{dots} в левом верхнем углу экрана.'
sync_status:
server_restarting: Сервер перезапускается
server_down: Сервер выключен
@ -22,7 +23,7 @@ sync_status:
tx_hashset_range_proofs_validation: 'Проверка доказательств: %{percent}%'
tx_hashset_kernels_validation: 'Проверка ядер: %{percent}%'
tx_hashset_save: Сохранение состояния цепи
body_sync: Загрузка блоков
body_sync: Загрузка блокчейна
body_sync_percent: 'Загрузка блоков: %{percent}%'
shutdown: Выключение сервера
network_node:

View file

@ -16,8 +16,7 @@ use egui::{Color32, Context, RichText, Spinner, Stroke, Widget};
use egui::os::OperatingSystem;
use egui::style::Margin;
use crate::gui::colors::{COLOR_DARK, COLOR_LIGHT, COLOR_YELLOW};
use crate::gui::Navigator;
use crate::gui::{Colors, Navigator};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::screens::Root;
use crate::gui::views::{Modal, ModalId, ModalLocation, View};
@ -38,7 +37,7 @@ impl App {
pub fn ui(&mut self, ctx: &Context, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) {
egui::CentralPanel::default()
.frame(egui::Frame {
fill: COLOR_LIGHT,
fill: Colors::FILL,
.. Default::default()
})
.show(ctx, |ui| {
@ -64,11 +63,11 @@ impl App {
}
ui.add_space(16.0);
ui.vertical_centered(|ui| {
Spinner::new().size(42.0).color(COLOR_YELLOW).ui(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(COLOR_DARK)
.color(Colors::INACTIVE_TEXT)
);
});
ui.add_space(12.0);
@ -82,7 +81,7 @@ 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::button(ui, t!("modal_exit.exit"), Color32::WHITE, || {
View::button(ui, t!("modal_exit.exit"), Colors::WHITE, || {
if !Node::is_running() {
Self::exit(frame, cb);
modal.close();
@ -94,7 +93,7 @@ impl App {
});
});
columns[1].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Color32::WHITE, || {
View::button(ui, t!("modal.cancel"), Colors::WHITE, || {
modal.close();
});
});

View file

@ -12,9 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
pub const COLOR_YELLOW: egui::Color32 = egui::Color32::from_rgb(254, 241, 2);
pub const COLOR_LIGHT: egui::Color32 = egui::Color32::from_gray(240);
pub const COLOR_DARK: egui::Color32 = egui::Color32::from_gray(60);
pub const COLOR_GRAY: egui::Color32 = egui::Color32::from_gray(120);
pub const COLOR_GRAY_LIGHT: egui::Color32 = egui::Color32::from_gray(220);
pub const COLOR_GRAY_DARK: egui::Color32 = egui::Color32::from_gray(80);
use egui::Color32;
pub struct Colors;
impl Colors {
pub const WHITE: Color32 = Color32::from_gray(253);
pub const BLACK: Color32 = Color32::from_gray(2);
pub const TRANSPARENT: Color32 = Color32::from_rgba_premultiplied(0, 0, 0, 0);
pub const SEMI_TRANSPARENT: Color32 = Color32::from_black_alpha(100);
pub const YELLOW: Color32 = Color32::from_rgb(254, 241, 2);
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 BUTTON: Color32 = Color32::from_gray(70);
pub const GRAY: Color32 = Color32::from_gray(120);
pub const STROKE: Color32 = Color32::from_gray(190);
pub const INACTIVE_TEXT: Color32 = Color32::from_gray(150);
pub const ITEM_STROKE: Color32 = Color32::from_gray(220);
}

View file

@ -19,8 +19,10 @@ pub use app::{App, PlatformApp};
mod navigator;
pub use navigator::Navigator;
mod colors;
pub use colors::Colors;
pub mod platform;
pub mod screens;
pub mod views;
pub mod icons;
pub mod colors;

View file

@ -20,17 +20,8 @@ use crate::gui::platform::PlatformCallbacks;
use crate::gui::screens::{Screen, ScreenId};
use crate::gui::views::{TitlePanel, TitlePanelAction, View};
pub struct Accounts {
title: String
}
impl Default for Accounts {
fn default() -> Self {
Self {
title: t!("screen_accounts.title").to_uppercase(),
}
}
}
#[derive(Default)]
pub struct Accounts;
impl Screen for Accounts {
fn id(&self) -> ScreenId {
@ -38,9 +29,7 @@ impl Screen for Accounts {
}
fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) {
let Self { title } = self;
TitlePanel::new(title)
TitlePanel::new(t!("screen_accounts.title"))
.ui(if !View::is_dual_panel_mode(frame) {
TitlePanelAction::new(GLOBE, || {
Navigator::toggle_side_panel();

View file

@ -30,6 +30,6 @@ mod network_metrics;
mod network_mining;
pub trait NetworkTab {
fn name(&self) -> &String;
fn name(&self) -> String;
fn ui(&mut self, ui: &mut egui::Ui);
}

View file

@ -15,12 +15,12 @@
use std::cmp::min;
use std::sync::atomic::{AtomicBool, Ordering};
use egui::{Align2, Color32, RichText, Rounding, Sense, Separator, Stroke, Vec2, Widget};
use egui::{Align2, RichText, Rounding, Sense, Separator, Stroke, Vec2, Widget};
use egui::epaint::RectShape;
use egui::style::Margin;
use egui_extras::{Size, StripBuilder};
use crate::gui::Colors;
use crate::gui::colors::{COLOR_DARK, COLOR_LIGHT, COLOR_YELLOW};
use crate::gui::views::View;
/// Identifier for [`Modal`] content to draw at [`Modal::ui`].
@ -128,7 +128,7 @@ impl Modal {
.fixed_pos(ui.next_widget_position())
.fixed_size(ui.available_size())
.frame(egui::Frame {
fill: Color32::from_black_alpha(100),
fill: Colors::SEMI_TRANSPARENT,
..Default::default()
})
.show(ui.ctx(), |ui| {
@ -144,7 +144,7 @@ impl Modal {
.anchor(self.modal_position(), Vec2::default())
.frame(egui::Frame {
rounding: Rounding::same(8.0),
fill: COLOR_YELLOW,
fill: Colors::YELLOW,
..Default::default()
})
.show(ui.ctx(), |ui| {
@ -202,7 +202,7 @@ impl Modal {
let mut bg_shape = RectShape {
rect,
rounding,
fill: COLOR_LIGHT,
fill: Colors::FILL,
stroke: Stroke::NONE,
};
let bg_idx = ui.painter().add(bg_shape);
@ -232,7 +232,7 @@ impl Modal {
sw: 0.0,
se: 0.0,
},
fill: COLOR_YELLOW,
fill: Colors::YELLOW,
stroke: Stroke::NONE,
};
let bg_idx = ui.painter().add(bg_shape);
@ -241,7 +241,10 @@ impl Modal {
let title_resp = ui.allocate_ui_at_rect(rect, |ui| {
ui.vertical_centered_justified(|ui| {
ui.add_space(8.0);
ui.label(RichText::new(self.title.as_ref().unwrap()).size(20.0).color(COLOR_DARK));
ui.label(RichText::new(self.title.as_ref().unwrap())
.size(20.0)
.color(Colors::TITLE)
);
ui.add_space(8.0);
});
}).response;
@ -250,10 +253,12 @@ impl Modal {
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());
// Draw line below title.
let line_size = Vec2::new(ui.available_width(), 1.0);
let (line_rect, _) = ui.allocate_exact_size(line_size, Sense::hover());
let painter = ui.painter();
painter.hline(rect.x_range(),
painter.round_to_pixel(rect.center().y),
View::DEFAULT_STROKE);
painter.hline(line_rect.x_range(),
painter.round_to_pixel(line_rect.center().y),
View::DEFAULT_STROKE);
}
}

View file

@ -20,12 +20,12 @@ use egui_extras::{Size, StripBuilder};
use grin_chain::SyncStatus;
use grin_core::global::ChainTypes;
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::{Colors, Navigator};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{NetworkTab, View};
use crate::gui::views::network_metrics::NetworkMetrics;
use crate::gui::views::network_mining::NetworkMining;
use crate::gui::views::network_node::NetworkNode;
use crate::node::Node;
@ -33,7 +33,7 @@ use crate::node::Node;
enum Mode {
Node,
Metrics,
Miner,
Mining,
Tuning
}
@ -42,6 +42,7 @@ pub struct Network {
node_view: NetworkNode,
metrics_view: NetworkMetrics,
mining_view: NetworkMining,
}
impl Default for Network {
@ -49,7 +50,8 @@ impl Default for Network {
Self {
current_mode: Mode::Node,
node_view: NetworkNode::default(),
metrics_view: NetworkMetrics::default()
metrics_view: NetworkMetrics::default(),
mining_view: NetworkMining::default()
}
}
}
@ -59,7 +61,7 @@ impl Network {
egui::TopBottomPanel::top("network_title")
.resizable(false)
.frame(egui::Frame {
fill: COLOR_YELLOW,
fill: Colors::YELLOW,
inner_margin: Margin::same(0.0),
outer_margin: Margin::same(0.0),
stroke: Stroke::NONE,
@ -83,7 +85,7 @@ impl Network {
.frame(egui::Frame {
stroke: View::DEFAULT_STROKE,
inner_margin: Margin::same(4.0),
fill: Color32::WHITE,
fill: Colors::WHITE,
.. Default::default()
})
.show_inside(ui, |ui| {
@ -110,8 +112,8 @@ impl Network {
});
});
columns[2].vertical_centered_justified(|ui| {
View::tab_button(ui, FACTORY, self.current_mode == Mode::Miner, || {
self.current_mode = Mode::Miner;
View::tab_button(ui, FACTORY, self.current_mode == Mode::Mining, || {
self.current_mode = Mode::Mining;
});
});
columns[3].vertical_centered_justified(|ui| {
@ -134,7 +136,7 @@ impl Network {
Mode::Tuning => {
self.node_view.ui(ui);
}
Mode::Miner => {}
Mode::Mining => {}
}
}
@ -180,8 +182,8 @@ impl Network {
Mode::Metrics => {
self.metrics_view.name()
}
Mode::Miner => {
self.node_view.name()
Mode::Mining => {
self.mining_view.name()
}
Mode::Tuning => {
self.node_view.name()
@ -195,9 +197,9 @@ impl Network {
strip.cell(|ui| {
ui.add_space(2.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(title_text)
ui.label(RichText::new(title_text.to_uppercase())
.size(18.0)
.color(COLOR_DARK));
.color(Colors::TITLE));
});
});
strip.cell(|ui| {
@ -211,13 +213,13 @@ impl Network {
};
let (dark, bright) = (0.3, 1.0);
let color_factor = if !idle {
lerp(dark..=bright, ui.input().time.cos().abs())
lerp(dark..=bright, ui.input().time.cos().abs()) as f32
} else {
bright
bright as f32
};
// Draw sync text
let status_color_rgba = Rgba::from(COLOR_GRAY_DARK) * color_factor as f32;
let status_color_rgba = Rgba::from(Colors::SUB_TITLE) * color_factor;
let status_color = Color32::from(status_color_rgba);
View::ellipsize_text(ui, Node::get_sync_status_text(), 15.0, status_color);
@ -233,18 +235,16 @@ 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))
View::center_content(ui, [ui.available_width() - 48.0, 160.0], |ui| {
let text = t!("network.inactive_message","dots" => DOTS_THREE_OUTLINE_VERTICAL);
ui.label(RichText::new(text)
.size(16.0)
.color(Colors::INACTIVE_TEXT)
);
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, || {
ui.add_space(10.0);
View::button(ui, format!("{} {}", POWER, t!("network.enable")), Colors::GOLD, || {
Node::start(ChainTypes::Mainnet);
});
});

View file

@ -18,30 +18,21 @@ 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, COLOR_YELLOW};
use crate::gui::Colors;
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 {
title: String
}
impl Default for NetworkMetrics {
fn default() -> Self {
Self {
title: t!("network.metrics").to_uppercase(),
}
}
}
#[derive(Default)]
pub struct NetworkMetrics;
const BLOCK_REWARD: f64 = 60.0;
// 1 year is calculated as 365 days and 6 hours (31557600).
const YEARLY_SUPPLY: f64 = ((60 * 60 * 24 * 365) + 6 * 60 * 60) as f64;
impl NetworkTab for NetworkMetrics {
fn name(&self) -> &String {
&self.title
fn name(&self) -> String {
t!("network.metrics")
}
fn ui(&mut self, ui: &mut egui::Ui) {
@ -51,11 +42,11 @@ impl NetworkTab for NetworkMetrics {
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);
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(Color32::from_gray(150))
.color(Colors::INACTIVE_TEXT)
);
});
}
@ -177,60 +168,60 @@ fn draw_diff_block(ui: &mut egui::Ui, db: &DiffBlock, rounding: [bool; 2]) {
se: if rounding[1] { 8.0 } else { 0.0 },
},
Color32::WHITE,
Stroke { width: 1.0, color: COLOR_GRAY_LIGHT }
Stroke { width: 1.0, color: Colors::ITEM_STROKE }
);
ui.add_space(2.0);
ui.horizontal_top(|ui| {
ui.add_space(5.0);
ui.heading(RichText::new(HASH)
.color(Color32::BLACK)
.color(Colors::BLACK)
.size(18.0));
ui.add_space(2.0);
// Draw block hash
ui.heading(RichText::new(db.block_hash.to_string())
.color(Color32::BLACK)
.color(Colors::BLACK)
.size(18.0));
});
ui.horizontal_top(|ui| {
ui.add_space(6.0);
ui.heading(RichText::new(CUBE_TRANSPARENT)
.color(COLOR_DARK)
.color(Colors::TITLE)
.size(16.0));
ui.add_space(4.0);
// Draw block difficulty and height
ui.heading(RichText::new(db.difficulty.to_string())
.color(COLOR_DARK)
.color(Colors::TITLE)
.size(16.0));
ui.add_space(2.0);
ui.heading(RichText::new(AT).color(COLOR_DARK).size(16.0));
ui.heading(RichText::new(AT).color(Colors::TITLE).size(16.0));
ui.add_space(2.0);
ui.heading(RichText::new(db.block_height.to_string())
.color(COLOR_DARK)
.color(Colors::TITLE)
.size(16.0));
});
ui.horizontal_top(|ui| {
ui.add_space(6.0);
ui.heading(RichText::new(TIMER)
.color(COLOR_GRAY)
.color(Colors::GRAY)
.size(16.0));
ui.add_space(4.0);
// Draw block time
ui.heading(RichText::new(format!("{}s", db.duration))
.color(COLOR_GRAY)
.color(Colors::GRAY)
.size(16.0));
ui.add_space(2.0);
ui.heading(RichText::new(HOURGLASS_LOW).color(COLOR_GRAY).size(16.0));
ui.heading(RichText::new(HOURGLASS_LOW).color(Colors::GRAY).size(16.0));
ui.add_space(2.0);
let naive_datetime = NaiveDateTime::from_timestamp_opt(db.time as i64, 0);
if naive_datetime.is_some() {
let datetime: DateTime<Utc> = DateTime::from_utc(naive_datetime.unwrap(), Utc);
ui.heading(RichText::new(datetime.to_string())
.color(COLOR_GRAY)
.color(Colors::GRAY)
.size(16.0));
}
});

View file

@ -15,21 +15,12 @@
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(),
}
}
}
#[derive(Default)]
pub struct NetworkMining;
impl NetworkTab for NetworkMining {
fn name(&self) -> &String {
&self.title
fn name(&self) -> String {
t!("network.mining")
}
fn ui(&mut self, ui: &mut Ui) {

View file

@ -17,27 +17,18 @@ 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;
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 {
title: String
}
impl Default for NetworkNode {
fn default() -> Self {
Self {
title: t!("network.node").to_uppercase(),
}
}
}
#[derive(Default)]
pub struct NetworkNode;
impl NetworkTab for NetworkNode {
fn name(&self) -> &String {
&self.title
fn name(&self) -> String {
t!("network.node")
}
fn ui(&mut self, ui: &mut egui::Ui) {
@ -47,7 +38,7 @@ impl NetworkTab for NetworkNode {
Network::server_off_content(ui);
} else {
ui.centered_and_justified(|ui| {
Spinner::new().size(104.0).color(COLOR_YELLOW).ui(ui);
Spinner::new().size(104.0).color(Colors::GOLD).ui(ui);
});
}
return;
@ -218,51 +209,51 @@ fn draw_peer_stats(ui: &mut egui::Ui, peer: &PeerStats, rounding: [bool; 2]) {
se: if rounding[1] { 8.0 } else { 0.0 },
},
Color32::WHITE,
Stroke { width: 1.0, color: COLOR_GRAY_LIGHT }
Stroke { width: 1.0, color: Colors::ITEM_STROKE }
);
ui.add_space(2.0);
ui.horizontal_top(|ui| {
ui.add_space(5.0);
ui.heading(RichText::new(PLUGS_CONNECTED)
.color(Color32::BLACK)
.color(Colors::BLACK)
.size(18.0));
ui.add_space(3.0);
// Draw peer address
ui.heading(RichText::new(&peer.addr)
.color(Color32::BLACK)
.color(Colors::BLACK)
.size(18.0));
});
ui.horizontal_top(|ui| {
ui.add_space(6.0);
ui.heading(RichText::new(PACKAGE)
.color(COLOR_DARK)
.color(Colors::TITLE)
.size(16.0));
ui.add_space(4.0);
// Draw peer difficulty and height
ui.heading(RichText::new(peer.total_difficulty.to_string())
.color(COLOR_DARK)
.color(Colors::TITLE)
.size(16.0));
ui.add_space(2.0);
ui.heading(RichText::new(AT).color(COLOR_DARK).size(16.0));
ui.heading(RichText::new(AT).color(Colors::TITLE).size(16.0));
ui.add_space(2.0);
ui.heading(RichText::new(peer.height.to_string())
.color(COLOR_DARK)
.color(Colors::TITLE)
.size(16.0));
});
ui.horizontal_top(|ui| {
ui.add_space(6.0);
ui.heading(RichText::new(DEVICES)
.color(COLOR_GRAY)
.color(Colors::GRAY)
.size(16.0));
ui.add_space(4.0);
// Draw peer user-agent
ui.heading(RichText::new(&peer.user_agent)
.color(COLOR_GRAY)
.color(Colors::GRAY)
.size(16.0));
});
ui.add_space(2.0);

View file

@ -15,7 +15,7 @@
use egui::style::Margin;
use egui_extras::{Size, StripBuilder};
use crate::gui::colors::{COLOR_DARK, COLOR_YELLOW};
use crate::gui::Colors;
use crate::gui::views::View;
pub struct TitlePanelAction<'action> {
@ -34,19 +34,17 @@ pub struct TitlePanel {
}
impl TitlePanel {
const PANEL_SIZE: f32 = 52.0;
const PANEL_HEIGHT: f32 = 52.0;
pub fn new(title: &String) -> Self {
pub fn new(title: String) -> Self {
Self { title: title.to_uppercase() }
}
pub fn ui(&self, l: Option<TitlePanelAction>, r: Option<TitlePanelAction>, ui: &mut egui::Ui) {
let Self { title } = self;
egui::TopBottomPanel::top("title_panel")
.resizable(false)
.frame(egui::Frame {
fill: COLOR_YELLOW,
fill: Colors::YELLOW,
inner_margin: Margin::same(0.0),
outer_margin: Margin::same(0.0),
stroke: egui::Stroke::NONE,
@ -54,40 +52,44 @@ impl TitlePanel {
})
.show_inside(ui, |ui| {
StripBuilder::new(ui)
.size(Size::exact(Self::PANEL_SIZE))
.size(Size::exact(Self::PANEL_HEIGHT))
.vertical(|mut strip| {
strip.strip(|builder| {
builder
.size(Size::exact(Self::PANEL_SIZE))
.size(Size::exact(Self::PANEL_HEIGHT))
.size(Size::remainder())
.size(Size::exact(Self::PANEL_SIZE))
.size(Size::exact(Self::PANEL_HEIGHT))
.horizontal(|mut strip| {
strip.cell(|ui| {
show_action(ui, l.as_ref());
self.draw_action(ui, l);
});
strip.cell(|ui| {
ui.centered_and_justified(|ui| {
View::ellipsize_text(ui, title.into(), 20.0, COLOR_DARK);
});
self.draw_title(ui);
});
strip.cell(|ui| {
show_action(ui, r.as_ref());
self.draw_action(ui, r);
});
});
});
});
});
}
}
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)();
fn draw_action(&self, 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)();
});
});
}
}
fn draw_title(&self, ui: &mut egui::Ui) {
let Self { title } = self;
ui.centered_and_justified(|ui| {
View::ellipsize_text(ui, title.into(), 20.0, Colors::TITLE);
});
}
}

View file

@ -12,19 +12,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use egui::epaint::{Color32, FontId, Rounding, Stroke};
use egui::text::{LayoutJob, TextFormat};
use egui::{Button, PointerState, Response, RichText, Sense, Vec2, Widget};
use egui::epaint::{Color32, FontId, Rounding, Stroke};
use egui::epaint::text::TextWrapping;
use egui::text::{LayoutJob, TextFormat};
use egui_extras::{Size, StripBuilder};
use crate::gui::colors::{COLOR_DARK, COLOR_GRAY, COLOR_LIGHT, COLOR_GRAY_LIGHT, COLOR_GRAY_DARK, COLOR_YELLOW};
use crate::gui::Colors;
pub struct View;
impl View {
/// Default stroke around views.
pub const DEFAULT_STROKE: Stroke = Stroke { width: 1.0, color: Color32::from_gray(190) };
pub const DEFAULT_STROKE: Stroke = Stroke { width: 1.0, color: Colors::STROKE };
/// Default width of side panel at application UI.
pub const SIDE_PANEL_MIN_WIDTH: i64 = 400;
@ -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(COLOR_GRAY_DARK));
ui.label(RichText::new(text.to_uppercase()).size(16.0).color(Colors::SUB_TITLE));
}
/// Temporary button click optimization for touch screens.
@ -77,9 +77,9 @@ impl View {
// Disable stroke around title buttons on hover
ui.style_mut().visuals.widgets.active.bg_stroke = Stroke::NONE;
let wt = RichText::new(icon.to_string()).size(24.0).color(COLOR_DARK);
let wt = RichText::new(icon.to_string()).size(24.0).color(Colors::TITLE);
let br = Button::new(wt)
.fill(Color32::TRANSPARENT)
.fill(Colors::TRANSPARENT)
.ui(ui).interact(Sense::click_and_drag());
Self::on_button_click(ui, br, action);
@ -89,8 +89,8 @@ impl View {
/// Tab button with white background fill color, contains only icon.
pub fn tab_button(ui: &mut egui::Ui, icon: &str, active: bool, action: impl FnOnce()) {
let text_color = match active {
true => { COLOR_GRAY_DARK }
false => { COLOR_DARK }
true => { Colors::TITLE }
false => { Colors::SUB_TITLE }
};
let wt = RichText::new(icon.to_string()).size(24.0).color(text_color);
@ -100,8 +100,8 @@ impl View {
};
let color = match active {
true => { COLOR_LIGHT }
false => { Color32::WHITE }
true => { Colors::FILL }
false => { Colors::WHITE }
};
let br = Button::new(wt)
.stroke(stroke)
@ -113,7 +113,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 wt = RichText::new(text.to_uppercase()).size(18.0).color(COLOR_GRAY_DARK);
let wt = RichText::new(text.to_uppercase()).size(18.0).color(Colors::BUTTON);
let br = Button::new(wt)
.stroke(Self::DEFAULT_STROKE)
.fill(fill_color)
@ -139,8 +139,8 @@ impl View {
sw: if r[2] { 8.0 } else { 0.0 },
se: if r[3] { 8.0 } else { 0.0 },
},
Color32::WHITE,
Stroke { width: 1.0, color: COLOR_GRAY_LIGHT },
Colors::WHITE,
Stroke { width: 1.0, color: Colors::ITEM_STROKE },
);
ui.vertical_centered_justified(|ui| {
@ -150,7 +150,7 @@ impl View {
// Draw box value
let mut job = LayoutJob::single_section(value, TextFormat {
font_id: FontId::proportional(18.0),
color: Color32::BLACK,
color: Colors::BLACK,
.. Default::default()
});
job.wrap = TextWrapping {
@ -162,7 +162,7 @@ impl View {
ui.label(job);
// Draw box label
ui.label(RichText::new(label).color(COLOR_GRAY).size(15.0));
ui.label(RichText::new(label).color(Colors::GRAY).size(15.0));
});
}