diff --git a/Cargo.toml b/Cargo.toml index 37932f9871..17377fb9c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -136,6 +136,7 @@ objc2-app-kit = { version = "0.2.2", features = [ "NSScreen", "NSTextInputClient", "NSTextInputContext", + "NSToolbar", "NSView", "NSWindow", "NSWindowScripting", diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index 8a22aed48c..7201d2f0df 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -62,6 +62,8 @@ changelog entry. - 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. +- On macOS, add `WindowExtMacOS::set_unified_titlebar` and `WindowAttributesExtMacOS::with_unified_titlebar` + to use a larger style of titlebar. - Add `WindowId::into_raw()` and `from_raw()`. - Add `PointerKind`, `PointerSource`, `ButtonSource`, `FingerId` and `position` to all pointer events as part of the pointer event overhaul. diff --git a/src/platform/macos.rs b/src/platform/macos.rs index 77fa35dc6c..5ab3d943e6 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -156,6 +156,13 @@ pub trait WindowExtMacOS { /// Getter for the [`WindowExtMacOS::set_borderless_game`]. fn is_borderless_game(&self) -> bool; + + /// Makes the titlebar bigger, effectively adding more space around the + /// window controls if the titlebar is invisible. + fn set_unified_titlebar(&self, unified_titlebar: bool); + + /// Getter for the [`WindowExtMacOS::set_unified_titlebar`]. + fn unified_titlebar(&self) -> bool; } impl WindowExtMacOS for dyn Window + '_ { @@ -254,6 +261,18 @@ impl WindowExtMacOS for dyn Window + '_ { let window = self.as_any().downcast_ref::().unwrap(); window.maybe_wait_on_main(|w| w.is_borderless_game()) } + + #[inline] + fn set_unified_titlebar(&self, unified_titlebar: bool) { + let window = self.as_any().downcast_ref::().unwrap(); + window.maybe_wait_on_main(|w| w.set_unified_titlebar(unified_titlebar)) + } + + #[inline] + fn unified_titlebar(&self) -> bool { + let window = self.as_any().downcast_ref::().unwrap(); + window.maybe_wait_on_main(|w| w.unified_titlebar()) + } } /// Corresponds to `NSApplicationActivationPolicy`. @@ -307,6 +326,8 @@ pub trait WindowAttributesExtMacOS { 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; + /// See [`WindowExtMacOS::set_unified_titlebar`] for details on what this means if set. + fn with_unified_titlebar(self, unified_titlebar: bool) -> Self; } impl WindowAttributesExtMacOS for WindowAttributes { @@ -381,6 +402,12 @@ impl WindowAttributesExtMacOS for WindowAttributes { self.platform_specific.borderless_game = borderless_game; self } + + #[inline] + fn with_unified_titlebar(mut self, unified_titlebar: bool) -> Self { + self.platform_specific.unified_titlebar = unified_titlebar; + self + } } pub trait EventLoopBuilderExtMacOS { diff --git a/src/platform_impl/apple/appkit/window_delegate.rs b/src/platform_impl/apple/appkit/window_delegate.rs index e5652ba2f8..ea7272ba8a 100644 --- a/src/platform_impl/apple/appkit/window_delegate.rs +++ b/src/platform_impl/apple/appkit/window_delegate.rs @@ -15,9 +15,10 @@ use objc2_app_kit::{ NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSAppearance, NSAppearanceCustomization, NSAppearanceNameAqua, NSApplication, NSApplicationPresentationOptions, NSBackingStoreType, NSColor, NSDraggingDestination, NSFilenamesPboardType, NSPasteboard, - NSRequestUserAttentionType, NSScreen, NSView, NSWindowButton, NSWindowDelegate, + NSRequestUserAttentionType, NSScreen, NSToolbar, NSView, NSWindowButton, NSWindowDelegate, NSWindowFullScreenButton, NSWindowLevel, NSWindowOcclusionState, NSWindowOrderingMode, NSWindowSharingType, NSWindowStyleMask, NSWindowTabbingMode, NSWindowTitleVisibility, + NSWindowToolbarStyle, }; use objc2_foundation::{ ns_string, CGFloat, MainThreadMarker, NSArray, NSCopying, NSDictionary, NSKeyValueChangeKey, @@ -57,6 +58,7 @@ pub struct PlatformSpecificWindowAttributes { pub tabbing_identifier: Option, pub option_as_alt: OptionAsAlt, pub borderless_game: bool, + pub unified_titlebar: bool, } impl Default for PlatformSpecificWindowAttributes { @@ -75,6 +77,7 @@ impl Default for PlatformSpecificWindowAttributes { tabbing_identifier: None, option_as_alt: Default::default(), borderless_game: false, + unified_titlebar: false, } } } @@ -616,6 +619,12 @@ fn new_window( if attrs.platform_specific.movable_by_window_background { window.setMovableByWindowBackground(true); } + if attrs.platform_specific.unified_titlebar { + unsafe { + window.setToolbar(Some(&NSToolbar::new(mtm))); + window.setToolbarStyle(NSWindowToolbarStyle::Unified); + } + } if !attrs.enabled_buttons.contains(WindowButtons::MAXIMIZE) { if let Some(button) = window.standardWindowButton(NSWindowButton::NSWindowZoomButton) { @@ -1837,6 +1846,32 @@ impl WindowExtMacOS for WindowDelegate { fn is_borderless_game(&self) -> bool { self.ivars().is_borderless_game.get() } + + fn set_unified_titlebar(&self, unified_titlebar: bool) { + let window = self.window(); + + if unified_titlebar { + let mtm = MainThreadMarker::from(self); + + unsafe { + window.setToolbar(Some(&NSToolbar::new(mtm))); + window.setToolbarStyle(NSWindowToolbarStyle::Unified); + } + } else { + unsafe { + window.setToolbar(None); + window.setToolbarStyle(NSWindowToolbarStyle::Automatic); + } + } + } + + fn unified_titlebar(&self) -> bool { + let window = self.window(); + + unsafe { + window.toolbar().is_some() && window.toolbarStyle() == NSWindowToolbarStyle::Unified + } + } } const DEFAULT_STANDARD_FRAME: NSRect =