diff --git a/app/src/main/java/mw/gri/android/MainActivity.java b/app/src/main/java/mw/gri/android/MainActivity.java index 49df7de..4723ee5 100644 --- a/app/src/main/java/mw/gri/android/MainActivity.java +++ b/app/src/main/java/mw/gri/android/MainActivity.java @@ -73,19 +73,19 @@ public class MainActivity extends GameActivity { Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); // Setup values to pass into native code. - int[] cutouts = new int[]{0, 0, 0, 0}; - cutouts[0] = Utils.pxToDp(Integer.max(cutoutTop, systemBars.top), this); - cutouts[1] = Utils.pxToDp(Integer.max(cutoutRight, systemBars.right), this); - cutouts[2] = Utils.pxToDp(Integer.max(cutoutBottom, systemBars.bottom), this); - cutouts[3] = Utils.pxToDp(Integer.max(cutoutLeft, systemBars.left), this); - onDisplayCutouts(cutouts); + int[] values = new int[]{0, 0, 0, 0}; + values[0] = Utils.pxToDp(Integer.max(cutoutTop, systemBars.top), this); + values[1] = Utils.pxToDp(Integer.max(cutoutRight, systemBars.right), this); + values[2] = Utils.pxToDp(Integer.max(cutoutBottom, systemBars.bottom), this); + values[3] = Utils.pxToDp(Integer.max(cutoutLeft, systemBars.left), this); + onDisplayInsets(values); return insets; }); } - // Implemented into native code to handle display cutouts change. - native void onDisplayCutouts(int[] cutouts); + // Implemented into native code to handle display insets change. + native void onDisplayInsets(int[] cutouts); @Override public boolean onKeyDown(int keyCode, KeyEvent event) { diff --git a/src/gui/app.rs b/src/gui/app.rs index 8ea6a06..eeeeada 100644 --- a/src/gui/app.rs +++ b/src/gui/app.rs @@ -12,10 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::sync::atomic::{AtomicI32, Ordering}; - use egui::Context; -use lazy_static::lazy_static; use crate::gui::Colors; use crate::gui::platform::PlatformCallbacks; @@ -37,9 +34,6 @@ impl PlatformApp { impl eframe::App for PlatformApp { fn update(&mut self, ctx: &Context, frame: &mut eframe::Frame) { - // Show panels to support display cutouts (insets). - padding_panels(ctx); - // Show main content. egui::CentralPanel::default() .frame(egui::Frame { @@ -57,102 +51,5 @@ impl eframe::App for PlatformApp { } } -/// Draw panels to support display cutouts (insets). -fn padding_panels(ctx: &Context) { - egui::TopBottomPanel::top("top_padding_panel") - .frame(egui::Frame { - inner_margin: egui::style::Margin::same(0.0), - fill: Colors::YELLOW, - ..Default::default() - }) - .show_separator_line(false) - .resizable(false) - .exact_height(get_top_display_cutout()) - .show(ctx, |_ui| {}); - egui::TopBottomPanel::bottom("bottom_padding_panel") - .frame(egui::Frame { - inner_margin: egui::style::Margin::same(0.0), - fill: Colors::BLACK, - ..Default::default() - }) - .show_separator_line(false) - .resizable(false) - .exact_height(get_bottom_display_cutout()) - .show(ctx, |_ui| {}); - - egui::SidePanel::right("right_padding_panel") - .frame(egui::Frame { - inner_margin: egui::style::Margin::same(0.0), - fill: Colors::YELLOW, - ..Default::default() - }) - .show_separator_line(false) - .resizable(false) - .max_width(get_right_display_cutout()) - .show(ctx, |_ui| {}); - - egui::SidePanel::left("left_padding_panel") - .frame(egui::Frame { - inner_margin: egui::style::Margin::same(0.0), - fill: Colors::YELLOW, - ..Default::default() - }) - .show_separator_line(false) - .resizable(false) - .max_width(get_left_display_cutout()) - .show(ctx, |_ui| {}); -} - -/// Get top display cutout (inset) size. -pub fn get_top_display_cutout() -> f32 { - TOP_DISPLAY_CUTOUT.load(Ordering::Relaxed) as f32 -} - -/// Get right display cutout (inset) size. -pub fn get_right_display_cutout() -> f32 { - RIGHT_DISPLAY_CUTOUT.load(Ordering::Relaxed) as f32 -} - -/// Get bottom display cutout (inset) size. -pub fn get_bottom_display_cutout() -> f32 { - BOTTOM_DISPLAY_CUTOUT.load(Ordering::Relaxed) as f32 -} - -/// Get left display cutout (inset) size. -pub fn get_left_display_cutout() -> f32 { - LEFT_DISPLAY_CUTOUT.load(Ordering::Relaxed) as f32 -} - -/// Fields to handle platform-specific display cutouts (insets). -lazy_static! { - static ref TOP_DISPLAY_CUTOUT: AtomicI32 = AtomicI32::new(0); - static ref RIGHT_DISPLAY_CUTOUT: AtomicI32 = AtomicI32::new(0); - static ref BOTTOM_DISPLAY_CUTOUT: AtomicI32 = AtomicI32::new(0); - static ref LEFT_DISPLAY_CUTOUT: AtomicI32 = AtomicI32::new(0); -} - -#[allow(dead_code)] -#[cfg(target_os = "android")] -#[allow(non_snake_case)] -#[no_mangle] -/// Callback from Java code to update display cutouts (insets). -pub extern "C" fn Java_mw_gri_android_MainActivity_onDisplayCutouts( - _env: jni::JNIEnv, - _class: jni::objects::JObject, - cutouts: jni::sys::jarray -) { - use jni::objects::{JObject, JPrimitiveArray}; - - let mut array: [i32; 4] = [0; 4]; - unsafe { - let j_obj = JObject::from_raw(cutouts); - let j_arr = JPrimitiveArray::from(j_obj); - _env.get_int_array_region(j_arr, 0, array.as_mut()).unwrap(); - } - TOP_DISPLAY_CUTOUT.store(array[0], Ordering::Relaxed); - RIGHT_DISPLAY_CUTOUT.store(array[1], Ordering::Relaxed); - BOTTOM_DISPLAY_CUTOUT.store(array[2], Ordering::Relaxed); - LEFT_DISPLAY_CUTOUT.store(array[3], Ordering::Relaxed); -} diff --git a/src/gui/views/accounts/accounts.rs b/src/gui/views/accounts/accounts.rs index 622280a..1df3fe5 100644 --- a/src/gui/views/accounts/accounts.rs +++ b/src/gui/views/accounts/accounts.rs @@ -42,7 +42,7 @@ impl Accounts { None }, TitleAction::new(PLUS, || { //TODO: add account - }), ui); + }), ui, frame); egui::CentralPanel::default() .frame(egui::Frame { diff --git a/src/gui/views/network/network.rs b/src/gui/views/network/network.rs index 1ac6390..34e9ffe 100644 --- a/src/gui/views/network/network.rs +++ b/src/gui/views/network/network.rs @@ -12,16 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -use egui::{Color32, lerp, Rgba, RichText}; +use egui::RichText; use egui::style::Margin; -use egui_extras::{Size, StripBuilder}; -use grin_chain::SyncStatus; 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, TitleType, TitlePanel, View}; +use crate::gui::views::{Modal, ModalContainer, NetworkMetrics, NetworkMining, NetworkNode, NetworkSettings, Root, TitleAction, TitlePanel, TitleType, View}; use crate::gui::views::network::setup::{DandelionSetup, NodeSetup, P2PSetup, PoolSetup, StratumSetup}; use crate::node::Node; @@ -122,7 +120,7 @@ impl Network { egui::TopBottomPanel::bottom("network_tabs") .frame(egui::Frame { fill: Colors::FILL, - inner_margin: Margin::same(4.0), + inner_margin: Self::tabs_inner_margin(ui, frame), ..Default::default() }) .show_inside(ui, |ui| { @@ -132,12 +130,7 @@ impl Network { egui::CentralPanel::default() .frame(egui::Frame { stroke: View::DEFAULT_STROKE, - inner_margin: Margin { - left: 4.0, - right: 4.0, - top: 3.0, - bottom: 4.0, - }, + inner_margin: Self::content_inner_margin(ui, frame), fill: Colors::WHITE, ..Default::default() }) @@ -151,6 +144,26 @@ impl Network { } } + /// Calculate tabs inner margin based on display insets (cutouts). + fn tabs_inner_margin(ui: &mut egui::Ui, frame: &mut eframe::Frame) -> Margin { + Margin { + left: View::far_left_inset_margin(ui) + 4.0, + right: View::far_right_inset_margin(ui, frame) + 4.0, + top: 4.0, + bottom: View::get_bottom_inset() + 4.0, + } + } + + /// Calculate content inner margin based on display insets (cutouts). + fn content_inner_margin(ui: &mut egui::Ui, frame: &mut eframe::Frame) -> Margin { + Margin { + left: View::far_left_inset_margin(ui) + 4.0, + right: View::far_right_inset_margin(ui, frame) + 4.0, + top: 3.0, + bottom: 4.0, + } + } + /// Draw tab buttons in the bottom of the screen. fn tabs_ui(&mut self, ui: &mut egui::Ui) { ui.scope(|ui| { @@ -202,7 +215,7 @@ impl Network { }) } else { None - }, ui); + }, ui, frame); } /// Content to draw when node is disabled. diff --git a/src/gui/views/root.rs b/src/gui/views/root.rs index 66e753b..c21e1f7 100644 --- a/src/gui/views/root.rs +++ b/src/gui/views/root.rs @@ -11,14 +11,11 @@ // 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::cmp::min; use std::sync::atomic::{AtomicBool, Ordering}; use egui::os::OperatingSystem; use egui::RichText; use lazy_static::lazy_static; -use crate::gui::app::{get_left_display_cutout, get_right_display_cutout}; use crate::gui::Colors; use crate::gui::platform::PlatformCallbacks; @@ -75,7 +72,7 @@ impl Root { pub const EXIT_MODAL_ID: &'static str = "exit_confirmation"; /// Default width of side panel at application UI. - pub const SIDE_PANEL_MIN_WIDTH: i64 = 400; + pub const SIDE_PANEL_MIN_WIDTH: f32 = 400.0; pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) { // Show opened exit confirmation Modal content. @@ -83,20 +80,20 @@ impl Root { self.exit_modal_content(ui, frame, cb); } - // Show network content on side panel. let (is_panel_open, panel_width) = Self::side_panel_state_width(frame); egui::SidePanel::left("network_panel") .resizable(false) .exact_width(panel_width) .frame(egui::Frame::none()) .show_animated_inside(ui, is_panel_open, |ui| { + // Show network content on side panel. self.side_panel.ui(ui, frame, cb); }); - // Show accounts content on central panel. egui::CentralPanel::default() .frame(egui::Frame::none()) .show_inside(ui, |ui| { + // Show accounts content on central panel. self.central_content.ui(ui, frame, cb); }); } @@ -105,12 +102,10 @@ impl Root { fn side_panel_state_width(frame: &mut eframe::Frame) -> (bool, f32) { let dual_panel_mode = Self::is_dual_panel_mode(frame); let is_panel_open = dual_panel_mode || Self::is_side_panel_open(); - let side_cutouts = get_left_display_cutout() + get_right_display_cutout(); let panel_width = if dual_panel_mode { - let available_width = (frame.info().window_info.size.x - side_cutouts) as i64; - min(available_width, Self::SIDE_PANEL_MIN_WIDTH) as f32 + Self::SIDE_PANEL_MIN_WIDTH + View::get_left_inset() } else { - frame.info().window_info.size.x - side_cutouts + frame.info().window_info.size.x }; (is_panel_open, panel_width) } @@ -122,9 +117,9 @@ impl Root { // Screen is wide if width is greater than height or just 20% smaller. let is_wide_screen = w > h || w + (w * 0.2) >= h; // Dual panel mode is available when window is wide and its width is at least 2 times - // greater than minimal width of the side panel plus display cutouts from both sides. - let side_cutouts = get_left_display_cutout() + get_right_display_cutout(); - is_wide_screen && w >= (Self::SIDE_PANEL_MIN_WIDTH as f32 * 2.0) + side_cutouts + // greater than minimal width of the side panel plus display insets from both sides. + let side_insets = View::get_left_inset() + View::get_right_inset(); + is_wide_screen && w >= (Self::SIDE_PANEL_MIN_WIDTH * 2.0) + side_insets } /// Toggle [`Network`] panel state. diff --git a/src/gui/views/title_panel.rs b/src/gui/views/title_panel.rs index 40fe6ab..80f3d55 100644 --- a/src/gui/views/title_panel.rs +++ b/src/gui/views/title_panel.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use egui::{Color32, Id, lerp, Rgba, RichText}; +use egui::{Color32, Id, lerp, Rgba}; use egui::style::Margin; use egui_extras::{Size, StripBuilder}; @@ -43,7 +43,12 @@ pub struct TitlePanel; impl TitlePanel { pub const DEFAULT_HEIGHT: f32 = 54.0; - pub fn ui(title: TitleType, l: Option, r: Option, ui: &mut egui::Ui) { + pub fn ui(title: TitleType, + left_action: Option, + right_action: Option, + ui: &mut egui::Ui, + frame: &mut eframe::Frame) { + // Setup identifier. let id = match &title { TitleType::Single(text) => Id::from(text.clone()), TitleType::WithSubTitle(text, _, _) => Id::from(text.clone()) @@ -52,7 +57,7 @@ impl TitlePanel { .resizable(false) .exact_height(Self::DEFAULT_HEIGHT) .frame(egui::Frame { - outer_margin: Margin::same(-1.0), + inner_margin: Self::inner_margin(ui, frame), fill: Colors::YELLOW, ..Default::default() }) @@ -63,7 +68,7 @@ impl TitlePanel { .size(Size::exact(Self::DEFAULT_HEIGHT)) .horizontal(|mut strip| { strip.cell(|ui| { - Self::draw_action(ui, l); + Self::action_ui(ui, left_action); }); match title { TitleType::Single(text) => { @@ -81,14 +86,24 @@ impl TitlePanel { } } strip.cell(|ui| { - Self::draw_action(ui, r); + Self::action_ui(ui, right_action); }); }); }); } + /// Calculate inner margin based on display insets (cutouts). + fn inner_margin(ui: &mut egui::Ui, frame: &mut eframe::Frame) -> Margin { + Margin { + left: View::far_left_inset_margin(ui), + right: View::far_right_inset_margin(ui, frame), + top: View::get_top_inset(), + bottom: 0.0, + } + } + /// Draw panel [`TitleAction`]. - fn draw_action(ui: &mut egui::Ui, action: Option) { + fn action_ui(ui: &mut egui::Ui, action: Option) { if action.is_some() { let action = action.unwrap(); ui.centered_and_justified(|ui| { diff --git a/src/gui/views/views.rs b/src/gui/views/views.rs index 93d84e8..a48ac36 100644 --- a/src/gui/views/views.rs +++ b/src/gui/views/views.rs @@ -12,10 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::sync::atomic::{AtomicI32, Ordering}; + use egui::{Button, PointerState, Response, RichText, Sense, Spinner, Widget}; use egui::epaint::{Color32, FontId, RectShape, Rounding, Stroke}; use egui::epaint::text::TextWrapping; use egui::text::{LayoutJob, TextFormat}; +use lazy_static::lazy_static; use crate::gui::Colors; use crate::gui::icons::{CHECK_SQUARE, SQUARE}; @@ -26,6 +29,26 @@ impl View { /// Default stroke around views. pub const DEFAULT_STROKE: Stroke = Stroke { width: 1.0, color: Colors::STROKE }; + /// Calculate margin for far left view based on display insets (cutouts). + pub fn far_left_inset_margin(ui: &mut egui::Ui) -> f32 { + if ui.available_rect_before_wrap().min.x == 0.0 { + Self::get_left_inset() + } else { + 0.0 + } + } + + /// Calculate margin for far left view based on display insets (cutouts). + pub fn far_right_inset_margin(ui: &mut egui::Ui, frame: &mut eframe::Frame) -> f32 { + let container_width = ui.available_rect_before_wrap().max.x as i32; + let display_width = frame.info().window_info.size.x as i32; + if container_width == display_width { + Self::get_right_inset() + } else { + 0.0 + } + } + /// Cut long text with ﹍ character. fn ellipsize(text: String, size: f32, color: Color32) -> LayoutJob { let mut job = LayoutJob::single_section(text, TextFormat { @@ -238,4 +261,57 @@ impl View { painter.round_to_pixel(line_rect.center().y), Stroke { width: 1.0, color }); } + + /// Get top display inset (cutout) size. + pub fn get_top_inset() -> f32 { + TOP_DISPLAY_INSET.load(Ordering::Relaxed) as f32 + } + + /// Get right display inset (cutout) size. + pub fn get_right_inset() -> f32 { + RIGHT_DISPLAY_INSET.load(Ordering::Relaxed) as f32 + } + + /// Get bottom display inset (cutout) size. + pub fn get_bottom_inset() -> f32 { + BOTTOM_DISPLAY_INSET.load(Ordering::Relaxed) as f32 + } + + /// Get left display inset (cutout) size. + pub fn get_left_inset() -> f32 { + LEFT_DISPLAY_INSET.load(Ordering::Relaxed) as f32 + } + +} + +/// Fields to handle platform-specific display insets (cutouts). +lazy_static! { + static ref TOP_DISPLAY_INSET: AtomicI32 = AtomicI32::new(0); + static ref RIGHT_DISPLAY_INSET: AtomicI32 = AtomicI32::new(0); + static ref BOTTOM_DISPLAY_INSET: AtomicI32 = AtomicI32::new(0); + static ref LEFT_DISPLAY_INSET: AtomicI32 = AtomicI32::new(0); +} + +#[allow(dead_code)] +#[cfg(target_os = "android")] +#[allow(non_snake_case)] +#[no_mangle] +/// Callback from Java code to update display insets (cutouts). +pub extern "C" fn Java_mw_gri_android_MainActivity_onDisplayInsets( + _env: jni::JNIEnv, + _class: jni::objects::JObject, + cutouts: jni::sys::jarray +) { + use jni::objects::{JObject, JPrimitiveArray}; + + let mut array: [i32; 4] = [0; 4]; + unsafe { + let j_obj = JObject::from_raw(cutouts); + let j_arr = JPrimitiveArray::from(j_obj); + _env.get_int_array_region(j_arr, 0, array.as_mut()).unwrap(); + } + TOP_DISPLAY_INSET.store(array[0], Ordering::Relaxed); + RIGHT_DISPLAY_INSET.store(array[1], Ordering::Relaxed); + BOTTOM_DISPLAY_INSET.store(array[2], Ordering::Relaxed); + LEFT_DISPLAY_INSET.store(array[3], Ordering::Relaxed); } \ No newline at end of file