Add macOS-specific `has_shadow` and `with_has_shadow` to ViewportBuilder (#6850)

* [X] I have followed the instructions in the PR template

This PR fixes a ghosting issue I encountered while making a native macOS
transparent overlay app using egui and eframe by exposing the [existing
macOS window attribute
`has_shadow`](https://docs.rs/winit/latest/winit/platform/macos/trait.WindowExtMacOS.html#tymethod.has_shadow)
to the `ViewportBuilder` via a new `with_has_shadow` option.

## Example of Ghosting Issue

### Before `ViewportBuilder::with_has_shadow`
By default, the underlying `winit` window's `.has_shadow()` defaults to
`true`.


https://github.com/user-attachments/assets/c3dcc2bd-535a-4960-918e-3ae5df503b12

### After `ViewportBuilder::with_has_shadow`


https://github.com/user-attachments/assets/484462a1-ea88-43e6-85b4-0bb9724e5f14

Source code for the above example can be found here:

https://github.com/gaelanmcmillan/egui-overlay-app-with-shadow-artifacts-example/blob/main/src/main.rs


### Further background
By default on macOS, `winit` windows have a drop-shadow effect. When
creating a fully transparent overlay GUI, this drop-shadow can create a
ghosting effect, as the window content has a drop shadow which is not
cleared by the app itself. This issue has been experienced by users of
`bevy`, another Rust project that has an upstream dependency on `winit`:
https://github.com/bevyengine/bevy/issues/18673
This commit is contained in:
Gaelan McMillan 2025-04-29 06:02:42 -04:00 committed by GitHub
parent 7d185acb41
commit 3a02963c33
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 27 additions and 2 deletions

View File

@ -1620,6 +1620,7 @@ pub fn create_winit_window_attributes(
title_shown: _title_shown,
titlebar_buttons_shown: _titlebar_buttons_shown,
titlebar_shown: _titlebar_shown,
has_shadow: _has_shadow,
// Windows:
drag_and_drop: _drag_and_drop,
@ -1764,7 +1765,8 @@ pub fn create_winit_window_attributes(
.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))
.with_movable_by_window_background(_movable_by_window_background.unwrap_or(false));
.with_movable_by_window_background(_movable_by_window_background.unwrap_or(false))
.with_has_shadow(_has_shadow.unwrap_or(true));
}
window_attributes

View File

@ -294,6 +294,7 @@ pub struct ViewportBuilder {
pub title_shown: Option<bool>,
pub titlebar_buttons_shown: Option<bool>,
pub titlebar_shown: Option<bool>,
pub has_shadow: Option<bool>,
// windows:
pub drag_and_drop: Option<bool>,
@ -380,6 +381,10 @@ impl ViewportBuilder {
/// The default is `false`.
/// If this is not working, it's because the graphic context doesn't support transparency,
/// you will need to set the transparency in the eframe!
///
/// ## Platform-specific
///
/// **macOS:** When using this feature to create an overlay-like UI, you likely want to combine this with [`Self::with_has_shadow`] set to `false` in order to avoid ghosting artifacts.
#[inline]
pub fn with_transparent(mut self, transparent: bool) -> Self {
self.transparent = Some(transparent);
@ -433,7 +438,6 @@ impl ViewportBuilder {
}
/// macOS: Set to `true` to allow the window to be moved by dragging the background.
///
/// Enabling this feature can result in unexpected behaviour with draggable UI widgets such as sliders.
#[inline]
pub fn with_movable_by_background(mut self, value: bool) -> Self {
@ -462,6 +466,19 @@ impl ViewportBuilder {
self
}
/// macOS: Set to `false` to make the window render without a drop shadow.
///
/// The default is `true`.
///
/// Disabling this feature can solve ghosting issues experienced if using [`Self::with_transparent`].
///
/// Look at winit for more details
#[inline]
pub fn with_has_shadow(mut self, has_shadow: bool) -> Self {
self.has_shadow = Some(has_shadow);
self
}
/// windows: Whether show or hide the window icon in the taskbar.
#[inline]
pub fn with_taskbar(mut self, show: bool) -> Self {
@ -653,6 +670,7 @@ impl ViewportBuilder {
title_shown: new_title_shown,
titlebar_buttons_shown: new_titlebar_buttons_shown,
titlebar_shown: new_titlebar_shown,
has_shadow: new_has_shadow,
close_button: new_close_button,
minimize_button: new_minimize_button,
maximize_button: new_maximize_button,
@ -823,6 +841,11 @@ impl ViewportBuilder {
recreate_window = true;
}
if new_has_shadow.is_some() && self.has_shadow != new_has_shadow {
self.has_shadow = new_has_shadow;
recreate_window = true;
}
if new_taskbar.is_some() && self.taskbar != new_taskbar {
self.taskbar = new_taskbar;
recreate_window = true;