ui: custom window title, small title button
This commit is contained in:
parent
167b222f60
commit
c7bcd64ae1
7 changed files with 164 additions and 39 deletions
128
src/gui/app.rs
128
src/gui/app.rs
|
@ -14,12 +14,15 @@
|
|||
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use egui::{Context, Modifiers};
|
||||
use egui::{Align, Context, Layout, Modifiers};
|
||||
use egui::os::OperatingSystem;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use crate::AppConfig;
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::{ARROWS_IN, ARROWS_OUT, CARET_DOWN, MOON, SUN, X};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::Root;
|
||||
use crate::gui::views::{Root, View};
|
||||
|
||||
lazy_static! {
|
||||
/// State to check if platform Back button was pressed.
|
||||
|
@ -69,7 +72,14 @@ impl<Platform: PlatformCallbacks> App<Platform> {
|
|||
}
|
||||
}
|
||||
|
||||
// Show main content.
|
||||
// Show main content with custom frame on desktop.
|
||||
let os = OperatingSystem::from_target_os();
|
||||
let custom_window = os != OperatingSystem::Android;
|
||||
if custom_window {
|
||||
custom_window_frame(ctx, |ui| {
|
||||
self.root.ui(ui, &self.platform);
|
||||
});
|
||||
} else {
|
||||
egui::CentralPanel::default()
|
||||
.frame(egui::Frame {
|
||||
..Default::default()
|
||||
|
@ -78,6 +88,7 @@ impl<Platform: PlatformCallbacks> App<Platform> {
|
|||
self.root.ui(ui, &self.platform);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// To draw with egui`s eframe (for wgpu, glow backends and wasm target).
|
||||
|
@ -85,6 +96,117 @@ impl<Platform: PlatformCallbacks> eframe::App for App<Platform> {
|
|||
fn update(&mut self, ctx: &Context, _: &mut eframe::Frame) {
|
||||
self.ui(ctx);
|
||||
}
|
||||
|
||||
fn clear_color(&self, _visuals: &egui::Visuals) -> [f32; 4] {
|
||||
egui::Rgba::TRANSPARENT.to_array()
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw custom window frame for desktop.
|
||||
fn custom_window_frame(ctx: &Context, add_contents: impl FnOnce(&mut egui::Ui)) {
|
||||
let panel_frame = egui::Frame {
|
||||
fill: Colors::yellow_dark(),
|
||||
rounding: egui::Rounding {
|
||||
nw: 8.0,
|
||||
ne: 8.0,
|
||||
sw: 0.0,
|
||||
se: 0.0,
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
egui::CentralPanel::default().frame(panel_frame).show(ctx, |ui| {
|
||||
let app_rect = ui.max_rect();
|
||||
|
||||
let title_bar_height = 38.0;
|
||||
let title_bar_rect = {
|
||||
let mut rect = app_rect;
|
||||
rect.max.y = rect.min.y + title_bar_height;
|
||||
rect
|
||||
};
|
||||
window_title_ui(ui, title_bar_rect);
|
||||
let content_rect = {
|
||||
let mut rect = app_rect;
|
||||
rect.min.y = title_bar_rect.max.y;
|
||||
rect
|
||||
};
|
||||
let mut content_ui = ui.child_ui(content_rect, *ui.layout());
|
||||
add_contents(&mut content_ui);
|
||||
});
|
||||
}
|
||||
|
||||
/// Draw custom window title content.
|
||||
fn window_title_ui(ui: &mut egui::Ui, title_bar_rect: egui::epaint::Rect) {
|
||||
let painter = ui.painter();
|
||||
|
||||
let title_bar_response = ui.interact(
|
||||
title_bar_rect,
|
||||
egui::Id::new("title_bar"),
|
||||
egui::Sense::click_and_drag(),
|
||||
);
|
||||
|
||||
// Paint the title.
|
||||
painter.text(
|
||||
title_bar_rect.center(),
|
||||
egui::Align2::CENTER_CENTER,
|
||||
"Grim 0.1.0",
|
||||
egui::FontId::proportional(15.0),
|
||||
egui::Color32::from_gray(60),
|
||||
);
|
||||
|
||||
// Interact with the title bar (drag to move window):
|
||||
if title_bar_response.double_clicked() {
|
||||
let is_maximized = ui.input(|i| i.viewport().maximized.unwrap_or(false));
|
||||
ui.ctx()
|
||||
.send_viewport_cmd(egui::ViewportCommand::Maximized(!is_maximized));
|
||||
}
|
||||
|
||||
if title_bar_response.drag_started_by(egui::PointerButton::Primary) {
|
||||
ui.ctx().send_viewport_cmd(egui::ViewportCommand::StartDrag);
|
||||
}
|
||||
|
||||
ui.allocate_ui_at_rect(title_bar_rect, |ui| {
|
||||
ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
|
||||
// Draw button to close window.
|
||||
View::title_button_small(ui, X, |_| {
|
||||
Root::show_exit_modal();
|
||||
});
|
||||
|
||||
// Draw fullscreen button.
|
||||
let is_fullscreen = ui.ctx().input(|i| {
|
||||
i.viewport().fullscreen.unwrap_or(false)
|
||||
});
|
||||
let fullscreen_icon = if is_fullscreen {
|
||||
ARROWS_IN
|
||||
} else {
|
||||
ARROWS_OUT
|
||||
};
|
||||
View::title_button_small(ui, fullscreen_icon, |ui| {
|
||||
ui.ctx().send_viewport_cmd(egui::ViewportCommand::Fullscreen(!is_fullscreen));
|
||||
});
|
||||
|
||||
// Draw button to minimize window.
|
||||
View::title_button_small(ui, CARET_DOWN, |ui| {
|
||||
ui.ctx().send_viewport_cmd(egui::ViewportCommand::Minimized(true));
|
||||
});
|
||||
|
||||
// Draw application icon.
|
||||
let layout_size = ui.available_size();
|
||||
ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| {
|
||||
// Draw button to minimize window.
|
||||
let use_dark = AppConfig::dark_theme().unwrap_or(false);
|
||||
let theme_icon = if use_dark {
|
||||
SUN
|
||||
} else {
|
||||
MOON
|
||||
};
|
||||
View::title_button_small(ui, theme_icon, |ui| {
|
||||
AppConfig::set_dark_theme(!use_dark);
|
||||
crate::setup_visuals(ui.ctx());
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
|
|
@ -28,6 +28,7 @@ const DARK_SEMI_TRANSPARENT: Color32 = Color32::from_black_alpha(170);
|
|||
const GOLD: Color32 = Color32::from_rgb(255, 215, 0);
|
||||
|
||||
const YELLOW: Color32 = Color32::from_rgb(254, 241, 2);
|
||||
const YELLOW_DARK: Color32 = Color32::from_rgb(239, 229, 3);
|
||||
|
||||
const GREEN: Color32 = Color32::from_rgb(0, 0x64, 0);
|
||||
|
||||
|
@ -118,6 +119,10 @@ impl Colors {
|
|||
YELLOW
|
||||
}
|
||||
|
||||
pub fn yellow_dark() -> Color32 {
|
||||
YELLOW_DARK
|
||||
}
|
||||
|
||||
pub fn green() -> Color32 {
|
||||
if use_dark() {
|
||||
GREEN.linear_multiply(1.3)
|
||||
|
|
|
@ -202,7 +202,7 @@ impl NetworkContent {
|
|||
// Draw title panel.
|
||||
TitlePanel::ui(TitleType::Single(title_content), |ui| {
|
||||
if !show_connections {
|
||||
View::title_button(ui, DOTS_THREE_OUTLINE_VERTICAL, || {
|
||||
View::title_button_big(ui, DOTS_THREE_OUTLINE_VERTICAL, |_| {
|
||||
AppConfig::toggle_show_connections_network_panel();
|
||||
if AppConfig::show_connections_network_panel() {
|
||||
ExternalConnection::start_ext_conn_availability_check();
|
||||
|
@ -211,7 +211,7 @@ impl NetworkContent {
|
|||
}
|
||||
}, |ui| {
|
||||
if !Root::is_dual_panel_mode(ui) {
|
||||
View::title_button(ui, BRIEFCASE, || {
|
||||
View::title_button_big(ui, BRIEFCASE, |_| {
|
||||
Root::toggle_network_panel();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -113,14 +113,8 @@ impl Root {
|
|||
..Default::default()
|
||||
})
|
||||
.show_animated_inside(ui, is_panel_open, |ui| {
|
||||
// Set content height as window height.
|
||||
let mut rect = ui.available_rect_before_wrap();
|
||||
let window_size = View::window_size(ui);
|
||||
rect.set_height(window_size.1);
|
||||
ui.allocate_ui_at_rect(rect, |ui| {
|
||||
self.network.ui(ui, cb);
|
||||
});
|
||||
});
|
||||
|
||||
// Show wallets content.
|
||||
egui::CentralPanel::default()
|
||||
|
@ -129,14 +123,8 @@ impl Root {
|
|||
..Default::default()
|
||||
})
|
||||
.show_inside(ui, |ui| {
|
||||
// Set content height as window height.
|
||||
let mut rect = ui.available_rect_before_wrap();
|
||||
let window_size = View::window_size(ui);
|
||||
rect.set_height(window_size.1);
|
||||
ui.allocate_ui_at_rect(rect, |ui| {
|
||||
self.wallets.ui(ui, cb);
|
||||
});
|
||||
});
|
||||
|
||||
// Show integrated node warning on Android if needed.
|
||||
if self.first_draw && OperatingSystem::from_target_os() == OperatingSystem::Android &&
|
||||
|
|
|
@ -77,7 +77,6 @@ impl View {
|
|||
|
||||
/// Get width and height of app window.
|
||||
pub fn window_size(ui: &mut egui::Ui) -> (f32, f32) {
|
||||
|
||||
ui.ctx().input(|i| {
|
||||
return match i.viewport().inner_rect {
|
||||
None => {
|
||||
|
@ -179,8 +178,18 @@ impl View {
|
|||
false
|
||||
}
|
||||
|
||||
/// Title button with transparent background fill color, contains only icon.
|
||||
pub fn title_button(ui: &mut egui::Ui, icon: &str, action: impl FnOnce()) {
|
||||
/// Draw big size title button.
|
||||
pub fn title_button_big(ui: &mut egui::Ui, icon: &str, action: impl FnOnce(&mut egui::Ui)) {
|
||||
Self::title_button(ui, 22.0, icon, action);
|
||||
}
|
||||
|
||||
/// Draw small size title button.
|
||||
pub fn title_button_small(ui: &mut egui::Ui, icon: &str, action: impl FnOnce(&mut egui::Ui)) {
|
||||
Self::title_button(ui, 17.0, icon, action);
|
||||
}
|
||||
|
||||
/// Draw title button with transparent background color, contains only icon.
|
||||
fn title_button(ui: &mut egui::Ui, size: f32, icon: &str, action: impl FnOnce(&mut egui::Ui)) {
|
||||
ui.scope(|ui| {
|
||||
// Disable strokes.
|
||||
ui.style_mut().visuals.widgets.inactive.bg_stroke = Stroke::NONE;
|
||||
|
@ -190,7 +199,7 @@ impl View {
|
|||
ui.style_mut().visuals.widgets.active.expansion = 0.0;
|
||||
|
||||
// Setup text.
|
||||
let wt = RichText::new(icon.to_string()).size(22.0).color(Colors::title(true));
|
||||
let wt = RichText::new(icon.to_string()).size(size).color(Colors::title(true));
|
||||
// Draw button.
|
||||
let br = Button::new(wt)
|
||||
.fill(Colors::TRANSPARENT)
|
||||
|
@ -198,7 +207,7 @@ impl View {
|
|||
.on_hover_cursor(CursorIcon::PointingHand);
|
||||
br.surrender_focus();
|
||||
if Self::touched(ui, br) {
|
||||
(action)();
|
||||
(action)(ui);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -263,11 +263,11 @@ impl WalletsContent {
|
|||
// Draw title panel.
|
||||
TitlePanel::ui(title_content, |ui| {
|
||||
if show_wallet && !dual_panel {
|
||||
View::title_button(ui, ARROW_LEFT, || {
|
||||
View::title_button_big(ui, ARROW_LEFT, |_| {
|
||||
self.wallets.select(None);
|
||||
});
|
||||
} else if create_wallet {
|
||||
View::title_button(ui, ARROW_LEFT, || {
|
||||
View::title_button_big(ui, ARROW_LEFT, |_| {
|
||||
self.creation_content.back();
|
||||
});
|
||||
} else if show_wallet && dual_panel {
|
||||
|
@ -276,17 +276,17 @@ impl WalletsContent {
|
|||
} else {
|
||||
SUITCASE
|
||||
};
|
||||
View::title_button(ui, list_icon, || {
|
||||
View::title_button_big(ui, list_icon, |_| {
|
||||
self.show_wallets_at_dual_panel = !show_list;
|
||||
AppConfig::toggle_show_wallets_at_dual_panel();
|
||||
});
|
||||
} else if !Root::is_dual_panel_mode(ui) {
|
||||
View::title_button(ui, GLOBE, || {
|
||||
View::title_button_big(ui, GLOBE, |_| {
|
||||
Root::toggle_network_panel();
|
||||
});
|
||||
};
|
||||
}, |ui| {
|
||||
View::title_button(ui, GEAR, || {
|
||||
View::title_button_big(ui, GEAR, |_| {
|
||||
// Show settings modal.
|
||||
Modal::new(Root::SETTINGS_MODAL)
|
||||
.position(ModalPosition::CenterTop)
|
||||
|
|
11
src/main.rs
11
src/main.rs
|
@ -12,9 +12,6 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use grim::gui::App;
|
||||
use grim::gui::platform::Desktop;
|
||||
|
||||
pub fn main() {
|
||||
#[allow(dead_code)]
|
||||
#[cfg(not(target_os = "android"))]
|
||||
|
@ -54,8 +51,7 @@ fn real_main() {
|
|||
// Setup window size.
|
||||
let (width, height) = AppConfig::window_size();
|
||||
|
||||
let mut viewport = egui::ViewportBuilder::default()
|
||||
.with_inner_size([width, height]);
|
||||
let mut viewport = egui::ViewportBuilder::default().with_inner_size([width, height]);
|
||||
|
||||
// Setup an icon.
|
||||
if let Ok(icon) = from_png_bytes(include_bytes!("../img/icon.png")) {
|
||||
|
@ -67,6 +63,11 @@ fn real_main() {
|
|||
viewport = viewport.with_position(pos2(x, y));
|
||||
}
|
||||
|
||||
// Setup window decorations.
|
||||
viewport = viewport
|
||||
.with_transparent(true)
|
||||
.with_decorations(false);
|
||||
|
||||
let mut options = eframe::NativeOptions {
|
||||
viewport,
|
||||
..Default::default()
|
||||
|
|
Loading…
Reference in a new issue