Skip to content

Commit

Permalink
macOS: add option to explicitly hide menu/dock in Borderless (#3882)
Browse files Browse the repository at this point in the history
  • Loading branch information
TarekAS authored Sep 4, 2024
1 parent 9419e4e commit d37c591
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/changelog/unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ changelog entry.
and `Serialize` on many types.
- Add `MonitorHandle::current_video_mode()`.
- Add basic iOS IME support. The soft keyboard can now be shown using `Window::set_ime_allowed`.
- On macOS, add `WindowExtMacOS::set_borderless_game` and `WindowAttributesExtMacOS::with_borderless_game`
to fully disable the menu bar and dock in Borderless Fullscreen as commonly done in games.

### Changed

Expand Down
26 changes: 26 additions & 0 deletions src/platform/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,12 @@ pub trait WindowExtMacOS {

/// Getter for the [`WindowExtMacOS::set_option_as_alt`].
fn option_as_alt(&self) -> OptionAsAlt;

/// Disable the Menu Bar and Dock in Borderless Fullscreen mode. Useful for games.
fn set_borderless_game(&self, borderless_game: bool);

/// Getter for the [`WindowExtMacOS::set_borderless_game`].
fn is_borderless_game(&self) -> bool;
}

impl WindowExtMacOS for dyn Window + '_ {
Expand Down Expand Up @@ -236,6 +242,18 @@ impl WindowExtMacOS for dyn Window + '_ {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(|w| w.option_as_alt())
}

#[inline]
fn set_borderless_game(&self, borderless_game: bool) {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(|w| w.set_borderless_game(borderless_game))
}

#[inline]
fn is_borderless_game(&self) -> bool {
let window = self.as_any().downcast_ref::<crate::platform_impl::Window>().unwrap();
window.maybe_wait_on_main(|w| w.is_borderless_game())
}
}

/// Corresponds to `NSApplicationActivationPolicy`.
Expand Down Expand Up @@ -287,6 +305,8 @@ pub trait WindowAttributesExtMacOS {
///
/// See [`WindowExtMacOS::set_option_as_alt`] for details on what this means if set.
fn with_option_as_alt(self, option_as_alt: OptionAsAlt) -> Self;
/// See [`WindowExtMacOS::set_borderless_game`] for details on what this means if set.
fn with_borderless_game(self, borderless_game: bool) -> Self;
}

impl WindowAttributesExtMacOS for WindowAttributes {
Expand Down Expand Up @@ -355,6 +375,12 @@ impl WindowAttributesExtMacOS for WindowAttributes {
self.platform_specific.option_as_alt = option_as_alt;
self
}

#[inline]
fn with_borderless_game(mut self, borderless_game: bool) -> Self {
self.platform_specific.borderless_game = borderless_game;
self
}
}

pub trait EventLoopBuilderExtMacOS {
Expand Down
25 changes: 24 additions & 1 deletion src/platform_impl/apple/appkit/window_delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ pub struct PlatformSpecificWindowAttributes {
pub accepts_first_mouse: bool,
pub tabbing_identifier: Option<String>,
pub option_as_alt: OptionAsAlt,
pub borderless_game: bool,
}

impl Default for PlatformSpecificWindowAttributes {
Expand All @@ -73,6 +74,7 @@ impl Default for PlatformSpecificWindowAttributes {
accepts_first_mouse: true,
tabbing_identifier: None,
option_as_alt: Default::default(),
borderless_game: false,
}
}
}
Expand Down Expand Up @@ -121,6 +123,7 @@ pub(crate) struct State {
standard_frame: Cell<Option<NSRect>>,
is_simple_fullscreen: Cell<bool>,
saved_style: Cell<Option<NSWindowStyleMask>>,
is_borderless_game: Cell<bool>,
}

declare_class!(
Expand Down Expand Up @@ -731,6 +734,7 @@ impl WindowDelegate {
standard_frame: Cell::new(None),
is_simple_fullscreen: Cell::new(false),
saved_style: Cell::new(None),
is_borderless_game: Cell::new(attrs.platform_specific.borderless_game),
});
let delegate: Retained<WindowDelegate> = unsafe { msg_send_id![super(delegate), init] };

Expand Down Expand Up @@ -1417,7 +1421,7 @@ impl WindowDelegate {
}

match (old_fullscreen, fullscreen) {
(None, Some(_)) => {
(None, Some(fullscreen)) => {
// `toggleFullScreen` doesn't work if the `StyleMask` is none, so we
// set a normal style temporarily. The previous state will be
// restored in `WindowDelegate::window_did_exit_fullscreen`.
Expand All @@ -1427,6 +1431,17 @@ impl WindowDelegate {
self.set_style_mask(required);
self.ivars().saved_style.set(Some(curr_mask));
}

// In borderless games, we want to disable the dock and menu bar
// by setting the presentation options. We do this here rather than in
// `window:willUseFullScreenPresentationOptions` because for some reason
// the menu bar remains interactable despite being hidden.
if self.is_borderless_game() && matches!(fullscreen, Fullscreen::Borderless(_)) {
let presentation_options = NSApplicationPresentationOptions::NSApplicationPresentationHideDock
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar;
app.setPresentationOptions(presentation_options);
}

toggle_fullscreen(self.window());
},
(Some(Fullscreen::Borderless(_)), None) => {
Expand Down Expand Up @@ -1813,6 +1828,14 @@ impl WindowExtMacOS for WindowDelegate {
fn option_as_alt(&self) -> OptionAsAlt {
self.view().option_as_alt()
}

fn set_borderless_game(&self, borderless_game: bool) {
self.ivars().is_borderless_game.set(borderless_game);
}

fn is_borderless_game(&self) -> bool {
self.ivars().is_borderless_game.get()
}
}

const DEFAULT_STANDARD_FRAME: NSRect =
Expand Down

0 comments on commit d37c591

Please sign in to comment.