2019-05-01 17:03:30 -06:00
|
|
|
use std::{
|
2019-06-21 11:33:15 -04:00
|
|
|
collections::VecDeque,
|
2022-01-23 21:35:26 +01:00
|
|
|
f64, ops,
|
2019-06-21 11:33:15 -04:00
|
|
|
os::raw::c_void,
|
|
|
|
|
sync::{
|
|
|
|
|
atomic::{AtomicBool, Ordering},
|
2022-11-29 12:58:35 +01:00
|
|
|
Mutex, MutexGuard,
|
2019-06-21 11:33:15 -04:00
|
|
|
},
|
2018-06-14 19:42:18 -04:00
|
|
|
};
|
2017-02-03 23:05:57 +11:00
|
|
|
|
2022-07-21 22:22:36 +03:00
|
|
|
use raw_window_handle::{
|
|
|
|
|
AppKitDisplayHandle, AppKitWindowHandle, RawDisplayHandle, RawWindowHandle,
|
|
|
|
|
};
|
|
|
|
|
|
2019-06-18 02:27:00 +08:00
|
|
|
use crate::{
|
2020-01-04 01:32:34 -05:00
|
|
|
dpi::{
|
|
|
|
|
LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size, Size::Logical,
|
|
|
|
|
},
|
2019-05-29 21:29:54 -04:00
|
|
|
error::{ExternalError, NotSupportedError, OsError as RootOsError},
|
2019-06-21 11:33:15 -04:00
|
|
|
icon::Icon,
|
2021-04-30 11:31:28 +02:00
|
|
|
platform::macos::WindowExtMacOS,
|
2019-06-21 11:33:15 -04:00
|
|
|
platform_impl::platform::{
|
|
|
|
|
app_state::AppState,
|
|
|
|
|
ffi,
|
2019-07-29 21:16:14 +03:00
|
|
|
monitor::{self, MonitorHandle, VideoMode},
|
2022-09-08 16:45:29 +02:00
|
|
|
util,
|
|
|
|
|
view::WinitView,
|
|
|
|
|
window_delegate::WinitWindowDelegate,
|
2022-09-21 10:04:28 +02:00
|
|
|
Fullscreen, OsError,
|
2019-05-01 17:03:30 -06:00
|
|
|
},
|
2020-11-27 03:03:08 +01:00
|
|
|
window::{
|
2022-11-29 12:03:51 +02:00
|
|
|
CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, WindowButtons,
|
2022-11-26 03:50:58 +02:00
|
|
|
WindowId as RootWindowId, WindowLevel,
|
2020-11-27 03:03:08 +01:00
|
|
|
},
|
2019-07-29 21:16:14 +03:00
|
|
|
};
|
2022-09-08 16:45:29 +02:00
|
|
|
use core_graphics::display::{CGDisplay, CGPoint};
|
|
|
|
|
use objc2::declare::{Ivar, IvarDrop};
|
|
|
|
|
use objc2::foundation::{
|
2022-11-22 11:08:56 +01:00
|
|
|
is_main_thread, CGFloat, NSArray, NSCopying, NSInteger, NSObject, NSPoint, NSRect, NSSize,
|
|
|
|
|
NSString,
|
2019-07-29 21:16:14 +03:00
|
|
|
};
|
2022-09-08 16:45:29 +02:00
|
|
|
use objc2::rc::{autoreleasepool, Id, Owned, Shared};
|
2022-10-19 03:34:36 +09:00
|
|
|
use objc2::{declare_class, msg_send, msg_send_id, sel, ClassType};
|
2022-09-02 18:46:18 +02:00
|
|
|
|
2022-09-08 16:45:29 +02:00
|
|
|
use super::appkit::{
|
2022-10-19 03:34:36 +09:00
|
|
|
NSApp, NSAppKitVersion, NSAppearance, NSApplicationPresentationOptions, NSBackingStoreType,
|
|
|
|
|
NSColor, NSCursor, NSFilenamesPboardType, NSRequestUserAttentionType, NSResponder, NSScreen,
|
2022-11-23 15:51:34 +02:00
|
|
|
NSWindow, NSWindowButton, NSWindowLevel, NSWindowSharingType, NSWindowStyleMask,
|
|
|
|
|
NSWindowTitleVisibility,
|
2022-09-08 16:45:29 +02:00
|
|
|
};
|
2017-02-03 23:05:57 +11:00
|
|
|
|
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
2022-03-18 14:09:39 +01:00
|
|
|
pub struct WindowId(pub usize);
|
2017-02-03 23:05:57 +11:00
|
|
|
|
2022-03-18 14:09:39 +01:00
|
|
|
impl WindowId {
|
2021-08-30 19:40:02 +02:00
|
|
|
pub const unsafe fn dummy() -> Self {
|
2022-03-18 14:09:39 +01:00
|
|
|
Self(0)
|
2018-12-21 09:51:48 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-02 14:27:19 +03:00
|
|
|
impl From<WindowId> for u64 {
|
|
|
|
|
fn from(window_id: WindowId) -> Self {
|
|
|
|
|
window_id.0 as u64
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<u64> for WindowId {
|
|
|
|
|
fn from(raw_id: u64) -> Self {
|
|
|
|
|
Self(raw_id as usize)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-14 02:10:34 +08:00
|
|
|
#[derive(Clone)]
|
2019-05-01 17:03:30 -06:00
|
|
|
pub struct PlatformSpecificWindowBuilderAttributes {
|
|
|
|
|
pub movable_by_window_background: bool,
|
|
|
|
|
pub titlebar_transparent: bool,
|
|
|
|
|
pub title_hidden: bool,
|
|
|
|
|
pub titlebar_hidden: bool,
|
|
|
|
|
pub titlebar_buttons_hidden: bool,
|
|
|
|
|
pub fullsize_content_view: bool,
|
2019-07-29 16:07:36 +08:00
|
|
|
pub disallow_hidpi: bool,
|
2020-08-14 02:10:34 +08:00
|
|
|
pub has_shadow: bool,
|
2022-09-13 21:11:18 +02:00
|
|
|
pub accepts_first_mouse: bool,
|
2020-08-14 02:10:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for PlatformSpecificWindowBuilderAttributes {
|
|
|
|
|
#[inline]
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
movable_by_window_background: false,
|
|
|
|
|
titlebar_transparent: false,
|
|
|
|
|
title_hidden: false,
|
|
|
|
|
titlebar_hidden: false,
|
|
|
|
|
titlebar_buttons_hidden: false,
|
|
|
|
|
fullsize_content_view: false,
|
|
|
|
|
disallow_hidpi: false,
|
|
|
|
|
has_shadow: true,
|
2022-09-13 21:11:18 +02:00
|
|
|
accepts_first_mouse: true,
|
2020-08-14 02:10:34 +08:00
|
|
|
}
|
|
|
|
|
}
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
2018-04-18 02:07:54 +08:00
|
|
|
|
2022-09-02 18:46:18 +02:00
|
|
|
declare_class!(
|
2022-09-08 16:45:29 +02:00
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub(crate) struct WinitWindow {
|
|
|
|
|
// TODO: Fix unnecessary boxing here
|
|
|
|
|
// SAFETY: These are initialized in WinitWindow::new, right after it is created.
|
2022-11-29 12:58:35 +01:00
|
|
|
shared_state: IvarDrop<Box<Mutex<SharedState>>>,
|
2022-09-08 16:45:29 +02:00
|
|
|
decorations: IvarDrop<Box<AtomicBool>>,
|
|
|
|
|
}
|
2019-05-01 17:03:30 -06:00
|
|
|
|
2022-09-02 18:46:18 +02:00
|
|
|
unsafe impl ClassType for WinitWindow {
|
|
|
|
|
#[inherits(NSResponder, NSObject)]
|
2022-09-08 16:45:29 +02:00
|
|
|
type Super = NSWindow;
|
2022-06-08 11:50:26 -07:00
|
|
|
}
|
2022-01-23 21:35:26 +01:00
|
|
|
|
2022-09-02 18:46:18 +02:00
|
|
|
unsafe impl WinitWindow {
|
|
|
|
|
#[sel(canBecomeMainWindow)]
|
|
|
|
|
fn can_become_main_window(&self) -> bool {
|
|
|
|
|
trace_scope!("canBecomeMainWindow");
|
|
|
|
|
true
|
|
|
|
|
}
|
2022-01-23 21:35:26 +01:00
|
|
|
|
2022-09-02 18:46:18 +02:00
|
|
|
#[sel(canBecomeKeyWindow)]
|
|
|
|
|
fn can_become_key_window(&self) -> bool {
|
|
|
|
|
trace_scope!("canBecomeKeyWindow");
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
);
|
2018-04-28 18:10:06 +02:00
|
|
|
|
2022-09-08 16:45:29 +02:00
|
|
|
#[derive(Debug, Default)]
|
2019-05-01 17:03:30 -06:00
|
|
|
pub struct SharedState {
|
|
|
|
|
pub resizable: bool,
|
2022-09-08 16:45:29 +02:00
|
|
|
/// This field tracks the current fullscreen state of the window
|
|
|
|
|
/// (as seen by `WindowDelegate`).
|
2022-09-21 10:04:28 +02:00
|
|
|
pub(crate) fullscreen: Option<Fullscreen>,
|
2019-12-25 03:56:56 +09:00
|
|
|
// This is true between windowWillEnterFullScreen and windowDidEnterFullScreen
|
|
|
|
|
// or windowWillExitFullScreen and windowDidExitFullScreen.
|
|
|
|
|
// We must not toggle fullscreen when this is true.
|
|
|
|
|
pub in_fullscreen_transition: bool,
|
|
|
|
|
// If it is attempted to toggle fullscreen when in_fullscreen_transition is true,
|
|
|
|
|
// Set target_fullscreen and do after fullscreen transition is end.
|
2022-09-21 10:04:28 +02:00
|
|
|
pub(crate) target_fullscreen: Option<Option<Fullscreen>>,
|
2019-05-01 17:03:30 -06:00
|
|
|
pub maximized: bool,
|
2019-06-18 09:34:27 +03:00
|
|
|
pub standard_frame: Option<NSRect>,
|
2019-05-01 17:03:30 -06:00
|
|
|
is_simple_fullscreen: bool,
|
|
|
|
|
pub saved_style: Option<NSWindowStyleMask>,
|
2019-07-29 21:16:14 +03:00
|
|
|
/// Presentation options saved before entering `set_simple_fullscreen`, and
|
Show the menu bar in borderless fullscreen on macOS (#2053)
* In MacOS, only disable menu bar in exclusive fullscreen
* Save and restore fullscreen options in set_fullscreen
* Don't always cache presentation options when entering exclusive fullscreen
This commit caches presentation options when entering exclusive fullscreen
only if we're coming from borderless fullscreen.
Then, when transitioning from exclusive -> borderless, if no cached presentation
options are present, then the default borderless options are applied.
This fixes the menu bar being unavailable when taking the following path:
[not fullscreen] -> [exclusive fullscreen] -> [borderless fullscreen].
Without this commit, the presentation options from [not fullscreen] were being
cached and then applied to [borderless fullscreen].
* Restore the window level when switching to exclusive fullscreen
The hack of using `CGShieldingWindowLevel() + 1` in borderless fullscreen needs
to be undone when switching from [borderless] -> [exclusive] fullscreen,
otherwise there are menu bar glitches when following a path through
[borderless] -> [exclusive] -> [borderless] modes.
Now, this might appear to conflict with the 'always on top' feature which uses
the 'floating window' level, but this feature appears to be broken anyway when
entering and exiting fullscreen with an always-on-top window. So, rather than
introducing logic to attempt to restore to the 'floating' level here, I think
it's better to do the simple thing for now and then introduce logic for
always-on-top windows when fixing the overall fullscreen behaviour.
* Update the changelog
Co-authored-by: Ehden Sinai <ehdens@gmail.com>
Co-authored-by: Francesca Lovebloom <francesca@brainiumstudios.com>
2021-11-19 22:05:36 +01:00
|
|
|
/// restored upon exiting it. Also used when transitioning from Borderless to
|
|
|
|
|
/// Exclusive fullscreen in `set_fullscreen` because we need to disable the menu
|
|
|
|
|
/// bar in exclusive fullscreen but want to restore the original options when
|
|
|
|
|
/// transitioning back to borderless fullscreen.
|
2019-05-01 17:03:30 -06:00
|
|
|
save_presentation_opts: Option<NSApplicationPresentationOptions>,
|
2022-10-19 03:34:36 +09:00
|
|
|
pub current_theme: Option<Theme>,
|
2017-02-03 23:05:57 +11:00
|
|
|
}
|
|
|
|
|
|
2019-06-18 09:34:27 +03:00
|
|
|
impl SharedState {
|
|
|
|
|
pub fn saved_standard_frame(&self) -> NSRect {
|
|
|
|
|
self.standard_frame
|
|
|
|
|
.unwrap_or_else(|| NSRect::new(NSPoint::new(50.0, 50.0), NSSize::new(800.0, 600.0)))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-23 21:35:26 +01:00
|
|
|
pub(crate) struct SharedStateMutexGuard<'a> {
|
|
|
|
|
guard: MutexGuard<'a, SharedState>,
|
|
|
|
|
called_from_fn: &'static str,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> SharedStateMutexGuard<'a> {
|
|
|
|
|
#[inline]
|
2022-11-29 12:58:35 +01:00
|
|
|
fn new(guard: MutexGuard<'a, SharedState>, called_from_fn: &'static str) -> Self {
|
2022-01-23 21:35:26 +01:00
|
|
|
trace!("Locked shared state in `{}`", called_from_fn);
|
|
|
|
|
Self {
|
|
|
|
|
guard,
|
|
|
|
|
called_from_fn,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ops::Deref for SharedStateMutexGuard<'_> {
|
|
|
|
|
type Target = SharedState;
|
|
|
|
|
#[inline]
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
|
self.guard.deref()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ops::DerefMut for SharedStateMutexGuard<'_> {
|
|
|
|
|
#[inline]
|
|
|
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
|
|
|
self.guard.deref_mut()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Drop for SharedStateMutexGuard<'_> {
|
|
|
|
|
#[inline]
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
trace!("Unlocked shared state in `{}`", self.called_from_fn);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-08 16:45:29 +02:00
|
|
|
impl WinitWindow {
|
|
|
|
|
#[allow(clippy::type_complexity)]
|
2022-06-10 19:05:28 +02:00
|
|
|
pub(crate) fn new(
|
2022-09-08 16:45:29 +02:00
|
|
|
attrs: WindowAttributes,
|
|
|
|
|
pl_attrs: PlatformSpecificWindowBuilderAttributes,
|
|
|
|
|
) -> Result<(Id<Self, Shared>, Id<WinitWindowDelegate, Shared>), RootOsError> {
|
|
|
|
|
trace_scope!("WinitWindow::new");
|
|
|
|
|
|
2022-09-02 15:48:02 +02:00
|
|
|
if !is_main_thread() {
|
|
|
|
|
panic!("Windows can only be created on the main thread on macOS");
|
2017-02-05 19:47:39 +11:00
|
|
|
}
|
2018-06-07 13:29:23 -04:00
|
|
|
|
2022-09-08 16:45:29 +02:00
|
|
|
let this = autoreleasepool(|_| {
|
2022-09-21 10:04:28 +02:00
|
|
|
let screen = match &attrs.fullscreen {
|
|
|
|
|
Some(Fullscreen::Borderless(Some(monitor)))
|
|
|
|
|
| Some(Fullscreen::Exclusive(VideoMode { monitor, .. })) => {
|
|
|
|
|
monitor.ns_screen().or_else(NSScreen::main)
|
|
|
|
|
}
|
2022-09-08 16:45:29 +02:00
|
|
|
Some(Fullscreen::Borderless(None)) => NSScreen::main(),
|
|
|
|
|
None => None,
|
|
|
|
|
};
|
|
|
|
|
let frame = match &screen {
|
|
|
|
|
Some(screen) => screen.frame(),
|
|
|
|
|
None => {
|
|
|
|
|
let scale_factor = NSScreen::main()
|
|
|
|
|
.map(|screen| screen.backingScaleFactor() as f64)
|
|
|
|
|
.unwrap_or(1.0);
|
|
|
|
|
let (width, height) = match attrs.inner_size {
|
|
|
|
|
Some(size) => {
|
|
|
|
|
let logical = size.to_logical(scale_factor);
|
|
|
|
|
(logical.width, logical.height)
|
|
|
|
|
}
|
|
|
|
|
None => (800.0, 600.0),
|
|
|
|
|
};
|
|
|
|
|
let (left, bottom) = match attrs.position {
|
|
|
|
|
Some(position) => {
|
|
|
|
|
let logical = util::window_position(position.to_logical(scale_factor));
|
|
|
|
|
// macOS wants the position of the bottom left corner,
|
|
|
|
|
// but caller is setting the position of top left corner
|
|
|
|
|
(logical.x, logical.y - height)
|
|
|
|
|
}
|
|
|
|
|
// This value is ignored by calling win.center() below
|
|
|
|
|
None => (0.0, 0.0),
|
|
|
|
|
};
|
|
|
|
|
NSRect::new(NSPoint::new(left, bottom), NSSize::new(width, height))
|
|
|
|
|
}
|
|
|
|
|
};
|
2021-04-29 12:52:41 +02:00
|
|
|
|
2022-09-08 16:45:29 +02:00
|
|
|
let mut masks = if (!attrs.decorations && screen.is_none()) || pl_attrs.titlebar_hidden
|
|
|
|
|
{
|
|
|
|
|
// Resizable without a titlebar or borders
|
|
|
|
|
// if decorations is set to false, ignore pl_attrs
|
|
|
|
|
//
|
|
|
|
|
// if the titlebar is hidden, ignore other pl_attrs
|
|
|
|
|
NSWindowStyleMask::NSBorderlessWindowMask
|
|
|
|
|
| NSWindowStyleMask::NSResizableWindowMask
|
|
|
|
|
| NSWindowStyleMask::NSMiniaturizableWindowMask
|
|
|
|
|
} else {
|
|
|
|
|
// default case, resizable window with titlebar and titlebar buttons
|
|
|
|
|
NSWindowStyleMask::NSClosableWindowMask
|
|
|
|
|
| NSWindowStyleMask::NSMiniaturizableWindowMask
|
|
|
|
|
| NSWindowStyleMask::NSResizableWindowMask
|
|
|
|
|
| NSWindowStyleMask::NSTitledWindowMask
|
|
|
|
|
};
|
2020-01-04 01:32:34 -05:00
|
|
|
|
2022-09-08 16:45:29 +02:00
|
|
|
if !attrs.resizable {
|
|
|
|
|
masks &= !NSWindowStyleMask::NSResizableWindowMask;
|
2017-02-03 23:05:57 +11:00
|
|
|
}
|
|
|
|
|
|
2022-11-29 12:03:51 +02:00
|
|
|
if !attrs.enabled_buttons.contains(WindowButtons::MINIMIZE) {
|
|
|
|
|
masks &= !NSWindowStyleMask::NSMiniaturizableWindowMask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !attrs.enabled_buttons.contains(WindowButtons::CLOSE) {
|
|
|
|
|
masks &= !NSWindowStyleMask::NSClosableWindowMask;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-08 16:45:29 +02:00
|
|
|
if pl_attrs.fullsize_content_view {
|
|
|
|
|
masks |= NSWindowStyleMask::NSFullSizeContentViewWindowMask;
|
2022-01-16 11:14:59 +11:00
|
|
|
}
|
2017-07-27 00:59:42 +08:00
|
|
|
|
2022-09-08 16:45:29 +02:00
|
|
|
let this: Option<Id<Self, Owned>> = unsafe {
|
|
|
|
|
msg_send_id![
|
|
|
|
|
msg_send_id![WinitWindow::class(), alloc],
|
|
|
|
|
initWithContentRect: frame,
|
|
|
|
|
styleMask: masks,
|
|
|
|
|
backing: NSBackingStoreType::NSBackingStoreBuffered,
|
|
|
|
|
defer: false,
|
|
|
|
|
]
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.map(|mut this| {
|
|
|
|
|
// Properly initialize the window's variables
|
|
|
|
|
//
|
|
|
|
|
// Ideally this should be done in an `init` method,
|
|
|
|
|
// but for convenience we do it here instead.
|
|
|
|
|
let state = SharedState {
|
|
|
|
|
resizable: attrs.resizable,
|
|
|
|
|
maximized: attrs.maximized,
|
|
|
|
|
..Default::default()
|
|
|
|
|
};
|
2022-11-29 12:58:35 +01:00
|
|
|
Ivar::write(&mut this.shared_state, Box::new(Mutex::new(state)));
|
2022-09-08 16:45:29 +02:00
|
|
|
Ivar::write(
|
|
|
|
|
&mut this.decorations,
|
|
|
|
|
Box::new(AtomicBool::new(attrs.decorations)),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
this.setReleasedWhenClosed(false);
|
|
|
|
|
this.setTitle(&NSString::from_str(&attrs.title));
|
|
|
|
|
this.setAcceptsMouseMovedEvents(true);
|
|
|
|
|
|
2022-11-23 15:51:34 +02:00
|
|
|
if attrs.content_protected {
|
|
|
|
|
this.setSharingType(NSWindowSharingType::NSWindowSharingNone);
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-08 16:45:29 +02:00
|
|
|
if pl_attrs.titlebar_transparent {
|
|
|
|
|
this.setTitlebarAppearsTransparent(true);
|
|
|
|
|
}
|
|
|
|
|
if pl_attrs.title_hidden {
|
|
|
|
|
this.setTitleVisibility(NSWindowTitleVisibility::Hidden);
|
|
|
|
|
}
|
|
|
|
|
if pl_attrs.titlebar_buttons_hidden {
|
|
|
|
|
for titlebar_button in &[
|
|
|
|
|
#[allow(deprecated)]
|
|
|
|
|
NSWindowButton::FullScreen,
|
|
|
|
|
NSWindowButton::Miniaturize,
|
|
|
|
|
NSWindowButton::Close,
|
|
|
|
|
NSWindowButton::Zoom,
|
|
|
|
|
] {
|
|
|
|
|
if let Some(button) = this.standardWindowButton(*titlebar_button) {
|
|
|
|
|
button.setHidden(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if pl_attrs.movable_by_window_background {
|
|
|
|
|
this.setMovableByWindowBackground(true);
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-29 12:03:51 +02:00
|
|
|
if !attrs.enabled_buttons.contains(WindowButtons::MAXIMIZE) {
|
|
|
|
|
if let Some(button) = this.standardWindowButton(NSWindowButton::Zoom) {
|
|
|
|
|
button.setEnabled(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-08 18:54:22 +02:00
|
|
|
if let Some(increments) = attrs.resize_increments {
|
|
|
|
|
let increments = increments.to_logical(this.scale_factor());
|
2022-09-08 16:45:29 +02:00
|
|
|
let (w, h) = (increments.width, increments.height);
|
|
|
|
|
if w >= 1.0 && h >= 1.0 {
|
2022-09-08 18:54:22 +02:00
|
|
|
let size = NSSize::new(w, h);
|
2022-09-08 16:45:29 +02:00
|
|
|
// It was concluded (#2411) that there is never a use-case for
|
|
|
|
|
// "outer" resize increments, hence we set "inner" ones here.
|
|
|
|
|
// ("outer" in macOS being just resizeIncrements, and "inner" - contentResizeIncrements)
|
|
|
|
|
// This is consistent with X11 size hints behavior
|
|
|
|
|
this.setContentResizeIncrements(size);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !pl_attrs.has_shadow {
|
|
|
|
|
this.setHasShadow(false);
|
|
|
|
|
}
|
|
|
|
|
if attrs.position.is_none() {
|
|
|
|
|
this.center();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Id::into_shared(this)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
.ok_or_else(|| os_error!(OsError::CreationError("Couldn't create `NSWindow`")))?;
|
|
|
|
|
|
2022-09-13 21:11:18 +02:00
|
|
|
let view = WinitView::new(&this, pl_attrs.accepts_first_mouse);
|
2022-09-08 16:45:29 +02:00
|
|
|
|
|
|
|
|
// The default value of `setWantsBestResolutionOpenGLSurface:` was `false` until
|
|
|
|
|
// macos 10.14 and `true` after 10.15, we should set it to `YES` or `NO` to avoid
|
|
|
|
|
// always the default system value in favour of the user's code
|
|
|
|
|
view.setWantsBestResolutionOpenGLSurface(!pl_attrs.disallow_hidpi);
|
|
|
|
|
|
|
|
|
|
// On Mojave, views automatically become layer-backed shortly after being added to
|
|
|
|
|
// a window. Changing the layer-backedness of a view breaks the association between
|
|
|
|
|
// the view and its associated OpenGL context. To work around this, on Mojave we
|
|
|
|
|
// explicitly make the view layer-backed up front so that AppKit doesn't do it
|
|
|
|
|
// itself and break the association with its context.
|
|
|
|
|
if NSAppKitVersion::current().floor() > NSAppKitVersion::NSAppKitVersionNumber10_12 {
|
|
|
|
|
view.setWantsLayer(true);
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
|
|
|
|
|
2022-09-08 16:45:29 +02:00
|
|
|
// Configure the new view as the "key view" for the window
|
|
|
|
|
this.setContentView(&view);
|
|
|
|
|
this.setInitialFirstResponder(&view);
|
|
|
|
|
|
|
|
|
|
if attrs.transparent {
|
|
|
|
|
this.setOpaque(false);
|
|
|
|
|
this.setBackgroundColor(&NSColor::clear());
|
|
|
|
|
}
|
2019-05-01 17:03:30 -06:00
|
|
|
|
2022-09-08 16:45:29 +02:00
|
|
|
if let Some(dim) = attrs.min_inner_size {
|
|
|
|
|
this.set_min_inner_size(Some(dim));
|
|
|
|
|
}
|
|
|
|
|
if let Some(dim) = attrs.max_inner_size {
|
|
|
|
|
this.set_max_inner_size(Some(dim));
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-26 03:50:58 +02:00
|
|
|
this.set_window_level(attrs.window_level);
|
|
|
|
|
|
2022-09-08 16:45:29 +02:00
|
|
|
// register for drag and drop operations.
|
|
|
|
|
this.registerForDraggedTypes(&NSArray::from_slice(&[
|
|
|
|
|
unsafe { NSFilenamesPboardType }.copy()
|
|
|
|
|
]));
|
|
|
|
|
|
2022-10-19 03:34:36 +09:00
|
|
|
match attrs.preferred_theme {
|
|
|
|
|
Some(theme) => {
|
2022-11-29 11:05:51 +02:00
|
|
|
set_ns_theme(Some(theme));
|
2022-11-29 12:58:35 +01:00
|
|
|
let mut state = this.lock_shared_state("WinitWindow::new");
|
2022-10-19 03:34:36 +09:00
|
|
|
state.current_theme = Some(theme);
|
|
|
|
|
}
|
|
|
|
|
None => {
|
2022-11-29 12:58:35 +01:00
|
|
|
let mut state = this.lock_shared_state("WinitWindow::new");
|
2022-10-19 03:34:36 +09:00
|
|
|
state.current_theme = Some(get_ns_theme());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-08 16:45:29 +02:00
|
|
|
let delegate = WinitWindowDelegate::new(&this, attrs.fullscreen.is_some());
|
2017-02-03 23:05:57 +11:00
|
|
|
|
2018-04-18 02:07:54 +08:00
|
|
|
// Set fullscreen mode after we setup everything
|
2022-09-08 16:45:29 +02:00
|
|
|
this.set_fullscreen(attrs.fullscreen);
|
2018-04-18 02:07:54 +08:00
|
|
|
|
2019-05-01 17:03:30 -06:00
|
|
|
// Setting the window as key has to happen *after* we set the fullscreen
|
|
|
|
|
// state, since otherwise we'll briefly see the window at normal size
|
|
|
|
|
// before it transitions.
|
2022-09-08 16:45:29 +02:00
|
|
|
if attrs.visible {
|
2021-04-29 19:49:17 +02:00
|
|
|
// Tightly linked with `app_state::window_activation_hack`
|
2022-09-08 16:45:29 +02:00
|
|
|
this.makeKeyAndOrderFront(None);
|
2018-04-18 02:07:54 +08:00
|
|
|
}
|
|
|
|
|
|
2022-09-08 16:45:29 +02:00
|
|
|
if attrs.maximized {
|
|
|
|
|
this.set_maximized(attrs.maximized);
|
2018-04-18 02:07:54 +08:00
|
|
|
}
|
2022-09-08 16:45:29 +02:00
|
|
|
|
|
|
|
|
Ok((this, delegate))
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-29 12:58:35 +01:00
|
|
|
pub(super) fn retain(&self) -> Id<WinitWindow, Shared> {
|
|
|
|
|
// SAFETY: The pointer is valid, and the window is always `Shared`
|
|
|
|
|
// TODO(madsmtm): Remove the need for unsafety here
|
|
|
|
|
unsafe { Id::retain(self as *const Self as *mut Self).unwrap() }
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-08 16:45:29 +02:00
|
|
|
pub(super) fn view(&self) -> Id<WinitView, Shared> {
|
|
|
|
|
// SAFETY: The view inside WinitWindow is always `WinitView`
|
|
|
|
|
unsafe { Id::cast(self.contentView()) }
|
2017-02-03 23:05:57 +11:00
|
|
|
}
|
|
|
|
|
|
2022-01-23 21:35:26 +01:00
|
|
|
#[track_caller]
|
|
|
|
|
pub(crate) fn lock_shared_state(
|
|
|
|
|
&self,
|
|
|
|
|
called_from_fn: &'static str,
|
|
|
|
|
) -> SharedStateMutexGuard<'_> {
|
|
|
|
|
SharedStateMutexGuard::new(self.shared_state.lock().unwrap(), called_from_fn)
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-01 17:03:30 -06:00
|
|
|
fn set_style_mask_async(&self, mask: NSWindowStyleMask) {
|
2022-09-08 16:45:29 +02:00
|
|
|
util::set_style_mask_async(self, mask);
|
2018-04-28 18:10:06 +02:00
|
|
|
}
|
|
|
|
|
|
2019-05-01 17:03:30 -06:00
|
|
|
fn set_style_mask_sync(&self, mask: NSWindowStyleMask) {
|
2022-09-08 16:45:29 +02:00
|
|
|
util::set_style_mask_sync(self, mask);
|
2017-02-03 23:05:57 +11:00
|
|
|
}
|
|
|
|
|
|
2022-03-18 14:09:39 +01:00
|
|
|
pub fn id(&self) -> WindowId {
|
2022-09-08 16:45:29 +02:00
|
|
|
WindowId(self as *const Self as usize)
|
2017-02-03 23:05:57 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn set_title(&self, title: &str) {
|
2022-09-08 16:45:29 +02:00
|
|
|
util::set_title_async(self, title.to_string());
|
2017-02-03 23:05:57 +11:00
|
|
|
}
|
|
|
|
|
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn set_visible(&self, visible: bool) {
|
|
|
|
|
match visible {
|
2022-09-08 16:45:29 +02:00
|
|
|
true => util::make_key_and_order_front_async(self),
|
|
|
|
|
false => util::order_out_async(self),
|
2019-05-29 21:29:54 -04:00
|
|
|
}
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
|
|
|
|
|
2022-02-17 20:44:14 +02:00
|
|
|
#[inline]
|
|
|
|
|
pub fn is_visible(&self) -> Option<bool> {
|
2022-09-08 16:45:29 +02:00
|
|
|
Some(self.isVisible())
|
2022-02-17 20:44:14 +02:00
|
|
|
}
|
|
|
|
|
|
2019-05-01 17:03:30 -06:00
|
|
|
pub fn request_redraw(&self) {
|
|
|
|
|
AppState::queue_redraw(RootWindowId(self.id()));
|
2017-02-03 23:05:57 +11:00
|
|
|
}
|
|
|
|
|
|
2020-01-04 01:33:07 -05:00
|
|
|
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
2022-09-08 16:45:29 +02:00
|
|
|
let frame_rect = self.frame();
|
2020-01-04 01:32:34 -05:00
|
|
|
let position = LogicalPosition::new(
|
2018-06-14 19:42:18 -04:00
|
|
|
frame_rect.origin.x as f64,
|
2018-05-17 21:28:30 -04:00
|
|
|
util::bottom_left_to_top_left(frame_rect),
|
2020-01-04 01:32:34 -05:00
|
|
|
);
|
2020-02-13 20:41:41 +01:00
|
|
|
let scale_factor = self.scale_factor();
|
|
|
|
|
Ok(position.to_physical(scale_factor))
|
2018-04-16 21:40:30 -04:00
|
|
|
}
|
2017-02-03 23:05:57 +11:00
|
|
|
|
2020-01-04 01:33:07 -05:00
|
|
|
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
2022-09-08 16:45:29 +02:00
|
|
|
let content_rect = self.contentRectForFrameRect(self.frame());
|
2020-01-04 01:32:34 -05:00
|
|
|
let position = LogicalPosition::new(
|
2018-06-14 19:42:18 -04:00
|
|
|
content_rect.origin.x as f64,
|
2018-05-17 21:28:30 -04:00
|
|
|
util::bottom_left_to_top_left(content_rect),
|
2020-01-04 01:32:34 -05:00
|
|
|
);
|
2020-02-13 20:41:41 +01:00
|
|
|
let scale_factor = self.scale_factor();
|
|
|
|
|
Ok(position.to_physical(scale_factor))
|
2017-02-03 23:05:57 +11:00
|
|
|
}
|
|
|
|
|
|
2020-01-04 01:32:34 -05:00
|
|
|
pub fn set_outer_position(&self, position: Position) {
|
2020-02-13 20:41:41 +01:00
|
|
|
let scale_factor = self.scale_factor();
|
|
|
|
|
let position = position.to_logical(scale_factor);
|
2022-09-08 16:45:29 +02:00
|
|
|
util::set_frame_top_left_point_async(self, util::window_position(position));
|
2017-02-03 23:05:57 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
2020-01-04 01:33:07 -05:00
|
|
|
pub fn inner_size(&self) -> PhysicalSize<u32> {
|
2022-09-08 16:45:29 +02:00
|
|
|
let frame = self.contentView().frame();
|
|
|
|
|
let logical: LogicalSize<f64> = (frame.size.width as f64, frame.size.height as f64).into();
|
2020-02-13 20:41:41 +01:00
|
|
|
let scale_factor = self.scale_factor();
|
|
|
|
|
logical.to_physical(scale_factor)
|
2017-02-03 23:05:57 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
2020-01-04 01:33:07 -05:00
|
|
|
pub fn outer_size(&self) -> PhysicalSize<u32> {
|
2022-09-08 16:45:29 +02:00
|
|
|
let frame = self.frame();
|
|
|
|
|
let logical: LogicalSize<f64> = (frame.size.width as f64, frame.size.height as f64).into();
|
2020-02-13 20:41:41 +01:00
|
|
|
let scale_factor = self.scale_factor();
|
|
|
|
|
logical.to_physical(scale_factor)
|
2017-02-03 23:05:57 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
2020-01-04 01:32:34 -05:00
|
|
|
pub fn set_inner_size(&self, size: Size) {
|
2022-09-08 16:45:29 +02:00
|
|
|
let scale_factor = self.scale_factor();
|
|
|
|
|
util::set_content_size_async(self, size.to_logical(scale_factor));
|
2017-02-03 23:05:57 +11:00
|
|
|
}
|
|
|
|
|
|
2020-01-04 01:32:34 -05:00
|
|
|
pub fn set_min_inner_size(&self, dimensions: Option<Size>) {
|
2022-09-08 16:45:29 +02:00
|
|
|
let dimensions = dimensions.unwrap_or(Logical(LogicalSize {
|
|
|
|
|
width: 0.0,
|
|
|
|
|
height: 0.0,
|
|
|
|
|
}));
|
|
|
|
|
let min_size = dimensions.to_logical::<CGFloat>(self.scale_factor());
|
|
|
|
|
|
|
|
|
|
let mut current_rect = self.frame();
|
|
|
|
|
let content_rect = self.contentRectForFrameRect(current_rect);
|
|
|
|
|
// Convert from client area size to window size
|
|
|
|
|
let min_size = NSSize::new(
|
|
|
|
|
min_size.width + (current_rect.size.width - content_rect.size.width), // this tends to be 0
|
|
|
|
|
min_size.height + (current_rect.size.height - content_rect.size.height),
|
|
|
|
|
);
|
|
|
|
|
self.setMinSize(min_size);
|
|
|
|
|
// If necessary, resize the window to match constraint
|
|
|
|
|
if current_rect.size.width < min_size.width {
|
|
|
|
|
current_rect.size.width = min_size.width;
|
|
|
|
|
self.setFrame_display(current_rect, false)
|
|
|
|
|
}
|
|
|
|
|
if current_rect.size.height < min_size.height {
|
|
|
|
|
// The origin point of a rectangle is at its bottom left in Cocoa.
|
|
|
|
|
// To ensure the window's top-left point remains the same:
|
|
|
|
|
current_rect.origin.y += current_rect.size.height - min_size.height;
|
|
|
|
|
current_rect.size.height = min_size.height;
|
|
|
|
|
self.setFrame_display(current_rect, false)
|
2018-03-23 05:35:35 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-04 01:32:34 -05:00
|
|
|
pub fn set_max_inner_size(&self, dimensions: Option<Size>) {
|
2022-09-08 16:45:29 +02:00
|
|
|
let dimensions = dimensions.unwrap_or(Logical(LogicalSize {
|
|
|
|
|
width: std::f32::MAX as f64,
|
|
|
|
|
height: std::f32::MAX as f64,
|
|
|
|
|
}));
|
|
|
|
|
let scale_factor = self.scale_factor();
|
|
|
|
|
let max_size = dimensions.to_logical::<CGFloat>(scale_factor);
|
|
|
|
|
|
|
|
|
|
let mut current_rect = self.frame();
|
|
|
|
|
let content_rect = self.contentRectForFrameRect(current_rect);
|
|
|
|
|
// Convert from client area size to window size
|
|
|
|
|
let max_size = NSSize::new(
|
|
|
|
|
max_size.width + (current_rect.size.width - content_rect.size.width), // this tends to be 0
|
|
|
|
|
max_size.height + (current_rect.size.height - content_rect.size.height),
|
|
|
|
|
);
|
|
|
|
|
self.setMaxSize(max_size);
|
|
|
|
|
// If necessary, resize the window to match constraint
|
|
|
|
|
if current_rect.size.width > max_size.width {
|
|
|
|
|
current_rect.size.width = max_size.width;
|
|
|
|
|
self.setFrame_display(current_rect, false)
|
|
|
|
|
}
|
|
|
|
|
if current_rect.size.height > max_size.height {
|
|
|
|
|
// The origin point of a rectangle is at its bottom left in Cocoa.
|
|
|
|
|
// To ensure the window's top-left point remains the same:
|
|
|
|
|
current_rect.origin.y += current_rect.size.height - max_size.height;
|
|
|
|
|
current_rect.size.height = max_size.height;
|
|
|
|
|
self.setFrame_display(current_rect, false)
|
2018-03-23 05:35:35 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-03 21:50:22 +03:00
|
|
|
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
2022-09-08 16:45:29 +02:00
|
|
|
let increments = self.contentResizeIncrements();
|
|
|
|
|
let (w, h) = (increments.width, increments.height);
|
|
|
|
|
if w > 1.0 || h > 1.0 {
|
|
|
|
|
Some(LogicalSize::new(w, h).to_physical(self.scale_factor()))
|
2022-09-03 21:50:22 +03:00
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn set_resize_increments(&self, increments: Option<Size>) {
|
|
|
|
|
let size = increments
|
|
|
|
|
.map(|increments| {
|
|
|
|
|
let logical = increments.to_logical::<f64>(self.scale_factor());
|
|
|
|
|
NSSize::new(logical.width.max(1.0), logical.height.max(1.0))
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or_else(|| NSSize::new(1.0, 1.0));
|
2022-09-08 16:45:29 +02:00
|
|
|
self.setContentResizeIncrements(size);
|
2022-09-03 21:50:22 +03:00
|
|
|
}
|
|
|
|
|
|
2018-06-07 13:29:23 -04:00
|
|
|
#[inline]
|
|
|
|
|
pub fn set_resizable(&self, resizable: bool) {
|
2019-05-01 17:03:30 -06:00
|
|
|
let fullscreen = {
|
2022-01-23 21:35:26 +01:00
|
|
|
let mut shared_state_lock = self.lock_shared_state("set_resizable");
|
2019-05-01 17:03:30 -06:00
|
|
|
shared_state_lock.resizable = resizable;
|
|
|
|
|
shared_state_lock.fullscreen.is_some()
|
|
|
|
|
};
|
|
|
|
|
if !fullscreen {
|
2022-09-08 16:45:29 +02:00
|
|
|
let mut mask = self.styleMask();
|
2018-06-07 13:29:23 -04:00
|
|
|
if resizable {
|
|
|
|
|
mask |= NSWindowStyleMask::NSResizableWindowMask;
|
|
|
|
|
} else {
|
|
|
|
|
mask &= !NSWindowStyleMask::NSResizableWindowMask;
|
|
|
|
|
}
|
2019-05-01 17:03:30 -06:00
|
|
|
self.set_style_mask_async(mask);
|
2018-06-07 13:29:23 -04:00
|
|
|
} // Otherwise, we don't change the mask until we exit fullscreen.
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-17 17:03:17 +02:00
|
|
|
#[inline]
|
|
|
|
|
pub fn is_resizable(&self) -> bool {
|
2022-09-08 16:45:29 +02:00
|
|
|
self.isResizable()
|
2022-02-17 17:03:17 +02:00
|
|
|
}
|
|
|
|
|
|
2022-11-29 12:03:51 +02:00
|
|
|
#[inline]
|
|
|
|
|
pub fn set_enabled_buttons(&self, buttons: WindowButtons) {
|
|
|
|
|
let mut mask = self.styleMask();
|
|
|
|
|
|
|
|
|
|
if buttons.contains(WindowButtons::CLOSE) {
|
|
|
|
|
mask |= NSWindowStyleMask::NSClosableWindowMask;
|
|
|
|
|
} else {
|
|
|
|
|
mask &= !NSWindowStyleMask::NSClosableWindowMask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if buttons.contains(WindowButtons::MINIMIZE) {
|
|
|
|
|
mask |= NSWindowStyleMask::NSMiniaturizableWindowMask;
|
|
|
|
|
} else {
|
|
|
|
|
mask &= !NSWindowStyleMask::NSMiniaturizableWindowMask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This must happen before the button's "enabled" status has been set,
|
|
|
|
|
// hence we do it synchronously.
|
|
|
|
|
self.set_style_mask_sync(mask);
|
|
|
|
|
|
|
|
|
|
// We edit the button directly instead of using `NSResizableWindowMask`,
|
|
|
|
|
// since that mask also affect the resizability of the window (which is
|
|
|
|
|
// controllable by other means in `winit`).
|
|
|
|
|
if let Some(button) = self.standardWindowButton(NSWindowButton::Zoom) {
|
|
|
|
|
button.setEnabled(buttons.contains(WindowButtons::MAXIMIZE));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn enabled_buttons(&self) -> WindowButtons {
|
|
|
|
|
let mut buttons = WindowButtons::empty();
|
|
|
|
|
if self.isMiniaturizable() {
|
|
|
|
|
buttons |= WindowButtons::MINIMIZE;
|
|
|
|
|
}
|
|
|
|
|
if self
|
|
|
|
|
.standardWindowButton(NSWindowButton::Zoom)
|
|
|
|
|
.map(|b| b.isEnabled())
|
|
|
|
|
.unwrap_or(true)
|
|
|
|
|
{
|
|
|
|
|
buttons |= WindowButtons::MAXIMIZE;
|
|
|
|
|
}
|
|
|
|
|
if self.hasCloseBox() {
|
|
|
|
|
buttons |= WindowButtons::CLOSE;
|
|
|
|
|
}
|
|
|
|
|
buttons
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-02 21:02:40 +02:00
|
|
|
pub fn set_cursor_icon(&self, icon: CursorIcon) {
|
2022-09-08 16:45:29 +02:00
|
|
|
let view = self.view();
|
|
|
|
|
let mut cursor_state = view.state.cursor_state.lock().unwrap();
|
2022-09-02 21:02:40 +02:00
|
|
|
cursor_state.cursor = NSCursor::from_icon(icon);
|
|
|
|
|
drop(cursor_state);
|
2022-09-08 16:45:29 +02:00
|
|
|
self.invalidateCursorRectsForView(&view);
|
2017-02-03 23:05:57 +11:00
|
|
|
}
|
|
|
|
|
|
2018-06-18 12:32:18 -04:00
|
|
|
#[inline]
|
2022-06-13 09:43:14 +03:00
|
|
|
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
|
|
|
|
|
let associate_mouse_cursor = match mode {
|
|
|
|
|
CursorGrabMode::Locked => false,
|
|
|
|
|
CursorGrabMode::None => true,
|
|
|
|
|
CursorGrabMode::Confined => {
|
|
|
|
|
return Err(ExternalError::NotSupported(NotSupportedError::new()))
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2018-06-18 12:32:18 -04:00
|
|
|
// TODO: Do this for real https://stackoverflow.com/a/40922095/5435443
|
2022-06-13 09:43:14 +03:00
|
|
|
CGDisplay::associate_mouse_and_mouse_cursor_position(associate_mouse_cursor)
|
2019-05-29 21:29:54 -04:00
|
|
|
.map_err(|status| ExternalError::Os(os_error!(OsError::CGError(status))))
|
2018-06-18 12:32:18 -04:00
|
|
|
}
|
2017-02-03 23:05:57 +11:00
|
|
|
|
2018-06-18 12:32:18 -04:00
|
|
|
#[inline]
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn set_cursor_visible(&self, visible: bool) {
|
2022-09-08 16:45:29 +02:00
|
|
|
let view = self.view();
|
|
|
|
|
let mut cursor_state = view.state.cursor_state.lock().unwrap();
|
2022-09-02 21:02:40 +02:00
|
|
|
if visible != cursor_state.visible {
|
|
|
|
|
cursor_state.visible = visible;
|
|
|
|
|
drop(cursor_state);
|
2022-09-08 16:45:29 +02:00
|
|
|
self.invalidateCursorRectsForView(&view);
|
2017-02-03 23:05:57 +11:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
2020-01-03 14:52:27 -05:00
|
|
|
pub fn scale_factor(&self) -> f64 {
|
2022-09-08 16:45:29 +02:00
|
|
|
self.backingScaleFactor() as f64
|
2017-02-03 23:05:57 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
2020-01-04 01:32:34 -05:00
|
|
|
pub fn set_cursor_position(&self, cursor_position: Position) -> Result<(), ExternalError> {
|
|
|
|
|
let physical_window_position = self.inner_position().unwrap();
|
2020-02-13 20:41:41 +01:00
|
|
|
let scale_factor = self.scale_factor();
|
|
|
|
|
let window_position = physical_window_position.to_logical::<CGFloat>(scale_factor);
|
|
|
|
|
let logical_cursor_position = cursor_position.to_logical::<CGFloat>(scale_factor);
|
2022-09-08 16:45:29 +02:00
|
|
|
let point = CGPoint {
|
2020-01-04 01:33:07 -05:00
|
|
|
x: logical_cursor_position.x + window_position.x,
|
|
|
|
|
y: logical_cursor_position.y + window_position.y,
|
2018-06-14 19:42:18 -04:00
|
|
|
};
|
|
|
|
|
CGDisplay::warp_mouse_cursor_position(point)
|
2019-05-29 21:29:54 -04:00
|
|
|
.map_err(|e| ExternalError::Os(os_error!(OsError::CGError(e))))?;
|
2018-06-14 19:42:18 -04:00
|
|
|
CGDisplay::associate_mouse_and_mouse_cursor_position(true)
|
2019-05-29 21:29:54 -04:00
|
|
|
.map_err(|e| ExternalError::Os(os_error!(OsError::CGError(e))))?;
|
2018-06-19 10:30:15 -04:00
|
|
|
|
2017-02-03 23:05:57 +11:00
|
|
|
Ok(())
|
|
|
|
|
}
|
2017-08-28 01:43:34 +01:00
|
|
|
|
2021-03-07 10:43:23 +01:00
|
|
|
#[inline]
|
|
|
|
|
pub fn drag_window(&self) -> Result<(), ExternalError> {
|
2022-09-08 16:45:29 +02:00
|
|
|
let event = NSApp().currentEvent();
|
|
|
|
|
self.performWindowDragWithEvent(event.as_deref());
|
2021-03-07 10:43:23 +01:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-12 19:10:46 +02:00
|
|
|
#[inline]
|
|
|
|
|
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
|
2022-09-08 16:45:29 +02:00
|
|
|
util::set_ignore_mouse_events(self, !hittest);
|
2022-04-12 19:10:46 +02:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-01 17:03:30 -06:00
|
|
|
pub(crate) fn is_zoomed(&self) -> bool {
|
|
|
|
|
// because `isZoomed` doesn't work if the window's borderless,
|
|
|
|
|
// we make it resizable temporalily.
|
2022-09-08 16:45:29 +02:00
|
|
|
let curr_mask = self.styleMask();
|
2019-05-01 17:03:30 -06:00
|
|
|
|
2019-06-21 11:33:15 -04:00
|
|
|
let required =
|
|
|
|
|
NSWindowStyleMask::NSTitledWindowMask | NSWindowStyleMask::NSResizableWindowMask;
|
2019-05-01 17:03:30 -06:00
|
|
|
let needs_temp_mask = !curr_mask.contains(required);
|
|
|
|
|
if needs_temp_mask {
|
|
|
|
|
self.set_style_mask_sync(required);
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-08 16:45:29 +02:00
|
|
|
let is_zoomed = self.isZoomed();
|
2019-05-01 17:03:30 -06:00
|
|
|
|
|
|
|
|
// Roll back temp styles
|
|
|
|
|
if needs_temp_mask {
|
|
|
|
|
self.set_style_mask_async(curr_mask);
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-02 15:48:02 +02:00
|
|
|
is_zoomed
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn saved_style(&self, shared_state: &mut SharedState) -> NSWindowStyleMask {
|
2019-06-21 11:33:15 -04:00
|
|
|
let base_mask = shared_state
|
|
|
|
|
.saved_style
|
2019-05-01 17:03:30 -06:00
|
|
|
.take()
|
2022-09-08 16:45:29 +02:00
|
|
|
.unwrap_or_else(|| self.styleMask());
|
2019-05-01 17:03:30 -06:00
|
|
|
if shared_state.resizable {
|
|
|
|
|
base_mask | NSWindowStyleMask::NSResizableWindowMask
|
|
|
|
|
} else {
|
|
|
|
|
base_mask & !NSWindowStyleMask::NSResizableWindowMask
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-29 21:16:14 +03:00
|
|
|
/// This is called when the window is exiting fullscreen, whether by the
|
|
|
|
|
/// user clicking on the green fullscreen button or programmatically by
|
|
|
|
|
/// `toggleFullScreen:`
|
2019-05-01 17:03:30 -06:00
|
|
|
pub(crate) fn restore_state_from_fullscreen(&self) {
|
2022-01-23 21:35:26 +01:00
|
|
|
let mut shared_state_lock = self.lock_shared_state("restore_state_from_fullscreen");
|
2019-05-01 17:03:30 -06:00
|
|
|
|
2019-07-29 21:16:14 +03:00
|
|
|
shared_state_lock.fullscreen = None;
|
2019-05-01 17:03:30 -06:00
|
|
|
|
2019-07-29 21:16:14 +03:00
|
|
|
let maximized = shared_state_lock.maximized;
|
2022-11-23 13:07:58 +01:00
|
|
|
let mask = self.saved_style(&mut shared_state_lock);
|
2019-05-01 17:03:30 -06:00
|
|
|
|
2019-07-29 21:16:14 +03:00
|
|
|
drop(shared_state_lock);
|
|
|
|
|
|
|
|
|
|
self.set_style_mask_async(mask);
|
2019-05-01 17:03:30 -06:00
|
|
|
self.set_maximized(maximized);
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-22 01:04:11 -05:00
|
|
|
#[inline]
|
|
|
|
|
pub fn set_minimized(&self, minimized: bool) {
|
2022-09-08 16:45:29 +02:00
|
|
|
let is_minimized = self.isMiniaturized();
|
2019-12-22 01:04:11 -05:00
|
|
|
if is_minimized == minimized {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if minimized {
|
2022-09-08 16:45:29 +02:00
|
|
|
self.miniaturize(Some(self));
|
2019-12-22 01:04:11 -05:00
|
|
|
} else {
|
2022-09-08 16:45:29 +02:00
|
|
|
self.deminiaturize(Some(self));
|
2019-12-22 01:04:11 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-28 01:43:34 +01:00
|
|
|
#[inline]
|
2018-04-18 02:07:54 +08:00
|
|
|
pub fn set_maximized(&self, maximized: bool) {
|
2019-05-01 17:03:30 -06:00
|
|
|
let is_zoomed = self.is_zoomed();
|
2019-06-18 09:34:27 +03:00
|
|
|
if is_zoomed == maximized {
|
2019-05-01 17:03:30 -06:00
|
|
|
return;
|
2019-06-18 09:34:27 +03:00
|
|
|
};
|
2022-11-29 12:58:35 +01:00
|
|
|
util::set_maximized_async(self.retain(), is_zoomed, maximized);
|
2017-09-07 09:33:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
2022-09-21 10:04:28 +02:00
|
|
|
pub(crate) fn fullscreen(&self) -> Option<Fullscreen> {
|
2022-01-23 21:35:26 +01:00
|
|
|
let shared_state_lock = self.lock_shared_state("fullscreen");
|
2019-05-16 00:26:59 -04:00
|
|
|
shared_state_lock.fullscreen.clone()
|
2019-04-26 03:09:32 +10:00
|
|
|
}
|
|
|
|
|
|
2021-01-27 20:01:17 +02:00
|
|
|
#[inline]
|
|
|
|
|
pub fn is_maximized(&self) -> bool {
|
|
|
|
|
self.is_zoomed()
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-07 09:33:46 +01:00
|
|
|
#[inline]
|
2022-09-21 10:04:28 +02:00
|
|
|
pub(crate) fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
|
2022-01-23 21:35:26 +01:00
|
|
|
let mut shared_state_lock = self.lock_shared_state("set_fullscreen");
|
2019-05-01 17:03:30 -06:00
|
|
|
if shared_state_lock.is_simple_fullscreen {
|
2019-07-29 21:16:14 +03:00
|
|
|
return;
|
|
|
|
|
}
|
2019-12-25 03:56:56 +09:00
|
|
|
if shared_state_lock.in_fullscreen_transition {
|
|
|
|
|
// We can't set fullscreen here.
|
|
|
|
|
// Set fullscreen after transition.
|
|
|
|
|
shared_state_lock.target_fullscreen = Some(fullscreen);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-07-29 21:16:14 +03:00
|
|
|
let old_fullscreen = shared_state_lock.fullscreen.clone();
|
|
|
|
|
if fullscreen == old_fullscreen {
|
2019-06-21 11:33:15 -04:00
|
|
|
return;
|
2018-12-19 15:07:33 +11:00
|
|
|
}
|
2019-07-29 21:16:14 +03:00
|
|
|
drop(shared_state_lock);
|
|
|
|
|
|
|
|
|
|
// If the fullscreen is on a different monitor, we must move the window
|
|
|
|
|
// to that monitor before we toggle fullscreen (as `toggleFullScreen`
|
|
|
|
|
// does not take a screen parameter, but uses the current screen)
|
|
|
|
|
if let Some(ref fullscreen) = fullscreen {
|
|
|
|
|
let new_screen = match fullscreen {
|
2022-09-21 10:04:28 +02:00
|
|
|
Fullscreen::Borderless(Some(monitor)) => monitor.clone(),
|
2022-11-05 10:30:39 +08:00
|
|
|
Fullscreen::Borderless(None) => {
|
|
|
|
|
if let Some(monitor) = self.current_monitor_inner() {
|
|
|
|
|
monitor
|
|
|
|
|
} else {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-21 10:04:28 +02:00
|
|
|
Fullscreen::Exclusive(video_mode) => video_mode.monitor(),
|
2019-07-29 21:16:14 +03:00
|
|
|
}
|
|
|
|
|
.ns_screen()
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
2022-09-08 16:45:29 +02:00
|
|
|
let old_screen = self.screen().unwrap();
|
|
|
|
|
if old_screen != new_screen {
|
|
|
|
|
let mut screen_frame = new_screen.frame();
|
|
|
|
|
// The coordinate system here has its origin at bottom-left
|
|
|
|
|
// and Y goes up
|
|
|
|
|
screen_frame.origin.y += screen_frame.size.height;
|
|
|
|
|
util::set_frame_top_left_point_async(self, screen_frame.origin);
|
2019-07-29 21:16:14 +03:00
|
|
|
}
|
|
|
|
|
}
|
2018-12-19 15:07:33 +11:00
|
|
|
|
2019-07-29 21:16:14 +03:00
|
|
|
if let Some(Fullscreen::Exclusive(ref video_mode)) = fullscreen {
|
|
|
|
|
// Note: `enterFullScreenMode:withOptions:` seems to do the exact
|
|
|
|
|
// same thing as we're doing here (captures the display, sets the
|
|
|
|
|
// video mode, and hides the menu bar and dock), with the exception
|
|
|
|
|
// of that I couldn't figure out how to set the display mode with
|
|
|
|
|
// it. I think `enterFullScreenMode:withOptions:` is still using the
|
|
|
|
|
// older display mode API where display modes were of the type
|
|
|
|
|
// `CFDictionary`, but this has changed, so we can't obtain the
|
|
|
|
|
// correct parameter for this any longer. Apple's code samples for
|
|
|
|
|
// this function seem to just pass in "YES" for the display mode
|
|
|
|
|
// parameter, which is not consistent with the docs saying that it
|
|
|
|
|
// takes a `NSDictionary`..
|
|
|
|
|
|
2022-09-21 10:04:28 +02:00
|
|
|
let display_id = video_mode.monitor().native_identifier();
|
2019-07-29 21:16:14 +03:00
|
|
|
|
|
|
|
|
let mut fade_token = ffi::kCGDisplayFadeReservationInvalidToken;
|
|
|
|
|
|
Show the menu bar in borderless fullscreen on macOS (#2053)
* In MacOS, only disable menu bar in exclusive fullscreen
* Save and restore fullscreen options in set_fullscreen
* Don't always cache presentation options when entering exclusive fullscreen
This commit caches presentation options when entering exclusive fullscreen
only if we're coming from borderless fullscreen.
Then, when transitioning from exclusive -> borderless, if no cached presentation
options are present, then the default borderless options are applied.
This fixes the menu bar being unavailable when taking the following path:
[not fullscreen] -> [exclusive fullscreen] -> [borderless fullscreen].
Without this commit, the presentation options from [not fullscreen] were being
cached and then applied to [borderless fullscreen].
* Restore the window level when switching to exclusive fullscreen
The hack of using `CGShieldingWindowLevel() + 1` in borderless fullscreen needs
to be undone when switching from [borderless] -> [exclusive] fullscreen,
otherwise there are menu bar glitches when following a path through
[borderless] -> [exclusive] -> [borderless] modes.
Now, this might appear to conflict with the 'always on top' feature which uses
the 'floating window' level, but this feature appears to be broken anyway when
entering and exiting fullscreen with an always-on-top window. So, rather than
introducing logic to attempt to restore to the 'floating' level here, I think
it's better to do the simple thing for now and then introduce logic for
always-on-top windows when fixing the overall fullscreen behaviour.
* Update the changelog
Co-authored-by: Ehden Sinai <ehdens@gmail.com>
Co-authored-by: Francesca Lovebloom <francesca@brainiumstudios.com>
2021-11-19 22:05:36 +01:00
|
|
|
if matches!(old_fullscreen, Some(Fullscreen::Borderless(_))) {
|
2022-09-08 16:45:29 +02:00
|
|
|
let app = NSApp();
|
|
|
|
|
let mut shared_state_lock = self.lock_shared_state("set_fullscreen");
|
|
|
|
|
shared_state_lock.save_presentation_opts = Some(app.presentationOptions());
|
Show the menu bar in borderless fullscreen on macOS (#2053)
* In MacOS, only disable menu bar in exclusive fullscreen
* Save and restore fullscreen options in set_fullscreen
* Don't always cache presentation options when entering exclusive fullscreen
This commit caches presentation options when entering exclusive fullscreen
only if we're coming from borderless fullscreen.
Then, when transitioning from exclusive -> borderless, if no cached presentation
options are present, then the default borderless options are applied.
This fixes the menu bar being unavailable when taking the following path:
[not fullscreen] -> [exclusive fullscreen] -> [borderless fullscreen].
Without this commit, the presentation options from [not fullscreen] were being
cached and then applied to [borderless fullscreen].
* Restore the window level when switching to exclusive fullscreen
The hack of using `CGShieldingWindowLevel() + 1` in borderless fullscreen needs
to be undone when switching from [borderless] -> [exclusive] fullscreen,
otherwise there are menu bar glitches when following a path through
[borderless] -> [exclusive] -> [borderless] modes.
Now, this might appear to conflict with the 'always on top' feature which uses
the 'floating window' level, but this feature appears to be broken anyway when
entering and exiting fullscreen with an always-on-top window. So, rather than
introducing logic to attempt to restore to the 'floating' level here, I think
it's better to do the simple thing for now and then introduce logic for
always-on-top windows when fixing the overall fullscreen behaviour.
* Update the changelog
Co-authored-by: Ehden Sinai <ehdens@gmail.com>
Co-authored-by: Francesca Lovebloom <francesca@brainiumstudios.com>
2021-11-19 22:05:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-07-29 21:16:14 +03:00
|
|
|
unsafe {
|
|
|
|
|
// Fade to black (and wait for the fade to complete) to hide the
|
|
|
|
|
// flicker from capturing the display and switching display mode
|
|
|
|
|
if ffi::CGAcquireDisplayFadeReservation(5.0, &mut fade_token)
|
|
|
|
|
== ffi::kCGErrorSuccess
|
|
|
|
|
{
|
|
|
|
|
ffi::CGDisplayFade(
|
|
|
|
|
fade_token,
|
|
|
|
|
0.3,
|
|
|
|
|
ffi::kCGDisplayBlendNormal,
|
|
|
|
|
ffi::kCGDisplayBlendSolidColor,
|
|
|
|
|
0.0,
|
|
|
|
|
0.0,
|
|
|
|
|
0.0,
|
|
|
|
|
ffi::TRUE,
|
|
|
|
|
);
|
2019-06-24 12:14:55 -04:00
|
|
|
}
|
2019-07-29 21:16:14 +03:00
|
|
|
|
|
|
|
|
assert_eq!(ffi::CGDisplayCapture(display_id), ffi::kCGErrorSuccess);
|
2018-04-18 02:07:54 +08:00
|
|
|
}
|
|
|
|
|
|
2019-07-29 21:16:14 +03:00
|
|
|
unsafe {
|
|
|
|
|
let result = ffi::CGDisplaySetDisplayMode(
|
|
|
|
|
display_id,
|
2022-09-21 10:04:28 +02:00
|
|
|
video_mode.native_mode.0,
|
2019-07-29 21:16:14 +03:00
|
|
|
std::ptr::null(),
|
|
|
|
|
);
|
|
|
|
|
assert!(result == ffi::kCGErrorSuccess, "failed to set video mode");
|
|
|
|
|
|
|
|
|
|
// After the display has been configured, fade back in
|
|
|
|
|
// asynchronously
|
|
|
|
|
if fade_token != ffi::kCGDisplayFadeReservationInvalidToken {
|
|
|
|
|
ffi::CGDisplayFade(
|
|
|
|
|
fade_token,
|
|
|
|
|
0.6,
|
|
|
|
|
ffi::kCGDisplayBlendSolidColor,
|
|
|
|
|
ffi::kCGDisplayBlendNormal,
|
|
|
|
|
0.0,
|
|
|
|
|
0.0,
|
|
|
|
|
0.0,
|
|
|
|
|
ffi::FALSE,
|
|
|
|
|
);
|
|
|
|
|
ffi::CGReleaseDisplayFadeReservation(fade_token);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-23 21:35:26 +01:00
|
|
|
let mut shared_state_lock = self.lock_shared_state("set_fullscreen");
|
2019-09-16 02:59:37 +03:00
|
|
|
shared_state_lock.fullscreen = fullscreen.clone();
|
|
|
|
|
|
2019-07-29 21:16:14 +03:00
|
|
|
match (&old_fullscreen, &fullscreen) {
|
2022-09-08 16:45:29 +02:00
|
|
|
(&None, &Some(_)) => {
|
2022-11-29 12:58:35 +01:00
|
|
|
util::toggle_full_screen_async(self.retain(), old_fullscreen.is_none());
|
2022-09-08 16:45:29 +02:00
|
|
|
}
|
|
|
|
|
(&Some(Fullscreen::Borderless(_)), &None) => {
|
2019-09-16 02:59:37 +03:00
|
|
|
// State is restored by `window_did_exit_fullscreen`
|
2022-11-29 12:58:35 +01:00
|
|
|
util::toggle_full_screen_async(self.retain(), old_fullscreen.is_none());
|
2022-09-08 16:45:29 +02:00
|
|
|
}
|
2022-09-21 10:04:28 +02:00
|
|
|
(&Some(Fullscreen::Exclusive(ref video_mode)), &None) => {
|
2022-09-08 16:45:29 +02:00
|
|
|
unsafe {
|
2022-09-21 10:04:28 +02:00
|
|
|
util::restore_display_mode_async(video_mode.monitor().native_identifier())
|
2022-09-08 16:45:29 +02:00
|
|
|
};
|
2019-09-16 02:59:37 +03:00
|
|
|
// Rest of the state is restored by `window_did_exit_fullscreen`
|
2022-11-29 12:58:35 +01:00
|
|
|
util::toggle_full_screen_async(self.retain(), old_fullscreen.is_none());
|
2022-09-08 16:45:29 +02:00
|
|
|
}
|
|
|
|
|
(&Some(Fullscreen::Borderless(_)), &Some(Fullscreen::Exclusive(_))) => {
|
2019-09-16 02:59:37 +03:00
|
|
|
// If we're already in fullscreen mode, calling
|
|
|
|
|
// `CGDisplayCapture` will place the shielding window on top of
|
|
|
|
|
// our window, which results in a black display and is not what
|
|
|
|
|
// we want. So, we must place our window on top of the shielding
|
|
|
|
|
// window. Unfortunately, this also makes our window be on top
|
|
|
|
|
// of the menu bar, and this looks broken, so we must make sure
|
|
|
|
|
// that the menu bar is disabled. This is done in the window
|
|
|
|
|
// delegate in `window:willUseFullScreenPresentationOptions:`.
|
Show the menu bar in borderless fullscreen on macOS (#2053)
* In MacOS, only disable menu bar in exclusive fullscreen
* Save and restore fullscreen options in set_fullscreen
* Don't always cache presentation options when entering exclusive fullscreen
This commit caches presentation options when entering exclusive fullscreen
only if we're coming from borderless fullscreen.
Then, when transitioning from exclusive -> borderless, if no cached presentation
options are present, then the default borderless options are applied.
This fixes the menu bar being unavailable when taking the following path:
[not fullscreen] -> [exclusive fullscreen] -> [borderless fullscreen].
Without this commit, the presentation options from [not fullscreen] were being
cached and then applied to [borderless fullscreen].
* Restore the window level when switching to exclusive fullscreen
The hack of using `CGShieldingWindowLevel() + 1` in borderless fullscreen needs
to be undone when switching from [borderless] -> [exclusive] fullscreen,
otherwise there are menu bar glitches when following a path through
[borderless] -> [exclusive] -> [borderless] modes.
Now, this might appear to conflict with the 'always on top' feature which uses
the 'floating window' level, but this feature appears to be broken anyway when
entering and exiting fullscreen with an always-on-top window. So, rather than
introducing logic to attempt to restore to the 'floating' level here, I think
it's better to do the simple thing for now and then introduce logic for
always-on-top windows when fixing the overall fullscreen behaviour.
* Update the changelog
Co-authored-by: Ehden Sinai <ehdens@gmail.com>
Co-authored-by: Francesca Lovebloom <francesca@brainiumstudios.com>
2021-11-19 22:05:36 +01:00
|
|
|
let app = NSApp();
|
2022-09-08 16:45:29 +02:00
|
|
|
shared_state_lock.save_presentation_opts = Some(app.presentationOptions());
|
Show the menu bar in borderless fullscreen on macOS (#2053)
* In MacOS, only disable menu bar in exclusive fullscreen
* Save and restore fullscreen options in set_fullscreen
* Don't always cache presentation options when entering exclusive fullscreen
This commit caches presentation options when entering exclusive fullscreen
only if we're coming from borderless fullscreen.
Then, when transitioning from exclusive -> borderless, if no cached presentation
options are present, then the default borderless options are applied.
This fixes the menu bar being unavailable when taking the following path:
[not fullscreen] -> [exclusive fullscreen] -> [borderless fullscreen].
Without this commit, the presentation options from [not fullscreen] were being
cached and then applied to [borderless fullscreen].
* Restore the window level when switching to exclusive fullscreen
The hack of using `CGShieldingWindowLevel() + 1` in borderless fullscreen needs
to be undone when switching from [borderless] -> [exclusive] fullscreen,
otherwise there are menu bar glitches when following a path through
[borderless] -> [exclusive] -> [borderless] modes.
Now, this might appear to conflict with the 'always on top' feature which uses
the 'floating window' level, but this feature appears to be broken anyway when
entering and exiting fullscreen with an always-on-top window. So, rather than
introducing logic to attempt to restore to the 'floating' level here, I think
it's better to do the simple thing for now and then introduce logic for
always-on-top windows when fixing the overall fullscreen behaviour.
* Update the changelog
Co-authored-by: Ehden Sinai <ehdens@gmail.com>
Co-authored-by: Francesca Lovebloom <francesca@brainiumstudios.com>
2021-11-19 22:05:36 +01:00
|
|
|
|
|
|
|
|
let presentation_options =
|
|
|
|
|
NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
|
|
|
|
|
| NSApplicationPresentationOptions::NSApplicationPresentationHideDock
|
|
|
|
|
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar;
|
2022-09-08 16:45:29 +02:00
|
|
|
app.setPresentationOptions(presentation_options);
|
Show the menu bar in borderless fullscreen on macOS (#2053)
* In MacOS, only disable menu bar in exclusive fullscreen
* Save and restore fullscreen options in set_fullscreen
* Don't always cache presentation options when entering exclusive fullscreen
This commit caches presentation options when entering exclusive fullscreen
only if we're coming from borderless fullscreen.
Then, when transitioning from exclusive -> borderless, if no cached presentation
options are present, then the default borderless options are applied.
This fixes the menu bar being unavailable when taking the following path:
[not fullscreen] -> [exclusive fullscreen] -> [borderless fullscreen].
Without this commit, the presentation options from [not fullscreen] were being
cached and then applied to [borderless fullscreen].
* Restore the window level when switching to exclusive fullscreen
The hack of using `CGShieldingWindowLevel() + 1` in borderless fullscreen needs
to be undone when switching from [borderless] -> [exclusive] fullscreen,
otherwise there are menu bar glitches when following a path through
[borderless] -> [exclusive] -> [borderless] modes.
Now, this might appear to conflict with the 'always on top' feature which uses
the 'floating window' level, but this feature appears to be broken anyway when
entering and exiting fullscreen with an always-on-top window. So, rather than
introducing logic to attempt to restore to the 'floating' level here, I think
it's better to do the simple thing for now and then introduce logic for
always-on-top windows when fixing the overall fullscreen behaviour.
* Update the changelog
Co-authored-by: Ehden Sinai <ehdens@gmail.com>
Co-authored-by: Francesca Lovebloom <francesca@brainiumstudios.com>
2021-11-19 22:05:36 +01:00
|
|
|
|
2022-11-22 11:08:56 +01:00
|
|
|
let window_level =
|
|
|
|
|
NSWindowLevel(unsafe { ffi::CGShieldingWindowLevel() } as NSInteger + 1);
|
|
|
|
|
self.setLevel(window_level);
|
2022-09-08 16:45:29 +02:00
|
|
|
}
|
2022-09-21 10:04:28 +02:00
|
|
|
(&Some(Fullscreen::Exclusive(ref video_mode)), &Some(Fullscreen::Borderless(_))) => {
|
Show the menu bar in borderless fullscreen on macOS (#2053)
* In MacOS, only disable menu bar in exclusive fullscreen
* Save and restore fullscreen options in set_fullscreen
* Don't always cache presentation options when entering exclusive fullscreen
This commit caches presentation options when entering exclusive fullscreen
only if we're coming from borderless fullscreen.
Then, when transitioning from exclusive -> borderless, if no cached presentation
options are present, then the default borderless options are applied.
This fixes the menu bar being unavailable when taking the following path:
[not fullscreen] -> [exclusive fullscreen] -> [borderless fullscreen].
Without this commit, the presentation options from [not fullscreen] were being
cached and then applied to [borderless fullscreen].
* Restore the window level when switching to exclusive fullscreen
The hack of using `CGShieldingWindowLevel() + 1` in borderless fullscreen needs
to be undone when switching from [borderless] -> [exclusive] fullscreen,
otherwise there are menu bar glitches when following a path through
[borderless] -> [exclusive] -> [borderless] modes.
Now, this might appear to conflict with the 'always on top' feature which uses
the 'floating window' level, but this feature appears to be broken anyway when
entering and exiting fullscreen with an always-on-top window. So, rather than
introducing logic to attempt to restore to the 'floating' level here, I think
it's better to do the simple thing for now and then introduce logic for
always-on-top windows when fixing the overall fullscreen behaviour.
* Update the changelog
Co-authored-by: Ehden Sinai <ehdens@gmail.com>
Co-authored-by: Francesca Lovebloom <francesca@brainiumstudios.com>
2021-11-19 22:05:36 +01:00
|
|
|
let presentation_options =
|
|
|
|
|
shared_state_lock.save_presentation_opts.unwrap_or_else(|| {
|
|
|
|
|
NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
|
|
|
|
|
| NSApplicationPresentationOptions::NSApplicationPresentationAutoHideDock
|
|
|
|
|
| NSApplicationPresentationOptions::NSApplicationPresentationAutoHideMenuBar
|
|
|
|
|
});
|
2022-09-08 16:45:29 +02:00
|
|
|
NSApp().setPresentationOptions(presentation_options);
|
Show the menu bar in borderless fullscreen on macOS (#2053)
* In MacOS, only disable menu bar in exclusive fullscreen
* Save and restore fullscreen options in set_fullscreen
* Don't always cache presentation options when entering exclusive fullscreen
This commit caches presentation options when entering exclusive fullscreen
only if we're coming from borderless fullscreen.
Then, when transitioning from exclusive -> borderless, if no cached presentation
options are present, then the default borderless options are applied.
This fixes the menu bar being unavailable when taking the following path:
[not fullscreen] -> [exclusive fullscreen] -> [borderless fullscreen].
Without this commit, the presentation options from [not fullscreen] were being
cached and then applied to [borderless fullscreen].
* Restore the window level when switching to exclusive fullscreen
The hack of using `CGShieldingWindowLevel() + 1` in borderless fullscreen needs
to be undone when switching from [borderless] -> [exclusive] fullscreen,
otherwise there are menu bar glitches when following a path through
[borderless] -> [exclusive] -> [borderless] modes.
Now, this might appear to conflict with the 'always on top' feature which uses
the 'floating window' level, but this feature appears to be broken anyway when
entering and exiting fullscreen with an always-on-top window. So, rather than
introducing logic to attempt to restore to the 'floating' level here, I think
it's better to do the simple thing for now and then introduce logic for
always-on-top windows when fixing the overall fullscreen behaviour.
* Update the changelog
Co-authored-by: Ehden Sinai <ehdens@gmail.com>
Co-authored-by: Francesca Lovebloom <francesca@brainiumstudios.com>
2021-11-19 22:05:36 +01:00
|
|
|
|
2022-09-08 16:45:29 +02:00
|
|
|
unsafe {
|
2022-09-21 10:04:28 +02:00
|
|
|
util::restore_display_mode_async(video_mode.monitor().native_identifier())
|
2022-09-08 16:45:29 +02:00
|
|
|
};
|
Show the menu bar in borderless fullscreen on macOS (#2053)
* In MacOS, only disable menu bar in exclusive fullscreen
* Save and restore fullscreen options in set_fullscreen
* Don't always cache presentation options when entering exclusive fullscreen
This commit caches presentation options when entering exclusive fullscreen
only if we're coming from borderless fullscreen.
Then, when transitioning from exclusive -> borderless, if no cached presentation
options are present, then the default borderless options are applied.
This fixes the menu bar being unavailable when taking the following path:
[not fullscreen] -> [exclusive fullscreen] -> [borderless fullscreen].
Without this commit, the presentation options from [not fullscreen] were being
cached and then applied to [borderless fullscreen].
* Restore the window level when switching to exclusive fullscreen
The hack of using `CGShieldingWindowLevel() + 1` in borderless fullscreen needs
to be undone when switching from [borderless] -> [exclusive] fullscreen,
otherwise there are menu bar glitches when following a path through
[borderless] -> [exclusive] -> [borderless] modes.
Now, this might appear to conflict with the 'always on top' feature which uses
the 'floating window' level, but this feature appears to be broken anyway when
entering and exiting fullscreen with an always-on-top window. So, rather than
introducing logic to attempt to restore to the 'floating' level here, I think
it's better to do the simple thing for now and then introduce logic for
always-on-top windows when fixing the overall fullscreen behaviour.
* Update the changelog
Co-authored-by: Ehden Sinai <ehdens@gmail.com>
Co-authored-by: Francesca Lovebloom <francesca@brainiumstudios.com>
2021-11-19 22:05:36 +01:00
|
|
|
|
|
|
|
|
// Restore the normal window level following the Borderless fullscreen
|
|
|
|
|
// `CGShieldingWindowLevel() + 1` hack.
|
2022-09-08 16:45:29 +02:00
|
|
|
self.setLevel(NSWindowLevel::Normal);
|
|
|
|
|
}
|
2022-06-08 08:22:54 -07:00
|
|
|
_ => {}
|
Show the menu bar in borderless fullscreen on macOS (#2053)
* In MacOS, only disable menu bar in exclusive fullscreen
* Save and restore fullscreen options in set_fullscreen
* Don't always cache presentation options when entering exclusive fullscreen
This commit caches presentation options when entering exclusive fullscreen
only if we're coming from borderless fullscreen.
Then, when transitioning from exclusive -> borderless, if no cached presentation
options are present, then the default borderless options are applied.
This fixes the menu bar being unavailable when taking the following path:
[not fullscreen] -> [exclusive fullscreen] -> [borderless fullscreen].
Without this commit, the presentation options from [not fullscreen] were being
cached and then applied to [borderless fullscreen].
* Restore the window level when switching to exclusive fullscreen
The hack of using `CGShieldingWindowLevel() + 1` in borderless fullscreen needs
to be undone when switching from [borderless] -> [exclusive] fullscreen,
otherwise there are menu bar glitches when following a path through
[borderless] -> [exclusive] -> [borderless] modes.
Now, this might appear to conflict with the 'always on top' feature which uses
the 'floating window' level, but this feature appears to be broken anyway when
entering and exiting fullscreen with an always-on-top window. So, rather than
introducing logic to attempt to restore to the 'floating' level here, I think
it's better to do the simple thing for now and then introduce logic for
always-on-top windows when fixing the overall fullscreen behaviour.
* Update the changelog
Co-authored-by: Ehden Sinai <ehdens@gmail.com>
Co-authored-by: Francesca Lovebloom <francesca@brainiumstudios.com>
2021-11-19 22:05:36 +01:00
|
|
|
};
|
2017-08-28 01:43:34 +01:00
|
|
|
}
|
|
|
|
|
|
2017-12-22 07:50:46 -05:00
|
|
|
#[inline]
|
2018-04-18 02:07:54 +08:00
|
|
|
pub fn set_decorations(&self, decorations: bool) {
|
2019-05-01 17:03:30 -06:00
|
|
|
if decorations != self.decorations.load(Ordering::Acquire) {
|
|
|
|
|
self.decorations.store(decorations, Ordering::Release);
|
|
|
|
|
|
|
|
|
|
let (fullscreen, resizable) = {
|
2022-01-23 21:35:26 +01:00
|
|
|
let shared_state_lock = self.lock_shared_state("set_decorations");
|
2019-05-01 17:03:30 -06:00
|
|
|
(
|
|
|
|
|
shared_state_lock.fullscreen.is_some(),
|
|
|
|
|
shared_state_lock.resizable,
|
|
|
|
|
)
|
|
|
|
|
};
|
2018-04-24 16:20:40 -04:00
|
|
|
|
2019-05-01 17:03:30 -06:00
|
|
|
// If we're in fullscreen mode, we wait to apply decoration changes
|
|
|
|
|
// until we're in `window_did_exit_fullscreen`.
|
2019-06-21 11:33:15 -04:00
|
|
|
if fullscreen {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-05-01 17:03:30 -06:00
|
|
|
|
|
|
|
|
let new_mask = {
|
|
|
|
|
let mut new_mask = if decorations {
|
|
|
|
|
NSWindowStyleMask::NSClosableWindowMask
|
|
|
|
|
| NSWindowStyleMask::NSMiniaturizableWindowMask
|
|
|
|
|
| NSWindowStyleMask::NSResizableWindowMask
|
|
|
|
|
| NSWindowStyleMask::NSTitledWindowMask
|
|
|
|
|
} else {
|
|
|
|
|
NSWindowStyleMask::NSBorderlessWindowMask
|
|
|
|
|
| NSWindowStyleMask::NSResizableWindowMask
|
|
|
|
|
};
|
|
|
|
|
if !resizable {
|
|
|
|
|
new_mask &= !NSWindowStyleMask::NSResizableWindowMask;
|
|
|
|
|
}
|
|
|
|
|
new_mask
|
|
|
|
|
};
|
|
|
|
|
self.set_style_mask_async(new_mask);
|
2018-04-18 02:07:54 +08:00
|
|
|
}
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
2018-04-18 02:07:54 +08:00
|
|
|
|
2022-02-17 15:31:13 +02:00
|
|
|
#[inline]
|
|
|
|
|
pub fn is_decorated(&self) -> bool {
|
|
|
|
|
self.decorations.load(Ordering::Acquire)
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-01 17:03:30 -06:00
|
|
|
#[inline]
|
2022-11-26 03:50:58 +02:00
|
|
|
pub fn set_window_level(&self, level: WindowLevel) {
|
|
|
|
|
let level = match level {
|
|
|
|
|
WindowLevel::AlwaysOnTop => NSWindowLevel::Floating,
|
|
|
|
|
WindowLevel::AlwaysOnBottom => NSWindowLevel::BELOW_NORMAL,
|
|
|
|
|
WindowLevel::Normal => NSWindowLevel::Normal,
|
2019-05-01 17:03:30 -06:00
|
|
|
};
|
2022-09-08 16:45:29 +02:00
|
|
|
util::set_level_async(self, level);
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
2018-04-18 02:07:54 +08:00
|
|
|
|
2019-05-01 17:03:30 -06:00
|
|
|
#[inline]
|
|
|
|
|
pub fn set_window_icon(&self, _icon: Option<Icon>) {
|
|
|
|
|
// macOS doesn't have window icons. Though, there is
|
|
|
|
|
// `setRepresentedFilename`, but that's semantically distinct and should
|
|
|
|
|
// only be used when the window is in some way representing a specific
|
|
|
|
|
// file/directory. For instance, Terminal.app uses this for the CWD.
|
|
|
|
|
// Anyway, that should eventually be implemented as
|
|
|
|
|
// `WindowBuilderExt::with_represented_file` or something, and doesn't
|
|
|
|
|
// have anything to do with `set_window_icon`.
|
|
|
|
|
// https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/WinPanel/Tasks/SettingWindowTitle.html
|
|
|
|
|
}
|
2018-04-18 02:07:54 +08:00
|
|
|
|
2019-05-01 17:03:30 -06:00
|
|
|
#[inline]
|
2020-01-04 01:32:34 -05:00
|
|
|
pub fn set_ime_position(&self, spot: Position) {
|
2020-02-13 20:41:41 +01:00
|
|
|
let scale_factor = self.scale_factor();
|
|
|
|
|
let logical_spot = spot.to_logical(scale_factor);
|
2022-09-08 16:45:29 +02:00
|
|
|
// TODO(madsmtm): Remove the need for this
|
|
|
|
|
unsafe { Id::from_shared(self.view()) }.set_ime_position(logical_spot);
|
2017-12-22 07:50:46 -05:00
|
|
|
}
|
|
|
|
|
|
2022-05-07 05:29:25 +03:00
|
|
|
#[inline]
|
|
|
|
|
pub fn set_ime_allowed(&self, allowed: bool) {
|
2022-09-08 16:45:29 +02:00
|
|
|
// TODO(madsmtm): Remove the need for this
|
|
|
|
|
unsafe { Id::from_shared(self.view()) }.set_ime_allowed(allowed);
|
2022-05-07 05:29:25 +03:00
|
|
|
}
|
|
|
|
|
|
2021-05-19 18:39:53 +02:00
|
|
|
#[inline]
|
|
|
|
|
pub fn focus_window(&self) {
|
2022-09-08 16:45:29 +02:00
|
|
|
let is_minimized = self.isMiniaturized();
|
|
|
|
|
let is_visible = self.isVisible();
|
2021-05-19 18:39:53 +02:00
|
|
|
|
|
|
|
|
if !is_minimized && is_visible {
|
2022-09-08 16:45:29 +02:00
|
|
|
NSApp().activateIgnoringOtherApps(true);
|
|
|
|
|
util::make_key_and_order_front_async(self);
|
2021-05-19 18:39:53 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-27 03:03:08 +01:00
|
|
|
#[inline]
|
|
|
|
|
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
|
|
|
|
|
let ns_request_type = request_type.map(|ty| match ty {
|
|
|
|
|
UserAttentionType::Critical => NSRequestUserAttentionType::NSCriticalRequest,
|
|
|
|
|
UserAttentionType::Informational => NSRequestUserAttentionType::NSInformationalRequest,
|
|
|
|
|
});
|
2022-09-08 16:45:29 +02:00
|
|
|
if let Some(ty) = ns_request_type {
|
|
|
|
|
NSApp().requestUserAttention(ty);
|
2020-11-27 03:03:08 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-20 10:24:05 -04:00
|
|
|
#[inline]
|
2020-09-07 20:09:24 +03:00
|
|
|
// Allow directly accessing the current monitor internally without unwrapping.
|
2022-11-05 10:30:39 +08:00
|
|
|
pub(crate) fn current_monitor_inner(&self) -> Option<MonitorHandle> {
|
|
|
|
|
let display_id = self.screen()?.display_id();
|
|
|
|
|
Some(MonitorHandle::new(display_id))
|
2018-05-20 10:24:05 -04:00
|
|
|
}
|
|
|
|
|
|
2020-09-07 20:09:24 +03:00
|
|
|
#[inline]
|
2022-09-21 10:04:28 +02:00
|
|
|
pub fn current_monitor(&self) -> Option<MonitorHandle> {
|
2022-11-05 10:30:39 +08:00
|
|
|
self.current_monitor_inner()
|
2020-09-07 20:09:24 +03:00
|
|
|
}
|
|
|
|
|
|
2018-05-07 17:36:21 -04:00
|
|
|
#[inline]
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
|
|
|
|
monitor::available_monitors()
|
2018-05-07 17:36:21 -04:00
|
|
|
}
|
|
|
|
|
|
2018-05-17 21:28:30 -04:00
|
|
|
#[inline]
|
2022-09-21 10:04:28 +02:00
|
|
|
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
2020-09-07 20:20:47 +03:00
|
|
|
let monitor = monitor::primary_monitor();
|
2022-09-21 10:04:28 +02:00
|
|
|
Some(monitor)
|
2018-05-17 21:28:30 -04:00
|
|
|
}
|
2019-08-14 07:57:16 -04:00
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn raw_window_handle(&self) -> RawWindowHandle {
|
2022-07-21 22:22:36 +03:00
|
|
|
let mut window_handle = AppKitWindowHandle::empty();
|
2022-09-08 16:45:29 +02:00
|
|
|
window_handle.ns_window = self.ns_window();
|
|
|
|
|
window_handle.ns_view = self.ns_view();
|
2022-07-21 22:22:36 +03:00
|
|
|
RawWindowHandle::AppKit(window_handle)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn raw_display_handle(&self) -> RawDisplayHandle {
|
|
|
|
|
RawDisplayHandle::AppKit(AppKitDisplayHandle::empty())
|
2019-08-14 07:57:16 -04:00
|
|
|
}
|
2022-09-08 16:45:29 +02:00
|
|
|
|
|
|
|
|
fn toggle_style_mask(&self, mask: NSWindowStyleMask, on: bool) {
|
|
|
|
|
let current_style_mask = self.styleMask();
|
|
|
|
|
if on {
|
|
|
|
|
util::set_style_mask_sync(self, current_style_mask | mask);
|
|
|
|
|
} else {
|
|
|
|
|
util::set_style_mask_sync(self, current_style_mask & (!mask));
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-10-19 03:34:36 +09:00
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn theme(&self) -> Option<Theme> {
|
2022-11-29 12:58:35 +01:00
|
|
|
self.lock_shared_state("theme").current_theme
|
2022-10-19 03:34:36 +09:00
|
|
|
}
|
2022-11-03 10:11:37 -07:00
|
|
|
|
2022-11-29 11:05:51 +02:00
|
|
|
#[inline]
|
|
|
|
|
pub fn set_theme(&self, theme: Option<Theme>) {
|
|
|
|
|
set_ns_theme(theme);
|
|
|
|
|
self.lock_shared_state("set_theme").current_theme = theme.or_else(|| Some(get_ns_theme()));
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-03 10:11:37 -07:00
|
|
|
#[inline]
|
2022-11-23 15:51:34 +02:00
|
|
|
pub fn set_content_protected(&self, protected: bool) {
|
|
|
|
|
self.setSharingType(if protected {
|
|
|
|
|
NSWindowSharingType::NSWindowSharingNone
|
|
|
|
|
} else {
|
|
|
|
|
NSWindowSharingType::NSWindowSharingReadOnly
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-03 10:11:37 -07:00
|
|
|
pub fn title(&self) -> String {
|
|
|
|
|
self.title_().to_string()
|
|
|
|
|
}
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
2018-05-17 21:28:30 -04:00
|
|
|
|
2022-09-08 16:45:29 +02:00
|
|
|
impl WindowExtMacOS for WinitWindow {
|
2017-08-28 01:43:34 +01:00
|
|
|
#[inline]
|
2019-06-11 01:09:38 +02:00
|
|
|
fn ns_window(&self) -> *mut c_void {
|
2022-09-08 16:45:29 +02:00
|
|
|
self as *const Self as *mut _
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
2019-06-11 01:09:38 +02:00
|
|
|
fn ns_view(&self) -> *mut c_void {
|
2022-09-08 16:45:29 +02:00
|
|
|
Id::as_ptr(&self.contentView()) as *mut _
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
|
|
|
|
|
2019-05-16 00:26:59 -04:00
|
|
|
#[inline]
|
2019-05-29 21:29:54 -04:00
|
|
|
fn simple_fullscreen(&self) -> bool {
|
2022-11-29 12:58:35 +01:00
|
|
|
self.lock_shared_state("simple_fullscreen")
|
|
|
|
|
.is_simple_fullscreen
|
2019-05-16 00:26:59 -04:00
|
|
|
}
|
|
|
|
|
|
2019-05-01 17:03:30 -06:00
|
|
|
#[inline]
|
|
|
|
|
fn set_simple_fullscreen(&self, fullscreen: bool) -> bool {
|
2022-11-29 12:58:35 +01:00
|
|
|
let mut shared_state_lock = self.lock_shared_state("set_simple_fullscreen");
|
2019-05-01 17:03:30 -06:00
|
|
|
|
2022-09-08 16:45:29 +02:00
|
|
|
let app = NSApp();
|
|
|
|
|
let is_native_fullscreen = shared_state_lock.fullscreen.is_some();
|
|
|
|
|
let is_simple_fullscreen = shared_state_lock.is_simple_fullscreen;
|
2019-05-01 17:03:30 -06:00
|
|
|
|
2022-09-08 16:45:29 +02:00
|
|
|
// Do nothing if native fullscreen is active.
|
|
|
|
|
if is_native_fullscreen
|
|
|
|
|
|| (fullscreen && is_simple_fullscreen)
|
|
|
|
|
|| (!fullscreen && !is_simple_fullscreen)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2019-05-01 17:03:30 -06:00
|
|
|
|
2022-09-08 16:45:29 +02:00
|
|
|
if fullscreen {
|
|
|
|
|
// Remember the original window's settings
|
|
|
|
|
// Exclude title bar
|
|
|
|
|
shared_state_lock.standard_frame = Some(self.contentRectForFrameRect(self.frame()));
|
|
|
|
|
shared_state_lock.saved_style = Some(self.styleMask());
|
|
|
|
|
shared_state_lock.save_presentation_opts = Some(app.presentationOptions());
|
2019-05-01 17:03:30 -06:00
|
|
|
|
2022-09-08 16:45:29 +02:00
|
|
|
// Tell our window's state that we're in fullscreen
|
|
|
|
|
shared_state_lock.is_simple_fullscreen = true;
|
2019-05-01 17:03:30 -06:00
|
|
|
|
2022-09-08 16:45:29 +02:00
|
|
|
// Simulate pre-Lion fullscreen by hiding the dock and menu bar
|
|
|
|
|
let presentation_options =
|
|
|
|
|
NSApplicationPresentationOptions::NSApplicationPresentationAutoHideDock
|
|
|
|
|
| NSApplicationPresentationOptions::NSApplicationPresentationAutoHideMenuBar;
|
|
|
|
|
app.setPresentationOptions(presentation_options);
|
2019-05-01 17:03:30 -06:00
|
|
|
|
2022-09-08 16:45:29 +02:00
|
|
|
// Hide the titlebar
|
|
|
|
|
self.toggle_style_mask(NSWindowStyleMask::NSTitledWindowMask, false);
|
|
|
|
|
|
|
|
|
|
// Set the window frame to the screen frame size
|
|
|
|
|
let screen = self.screen().expect("expected screen to be available");
|
|
|
|
|
self.setFrame_display(screen.frame(), true);
|
2019-05-01 17:03:30 -06:00
|
|
|
|
2022-09-08 16:45:29 +02:00
|
|
|
// Fullscreen windows can't be resized, minimized, or moved
|
|
|
|
|
self.toggle_style_mask(NSWindowStyleMask::NSMiniaturizableWindowMask, false);
|
|
|
|
|
self.toggle_style_mask(NSWindowStyleMask::NSResizableWindowMask, false);
|
|
|
|
|
self.setMovable(false);
|
2019-05-01 17:03:30 -06:00
|
|
|
|
2022-09-08 16:45:29 +02:00
|
|
|
true
|
|
|
|
|
} else {
|
2022-11-23 13:07:58 +01:00
|
|
|
let new_mask = self.saved_style(&mut shared_state_lock);
|
2022-09-08 16:45:29 +02:00
|
|
|
self.set_style_mask_async(new_mask);
|
|
|
|
|
shared_state_lock.is_simple_fullscreen = false;
|
|
|
|
|
|
|
|
|
|
if let Some(presentation_opts) = shared_state_lock.save_presentation_opts {
|
|
|
|
|
app.setPresentationOptions(presentation_opts);
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
2022-09-08 16:45:29 +02:00
|
|
|
|
|
|
|
|
let frame = shared_state_lock.saved_standard_frame();
|
|
|
|
|
self.setFrame_display(frame, true);
|
|
|
|
|
self.setMovable(true);
|
|
|
|
|
|
|
|
|
|
true
|
2018-04-18 02:07:54 +08:00
|
|
|
}
|
2017-08-28 01:43:34 +01:00
|
|
|
}
|
2020-08-14 02:10:34 +08:00
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
fn has_shadow(&self) -> bool {
|
2022-09-08 16:45:29 +02:00
|
|
|
self.hasShadow()
|
2020-08-14 02:10:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
fn set_has_shadow(&self, has_shadow: bool) {
|
2022-09-08 16:45:29 +02:00
|
|
|
self.setHasShadow(has_shadow)
|
2017-02-03 23:05:57 +11:00
|
|
|
}
|
2022-11-23 17:07:41 +02:00
|
|
|
|
|
|
|
|
fn is_document_edited(&self) -> bool {
|
|
|
|
|
self.isDocumentEdited()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_document_edited(&self, edited: bool) {
|
|
|
|
|
self.setDocumentEdited(edited)
|
|
|
|
|
}
|
2017-02-03 23:05:57 +11:00
|
|
|
}
|
2022-10-19 03:34:36 +09:00
|
|
|
|
|
|
|
|
pub(super) fn get_ns_theme() -> Theme {
|
|
|
|
|
let app = NSApp();
|
|
|
|
|
let has_theme: bool = unsafe { msg_send![&app, respondsToSelector: sel!(effectiveAppearance)] };
|
|
|
|
|
if !has_theme {
|
|
|
|
|
return Theme::Light;
|
|
|
|
|
}
|
|
|
|
|
let appearance = app.effectiveAppearance();
|
|
|
|
|
let name = appearance.bestMatchFromAppearancesWithNames(&NSArray::from_slice(&[
|
|
|
|
|
NSString::from_str("NSAppearanceNameAqua"),
|
|
|
|
|
NSString::from_str("NSAppearanceNameDarkAqua"),
|
|
|
|
|
]));
|
|
|
|
|
match &*name.to_string() {
|
|
|
|
|
"NSAppearanceNameDarkAqua" => Theme::Dark,
|
|
|
|
|
_ => Theme::Light,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-29 11:05:51 +02:00
|
|
|
fn set_ns_theme(theme: Option<Theme>) {
|
2022-10-19 03:34:36 +09:00
|
|
|
let app = NSApp();
|
|
|
|
|
let has_theme: bool = unsafe { msg_send![&app, respondsToSelector: sel!(effectiveAppearance)] };
|
|
|
|
|
if has_theme {
|
2022-11-29 11:05:51 +02:00
|
|
|
let appearance = theme.map(|t| {
|
|
|
|
|
let name = match t {
|
|
|
|
|
Theme::Dark => NSString::from_str("NSAppearanceNameDarkAqua"),
|
|
|
|
|
Theme::Light => NSString::from_str("NSAppearanceNameAqua"),
|
|
|
|
|
};
|
|
|
|
|
NSAppearance::appearanceNamed(&name)
|
|
|
|
|
});
|
|
|
|
|
app.setAppearance(appearance.as_ref().map(|a| a.as_ref()));
|
2022-10-19 03:34:36 +09:00
|
|
|
}
|
|
|
|
|
}
|