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:
parent
430cbe541c
commit
660566c499
3 changed files with 84 additions and 43 deletions
|
@ -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,
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 = {
|
||||||
|
|
Loading…
Reference in a new issue