async_winit/platform/
macos.rs

1/*
2
3`async-winit` is free software: you can redistribute it and/or modify it under the terms of one of
4the following licenses:
5
6* GNU Lesser General Public License as published by the Free Software Foundation, either
7  version 3 of the License, or (at your option) any later version.
8* Mozilla Public License as published by the Mozilla Foundation, version 2.
9
10`async-winit` is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
11the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General
12Public License and the Patron License for more details.
13
14You should have received a copy of the GNU Lesser General Public License and the Mozilla
15Public License along with `async-winit`. If not, see <https://www.gnu.org/licenses/>.
16
17*/
18
19// This file is partially derived from `winit`, which was originally created by Pierre Krieger and
20// contributers. It was originally released under the MIT license.
21
22//! Platform-specific macOS features.
23
24#[doc(inline)]
25pub use winit::platform::macos::{ActivationPolicy, OptionAsAlt};
26
27use winit::platform::macos::{EventLoopBuilderExtMacOS as _, WindowExtMacOS as _};
28
29use std::os::raw::c_void;
30
31use super::__private as sealed;
32use crate::event_loop::EventLoopBuilder;
33use crate::window::{Window, WindowBuilder};
34use crate::ThreadSafety;
35
36/// Additional methods on [`Window`] that are specific to MacOS.
37///
38/// [`Window`]: crate::window::Window
39pub trait WindowExtMacOS: sealed::WindowPrivate {
40    /// Returns a pointer to the cocoa `NSWindow` that is used by this window.
41    ///
42    /// The pointer will become invalid when the [`Window`] is destroyed.
43    fn ns_window(&self) -> *mut c_void;
44
45    /// Returns a pointer to the cocoa `NSView` that is used by this window.
46    ///
47    /// The pointer will become invalid when the [`Window`] is destroyed.
48    fn ns_view(&self) -> *mut c_void;
49
50    /// Returns whether or not the window is in simple fullscreen mode.
51    fn simple_fullscreen(&self) -> bool;
52
53    /// Toggles a fullscreen mode that doesn't require a new macOS space.
54    /// Returns a boolean indicating whether the transition was successful (this
55    /// won't work if the window was already in the native fullscreen).
56    ///
57    /// This is how fullscreen used to work on macOS in versions before Lion.
58    /// And allows the user to have a fullscreen window without using another
59    /// space or taking control over the entire monitor.
60    fn set_simple_fullscreen(&self, fullscreen: bool) -> bool;
61
62    /// Returns whether or not the window has shadow.
63    fn has_shadow(&self) -> bool;
64
65    /// Sets whether or not the window has shadow.
66    fn set_has_shadow(&self, has_shadow: bool);
67
68    /// Get the window's edit state.
69    ///
70    /// # Examples
71    ///
72    /// ```ignore
73    /// WindowEvent::CloseRequested => {
74    ///     if window.is_document_edited() {
75    ///         // Show the user a save pop-up or similar
76    ///     } else {
77    ///         // Close the window
78    ///         drop(window);
79    ///     }
80    /// }
81    /// ```
82    fn is_document_edited(&self) -> bool;
83
84    /// Put the window in a state which indicates a file save is required.
85    fn set_document_edited(&self, edited: bool);
86
87    /// Set option as alt behavior as described in [`OptionAsAlt`].
88    ///
89    /// This will ignore diacritical marks and accent characters from
90    /// being processed as received characters. Instead, the input
91    /// device's raw character will be placed in event queues with the
92    /// Alt modifier set.
93    fn set_option_as_alt(&self, option_as_alt: OptionAsAlt);
94
95    /// Getter for the [`WindowExtMacOS::set_option_as_alt`].
96    fn option_as_alt(&self) -> OptionAsAlt;
97}
98
99impl<TS: ThreadSafety> WindowExtMacOS for Window<TS> {
100    fn ns_view(&self) -> *mut c_void {
101        self.window().ns_view()
102    }
103
104    fn ns_window(&self) -> *mut c_void {
105        self.window().ns_window()
106    }
107
108    fn simple_fullscreen(&self) -> bool {
109        self.window().simple_fullscreen()
110    }
111
112    fn set_simple_fullscreen(&self, fullscreen: bool) -> bool {
113        self.window().set_simple_fullscreen(fullscreen)
114    }
115
116    fn has_shadow(&self) -> bool {
117        self.window().has_shadow()
118    }
119
120    fn set_has_shadow(&self, has_shadow: bool) {
121        self.window().set_has_shadow(has_shadow)
122    }
123
124    fn is_document_edited(&self) -> bool {
125        self.window().is_document_edited()
126    }
127
128    fn set_document_edited(&self, edited: bool) {
129        self.window().set_document_edited(edited)
130    }
131
132    fn set_option_as_alt(&self, option_as_alt: OptionAsAlt) {
133        self.window().set_option_as_alt(option_as_alt)
134    }
135
136    fn option_as_alt(&self) -> OptionAsAlt {
137        self.window().option_as_alt()
138    }
139}
140
141/// Additional methods on [`WindowBuilder`] that are specific to MacOS.
142///
143/// **Note:** Properties dealing with the titlebar will be overwritten by the [`WindowBuilder::with_decorations`] method:
144/// - `with_titlebar_transparent`
145/// - `with_title_hidden`
146/// - `with_titlebar_hidden`
147/// - `with_titlebar_buttons_hidden`
148/// - `with_fullsize_content_view`
149///
150/// [`WindowBuilder`]: crate::window::WindowBuilder
151pub trait WindowBuilderExtMacOS: sealed::WindowBuilderPrivate {
152    /// Enables click-and-drag behavior for the entire window, not just the titlebar.
153    fn with_movable_by_window_background(self, movable_by_window_background: bool)
154        -> WindowBuilder;
155    /// Makes the titlebar transparent and allows the content to appear behind it.
156    fn with_titlebar_transparent(self, titlebar_transparent: bool) -> WindowBuilder;
157    /// Hides the window title.
158    fn with_title_hidden(self, title_hidden: bool) -> WindowBuilder;
159    /// Hides the window titlebar.
160    fn with_titlebar_hidden(self, titlebar_hidden: bool) -> WindowBuilder;
161    /// Hides the window titlebar buttons.
162    fn with_titlebar_buttons_hidden(self, titlebar_buttons_hidden: bool) -> WindowBuilder;
163    /// Makes the window content appear behind the titlebar.
164    fn with_fullsize_content_view(self, fullsize_content_view: bool) -> WindowBuilder;
165    fn with_disallow_hidpi(self, disallow_hidpi: bool) -> WindowBuilder;
166    fn with_has_shadow(self, has_shadow: bool) -> WindowBuilder;
167    /// Window accepts click-through mouse events.
168    fn with_accepts_first_mouse(self, accepts_first_mouse: bool) -> WindowBuilder;
169
170    /// Set whether the `OptionAsAlt` key is interpreted as the `Alt` modifier.
171    ///
172    /// See [`WindowExtMacOS::set_option_as_alt`] for details on what this means if set.
173    fn with_option_as_alt(self, option_as_alt: OptionAsAlt) -> WindowBuilder;
174}
175
176impl WindowBuilderExtMacOS for WindowBuilder {
177    fn with_accepts_first_mouse(mut self, accepts_first_mouse: bool) -> WindowBuilder {
178        self.platform.accepts_first_mouse = Some(accepts_first_mouse);
179        self
180    }
181
182    fn with_movable_by_window_background(
183        mut self,
184        movable_by_window_background: bool,
185    ) -> WindowBuilder {
186        self.platform.movable_by_window_background = Some(movable_by_window_background);
187        self
188    }
189
190    fn with_disallow_hidpi(mut self, disallow_hidpi: bool) -> WindowBuilder {
191        self.platform.disallow_hidpi = Some(disallow_hidpi);
192        self
193    }
194
195    fn with_has_shadow(mut self, has_shadow: bool) -> WindowBuilder {
196        self.platform.has_shadow = Some(has_shadow);
197        self
198    }
199
200    fn with_fullsize_content_view(mut self, fullsize_content_view: bool) -> WindowBuilder {
201        self.platform.fullsize_content_view = Some(fullsize_content_view);
202        self
203    }
204
205    fn with_titlebar_buttons_hidden(mut self, titlebar_buttons_hidden: bool) -> WindowBuilder {
206        self.platform.titlebar_buttons_hidden = Some(titlebar_buttons_hidden);
207        self
208    }
209
210    fn with_titlebar_hidden(mut self, titlebar_hidden: bool) -> WindowBuilder {
211        self.platform.titlebar_hidden = Some(titlebar_hidden);
212        self
213    }
214
215    fn with_option_as_alt(mut self, option_as_alt: OptionAsAlt) -> WindowBuilder {
216        self.platform.option_as_alt = Some(option_as_alt);
217        self
218    }
219
220    fn with_title_hidden(mut self, title_hidden: bool) -> WindowBuilder {
221        self.platform.title_hidden = Some(title_hidden);
222        self
223    }
224
225    fn with_titlebar_transparent(mut self, titlebar_transparent: bool) -> WindowBuilder {
226        self.platform.titlebar_transparent = Some(titlebar_transparent);
227        self
228    }
229}
230
231/// Additional methods on [`EventLoopBuilder`] that are specific to MacOS.
232///
233/// [`EventLoopBuilder`]: crate::event_loop::EventLoopBuilder
234pub trait EventLoopBuilderExtMacOS: sealed::EventLoopBuilderPrivate {
235    /// Sets the activation policy for the application.
236    ///
237    /// It is set to [`ActivationPolicy::Regular`] by default.
238    ///
239    /// # Example
240    ///
241    /// Set the activation policy to "accessory".
242    ///
243    /// ```
244    /// use winit::event_loop::EventLoopBuilder;
245    /// #[cfg(target_os = "macos")]
246    /// use winit::platform::macos::{EventLoopBuilderExtMacOS, ActivationPolicy};
247    ///
248    /// let mut builder = EventLoopBuilder::new();
249    /// #[cfg(target_os = "macos")]
250    /// builder.with_activation_policy(ActivationPolicy::Accessory);
251    /// # if false { // We can't test this part
252    /// let event_loop = builder.build();
253    /// # }
254    /// ```
255    fn with_activation_policy(&mut self, activation_policy: ActivationPolicy) -> &mut Self;
256
257    /// Used to control whether a default menubar menu is created.
258    ///
259    /// Menu creation is enabled by default.
260    ///
261    /// # Example
262    ///
263    /// Disable creating a default menubar.
264    ///
265    /// ```
266    /// use winit::event_loop::EventLoopBuilder;
267    /// #[cfg(target_os = "macos")]
268    /// use winit::platform::macos::EventLoopBuilderExtMacOS;
269    ///
270    /// let mut builder = EventLoopBuilder::new();
271    /// #[cfg(target_os = "macos")]
272    /// builder.with_default_menu(false);
273    /// # if false { // We can't test this part
274    /// let event_loop = builder.build();
275    /// # }
276    /// ```
277    fn with_default_menu(&mut self, enable: bool) -> &mut Self;
278
279    /// Used to prevent the application from automatically activating when launched if
280    /// another application is already active.
281    ///
282    /// The default behavior is to ignore other applications and activate when launched.
283    fn with_activate_ignoring_other_apps(&mut self, ignore: bool) -> &mut Self;
284}
285
286impl EventLoopBuilderExtMacOS for EventLoopBuilder {
287    fn with_activate_ignoring_other_apps(&mut self, ignore: bool) -> &mut Self {
288        self.inner.with_activate_ignoring_other_apps(ignore);
289        self
290    }
291
292    fn with_activation_policy(&mut self, activation_policy: ActivationPolicy) -> &mut Self {
293        self.inner.with_activation_policy(activation_policy);
294        self
295    }
296
297    fn with_default_menu(&mut self, enable: bool) -> &mut Self {
298        self.inner.with_default_menu(enable);
299        self
300    }
301}
302
303#[derive(Default)]
304pub(crate) struct PlatformSpecific {
305    movable_by_window_background: Option<bool>,
306    titlebar_transparent: Option<bool>,
307    title_hidden: Option<bool>,
308    titlebar_hidden: Option<bool>,
309    titlebar_buttons_hidden: Option<bool>,
310    fullsize_content_view: Option<bool>,
311    disallow_hidpi: Option<bool>,
312    has_shadow: Option<bool>,
313    accepts_first_mouse: Option<bool>,
314    option_as_alt: Option<OptionAsAlt>,
315}
316
317impl PlatformSpecific {
318    pub(crate) fn apply_to(
319        self,
320        mut wb: winit::window::WindowBuilder,
321    ) -> winit::window::WindowBuilder {
322        use winit::platform::macos::WindowBuilderExtMacOS as _;
323
324        if let Some(movable_by_window_background) = self.movable_by_window_background {
325            wb = wb.with_movable_by_window_background(movable_by_window_background);
326        }
327
328        if let Some(titlebar_transparent) = self.titlebar_transparent {
329            wb = wb.with_titlebar_transparent(titlebar_transparent);
330        }
331
332        if let Some(title_hidden) = self.title_hidden {
333            wb = wb.with_title_hidden(title_hidden);
334        }
335
336        if let Some(titlebar_hidden) = self.titlebar_hidden {
337            wb = wb.with_titlebar_hidden(titlebar_hidden);
338        }
339
340        if let Some(titlebar_buttons_hidden) = self.titlebar_buttons_hidden {
341            wb = wb.with_titlebar_buttons_hidden(titlebar_buttons_hidden);
342        }
343
344        if let Some(fullsize_content_view) = self.fullsize_content_view {
345            wb = wb.with_fullsize_content_view(fullsize_content_view);
346        }
347
348        if let Some(disallow_hidpi) = self.disallow_hidpi {
349            wb = wb.with_disallow_hidpi(disallow_hidpi);
350        }
351
352        if let Some(has_shadow) = self.has_shadow {
353            wb = wb.with_has_shadow(has_shadow);
354        }
355
356        if let Some(accepts_first_mouse) = self.accepts_first_mouse {
357            wb = wb.with_accepts_first_mouse(accepts_first_mouse);
358        }
359
360        if let Some(option_as_alt) = self.option_as_alt {
361            wb = wb.with_option_as_alt(option_as_alt);
362        }
363
364        wb
365    }
366}