ui: move global modal to root screen, refactor rounded box background painting, optimize center content
This commit is contained in:
parent
5143a39700
commit
f95645ea81
8 changed files with 134 additions and 126 deletions
|
@ -12,15 +12,12 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use egui::{Color32, Context, RichText, Spinner, Stroke, Widget};
|
use egui::{Context, Stroke};
|
||||||
use egui::os::OperatingSystem;
|
use egui::os::OperatingSystem;
|
||||||
use egui::style::Margin;
|
|
||||||
|
|
||||||
use crate::gui::{Colors, Navigator};
|
use crate::gui::Colors;
|
||||||
use crate::gui::platform::PlatformCallbacks;
|
use crate::gui::platform::PlatformCallbacks;
|
||||||
use crate::gui::screens::Root;
|
use crate::gui::screens::Root;
|
||||||
use crate::gui::views::{Modal, ModalId, ModalLocation, View};
|
|
||||||
use crate::node::Node;
|
|
||||||
|
|
||||||
pub struct PlatformApp<Platform> {
|
pub struct PlatformApp<Platform> {
|
||||||
pub(crate) app: App,
|
pub(crate) app: App,
|
||||||
|
@ -30,7 +27,6 @@ pub struct PlatformApp<Platform> {
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct App {
|
pub struct App {
|
||||||
root: Root,
|
root: Root,
|
||||||
show_exit_progress: bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
|
@ -41,71 +37,11 @@ impl App {
|
||||||
.. Default::default()
|
.. Default::default()
|
||||||
})
|
})
|
||||||
.show(ctx, |ui| {
|
.show(ctx, |ui| {
|
||||||
if Navigator::is_modal_open(ModalLocation::Global) {
|
|
||||||
self.show_global_modal(ui, frame, cb);
|
|
||||||
}
|
|
||||||
self.root.ui(ui, frame, cb);
|
self.root.ui(ui, frame, cb);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_global_modal(&mut self,
|
pub fn exit(frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) {
|
||||||
ui: &mut egui::Ui,
|
|
||||||
frame: &mut eframe::Frame,
|
|
||||||
cb: &dyn PlatformCallbacks) {
|
|
||||||
let location = ModalLocation::Global;
|
|
||||||
Navigator::modal_ui(ui, location, |ui, modal| {
|
|
||||||
match modal.id {
|
|
||||||
ModalId::Exit => {
|
|
||||||
if self.show_exit_progress {
|
|
||||||
if !Node::is_running() {
|
|
||||||
Self::exit(frame, cb);
|
|
||||||
modal.close();
|
|
||||||
}
|
|
||||||
ui.add_space(16.0);
|
|
||||||
ui.vertical_centered(|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(Colors::INACTIVE_TEXT)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
ui.add_space(12.0);
|
|
||||||
} else {
|
|
||||||
ui.add_space(8.0);
|
|
||||||
ui.vertical_centered(|ui| {
|
|
||||||
ui.label(t!("modal_exit.description"));
|
|
||||||
});
|
|
||||||
ui.add_space(10.0);
|
|
||||||
// Setup spacing between buttons
|
|
||||||
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"), Colors::WHITE, || {
|
|
||||||
if !Node::is_running() {
|
|
||||||
Self::exit(frame, cb);
|
|
||||||
modal.close();
|
|
||||||
} else {
|
|
||||||
Node::stop();
|
|
||||||
modal.disable_closing();
|
|
||||||
self.show_exit_progress = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
columns[1].vertical_centered_justified(|ui| {
|
|
||||||
View::button(ui, t!("modal.cancel"), Colors::WHITE, || {
|
|
||||||
modal.close();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
ui.add_space(6.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn exit(frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) {
|
|
||||||
match OperatingSystem::from_target_os() {
|
match OperatingSystem::from_target_os() {
|
||||||
OperatingSystem::Android => {
|
OperatingSystem::Android => {
|
||||||
cb.exit();
|
cb.exit();
|
||||||
|
|
|
@ -25,7 +25,7 @@ impl Colors {
|
||||||
pub const GOLD: Color32 = Color32::from_rgb(255, 215, 0);
|
pub const GOLD: Color32 = Color32::from_rgb(255, 215, 0);
|
||||||
pub const FILL: Color32 = Color32::from_gray(240);
|
pub const FILL: Color32 = Color32::from_gray(240);
|
||||||
pub const TITLE: Color32 = Color32::from_gray(60);
|
pub const TITLE: Color32 = Color32::from_gray(60);
|
||||||
pub const SUB_TITLE: Color32 = Color32::from_gray(80);
|
pub const TEXT: Color32 = Color32::from_gray(80);
|
||||||
pub const BUTTON: Color32 = Color32::from_gray(70);
|
pub const BUTTON: Color32 = Color32::from_gray(70);
|
||||||
pub const GRAY: Color32 = Color32::from_gray(120);
|
pub const GRAY: Color32 = Color32::from_gray(120);
|
||||||
pub const STROKE: Color32 = Color32::from_gray(190);
|
pub const STROKE: Color32 = Color32::from_gray(190);
|
||||||
|
|
|
@ -14,14 +14,18 @@
|
||||||
|
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
|
|
||||||
use crate::gui::Navigator;
|
use egui::{RichText, Spinner, Widget};
|
||||||
|
|
||||||
|
use crate::gui::{App, Colors, Navigator};
|
||||||
use crate::gui::platform::PlatformCallbacks;
|
use crate::gui::platform::PlatformCallbacks;
|
||||||
use crate::gui::screens::{Account, Accounts, Screen, ScreenId};
|
use crate::gui::screens::{Account, Accounts, Screen, ScreenId};
|
||||||
use crate::gui::views::{Network, View};
|
use crate::gui::views::{ModalId, ModalLocation, Network, View};
|
||||||
|
use crate::node::Node;
|
||||||
|
|
||||||
pub struct Root {
|
pub struct Root {
|
||||||
screens: Vec<Box<dyn Screen>>,
|
screens: Vec<Box<dyn Screen>>,
|
||||||
network: Network
|
network: Network,
|
||||||
|
show_exit_progress: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Root {
|
impl Default for Root {
|
||||||
|
@ -33,14 +37,19 @@ impl Default for Root {
|
||||||
Box::new(Accounts::default()),
|
Box::new(Accounts::default()),
|
||||||
Box::new(Account::default())
|
Box::new(Account::default())
|
||||||
]),
|
]),
|
||||||
network: Network::default()
|
network: Network::default(),
|
||||||
|
show_exit_progress: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Root {
|
impl Root {
|
||||||
pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) {
|
pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) {
|
||||||
let (is_panel_open, panel_width) = dual_panel_state_width(frame);
|
if Navigator::is_modal_open(ModalLocation::Global) {
|
||||||
|
self.show_global_modal(ui, frame, cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (is_panel_open, panel_width) = self.dual_panel_state_width(frame);
|
||||||
egui::SidePanel::left("network_panel")
|
egui::SidePanel::left("network_panel")
|
||||||
.resizable(false)
|
.resizable(false)
|
||||||
.exact_width(panel_width)
|
.exact_width(panel_width)
|
||||||
|
@ -56,6 +65,63 @@ impl Root {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn show_global_modal(&mut self,
|
||||||
|
ui: &mut egui::Ui,
|
||||||
|
frame: &mut eframe::Frame,
|
||||||
|
cb: &dyn PlatformCallbacks) {
|
||||||
|
let location = ModalLocation::Global;
|
||||||
|
Navigator::modal_ui(ui, location, |ui, modal| {
|
||||||
|
match modal.id {
|
||||||
|
ModalId::Exit => {
|
||||||
|
if self.show_exit_progress {
|
||||||
|
if !Node::is_running() {
|
||||||
|
App::exit(frame, cb);
|
||||||
|
modal.close();
|
||||||
|
}
|
||||||
|
ui.add_space(16.0);
|
||||||
|
ui.vertical_centered(|ui| {
|
||||||
|
Spinner::new().size(48.0).color(Colors::GRAY).ui(ui);
|
||||||
|
ui.add_space(12.0);
|
||||||
|
ui.label(RichText::new(t!("sync_status.shutdown"))
|
||||||
|
.size(17.0)
|
||||||
|
.color(Colors::TEXT)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
ui.add_space(10.0);
|
||||||
|
} else {
|
||||||
|
ui.add_space(8.0);
|
||||||
|
ui.vertical_centered(|ui| {
|
||||||
|
ui.label(t!("modal_exit.description"));
|
||||||
|
});
|
||||||
|
ui.add_space(10.0);
|
||||||
|
// Setup spacing between buttons
|
||||||
|
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"), Colors::WHITE, || {
|
||||||
|
if !Node::is_running() {
|
||||||
|
App::exit(frame, cb);
|
||||||
|
modal.close();
|
||||||
|
} else {
|
||||||
|
Node::stop();
|
||||||
|
modal.disable_closing();
|
||||||
|
self.show_exit_progress = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
columns[1].vertical_centered_justified(|ui| {
|
||||||
|
View::button(ui, t!("modal.cancel"), Colors::WHITE, || {
|
||||||
|
modal.close();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
ui.add_space(6.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn show_current_screen(&mut self,
|
fn show_current_screen(&mut self,
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
frame: &mut eframe::Frame,
|
frame: &mut eframe::Frame,
|
||||||
|
@ -68,18 +134,18 @@ impl Root {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Get dual panel state and width
|
/// Get dual panel state and width
|
||||||
fn dual_panel_state_width(frame: &mut eframe::Frame) -> (bool, f32) {
|
fn dual_panel_state_width(&self, frame: &mut eframe::Frame) -> (bool, f32) {
|
||||||
let dual_panel_mode = View::is_dual_panel_mode(frame);
|
let dual_panel_mode = View::is_dual_panel_mode(frame);
|
||||||
let is_panel_open = dual_panel_mode || Navigator::is_side_panel_open();
|
let is_panel_open = dual_panel_mode || Navigator::is_side_panel_open();
|
||||||
let panel_width = if dual_panel_mode {
|
let panel_width = if dual_panel_mode {
|
||||||
min(frame.info().window_info.size.x as i64, View::SIDE_PANEL_MIN_WIDTH) as f32
|
min(frame.info().window_info.size.x as i64, View::SIDE_PANEL_MIN_WIDTH) as f32
|
||||||
} else {
|
} else {
|
||||||
frame.info().window_info.size.x
|
frame.info().window_info.size.x
|
||||||
};
|
};
|
||||||
(is_panel_open, panel_width)
|
(is_panel_open, panel_width)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
|
|
@ -76,7 +76,6 @@ impl Network {
|
||||||
outer_margin: Margin::same(5.0),
|
outer_margin: Margin::same(5.0),
|
||||||
.. Default::default()
|
.. Default::default()
|
||||||
})
|
})
|
||||||
.resizable(false)
|
|
||||||
.show_inside(ui, |ui| {
|
.show_inside(ui, |ui| {
|
||||||
self.draw_tabs(ui);
|
self.draw_tabs(ui);
|
||||||
});
|
});
|
||||||
|
@ -219,7 +218,7 @@ impl Network {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Draw sync text
|
// Draw sync text
|
||||||
let status_color_rgba = Rgba::from(Colors::SUB_TITLE) * color_factor;
|
let status_color_rgba = Rgba::from(Colors::TEXT) * color_factor;
|
||||||
let status_color = Color32::from(status_color_rgba);
|
let status_color = Color32::from(status_color_rgba);
|
||||||
View::ellipsize_text(ui, Node::get_sync_status_text(), 15.0, status_color);
|
View::ellipsize_text(ui, Node::get_sync_status_text(), 15.0, status_color);
|
||||||
|
|
||||||
|
@ -234,9 +233,9 @@ impl Network {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn server_off_content(ui: &mut egui::Ui) {
|
pub fn disabled_server_content(ui: &mut egui::Ui) {
|
||||||
View::center_content(ui, [ui.available_width() - 48.0, 160.0], |ui| {
|
View::center_content(ui, 142.0, |ui| {
|
||||||
let text = t!("network.inactive_message","dots" => DOTS_THREE_OUTLINE_VERTICAL);
|
let text = t!("network.disabled_server", "dots" => DOTS_THREE_OUTLINE_VERTICAL);
|
||||||
ui.label(RichText::new(text)
|
ui.label(RichText::new(text)
|
||||||
.size(16.0)
|
.size(16.0)
|
||||||
.color(Colors::INACTIVE_TEXT)
|
.color(Colors::INACTIVE_TEXT)
|
||||||
|
|
|
@ -39,14 +39,14 @@ impl NetworkTab for NetworkMetrics {
|
||||||
let server_stats = Node::get_stats();
|
let server_stats = Node::get_stats();
|
||||||
if server_stats.is_none() || server_stats.as_ref().unwrap().diff_stats.height == 0 {
|
if server_stats.is_none() || server_stats.as_ref().unwrap().diff_stats.height == 0 {
|
||||||
if !Node::is_running() {
|
if !Node::is_running() {
|
||||||
Network::server_off_content(ui);
|
Network::disabled_server_content(ui);
|
||||||
} else {
|
} else {
|
||||||
View::center_content(ui, [280.0, 160.0], |ui| {
|
View::center_content(ui, 160.0, |ui| {
|
||||||
Spinner::new().size(104.0).color(Colors::GOLD).ui(ui);
|
Spinner::new().size(104.0).color(Colors::GOLD).ui(ui);
|
||||||
ui.add_space(18.0);
|
ui.add_space(18.0);
|
||||||
ui.label(RichText::new(t!("network_metrics.loading"))
|
ui.label(RichText::new(t!("network_metrics.loading"))
|
||||||
.size(16.0)
|
.size(16.0)
|
||||||
.color(Colors::INACTIVE_TEXT)
|
.color(Colors::TEXT)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ impl NetworkTab for NetworkMining {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ui(&mut self, ui: &mut Ui) {
|
fn ui(&mut self, ui: &mut Ui) {
|
||||||
todo!()
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -35,7 +35,7 @@ impl NetworkTab for NetworkNode {
|
||||||
let server_stats = Node::get_stats();
|
let server_stats = Node::get_stats();
|
||||||
if !server_stats.is_some() {
|
if !server_stats.is_some() {
|
||||||
if !Node::is_running() {
|
if !Node::is_running() {
|
||||||
Network::server_off_content(ui);
|
Network::disabled_server_content(ui);
|
||||||
} else {
|
} else {
|
||||||
ui.centered_and_justified(|ui| {
|
ui.centered_and_justified(|ui| {
|
||||||
Spinner::new().size(104.0).color(Colors::GOLD).ui(ui);
|
Spinner::new().size(104.0).color(Colors::GOLD).ui(ui);
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use egui::{Button, PointerState, Response, RichText, Sense, Vec2, Widget};
|
use egui::{Button, PointerState, Response, RichText, Sense, Vec2, Widget};
|
||||||
use egui::epaint::{Color32, FontId, Rounding, Stroke};
|
use egui::epaint::{Color32, FontId, RectShape, Rounding, Stroke};
|
||||||
use egui::epaint::text::TextWrapping;
|
use egui::epaint::text::TextWrapping;
|
||||||
use egui::text::{LayoutJob, TextFormat};
|
use egui::text::{LayoutJob, TextFormat};
|
||||||
use egui_extras::{Size, StripBuilder};
|
use egui_extras::{Size, StripBuilder};
|
||||||
|
@ -56,7 +56,7 @@ impl View {
|
||||||
|
|
||||||
/// Sub-header with uppercase characters and more lighter color.
|
/// Sub-header with uppercase characters and more lighter color.
|
||||||
pub fn sub_header(ui: &mut egui::Ui, text: String) {
|
pub fn sub_header(ui: &mut egui::Ui, text: String) {
|
||||||
ui.label(RichText::new(text.to_uppercase()).size(16.0).color(Colors::SUB_TITLE));
|
ui.label(RichText::new(text.to_uppercase()).size(16.0).color(Colors::TEXT));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Temporary button click optimization for touch screens.
|
/// Temporary button click optimization for touch screens.
|
||||||
|
@ -90,7 +90,7 @@ impl View {
|
||||||
pub fn tab_button(ui: &mut egui::Ui, icon: &str, active: bool, action: impl FnOnce()) {
|
pub fn tab_button(ui: &mut egui::Ui, icon: &str, active: bool, action: impl FnOnce()) {
|
||||||
let text_color = match active {
|
let text_color = match active {
|
||||||
true => { Colors::TITLE }
|
true => { Colors::TITLE }
|
||||||
false => { Colors::SUB_TITLE }
|
false => { Colors::TEXT }
|
||||||
};
|
};
|
||||||
let wt = RichText::new(icon.to_string()).size(24.0).color(text_color);
|
let wt = RichText::new(icon.to_string()).size(24.0).color(text_color);
|
||||||
|
|
||||||
|
@ -128,52 +128,58 @@ impl View {
|
||||||
/// | label |
|
/// | label |
|
||||||
pub fn rounded_box(ui: &mut egui::Ui, value: String, label: String, r: [bool; 4]) {
|
pub fn rounded_box(ui: &mut egui::Ui, value: String, label: String, r: [bool; 4]) {
|
||||||
let mut rect = ui.available_rect_before_wrap();
|
let mut rect = ui.available_rect_before_wrap();
|
||||||
rect.set_height(46.0);
|
|
||||||
|
|
||||||
// Draw box background
|
// Create background shape.
|
||||||
ui.painter().rect(
|
let mut bg_shape = RectShape {
|
||||||
rect,
|
rect,
|
||||||
Rounding {
|
rounding: Rounding {
|
||||||
nw: if r[0] { 8.0 } else { 0.0 },
|
nw: if r[0] { 8.0 } else { 0.0 },
|
||||||
ne: if r[1] { 8.0 } else { 0.0 },
|
ne: if r[1] { 8.0 } else { 0.0 },
|
||||||
sw: if r[2] { 8.0 } else { 0.0 },
|
sw: if r[2] { 8.0 } else { 0.0 },
|
||||||
se: if r[3] { 8.0 } else { 0.0 },
|
se: if r[3] { 8.0 } else { 0.0 },
|
||||||
},
|
},
|
||||||
Colors::WHITE,
|
fill: Colors::WHITE,
|
||||||
Stroke { width: 1.0, color: Colors::ITEM_STROKE },
|
stroke: Stroke { width: 1.0, color: Colors::ITEM_STROKE },
|
||||||
);
|
};
|
||||||
|
let bg_idx = ui.painter().add(bg_shape);
|
||||||
|
|
||||||
ui.vertical_centered_justified(|ui| {
|
// Draw box content.
|
||||||
// Correct vertical spacing between items
|
let content_resp = ui.allocate_ui_at_rect(rect, |ui| {
|
||||||
ui.style_mut().spacing.item_spacing.y = -4.0;
|
ui.vertical_centered_justified(|ui| {
|
||||||
|
// Correct vertical spacing between items.
|
||||||
|
ui.style_mut().spacing.item_spacing.y = -4.0;
|
||||||
|
|
||||||
// Draw box value
|
// Draw box value.
|
||||||
let mut job = LayoutJob::single_section(value, TextFormat {
|
let mut job = LayoutJob::single_section(value, TextFormat {
|
||||||
font_id: FontId::proportional(18.0),
|
font_id: FontId::proportional(18.0),
|
||||||
color: Colors::BLACK,
|
color: Colors::BLACK,
|
||||||
.. Default::default()
|
..Default::default()
|
||||||
|
});
|
||||||
|
job.wrap = TextWrapping {
|
||||||
|
max_rows: 1,
|
||||||
|
break_anywhere: false,
|
||||||
|
overflow_character: Option::from('﹍'),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
ui.label(job);
|
||||||
|
|
||||||
|
// Draw box label.
|
||||||
|
ui.label(RichText::new(label).color(Colors::GRAY).size(15.0));
|
||||||
});
|
});
|
||||||
job.wrap = TextWrapping {
|
}).response;
|
||||||
max_rows: 1,
|
|
||||||
break_anywhere: false,
|
|
||||||
overflow_character: Option::from('﹍'),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
ui.label(job);
|
|
||||||
|
|
||||||
// Draw box label
|
// Setup background shape to be painted behind box content.
|
||||||
ui.label(RichText::new(label).color(Colors::GRAY).size(15.0));
|
bg_shape.rect = content_resp.rect;
|
||||||
});
|
ui.painter().set(bg_idx, bg_shape);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw content in the center of current layout with specified width and height.
|
/// Draw content in the center of current layout with specified width and height.
|
||||||
pub fn center_content(ui: &mut egui::Ui, w_h: [f32; 2], content: impl FnOnce(&mut egui::Ui)) {
|
pub fn center_content(ui: &mut egui::Ui, height: f32, content: impl FnOnce(&mut egui::Ui)) {
|
||||||
ui.vertical_centered(|ui| {
|
ui.vertical_centered(|ui| {
|
||||||
let mut rect = ui.available_rect_before_wrap();
|
let mut rect = ui.available_rect_before_wrap();
|
||||||
let side_margin = (ui.available_width() - w_h[0]) / 2.0;
|
let side_margin = 24.0;
|
||||||
rect.min += egui::emath::vec2(side_margin, ui.available_height() / 2.0 - w_h[1] / 2.0);
|
rect.min += egui::emath::vec2(side_margin, ui.available_height() / 2.0 - height / 2.0);
|
||||||
rect.max -= egui::emath::vec2(side_margin, 0.0);
|
rect.max -= egui::emath::vec2(side_margin, 0.0);
|
||||||
// rect.set_width(w_h[0]);
|
|
||||||
ui.allocate_ui_at_rect(rect, |ui| {
|
ui.allocate_ui_at_rect(rect, |ui| {
|
||||||
(content)(ui);
|
(content)(ui);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue