ui: display insets (cutouts) refactoring

This commit is contained in:
ardocrat 2023-07-16 11:23:56 +03:00
parent d3d7ca3159
commit 468783a795
7 changed files with 139 additions and 143 deletions

View file

@ -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) {

View file

@ -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<Platform> PlatformApp<Platform> {
impl<Platform: PlatformCallbacks> eframe::App for PlatformApp<Platform> {
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<Platform: PlatformCallbacks> eframe::App for PlatformApp<Platform> {
}
}
/// 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);
}

View file

@ -42,7 +42,7 @@ impl Accounts {
None
}, TitleAction::new(PLUS, || {
//TODO: add account
}), ui);
}), ui, frame);
egui::CentralPanel::default()
.frame(egui::Frame {

View file

@ -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.

View file

@ -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.

View file

@ -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<TitleAction>, r: Option<TitleAction>, ui: &mut egui::Ui) {
pub fn ui(title: TitleType,
left_action: Option<TitleAction>,
right_action: Option<TitleAction>,
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<TitleAction>) {
fn action_ui(ui: &mut egui::Ui, action: Option<TitleAction>) {
if action.is_some() {
let action = action.unwrap();
ui.centered_and_justified(|ui| {

View file

@ -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);
}