eframe: ask if the window is minimized or maximized (#2672)

* eframe: ask if the window is minimized or maximized

* Improve note
This commit is contained in:
Emil Ernerfeldt 2023-02-04 16:05:23 +01:00 committed by GitHub
parent 430cbe541c
commit 660566c499
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 84 additions and 43 deletions

View file

@ -841,6 +841,12 @@ pub struct WindowInfo {
/// Are we in fullscreen mode? /// Are we in fullscreen mode?
pub fullscreen: bool, pub fullscreen: bool,
/// Are we minimized?
pub minimized: bool,
/// Are we maximized?
pub maximized: bool,
/// Window inner size in egui points (logical pixels). /// Window inner size in egui points (logical pixels).
pub size: egui::Vec2, pub size: egui::Vec2,

View file

@ -12,6 +12,14 @@ use egui_winit::{native_pixels_per_point, EventResponse, WindowSettings};
use crate::{epi, Theme, WindowInfo}; use crate::{epi, Theme, WindowInfo};
#[derive(Default)]
pub struct WindowState {
// We cannot simply call `winit::Window::is_minimized/is_maximized`
// because that deadlocks on mac.
pub minimized: bool,
pub maximized: bool,
}
pub fn points_to_size(points: egui::Vec2) -> winit::dpi::LogicalSize<f64> { pub fn points_to_size(points: egui::Vec2) -> winit::dpi::LogicalSize<f64> {
winit::dpi::LogicalSize { winit::dpi::LogicalSize {
width: points.x as f64, width: points.x as f64,
@ -19,7 +27,11 @@ pub fn points_to_size(points: egui::Vec2) -> winit::dpi::LogicalSize<f64> {
} }
} }
pub fn read_window_info(window: &winit::window::Window, pixels_per_point: f32) -> WindowInfo { pub fn read_window_info(
window: &winit::window::Window,
pixels_per_point: f32,
window_state: &WindowState,
) -> WindowInfo {
let position = window let position = window
.outer_position() .outer_position()
.ok() .ok()
@ -38,9 +50,13 @@ pub fn read_window_info(window: &winit::window::Window, pixels_per_point: f32) -
.inner_size() .inner_size()
.to_logical::<f32>(pixels_per_point.into()); .to_logical::<f32>(pixels_per_point.into());
// NOTE: calling window.is_minimized() or window.is_maximized() deadlocks on Mac.
WindowInfo { WindowInfo {
position, position,
fullscreen: window.fullscreen().is_some(), fullscreen: window.fullscreen().is_some(),
minimized: window_state.minimized,
maximized: window_state.maximized,
size: egui::Vec2 { size: egui::Vec2 {
x: size.width, x: size.width,
y: size.height, y: size.height,
@ -198,6 +214,7 @@ pub fn handle_app_output(
window: &winit::window::Window, window: &winit::window::Window,
current_pixels_per_point: f32, current_pixels_per_point: f32,
app_output: epi::backend::AppOutput, app_output: epi::backend::AppOutput,
window_state: &mut WindowState,
) { ) {
let epi::backend::AppOutput { let epi::backend::AppOutput {
close: _, close: _,
@ -257,10 +274,12 @@ pub fn handle_app_output(
if let Some(minimized) = minimized { if let Some(minimized) = minimized {
window.set_minimized(minimized); window.set_minimized(minimized);
window_state.minimized = minimized;
} }
if let Some(maximized) = maximized { if let Some(maximized) = maximized {
window.set_maximized(maximized); window.set_maximized(maximized);
window_state.maximized = maximized;
} }
} }
@ -287,6 +306,7 @@ pub struct EpiIntegration {
/// When set, it is time to close the native window. /// When set, it is time to close the native window.
close: bool, close: bool,
can_drag_window: bool, can_drag_window: bool,
window_state: WindowState,
} }
impl EpiIntegration { impl EpiIntegration {
@ -306,12 +326,17 @@ impl EpiIntegration {
let native_pixels_per_point = window.scale_factor() as f32; let native_pixels_per_point = window.scale_factor() as f32;
let window_state = WindowState {
minimized: window.is_minimized().unwrap_or(false),
maximized: window.is_maximized(),
};
let frame = epi::Frame { let frame = epi::Frame {
info: epi::IntegrationInfo { info: epi::IntegrationInfo {
system_theme, system_theme,
cpu_usage: None, cpu_usage: None,
native_pixels_per_point: Some(native_pixels_per_point), native_pixels_per_point: Some(native_pixels_per_point),
window_info: read_window_info(window, egui_ctx.pixels_per_point()), window_info: read_window_info(window, egui_ctx.pixels_per_point(), &window_state),
}, },
output: epi::backend::AppOutput { output: epi::backend::AppOutput {
visible: Some(true), visible: Some(true),
@ -336,6 +361,7 @@ impl EpiIntegration {
pending_full_output: Default::default(), pending_full_output: Default::default(),
close: false, close: false,
can_drag_window: false, can_drag_window: false,
window_state,
} }
} }
@ -417,12 +443,16 @@ impl EpiIntegration {
) -> egui::FullOutput { ) -> egui::FullOutput {
let frame_start = std::time::Instant::now(); let frame_start = std::time::Instant::now();
self.frame.info.window_info = read_window_info(window, self.egui_ctx.pixels_per_point()); self.frame.info.window_info =
read_window_info(window, self.egui_ctx.pixels_per_point(), &self.window_state);
let raw_input = self.egui_winit.take_egui_input(window); let raw_input = self.egui_winit.take_egui_input(window);
// Run user code:
let full_output = self.egui_ctx.run(raw_input, |egui_ctx| { let full_output = self.egui_ctx.run(raw_input, |egui_ctx| {
crate::profile_scope!("App::update"); crate::profile_scope!("App::update");
app.update(egui_ctx, &mut self.frame); app.update(egui_ctx, &mut self.frame);
}); });
self.pending_full_output.append(full_output); self.pending_full_output.append(full_output);
let full_output = std::mem::take(&mut self.pending_full_output); let full_output = std::mem::take(&mut self.pending_full_output);
@ -435,7 +465,12 @@ impl EpiIntegration {
tracing::debug!("App::on_close_event returned {}", self.close); tracing::debug!("App::on_close_event returned {}", self.close);
} }
self.frame.output.visible = app_output.visible; // this is handled by post_present self.frame.output.visible = app_output.visible; // this is handled by post_present
handle_app_output(window, self.egui_ctx.pixels_per_point(), app_output); handle_app_output(
window,
self.egui_ctx.pixels_per_point(),
app_output,
&mut self.window_state,
);
} }
let frame_time = frame_start.elapsed().as_secs_f64() as f32; let frame_time = frame_start.elapsed().as_secs_f64() as f32;

View file

@ -22,9 +22,7 @@ fn main() -> Result<(), eframe::Error> {
} }
#[derive(Default)] #[derive(Default)]
struct MyApp { struct MyApp {}
maximized: bool,
}
impl eframe::App for MyApp { impl eframe::App for MyApp {
fn clear_color(&self, _visuals: &egui::Visuals) -> [f32; 4] { fn clear_color(&self, _visuals: &egui::Visuals) -> [f32; 4] {
@ -32,7 +30,7 @@ impl eframe::App for MyApp {
} }
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) { fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
custom_window_frame(self, ctx, frame, "egui with custom frame", |ui| { custom_window_frame(ctx, frame, "egui with custom frame", |ui| {
ui.label("This is just the contents of the window"); ui.label("This is just the contents of the window");
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.label("egui theme:"); ui.label("egui theme:");
@ -43,7 +41,6 @@ impl eframe::App for MyApp {
} }
fn custom_window_frame( fn custom_window_frame(
app: &mut MyApp,
ctx: &egui::Context, ctx: &egui::Context,
frame: &mut eframe::Frame, frame: &mut eframe::Frame,
title: &str, title: &str,
@ -55,6 +52,8 @@ fn custom_window_frame(
// Height of the title bar // Height of the title bar
let height = 28.0; let height = 28.0;
let button_height = 16.0;
CentralPanel::default() CentralPanel::default()
.frame(Frame::none()) .frame(Frame::none())
.show(ctx, |ui| { .show(ctx, |ui| {
@ -97,46 +96,47 @@ fn custom_window_frame(
ui.interact(title_bar_rect, Id::new("title_bar"), Sense::click()); ui.interact(title_bar_rect, Id::new("title_bar"), Sense::click());
if title_bar_response.double_clicked() { if title_bar_response.double_clicked() {
app.maximized = !app.maximized; frame.set_maximized(!frame.info().window_info.maximized);
frame.set_maximized(app.maximized);
} else if title_bar_response.is_pointer_button_down_on() { } else if title_bar_response.is_pointer_button_down_on() {
frame.drag_window(); frame.drag_window();
} }
// Add the close button: ui.allocate_ui_at_rect(title_bar_rect, |ui| {
let close_response = ui.put( ui.horizontal_centered(|ui| {
Rect::from_min_size(rect.left_top(), Vec2::splat(height)), ui.spacing_mut().item_spacing.x = 0.0;
Button::new(RichText::new("").size(height - 4.0)).frame(false), ui.visuals_mut().button_frame = false;
);
if close_response.clicked() {
frame.close();
}
let minimized_response = ui.put( let close_response = ui
Rect::from_min_size( .add(Button::new(RichText::new("").size(button_height)))
rect.left_top() + vec2((height - 4.0) * 1.0, 0.0), .on_hover_text("Close the window");
Vec2::splat(height), if close_response.clicked() {
), frame.close();
Button::new(RichText::new("🗕").size(height - 4.0)).frame(false), }
);
if minimized_response.clicked() {
frame.set_minimized(true);
}
let maximized_response = ui.put( let minimized_response = ui
Rect::from_min_size( .add(Button::new(RichText::new("🗕").size(button_height)))
rect.left_top() + vec2((height - 4.0) * 2.0, 0.0), .on_hover_text("Minimize the window");
Vec2::splat(height), if minimized_response.clicked() {
), frame.set_minimized(true);
Button::new( }
RichText::new(if app.maximized { "🗗" } else { "🗖" }).size(height - 4.0),
) if frame.info().window_info.maximized {
.frame(false), let maximized_response = ui
); .add(Button::new(RichText::new("🗖").size(button_height)))
if maximized_response.clicked() { .on_hover_text("Restore window");
app.maximized = !app.maximized; if maximized_response.clicked() {
frame.set_maximized(app.maximized); frame.set_maximized(false);
} }
} else {
let maximized_response = ui
.add(Button::new(RichText::new("🗗").size(button_height)))
.on_hover_text("Maximize window");
if maximized_response.clicked() {
frame.set_maximized(true);
}
}
});
});
// Add the contents: // Add the contents:
let content_rect = { let content_rect = {