Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ objc2-app-kit = { version = "0.2.2", features = [
"NSMenu",
"NSMenuItem",
"NSOpenGLView",
"NSPanel",
"NSPasteboard",
"NSResponder",
"NSRunningApplication",
Expand Down
1 change: 1 addition & 0 deletions src/changelog/unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ changelog entry.
- Added `Window::surface_position`, which is the position of the surface inside the window.
- Added `Window::safe_area`, which describes the area of the surface that is unobstructed.
- On X11, Wayland, Windows and macOS, improved scancode conversions for more obscure key codes.
- Add ability to make non-activating window on macOS using `NSPanel` with `NSWindowStyleMask::NonactivatingPanel`.

### Changed

Expand Down
13 changes: 13 additions & 0 deletions src/platform/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,13 @@ pub trait WindowAttributesExtMacOS {
fn with_borderless_game(self, borderless_game: bool) -> Self;
/// See [`WindowExtMacOS::set_unified_titlebar`] for details on what this means if set.
fn with_unified_titlebar(self, unified_titlebar: bool) -> Self;
/// Use [`NSPanel`] window with [`NonactivatingPanel`] window style mask instead of
/// [`NSWindow`].
///
/// [`NSWindow`]: https://developer.apple.com/documentation/appkit/NSWindow?language=objc
/// [`NSPanel`]: https://developer.apple.com/documentation/appkit/NSPanel?language=objc
/// [`NonactivatingPanel`]: https://developer.apple.com/documentation/appkit/nswindow/stylemask-swift.struct/nonactivatingpanel?language=objc
fn with_panel(self, panel: bool) -> Self;
}

impl WindowAttributesExtMacOS for WindowAttributes {
Expand Down Expand Up @@ -412,6 +419,12 @@ impl WindowAttributesExtMacOS for WindowAttributes {
self.platform_specific.unified_titlebar = unified_titlebar;
self
}

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

pub trait EventLoopBuilderExtMacOS {
Expand Down
21 changes: 7 additions & 14 deletions src/platform_impl/apple/appkit/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use objc2::runtime::{AnyObject, Sel};
use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass};
use objc2_app_kit::{
NSApplication, NSCursor, NSEvent, NSEventPhase, NSResponder, NSTextInputClient,
NSTrackingRectTag, NSView,
NSTrackingRectTag, NSView, NSWindow,
};
use objc2_foundation::{
MainThreadMarker, NSArray, NSAttributedString, NSAttributedStringKey, NSCopying,
Expand All @@ -23,7 +23,7 @@ use super::event::{
code_to_key, code_to_location, create_key_event, event_mods, lalt_pressed, ralt_pressed,
scancode_to_physicalkey, KeyEventExtra,
};
use super::window::WinitWindow;
use super::window::window_id;
use crate::dpi::{LogicalPosition, LogicalSize};
use crate::event::{
DeviceEvent, ElementState, Ime, KeyEvent, Modifiers, MouseButton, MouseScrollDelta,
Expand Down Expand Up @@ -201,7 +201,7 @@ declare_class!(
fn draw_rect(&self, _rect: NSRect) {
trace_scope!("drawRect:");

self.ivars().app_state.handle_redraw(self.window().id());
self.ivars().app_state.handle_redraw(window_id(&self.window()));

// This is a direct subclass of NSView, no need to call superclass' drawRect:
}
Expand Down Expand Up @@ -426,7 +426,7 @@ declare_class!(
}

// Send command action to user if they requested it.
let window_id = self.window().id();
let window_id = window_id(&self.window());
self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop| {
if let Some(handler) = app.macos_handler() {
handler.standard_key_binding(event_loop, window_id, command.name());
Expand Down Expand Up @@ -828,19 +828,12 @@ impl WinitView {
this
}

fn window(&self) -> Retained<WinitWindow> {
let window = (**self).window().expect("view must be installed in a window");

if !window.isKindOfClass(WinitWindow::class()) {
unreachable!("view installed in non-WinitWindow");
}

// SAFETY: Just checked that the window is `WinitWindow`
unsafe { Retained::cast(window) }
fn window(&self) -> Retained<NSWindow> {
(**self).window().expect("view must be installed in a window")
}

fn queue_event(&self, event: WindowEvent) {
let window_id = self.window().id();
let window_id = window_id(&self.window());
self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop| {
app.window_event(event_loop, window_id, event);
});
Expand Down
32 changes: 27 additions & 5 deletions src/platform_impl/apple/appkit/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use dpi::{Position, Size};
use objc2::rc::{autoreleasepool, Retained};
use objc2::{declare_class, mutability, ClassType, DeclaredClass};
use objc2_app_kit::{NSResponder, NSWindow};
use objc2_app_kit::{NSPanel, NSResponder, NSWindow};
use objc2_foundation::{MainThreadBound, MainThreadMarker, NSObject};

use super::event_loop::ActiveEventLoop;
Expand All @@ -16,7 +16,7 @@ use crate::window::{
};

pub(crate) struct Window {
window: MainThreadBound<Retained<WinitWindow>>,
window: MainThreadBound<Retained<NSWindow>>,
/// The window only keeps a weak reference to this, so we must keep it around here.
delegate: MainThreadBound<Retained<WindowDelegate>>,
}
Expand Down Expand Up @@ -360,8 +360,30 @@ declare_class!(
}
);

impl WinitWindow {
pub(super) fn id(&self) -> WindowId {
WindowId::from_raw(self as *const Self as usize)
declare_class!(
#[derive(Debug)]
pub struct WinitPanel;

unsafe impl ClassType for WinitPanel {
#[inherits(NSWindow, NSResponder, NSObject)]
type Super = NSPanel;
type Mutability = mutability::MainThreadOnly;
const NAME: &'static str = "WinitPanel";
}

impl DeclaredClass for WinitPanel {}

unsafe impl WinitPanel {
// although NSPanel can become key window
// it doesn't if window doesn't have NSWindowStyleMask::Titled
#[method(canBecomeKeyWindow)]
fn can_become_key_window(&self) -> bool {
trace_scope!("canBecomeKeyWindow");
true
}
}
);

pub(super) fn window_id(window: &NSWindow) -> WindowId {
WindowId::from_raw(window as *const _ as usize)
}
55 changes: 37 additions & 18 deletions src/platform_impl/apple/appkit/window_delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use objc2_app_kit::{
NSAppearanceNameAqua, NSApplication, NSApplicationPresentationOptions, NSBackingStoreType,
NSColor, NSDraggingDestination, NSFilenamesPboardType, NSPasteboard,
NSRequestUserAttentionType, NSScreen, NSToolbar, NSView, NSViewFrameDidChangeNotification,
NSWindowButton, NSWindowDelegate, NSWindowFullScreenButton, NSWindowLevel,
NSWindow, NSWindowButton, NSWindowDelegate, NSWindowFullScreenButton, NSWindowLevel,
NSWindowOcclusionState, NSWindowOrderingMode, NSWindowSharingType, NSWindowStyleMask,
NSWindowTabbingMode, NSWindowTitleVisibility, NSWindowToolbarStyle,
};
Expand All @@ -33,7 +33,7 @@ use super::cursor::cursor_from_icon;
use super::monitor::{self, flip_window_screen_coordinates, get_display_id};
use super::observer::RunLoop;
use super::view::WinitView;
use super::window::WinitWindow;
use super::window::{window_id, WinitPanel, WinitWindow};
use super::{ffi, Fullscreen, MonitorHandle};
use crate::dpi::{
LogicalInsets, LogicalPosition, LogicalSize, PhysicalInsets, PhysicalPosition, PhysicalSize,
Expand Down Expand Up @@ -62,6 +62,7 @@ pub struct PlatformSpecificWindowAttributes {
pub option_as_alt: OptionAsAlt,
pub borderless_game: bool,
pub unified_titlebar: bool,
pub panel: bool,
}

impl Default for PlatformSpecificWindowAttributes {
Expand All @@ -81,6 +82,7 @@ impl Default for PlatformSpecificWindowAttributes {
option_as_alt: Default::default(),
borderless_game: false,
unified_titlebar: false,
panel: false,
}
}
}
Expand All @@ -90,7 +92,7 @@ pub(crate) struct State {
/// Strong reference to the global application state.
app_state: Rc<AppState>,

window: Retained<WinitWindow>,
window: Retained<NSWindow>,

// During `windowDidResize`, we use this to only send Moved if the position changed.
//
Expand Down Expand Up @@ -501,7 +503,7 @@ fn new_window(
app_state: &Rc<AppState>,
attrs: &WindowAttributes,
mtm: MainThreadMarker,
) -> Option<Retained<WinitWindow>> {
) -> Option<Retained<NSWindow>> {
autoreleasepool(|_| {
let screen = match attrs.fullscreen.clone().map(Into::into) {
Some(Fullscreen::Borderless(Some(monitor)))
Expand Down Expand Up @@ -584,16 +586,33 @@ fn new_window(
// confusing issues with the window not being properly activated.
//
// Winit ensures this by not allowing access to `ActiveEventLoop` before handling events.
let window: Option<Retained<WinitWindow>> = unsafe {
msg_send_id![
super(mtm.alloc().set_ivars(())),
initWithContentRect: frame,
styleMask: masks,
backing: NSBackingStoreType::NSBackingStoreBuffered,
defer: false,
]
let window: Retained<NSWindow> = if attrs.platform_specific.panel {
masks |= NSWindowStyleMask::NonactivatingPanel;

let window: Option<Retained<WinitPanel>> = unsafe {
msg_send_id![
super(mtm.alloc().set_ivars(())),
initWithContentRect: frame,
styleMask: masks,
backing: NSBackingStoreType::NSBackingStoreBuffered,
defer: false,
]
};

window?.as_super().as_super().retain()
} else {
let window: Option<Retained<WinitWindow>> = unsafe {
msg_send_id![
super(mtm.alloc().set_ivars(())),
initWithContentRect: frame,
styleMask: masks,
backing: NSBackingStoreType::NSBackingStoreBuffered,
defer: false,
]
};

window?.as_super().retain()
};
let window = window?;

// It is very important for correct memory management that we
// disable the extra release that would otherwise happen when
Expand Down Expand Up @@ -841,17 +860,17 @@ impl WindowDelegate {
}

#[track_caller]
pub(super) fn window(&self) -> &WinitWindow {
pub(super) fn window(&self) -> &NSWindow {
&self.ivars().window
}

#[track_caller]
pub(crate) fn id(&self) -> WindowId {
self.window().id()
window_id(self.window())
}

pub(crate) fn queue_event(&self, event: WindowEvent) {
let window_id = self.window().id();
let window_id = window_id(self.window());
self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop| {
app.window_event(event_loop, window_id, event);
});
Expand Down Expand Up @@ -950,7 +969,7 @@ impl WindowDelegate {
}

pub fn request_redraw(&self) {
self.ivars().app_state.queue_redraw(self.window().id());
self.ivars().app_state.queue_redraw(window_id(self.window()));
}

#[inline]
Expand Down Expand Up @@ -1488,7 +1507,7 @@ impl WindowDelegate {

self.ivars().fullscreen.replace(fullscreen.clone());

fn toggle_fullscreen(window: &WinitWindow) {
fn toggle_fullscreen(window: &NSWindow) {
// Window level must be restored from `CGShieldingWindowLevel()
// + 1` back to normal in order for `toggleFullScreen` to do
// anything
Expand Down