From 660566c4996535308bb09330caf346c0be10be33 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Sat, 4 Feb 2023 16:05:23 +0100 Subject: [PATCH] eframe: ask if the window is minimized or maximized (#2672) * eframe: ask if the window is minimized or maximized * Improve note --- crates/eframe/src/epi.rs | 6 ++ crates/eframe/src/native/epi_integration.rs | 43 ++++++++++-- examples/custom_window_frame/src/main.rs | 78 ++++++++++----------- 3 files changed, 84 insertions(+), 43 deletions(-) diff --git a/crates/eframe/src/epi.rs b/crates/eframe/src/epi.rs index 51700d2a..1c9cb021 100644 --- a/crates/eframe/src/epi.rs +++ b/crates/eframe/src/epi.rs @@ -841,6 +841,12 @@ pub struct WindowInfo { /// Are we in fullscreen mode? pub fullscreen: bool, + /// Are we minimized? + pub minimized: bool, + + /// Are we maximized? + pub maximized: bool, + /// Window inner size in egui points (logical pixels). pub size: egui::Vec2, diff --git a/crates/eframe/src/native/epi_integration.rs b/crates/eframe/src/native/epi_integration.rs index 0ec6f779..f38766cb 100644 --- a/crates/eframe/src/native/epi_integration.rs +++ b/crates/eframe/src/native/epi_integration.rs @@ -12,6 +12,14 @@ use egui_winit::{native_pixels_per_point, EventResponse, WindowSettings}; 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 { winit::dpi::LogicalSize { width: points.x as f64, @@ -19,7 +27,11 @@ pub fn points_to_size(points: egui::Vec2) -> winit::dpi::LogicalSize { } } -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 .outer_position() .ok() @@ -38,9 +50,13 @@ pub fn read_window_info(window: &winit::window::Window, pixels_per_point: f32) - .inner_size() .to_logical::(pixels_per_point.into()); + // NOTE: calling window.is_minimized() or window.is_maximized() deadlocks on Mac. + WindowInfo { position, fullscreen: window.fullscreen().is_some(), + minimized: window_state.minimized, + maximized: window_state.maximized, size: egui::Vec2 { x: size.width, y: size.height, @@ -198,6 +214,7 @@ pub fn handle_app_output( window: &winit::window::Window, current_pixels_per_point: f32, app_output: epi::backend::AppOutput, + window_state: &mut WindowState, ) { let epi::backend::AppOutput { close: _, @@ -257,10 +274,12 @@ pub fn handle_app_output( if let Some(minimized) = minimized { window.set_minimized(minimized); + window_state.minimized = minimized; } if let Some(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. close: bool, can_drag_window: bool, + window_state: WindowState, } impl EpiIntegration { @@ -306,12 +326,17 @@ impl EpiIntegration { 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 { info: epi::IntegrationInfo { system_theme, cpu_usage: None, 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 { visible: Some(true), @@ -336,6 +361,7 @@ impl EpiIntegration { pending_full_output: Default::default(), close: false, can_drag_window: false, + window_state, } } @@ -417,12 +443,16 @@ impl EpiIntegration { ) -> egui::FullOutput { 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); + + // Run user code: let full_output = self.egui_ctx.run(raw_input, |egui_ctx| { crate::profile_scope!("App::update"); app.update(egui_ctx, &mut self.frame); }); + self.pending_full_output.append(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); } 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; diff --git a/examples/custom_window_frame/src/main.rs b/examples/custom_window_frame/src/main.rs index fcc62332..87813ae2 100644 --- a/examples/custom_window_frame/src/main.rs +++ b/examples/custom_window_frame/src/main.rs @@ -22,9 +22,7 @@ fn main() -> Result<(), eframe::Error> { } #[derive(Default)] -struct MyApp { - maximized: bool, -} +struct MyApp {} impl eframe::App for MyApp { 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) { - 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.horizontal(|ui| { ui.label("egui theme:"); @@ -43,7 +41,6 @@ impl eframe::App for MyApp { } fn custom_window_frame( - app: &mut MyApp, ctx: &egui::Context, frame: &mut eframe::Frame, title: &str, @@ -55,6 +52,8 @@ fn custom_window_frame( // Height of the title bar let height = 28.0; + let button_height = 16.0; + CentralPanel::default() .frame(Frame::none()) .show(ctx, |ui| { @@ -97,46 +96,47 @@ fn custom_window_frame( ui.interact(title_bar_rect, Id::new("title_bar"), Sense::click()); if title_bar_response.double_clicked() { - app.maximized = !app.maximized; - frame.set_maximized(app.maximized); + frame.set_maximized(!frame.info().window_info.maximized); } else if title_bar_response.is_pointer_button_down_on() { frame.drag_window(); } - // Add the close button: - let close_response = ui.put( - Rect::from_min_size(rect.left_top(), Vec2::splat(height)), - Button::new(RichText::new("❌").size(height - 4.0)).frame(false), - ); - if close_response.clicked() { - frame.close(); - } + ui.allocate_ui_at_rect(title_bar_rect, |ui| { + ui.horizontal_centered(|ui| { + ui.spacing_mut().item_spacing.x = 0.0; + ui.visuals_mut().button_frame = false; - let minimized_response = ui.put( - Rect::from_min_size( - rect.left_top() + vec2((height - 4.0) * 1.0, 0.0), - Vec2::splat(height), - ), - Button::new(RichText::new("🗕").size(height - 4.0)).frame(false), - ); - if minimized_response.clicked() { - frame.set_minimized(true); - } + let close_response = ui + .add(Button::new(RichText::new("❌").size(button_height))) + .on_hover_text("Close the window"); + if close_response.clicked() { + frame.close(); + } - let maximized_response = ui.put( - Rect::from_min_size( - rect.left_top() + vec2((height - 4.0) * 2.0, 0.0), - Vec2::splat(height), - ), - Button::new( - RichText::new(if app.maximized { "🗗" } else { "🗖" }).size(height - 4.0), - ) - .frame(false), - ); - if maximized_response.clicked() { - app.maximized = !app.maximized; - frame.set_maximized(app.maximized); - } + let minimized_response = ui + .add(Button::new(RichText::new("🗕").size(button_height))) + .on_hover_text("Minimize the window"); + if minimized_response.clicked() { + frame.set_minimized(true); + } + + if frame.info().window_info.maximized { + let maximized_response = ui + .add(Button::new(RichText::new("🗖").size(button_height))) + .on_hover_text("Restore window"); + if maximized_response.clicked() { + 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: let content_rect = {