winit_appkit/lib.rs
1//! # macOS / AppKit
2//!
3//! Winit has [the same macOS version requirements as `rustc`][rustc-macos-version], and is tested
4//! once in a while on as low as macOS 10.14.
5//!
6//! [rustc-macos-version]: https://doc.rust-lang.org/rustc/platform-support/apple-darwin.html#os-version
7//!
8//! ## Custom `NSApplicationDelegate`
9//!
10//! Winit usually handles everything related to the lifecycle events of the application. Sometimes,
11//! though, you might want to do more niche stuff, such as [handle when the user re-activates the
12//! application][reopen]. Such functionality is not exposed directly in Winit, since it would
13//! increase the API surface by quite a lot.
14//!
15//! [reopen]: https://developer.apple.com/documentation/appkit/nsapplicationdelegate/1428638-applicationshouldhandlereopen?language=objc
16//!
17//! Instead, Winit guarantees that it will not register an application delegate, so the solution is
18//! to register your own application delegate, as outlined in the following example (see
19//! `objc2-app-kit` for more detailed information).
20//! ```
21//! use objc2::rc::Retained;
22//! use objc2::runtime::ProtocolObject;
23//! use objc2::{DefinedClass, MainThreadMarker, MainThreadOnly, define_class, msg_send};
24//! use objc2_app_kit::{NSApplication, NSApplicationDelegate};
25//! use objc2_foundation::{NSArray, NSObject, NSObjectProtocol, NSURL};
26//! use winit::event_loop::EventLoop;
27//!
28//! define_class!(
29//! #[unsafe(super(NSObject))]
30//! #[thread_kind = MainThreadOnly]
31//! #[name = "AppDelegate"]
32//! struct AppDelegate;
33//!
34//! unsafe impl NSObjectProtocol for AppDelegate {}
35//!
36//! unsafe impl NSApplicationDelegate for AppDelegate {
37//! #[unsafe(method(application:openURLs:))]
38//! fn application_openURLs(&self, application: &NSApplication, urls: &NSArray<NSURL>) {
39//! // Note: To specifically get `application:openURLs:` to work, you _might_
40//! // have to bundle your application. This is not done in this example.
41//! println!("open urls: {application:?}, {urls:?}");
42//! }
43//! }
44//! );
45//!
46//! impl AppDelegate {
47//! fn new(mtm: MainThreadMarker) -> Retained<Self> {
48//! unsafe { msg_send![super(Self::alloc(mtm).set_ivars(())), init] }
49//! }
50//! }
51//!
52//! fn main() -> Result<(), Box<dyn std::error::Error>> {
53//! let event_loop = EventLoop::new()?;
54//!
55//! let mtm = MainThreadMarker::new().unwrap();
56//! let delegate = AppDelegate::new(mtm);
57//! // Important: Call `sharedApplication` after `EventLoop::new`,
58//! // doing it before is not yet supported.
59//! let app = NSApplication::sharedApplication(mtm);
60//! app.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));
61//!
62//! // event_loop.run_app(&mut my_app);
63//! Ok(())
64//! }
65//! ```
66#![cfg(target_vendor = "apple")] // TODO: Remove once `objc2` allows compiling on all platforms
67#![warn(clippy::exhaustive_enums)]
68
69#[macro_use]
70mod util;
71
72mod app;
73mod app_state;
74mod cursor;
75mod dnd;
76mod event;
77mod event_loop;
78mod ffi;
79mod menu;
80mod monitor;
81mod observer;
82mod view;
83mod window;
84mod window_delegate;
85
86use std::os::raw::c_void;
87
88#[cfg(feature = "serde")]
89use serde::{Deserialize, Serialize};
90#[doc(inline)]
91pub use winit_core::application::macos::ApplicationHandlerExtMacOS;
92use winit_core::event_loop::ActiveEventLoop;
93use winit_core::monitor::MonitorHandle;
94use winit_core::window::{PlatformWindowAttributes, Window};
95
96pub use self::dnd::{Pasteboard, PasteboardType, PasteboardValue};
97pub use self::event::{physicalkey_to_scancode, scancode_to_physicalkey};
98use self::event_loop::ActiveEventLoop as AppKitActiveEventLoop;
99pub use self::event_loop::{EventLoop, PlatformSpecificEventLoopAttributes};
100use self::monitor::MonitorHandle as AppKitMonitorHandle;
101use self::window::Window as AppKitWindow;
102
103/// Additional methods on [`Window`] that are specific to MacOS.
104pub trait WindowExtMacOS {
105 /// Returns whether or not the window is in simple fullscreen mode.
106 fn simple_fullscreen(&self) -> bool;
107
108 /// Toggles a fullscreen mode that doesn't require a new macOS space.
109 /// Returns a boolean indicating whether the transition was successful (this
110 /// won't work if the window was already in the native fullscreen).
111 ///
112 /// This is how fullscreen used to work on macOS in versions before Lion.
113 /// And allows the user to have a fullscreen window without using another
114 /// space or taking control over the entire monitor.
115 ///
116 /// Make sure you only draw your important content inside the safe area so that it does not
117 /// overlap with the notch on newer devices, see [`Window::safe_area`] for details.
118 fn set_simple_fullscreen(&self, fullscreen: bool) -> bool;
119
120 /// Returns whether or not the window has shadow.
121 fn has_shadow(&self) -> bool;
122
123 /// Sets whether or not the window has shadow.
124 fn set_has_shadow(&self, has_shadow: bool);
125
126 /// Group windows together by using the same tabbing identifier.
127 ///
128 /// <https://developer.apple.com/documentation/appkit/nswindow/1644704-tabbingidentifier>
129 fn set_tabbing_identifier(&self, identifier: &str);
130
131 /// Returns the window's tabbing identifier.
132 fn tabbing_identifier(&self) -> String;
133
134 /// Select next tab.
135 fn select_next_tab(&self);
136
137 /// Select previous tab.
138 fn select_previous_tab(&self);
139
140 /// Select the tab with the given index.
141 ///
142 /// Will no-op when the index is out of bounds.
143 fn select_tab_at_index(&self, index: usize);
144
145 /// Get the number of tabs in the window tab group.
146 fn num_tabs(&self) -> usize;
147
148 /// Get the window's edit state.
149 ///
150 /// # Examples
151 ///
152 /// ```ignore
153 /// WindowEvent::CloseRequested => {
154 /// if window.is_document_edited() {
155 /// // Show the user a save pop-up or similar
156 /// } else {
157 /// // Close the window
158 /// drop(window);
159 /// }
160 /// }
161 /// ```
162 fn is_document_edited(&self) -> bool;
163
164 /// Put the window in a state which indicates a file save is required.
165 fn set_document_edited(&self, edited: bool);
166
167 /// Set option as alt behavior as described in [`OptionAsAlt`].
168 ///
169 /// This will ignore diacritical marks and accent characters from
170 /// being processed as received characters. Instead, the input
171 /// device's raw character will be placed in event queues with the
172 /// Alt modifier set.
173 fn set_option_as_alt(&self, option_as_alt: OptionAsAlt);
174
175 /// Getter for the [`WindowExtMacOS::set_option_as_alt`].
176 fn option_as_alt(&self) -> OptionAsAlt;
177
178 /// Disable the Menu Bar and Dock in Simple or Borderless Fullscreen mode. Useful for games.
179 /// The effect is applied when [`WindowExtMacOS::set_simple_fullscreen`] or
180 /// [`Window::set_fullscreen`] is called.
181 fn set_borderless_game(&self, borderless_game: bool);
182
183 /// Getter for the [`WindowExtMacOS::set_borderless_game`].
184 fn is_borderless_game(&self) -> bool;
185
186 /// Makes the titlebar bigger, effectively adding more space around the
187 /// window controls if the titlebar is invisible.
188 fn set_unified_titlebar(&self, unified_titlebar: bool);
189
190 /// Getter for the [`WindowExtMacOS::set_unified_titlebar`].
191 fn unified_titlebar(&self) -> bool;
192
193 /// Sets whether the window can be shown on the same Space as a fullscreen window.
194 ///
195 /// This corresponds to [`NSWindowCollectionBehaviorFullScreenAuxiliary`], and is useful
196 /// for floating palettes, inspectors and other secondary windows accompanying a fullscreen
197 /// window. Without it, ordering a new window on screen while another window of the
198 /// application is fullscreen on the active Space makes macOS switch Spaces or attempt
199 /// Split View tiling.
200 ///
201 /// A window marked as fullscreen auxiliary cannot itself enter (native) fullscreen;
202 /// [`Window::set_fullscreen`] will warn and do nothing. Call
203 /// `set_fullscreen_auxiliary(false)` first if you want to make the window fullscreen.
204 ///
205 /// [`NSWindowCollectionBehaviorFullScreenAuxiliary`]: https://developer.apple.com/documentation/appkit/nswindow/collectionbehavior-swift.struct/fullscreenauxiliary?language=objc
206 /// [`Window::set_fullscreen`]: winit_core::window::Window::set_fullscreen
207 fn set_fullscreen_auxiliary(&self, fullscreen_auxiliary: bool);
208
209 /// Getter for the [`WindowExtMacOS::set_fullscreen_auxiliary`].
210 fn fullscreen_auxiliary(&self) -> bool;
211
212 /// Sets the material drawn behind the window when it is blurred.
213 ///
214 /// Takes effect the next time the window is blurred, and immediately if it already is. See
215 /// [`Window::set_blur`].
216 ///
217 /// [`Window::set_blur`]: winit_core::window::Window::set_blur
218 fn set_blur_material(&self, blur_material: BlurMaterial);
219
220 /// Getter for the [`WindowExtMacOS::set_blur_material`].
221 fn blur_material(&self) -> BlurMaterial;
222}
223
224impl WindowExtMacOS for dyn Window + '_ {
225 #[inline]
226 fn simple_fullscreen(&self) -> bool {
227 let window = self.cast_ref::<AppKitWindow>().unwrap();
228 window.maybe_wait_on_main(|w| w.simple_fullscreen())
229 }
230
231 #[inline]
232 fn set_simple_fullscreen(&self, fullscreen: bool) -> bool {
233 let window = self.cast_ref::<AppKitWindow>().unwrap();
234 window.maybe_wait_on_main(move |w| w.set_simple_fullscreen(fullscreen))
235 }
236
237 #[inline]
238 fn has_shadow(&self) -> bool {
239 let window = self.cast_ref::<AppKitWindow>().unwrap();
240 window.maybe_wait_on_main(|w| w.has_shadow())
241 }
242
243 #[inline]
244 fn set_has_shadow(&self, has_shadow: bool) {
245 let window = self.cast_ref::<AppKitWindow>().unwrap();
246 window.maybe_wait_on_main(move |w| w.set_has_shadow(has_shadow));
247 }
248
249 #[inline]
250 fn set_tabbing_identifier(&self, identifier: &str) {
251 let window = self.cast_ref::<AppKitWindow>().unwrap();
252 window.maybe_wait_on_main(|w| w.set_tabbing_identifier(identifier))
253 }
254
255 #[inline]
256 fn tabbing_identifier(&self) -> String {
257 let window = self.cast_ref::<AppKitWindow>().unwrap();
258 window.maybe_wait_on_main(|w| w.tabbing_identifier())
259 }
260
261 #[inline]
262 fn select_next_tab(&self) {
263 let window = self.cast_ref::<AppKitWindow>().unwrap();
264 window.maybe_wait_on_main(|w| w.select_next_tab());
265 }
266
267 #[inline]
268 fn select_previous_tab(&self) {
269 let window = self.cast_ref::<AppKitWindow>().unwrap();
270 window.maybe_wait_on_main(|w| w.select_previous_tab());
271 }
272
273 #[inline]
274 fn select_tab_at_index(&self, index: usize) {
275 let window = self.cast_ref::<AppKitWindow>().unwrap();
276 window.maybe_wait_on_main(move |w| w.select_tab_at_index(index));
277 }
278
279 #[inline]
280 fn num_tabs(&self) -> usize {
281 let window = self.cast_ref::<AppKitWindow>().unwrap();
282 window.maybe_wait_on_main(|w| w.num_tabs())
283 }
284
285 #[inline]
286 fn is_document_edited(&self) -> bool {
287 let window = self.cast_ref::<AppKitWindow>().unwrap();
288 window.maybe_wait_on_main(|w| w.is_document_edited())
289 }
290
291 #[inline]
292 fn set_document_edited(&self, edited: bool) {
293 let window = self.cast_ref::<AppKitWindow>().unwrap();
294 window.maybe_wait_on_main(move |w| w.set_document_edited(edited));
295 }
296
297 #[inline]
298 fn set_option_as_alt(&self, option_as_alt: OptionAsAlt) {
299 let window = self.cast_ref::<AppKitWindow>().unwrap();
300 window.maybe_wait_on_main(move |w| w.set_option_as_alt(option_as_alt));
301 }
302
303 #[inline]
304 fn option_as_alt(&self) -> OptionAsAlt {
305 let window = self.cast_ref::<AppKitWindow>().unwrap();
306 window.maybe_wait_on_main(|w| w.option_as_alt())
307 }
308
309 #[inline]
310 fn set_borderless_game(&self, borderless_game: bool) {
311 let window = self.cast_ref::<AppKitWindow>().unwrap();
312 window.maybe_wait_on_main(|w| w.set_borderless_game(borderless_game))
313 }
314
315 #[inline]
316 fn is_borderless_game(&self) -> bool {
317 let window = self.cast_ref::<AppKitWindow>().unwrap();
318 window.maybe_wait_on_main(|w| w.is_borderless_game())
319 }
320
321 #[inline]
322 fn set_unified_titlebar(&self, unified_titlebar: bool) {
323 let window = self.cast_ref::<AppKitWindow>().unwrap();
324 window.maybe_wait_on_main(|w| w.set_unified_titlebar(unified_titlebar))
325 }
326
327 #[inline]
328 fn unified_titlebar(&self) -> bool {
329 let window = self.cast_ref::<AppKitWindow>().unwrap();
330 window.maybe_wait_on_main(|w| w.unified_titlebar())
331 }
332
333 #[inline]
334 fn set_fullscreen_auxiliary(&self, fullscreen_auxiliary: bool) {
335 let window = self.cast_ref::<AppKitWindow>().unwrap();
336 window.maybe_wait_on_main(move |w| w.set_fullscreen_auxiliary(fullscreen_auxiliary))
337 }
338
339 #[inline]
340 fn fullscreen_auxiliary(&self) -> bool {
341 let window = self.cast_ref::<AppKitWindow>().unwrap();
342 window.maybe_wait_on_main(|w| w.fullscreen_auxiliary())
343 }
344
345 #[inline]
346 fn set_blur_material(&self, blur_material: BlurMaterial) {
347 let window = self.cast_ref::<AppKitWindow>().unwrap();
348 window.maybe_wait_on_main(move |w| w.set_blur_material(blur_material))
349 }
350
351 #[inline]
352 fn blur_material(&self) -> BlurMaterial {
353 let window = self.cast_ref::<AppKitWindow>().unwrap();
354 window.maybe_wait_on_main(|w| w.blur_material())
355 }
356}
357
358/// Corresponds to `NSApplicationActivationPolicy`.
359#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
360#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
361#[non_exhaustive]
362pub enum ActivationPolicy {
363 /// Corresponds to `NSApplicationActivationPolicyRegular`.
364 #[default]
365 Regular,
366
367 /// Corresponds to `NSApplicationActivationPolicyAccessory`.
368 Accessory,
369
370 /// Corresponds to `NSApplicationActivationPolicyProhibited`.
371 Prohibited,
372}
373
374/// Window attributes that are specific to MacOS.
375///
376/// **Note:** Properties dealing with the titlebar will be overwritten by the
377/// [`WindowAttributes::with_decorations`] method:
378/// - `with_titlebar_transparent`
379/// - `with_title_hidden`
380/// - `with_titlebar_hidden`
381/// - `with_titlebar_buttons_hidden`
382/// - `with_fullsize_content_view`
383///
384/// [`WindowAttributes::with_decorations`]: crate::window::WindowAttributes::with_decorations
385#[derive(Clone, Debug, PartialEq)]
386pub struct WindowAttributesMacOS {
387 pub(crate) movable_by_window_background: bool,
388 pub(crate) titlebar_transparent: bool,
389 pub(crate) title_hidden: bool,
390 pub(crate) titlebar_hidden: bool,
391 pub(crate) titlebar_buttons_hidden: bool,
392 pub(crate) fullsize_content_view: bool,
393 pub(crate) disallow_hidpi: bool,
394 pub(crate) has_shadow: bool,
395 pub(crate) tabbing_identifier: Option<String>,
396 pub(crate) option_as_alt: OptionAsAlt,
397 pub(crate) borderless_game: bool,
398 pub(crate) unified_titlebar: bool,
399 pub(crate) panel: bool,
400 pub(crate) fullscreen_auxiliary: bool,
401 pub(crate) blur_material: BlurMaterial,
402}
403
404impl WindowAttributesMacOS {
405 /// Enables click-and-drag behavior for the entire window, not just the titlebar.
406 #[inline]
407 pub fn with_movable_by_window_background(mut self, movable_by_window_background: bool) -> Self {
408 self.movable_by_window_background = movable_by_window_background;
409 self
410 }
411
412 /// Sets the material drawn behind the window when it is blurred.
413 ///
414 /// Has no effect unless the window is blurred, see [`Window::set_blur`].
415 ///
416 /// [`Window::set_blur`]: winit_core::window::Window::set_blur
417 #[inline]
418 pub fn with_blur_material(mut self, blur_material: BlurMaterial) -> Self {
419 self.blur_material = blur_material;
420 self
421 }
422
423 /// Makes the titlebar transparent and allows the content to appear behind it.
424 #[inline]
425 pub fn with_titlebar_transparent(mut self, titlebar_transparent: bool) -> Self {
426 self.titlebar_transparent = titlebar_transparent;
427 self
428 }
429
430 /// Hides the window titlebar.
431 #[inline]
432 pub fn with_titlebar_hidden(mut self, titlebar_hidden: bool) -> Self {
433 self.titlebar_hidden = titlebar_hidden;
434 self
435 }
436
437 /// Hides the window titlebar buttons.
438 #[inline]
439 pub fn with_titlebar_buttons_hidden(mut self, titlebar_buttons_hidden: bool) -> Self {
440 self.titlebar_buttons_hidden = titlebar_buttons_hidden;
441 self
442 }
443
444 /// Hides the window title.
445 #[inline]
446 pub fn with_title_hidden(mut self, title_hidden: bool) -> Self {
447 self.title_hidden = title_hidden;
448 self
449 }
450
451 /// Makes the window content appear behind the titlebar.
452 #[inline]
453 pub fn with_fullsize_content_view(mut self, fullsize_content_view: bool) -> Self {
454 self.fullsize_content_view = fullsize_content_view;
455 self
456 }
457
458 #[inline]
459 pub fn with_disallow_hidpi(mut self, disallow_hidpi: bool) -> Self {
460 self.disallow_hidpi = disallow_hidpi;
461 self
462 }
463
464 #[inline]
465 pub fn with_has_shadow(mut self, has_shadow: bool) -> Self {
466 self.has_shadow = has_shadow;
467 self
468 }
469
470 /// Defines the window tabbing identifier.
471 ///
472 /// <https://developer.apple.com/documentation/appkit/nswindow/1644704-tabbingidentifier>
473 #[inline]
474 pub fn with_tabbing_identifier(mut self, tabbing_identifier: &str) -> Self {
475 self.tabbing_identifier.replace(tabbing_identifier.to_string());
476 self
477 }
478
479 /// Set how the <kbd>Option</kbd> keys are interpreted.
480 ///
481 /// See [`WindowExtMacOS::set_option_as_alt`] for details on what this means if set.
482 #[inline]
483 pub fn with_option_as_alt(mut self, option_as_alt: OptionAsAlt) -> Self {
484 self.option_as_alt = option_as_alt;
485 self
486 }
487
488 /// See [`WindowExtMacOS::set_borderless_game`] for details on what this means if set.
489 #[inline]
490 pub fn with_borderless_game(mut self, borderless_game: bool) -> Self {
491 self.borderless_game = borderless_game;
492 self
493 }
494
495 /// See [`WindowExtMacOS::set_unified_titlebar`] for details on what this means if set.
496 #[inline]
497 pub fn with_unified_titlebar(mut self, unified_titlebar: bool) -> Self {
498 self.unified_titlebar = unified_titlebar;
499 self
500 }
501
502 /// Use [`NSPanel`] window with [`NonactivatingPanel`] window style mask instead of
503 /// [`NSWindow`].
504 ///
505 /// [`NSWindow`]: https://developer.apple.com/documentation/appkit/NSWindow?language=objc
506 /// [`NSPanel`]: https://developer.apple.com/documentation/appkit/NSPanel?language=objc
507 /// [`NonactivatingPanel`]: https://developer.apple.com/documentation/appkit/nswindow/stylemask-swift.struct/nonactivatingpanel?language=objc
508 #[inline]
509 pub fn with_panel(mut self, panel: bool) -> Self {
510 self.panel = panel;
511 self
512 }
513
514 /// See [`WindowExtMacOS::set_fullscreen_auxiliary`] for details on what this means if set.
515 ///
516 /// Contrary to the runtime setter, setting this attribute guarantees that the collection
517 /// behavior is already in place when the window is first ordered on screen, which is
518 /// required to avoid disturbing an active fullscreen Space.
519 #[inline]
520 pub fn with_fullscreen_auxiliary(mut self, fullscreen_auxiliary: bool) -> Self {
521 self.fullscreen_auxiliary = fullscreen_auxiliary;
522 self
523 }
524}
525
526impl Default for WindowAttributesMacOS {
527 #[inline]
528 fn default() -> Self {
529 Self {
530 movable_by_window_background: false,
531 titlebar_transparent: false,
532 title_hidden: false,
533 titlebar_hidden: false,
534 titlebar_buttons_hidden: false,
535 fullsize_content_view: false,
536 disallow_hidpi: false,
537 has_shadow: true,
538 tabbing_identifier: None,
539 option_as_alt: Default::default(),
540 borderless_game: false,
541 unified_titlebar: false,
542 panel: false,
543 fullscreen_auxiliary: false,
544 blur_material: Default::default(),
545 }
546 }
547}
548
549impl PlatformWindowAttributes for WindowAttributesMacOS {
550 fn box_clone(&self) -> Box<dyn PlatformWindowAttributes> {
551 Box::from(self.clone())
552 }
553}
554
555pub trait EventLoopBuilderExtMacOS {
556 /// Sets the activation policy for the application. If used, this will override
557 /// any relevant settings provided in the package manifest.
558 /// For instance, `with_activation_policy(ActivationPolicy::Regular)` will prevent
559 /// the application from running as an "agent", even if LSUIElement is set to true.
560 ///
561 /// If unused, the Winit will honor the package manifest.
562 ///
563 /// # Example
564 ///
565 /// Set the activation policy to "accessory".
566 ///
567 /// ```
568 /// use winit::event_loop::EventLoop;
569 /// #[cfg(target_os = "macos")]
570 /// use winit::platform::macos::{ActivationPolicy, EventLoopBuilderExtMacOS};
571 ///
572 /// let mut builder = EventLoop::builder();
573 /// #[cfg(target_os = "macos")]
574 /// builder.with_activation_policy(ActivationPolicy::Accessory);
575 /// # if false { // We can't test this part
576 /// let event_loop = builder.build();
577 /// # }
578 /// ```
579 fn with_activation_policy(&mut self, activation_policy: ActivationPolicy) -> &mut Self;
580
581 /// Used to control whether a default menubar menu is created.
582 ///
583 /// Menu creation is enabled by default.
584 ///
585 /// # Example
586 ///
587 /// Disable creating a default menubar.
588 ///
589 /// ```
590 /// use winit::event_loop::EventLoop;
591 /// #[cfg(target_os = "macos")]
592 /// use winit::platform::macos::EventLoopBuilderExtMacOS;
593 ///
594 /// let mut builder = EventLoop::builder();
595 /// #[cfg(target_os = "macos")]
596 /// builder.with_default_menu(false);
597 /// # if false { // We can't test this part
598 /// let event_loop = builder.build();
599 /// # }
600 /// ```
601 fn with_default_menu(&mut self, enable: bool) -> &mut Self;
602
603 /// Used to prevent the application from automatically activating when launched if
604 /// another application is already active.
605 ///
606 /// The default behavior is to ignore other applications and activate when launched.
607 fn with_activate_ignoring_other_apps(&mut self, ignore: bool) -> &mut Self;
608}
609
610/// Additional methods on [`MonitorHandle`] that are specific to MacOS.
611pub trait MonitorHandleExtMacOS {
612 /// Returns a pointer to the NSScreen representing this monitor.
613 fn ns_screen(&self) -> Option<*mut c_void>;
614}
615
616impl MonitorHandleExtMacOS for MonitorHandle {
617 fn ns_screen(&self) -> Option<*mut c_void> {
618 let monitor = self.cast_ref::<AppKitMonitorHandle>().unwrap();
619 // SAFETY: We only use the marker to get a pointer
620 let mtm = unsafe { objc2::MainThreadMarker::new_unchecked() };
621 monitor.ns_screen(mtm).map(|s| objc2::rc::Retained::as_ptr(&s) as _)
622 }
623}
624
625/// Additional methods on [`ActiveEventLoop`] that are specific to macOS.
626pub trait ActiveEventLoopExtMacOS {
627 /// Hide the entire application. In most applications this is typically triggered with
628 /// Command-H.
629 fn hide_application(&self);
630 /// Hide the other applications. In most applications this is typically triggered with
631 /// Command+Option-H.
632 fn hide_other_applications(&self);
633 /// Set whether the system can automatically organize windows into tabs.
634 ///
635 /// <https://developer.apple.com/documentation/appkit/nswindow/1646657-allowsautomaticwindowtabbing>
636 fn set_allows_automatic_window_tabbing(&self, enabled: bool);
637 /// Returns whether the system can automatically organize windows into tabs.
638 fn allows_automatic_window_tabbing(&self) -> bool;
639}
640
641impl ActiveEventLoopExtMacOS for dyn ActiveEventLoop + '_ {
642 fn hide_application(&self) {
643 let event_loop =
644 self.cast_ref::<AppKitActiveEventLoop>().expect("non macOS event loop on macOS");
645 event_loop.hide_application()
646 }
647
648 fn hide_other_applications(&self) {
649 let event_loop =
650 self.cast_ref::<AppKitActiveEventLoop>().expect("non macOS event loop on macOS");
651 event_loop.hide_other_applications()
652 }
653
654 fn set_allows_automatic_window_tabbing(&self, enabled: bool) {
655 let event_loop =
656 self.cast_ref::<AppKitActiveEventLoop>().expect("non macOS event loop on macOS");
657 event_loop.set_allows_automatic_window_tabbing(enabled);
658 }
659
660 fn allows_automatic_window_tabbing(&self) -> bool {
661 let event_loop =
662 self.cast_ref::<AppKitActiveEventLoop>().expect("non macOS event loop on macOS");
663 event_loop.allows_automatic_window_tabbing()
664 }
665}
666
667/// Option as alt behavior.
668///
669/// The default is `None`.
670#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
671#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
672#[allow(clippy::exhaustive_enums)]
673pub enum OptionAsAlt {
674 /// The left `Option` key is treated as `Alt`.
675 OnlyLeft,
676
677 /// The right `Option` key is treated as `Alt`.
678 OnlyRight,
679
680 /// Both `Option` keys are treated as `Alt`.
681 Both,
682
683 /// No special handling is applied for `Option` key.
684 #[default]
685 None,
686}
687
688/// The material drawn behind a blurred window, corresponding to `NSVisualEffectMaterial`.
689///
690/// Only the materials that AppKit renders behind the window are listed. The remaining
691/// `NSVisualEffectMaterial` values are meant for opaque backgrounds, and draw a placeholder
692/// rather than a blur when used this way.
693///
694/// The material determines the tint and translucency of the blur, so the result is not an
695/// untinted blur of a chosen radius; AppKit exposes no public API for that. Enable the
696/// `private-apple-apis` Cargo feature for the private one, which this setting then no longer
697/// affects.
698///
699/// See [`Window::set_blur`] for the blur itself.
700///
701/// [`Window::set_blur`]: winit_core::window::Window::set_blur
702#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
703#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
704#[non_exhaustive]
705pub enum BlurMaterial {
706 /// Corresponds to `NSVisualEffectMaterialFullScreenUI`.
707 ///
708 /// Tints the least of the available materials, making it the closest to a plain backdrop
709 /// blur. This is the default.
710 #[default]
711 FullScreenUI,
712
713 /// Corresponds to `NSVisualEffectMaterialHUDWindow`.
714 HudWindow,
715
716 /// Corresponds to `NSVisualEffectMaterialMenu`.
717 Menu,
718
719 /// Corresponds to `NSVisualEffectMaterialPopover`.
720 Popover,
721
722 /// Corresponds to `NSVisualEffectMaterialSidebar`.
723 Sidebar,
724
725 /// Corresponds to `NSVisualEffectMaterialSelection`.
726 Selection,
727
728 /// Corresponds to `NSVisualEffectMaterialTitlebar`.
729 Titlebar,
730
731 /// Corresponds to `NSVisualEffectMaterialHeaderView`.
732 HeaderView,
733
734 /// Corresponds to `NSVisualEffectMaterialToolTip`.
735 ToolTip,
736
737 /// Corresponds to `NSVisualEffectMaterialUnderWindowBackground`.
738 ///
739 /// Tints heavily towards the window's background colour, which can leave the blur barely
740 /// visible over light content.
741 UnderWindowBackground,
742}