diff --git a/crates/eframe/CHANGELOG.md b/crates/eframe/CHANGELOG.md index 2a0b5136eaf..e56e7695772 100644 --- a/crates/eframe/CHANGELOG.md +++ b/crates/eframe/CHANGELOG.md @@ -6,6 +6,9 @@ NOTE: [`egui-winit`](../egui-winit/CHANGELOG.md), [`egui_glium`](../egui_glium/C This file is updated upon each release. Changes since the last release can be found by running the `scripts/generate_changelog.py` script. +## Unreleased +* `NativeOptions::fullsize_content` has been replaced with four settings: `ViewportBuilder::with_fullsize_content_view`, `with_title_shown`, `with_titlebar_shown`, `with_titlebar_buttons_shown` + ## 0.23.0 - 2023-09-27 * Update MSRV to Rust 1.70.0 [#3310](https://github.com/emilk/egui/pull/3310) * Update to puffin 0.16 [#3144](https://github.com/emilk/egui/pull/3144) diff --git a/crates/eframe/src/native/glow_integration.rs b/crates/eframe/src/native/glow_integration.rs index d3dd4cb4f49..c3e8850e1aa 100644 --- a/crates/eframe/src/native/glow_integration.rs +++ b/crates/eframe/src/native/glow_integration.rs @@ -1224,13 +1224,13 @@ fn initialize_or_update_viewport<'vp>( viewport.class = class; viewport.viewport_ui_cb = viewport_ui_cb; - let (delta_commands, recreate) = viewport.builder.patch(&builder); + let (delta_commands, recreate) = viewport.builder.patch(builder); if recreate { log::debug!( "Recreating window for viewport {:?} ({:?})", ids.this, - builder.title + viewport.builder.title ); viewport.window = None; viewport.egui_winit = None; diff --git a/crates/eframe/src/native/wgpu_integration.rs b/crates/eframe/src/native/wgpu_integration.rs index 43ab83c29f2..a149b396547 100644 --- a/crates/eframe/src/native/wgpu_integration.rs +++ b/crates/eframe/src/native/wgpu_integration.rs @@ -1037,13 +1037,13 @@ fn initialize_or_update_viewport<'vp>( viewport.ids.parent = ids.parent; viewport.viewport_ui_cb = viewport_ui_cb; - let (delta_commands, recreate) = viewport.builder.patch(&builder); + let (delta_commands, recreate) = viewport.builder.patch(builder); if recreate { log::debug!( "Recreating window for viewport {:?} ({:?})", ids.this, - builder.title + viewport.builder.title ); viewport.window = None; viewport.egui_winit = None; diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index fa454b085c6..8bcd1dc8cfa 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -1292,13 +1292,18 @@ pub fn create_winit_window_builder( maximize_button, window_level, - // only handled on some platforms: - title_hidden: _title_hidden, - titlebar_transparent: _titlebar_transparent, + // macOS: fullsize_content_view: _fullsize_content_view, - app_id: _app_id, + title_shown: _title_shown, + titlebar_buttons_shown: _titlebar_buttons_shown, + titlebar_shown: _titlebar_shown, + + // Windows: drag_and_drop: _drag_and_drop, + // wayland: + app_id: _app_id, + mouse_passthrough: _, // handled in `apply_viewport_builder_to_new_window` } = viewport_builder; @@ -1309,7 +1314,7 @@ pub fn create_winit_window_builder( .with_resizable(resizable.unwrap_or(true)) .with_visible(visible.unwrap_or(true)) .with_maximized(maximized.unwrap_or(false)) - .with_window_level(match window_level { + .with_window_level(match window_level.unwrap_or_default() { egui::viewport::WindowLevel::AlwaysOnBottom => WindowLevel::AlwaysOnBottom, egui::viewport::WindowLevel::AlwaysOnTop => WindowLevel::AlwaysOnTop, egui::viewport::WindowLevel::Normal => WindowLevel::Normal, @@ -1383,8 +1388,9 @@ pub fn create_winit_window_builder( { use winit::platform::macos::WindowBuilderExtMacOS as _; window_builder = window_builder - .with_title_hidden(_title_hidden.unwrap_or(false)) - .with_titlebar_transparent(_titlebar_transparent.unwrap_or(false)) + .with_title_hidden(!_title_shown.unwrap_or(true)) + .with_titlebar_buttons_hidden(!_titlebar_buttons_shown.unwrap_or(true)) + .with_titlebar_transparent(!_titlebar_shown.unwrap_or(true)) .with_fullsize_content_view(_fullsize_content_view.unwrap_or(false)); } diff --git a/crates/egui/src/viewport.rs b/crates/egui/src/viewport.rs index 7fa8839645f..1ee09f57fae 100644 --- a/crates/egui/src/viewport.rs +++ b/crates/egui/src/viewport.rs @@ -275,16 +275,19 @@ pub struct ViewportBuilder { pub icon: Option>, pub active: Option, pub visible: Option, - pub title_hidden: Option, - pub titlebar_transparent: Option, - pub fullsize_content_view: Option, pub drag_and_drop: Option, + // macOS: + pub fullsize_content_view: Option, + pub title_shown: Option, + pub titlebar_buttons_shown: Option, + pub titlebar_shown: Option, + pub close_button: Option, pub minimize_button: Option, pub maximize_button: Option, - pub window_level: WindowLevel, + pub window_level: Option, pub mouse_passthrough: Option, } @@ -400,32 +403,34 @@ impl ViewportBuilder { self } - /// Hides the window title. + /// macOS: Makes the window content appear behind the titlebar. /// - /// Mac Os only. + /// You often want to combine this with [`Self::with_titlebar_shown`] + /// and [`Self::with_title_shown`]. #[inline] - pub fn with_title_hidden(mut self, title_hidden: bool) -> Self { - self.title_hidden = Some(title_hidden); + pub fn with_fullsize_content_view(mut self, value: bool) -> Self { + self.fullsize_content_view = Some(value); self } - /// Makes the titlebar transparent and allows the content to appear behind it. - /// - /// Mac Os only. + /// macOS: Set to `false` to hide the window title. #[inline] - pub fn with_titlebar_transparent(mut self, value: bool) -> Self { - self.titlebar_transparent = Some(value); + pub fn with_title_shown(mut self, title_shown: bool) -> Self { + self.title_shown = Some(title_shown); self } - /// On Mac: the window doesn't have a titlebar, but floating window buttons. - /// - /// See [winit's documentation][with_fullsize_content_view] for information on Mac-specific options. - /// - /// [with_fullsize_content_view]: https://docs.rs/winit/latest/x86_64-apple-darwin/winit/platform/macos/trait.WindowBuilderExtMacOS.html#tymethod.with_fullsize_content_view + /// macOS: Set to `false` to hide the titlebar button (close, minimize, maximize) #[inline] - pub fn with_fullsize_content_view(mut self, value: bool) -> Self { - self.fullsize_content_view = Some(value); + pub fn with_titlebar_buttons_shown(mut self, titlebar_buttons_shown: bool) -> Self { + self.titlebar_buttons_shown = Some(titlebar_buttons_shown); + self + } + + /// macOS: Set to `false` to make the titlebar transparent, allowing the content to appear behind it. + #[inline] + pub fn with_titlebar_shown(mut self, shown: bool) -> Self { + self.titlebar_shown = Some(shown); self } @@ -539,7 +544,7 @@ impl ViewportBuilder { /// Control if window i always-on-top, always-on-bottom, or neither. #[inline] pub fn with_window_level(mut self, level: WindowLevel) -> Self { - self.window_level = level; + self.window_level = Some(level); self } @@ -561,156 +566,199 @@ impl ViewportBuilder { /// Update this `ViewportBuilder` with a delta, /// returning a list of commands and a bool intdicating if the window needs to be recreated. - pub fn patch(&mut self, new: &ViewportBuilder) -> (Vec, bool) { + #[must_use] + pub fn patch(&mut self, new_vp_builder: ViewportBuilder) -> (Vec, bool) { + let ViewportBuilder { + title: new_title, + app_id: new_app_id, + position: new_position, + inner_size: new_inner_size, + min_inner_size: new_min_inner_size, + max_inner_size: new_max_inner_size, + fullscreen: new_fullscreen, + maximized: new_maximized, + resizable: new_resizable, + transparent: new_transparent, + decorations: new_decorations, + icon: new_icon, + active: new_active, + visible: new_visible, + drag_and_drop: new_drag_and_drop, + fullsize_content_view: new_fullsize_content_view, + title_shown: new_title_shown, + titlebar_buttons_shown: new_titlebar_buttons_shown, + titlebar_shown: new_titlebar_shown, + close_button: new_close_button, + minimize_button: new_minimize_button, + maximize_button: new_maximize_button, + window_level: new_window_level, + mouse_passthrough: new_mouse_passthrough, + } = new_vp_builder; + let mut commands = Vec::new(); - if let Some(new_title) = &new.title { - if Some(new_title) != self.title.as_ref() { + if let Some(new_title) = new_title { + if Some(&new_title) != self.title.as_ref() { self.title = Some(new_title.clone()); - commands.push(ViewportCommand::Title(new_title.clone())); + commands.push(ViewportCommand::Title(new_title)); } } - if let Some(new_position) = new.position { + if let Some(new_position) = new_position { if Some(new_position) != self.position { self.position = Some(new_position); commands.push(ViewportCommand::OuterPosition(new_position)); } } - if let Some(new_inner_size) = new.inner_size { + if let Some(new_inner_size) = new_inner_size { if Some(new_inner_size) != self.inner_size { self.inner_size = Some(new_inner_size); commands.push(ViewportCommand::InnerSize(new_inner_size)); } } - if let Some(new_min_inner_size) = new.min_inner_size { + if let Some(new_min_inner_size) = new_min_inner_size { if Some(new_min_inner_size) != self.min_inner_size { self.min_inner_size = Some(new_min_inner_size); commands.push(ViewportCommand::MinInnerSize(new_min_inner_size)); } } - if let Some(new_max_inner_size) = new.max_inner_size { + if let Some(new_max_inner_size) = new_max_inner_size { if Some(new_max_inner_size) != self.max_inner_size { self.max_inner_size = Some(new_max_inner_size); commands.push(ViewportCommand::MaxInnerSize(new_max_inner_size)); } } - if let Some(new_fullscreen) = new.fullscreen { + if let Some(new_fullscreen) = new_fullscreen { if Some(new_fullscreen) != self.fullscreen { self.fullscreen = Some(new_fullscreen); commands.push(ViewportCommand::Fullscreen(new_fullscreen)); } } - if let Some(new_maximized) = new.maximized { + if let Some(new_maximized) = new_maximized { if Some(new_maximized) != self.maximized { self.maximized = Some(new_maximized); commands.push(ViewportCommand::Maximized(new_maximized)); } } - if let Some(new_resizable) = new.resizable { + if let Some(new_resizable) = new_resizable { if Some(new_resizable) != self.resizable { self.resizable = Some(new_resizable); commands.push(ViewportCommand::Resizable(new_resizable)); } } - if let Some(new_transparent) = new.transparent { + if let Some(new_transparent) = new_transparent { if Some(new_transparent) != self.transparent { self.transparent = Some(new_transparent); commands.push(ViewportCommand::Transparent(new_transparent)); } } - if let Some(new_decorations) = new.decorations { + if let Some(new_decorations) = new_decorations { if Some(new_decorations) != self.decorations { self.decorations = Some(new_decorations); commands.push(ViewportCommand::Decorations(new_decorations)); } } - if let Some(new_icon) = &new.icon { + if let Some(new_icon) = new_icon { let is_new = match &self.icon { - Some(existing) => !Arc::ptr_eq(new_icon, existing), + Some(existing) => !Arc::ptr_eq(&new_icon, existing), None => true, }; if is_new { commands.push(ViewportCommand::Icon(Some(new_icon.clone()))); - self.icon = Some(new_icon.clone()); + self.icon = Some(new_icon); } } - if let Some(new_visible) = new.visible { + if let Some(new_visible) = new_visible { if Some(new_visible) != self.active { self.visible = Some(new_visible); commands.push(ViewportCommand::Visible(new_visible)); } } - if let Some(new_mouse_passthrough) = new.mouse_passthrough { + if let Some(new_mouse_passthrough) = new_mouse_passthrough { if Some(new_mouse_passthrough) != self.mouse_passthrough { self.mouse_passthrough = Some(new_mouse_passthrough); commands.push(ViewportCommand::MousePassthrough(new_mouse_passthrough)); } } - // TODO: Implement compare for windows buttons + if let Some(new_window_level) = new_window_level { + if Some(new_window_level) != self.window_level { + self.window_level = Some(new_window_level); + commands.push(ViewportCommand::WindowLevel(new_window_level)); + } + } + + // -------------------------------------------------------------- + // Things we don't have commands for require a full window recreation. + // The reason we don't have commands for them is that `winit` doesn't support + // changing them without recreating the window. let mut recreate_window = false; - if let Some(new_active) = new.active { - if Some(new_active) != self.active { - self.active = Some(new_active); - recreate_window = true; - } + if new_active.is_some() && self.active != new_active { + self.active = new_active; + recreate_window = true; } - if let Some(new_close_button) = new.close_button { - if Some(new_close_button) != self.close_button { - self.close_button = Some(new_close_button); - recreate_window = true; - } + if new_app_id.is_some() && self.app_id != new_app_id { + self.app_id = new_app_id; + recreate_window = true; } - if let Some(new_minimize_button) = new.minimize_button { - if Some(new_minimize_button) != self.minimize_button { - self.minimize_button = Some(new_minimize_button); - recreate_window = true; - } + if new_close_button.is_some() && self.close_button != new_close_button { + self.close_button = new_close_button; + recreate_window = true; } - if let Some(new_maximized_button) = new.maximize_button { - if Some(new_maximized_button) != self.maximize_button { - self.maximize_button = Some(new_maximized_button); - recreate_window = true; - } + if new_minimize_button.is_some() && self.minimize_button != new_minimize_button { + self.minimize_button = new_minimize_button; + recreate_window = true; } - if let Some(new_title_hidden) = new.title_hidden { - if Some(new_title_hidden) != self.title_hidden { - self.title_hidden = Some(new_title_hidden); - recreate_window = true; - } + if new_maximize_button.is_some() && self.maximize_button != new_maximize_button { + self.maximize_button = new_maximize_button; + recreate_window = true; } - if let Some(new_titlebar_transparent) = new.titlebar_transparent { - if Some(new_titlebar_transparent) != self.titlebar_transparent { - self.titlebar_transparent = Some(new_titlebar_transparent); - recreate_window = true; - } + if new_title_shown.is_some() && self.title_shown != new_title_shown { + self.title_shown = new_title_shown; + recreate_window = true; } - if let Some(new_fullsize_content_view) = new.fullsize_content_view { - if Some(new_fullsize_content_view) != self.fullsize_content_view { - self.fullsize_content_view = Some(new_fullsize_content_view); - recreate_window = true; - } + if new_titlebar_buttons_shown.is_some() + && self.titlebar_buttons_shown != new_titlebar_buttons_shown + { + self.titlebar_buttons_shown = new_titlebar_buttons_shown; + recreate_window = true; + } + + if new_titlebar_shown.is_some() && self.titlebar_shown != new_titlebar_shown { + self.titlebar_shown = new_titlebar_shown; + recreate_window = true; + } + + if new_fullsize_content_view.is_some() + && self.fullsize_content_view != new_fullsize_content_view + { + self.fullsize_content_view = new_fullsize_content_view; + recreate_window = true; + } + + if new_drag_and_drop.is_some() && self.drag_and_drop != new_drag_and_drop { + self.drag_and_drop = new_drag_and_drop; + recreate_window = true; } (commands, recreate_window) @@ -958,7 +1006,7 @@ impl ViewportOutput { self.parent = parent; self.class = class; - self.builder.patch(&builder); + let _ = self.builder.patch(builder); // we ignore the returned command, because `self.builder` will be the basis of a new patch self.viewport_ui_cb = viewport_ui_cb; self.commands.append(&mut commands); self.repaint_delay = self.repaint_delay.min(repaint_delay); diff --git a/examples/custom_window_frame/src/main.rs b/examples/custom_window_frame/src/main.rs index bda82a4a5f4..6008788bd0f 100644 --- a/examples/custom_window_frame/src/main.rs +++ b/examples/custom_window_frame/src/main.rs @@ -8,9 +8,9 @@ fn main() -> Result<(), eframe::Error> { env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`). let options = eframe::NativeOptions { viewport: egui::ViewportBuilder::default() + .with_decorations(false) // Hide the OS-specific "chrome" around the window .with_inner_size([400.0, 100.0]) .with_min_inner_size([400.0, 100.0]) - .with_decorations(false) // Hide the OS-specific "chrome" around the window .with_transparent(true), // To have rounded corners we need transparency ..Default::default()