macOS: basic event handling (#52)
* macOS: add basic event handling * macos: don't store subview pointer in WindowHandle * macOS: mention inspiration from antonok's vst_window crate, clean up * Add Anton Lazarev and myself to author list * macOS: fix event handling issues - Rename EventDelegate to WindowState - Make Window.ns_window optional, only set it if parentless - Put our own NSView subclass in Window.ns_view - Don't create useless "intermediate" NSView in parentless mode * macOS: use Arc::from_raw in WindowHandler dealloc fn * macOS: move subview code own file, handle more mouse events * macOS: add (non-tested) support for AsIfParented window * macOS: rename subview module to view * macOS: rename "mouse_click_extern_fn!" to "mouse_button_extern_fn!" This avoids confusion with the click event * macOS: make WindowState Arc wrapping code clearer * macOS: handle basic key press and release events * macOS: accept mouseMoved events, don't trigger them on clicks * macOS: fix cursor movement location conversion * macOS: add WindowState.trigger_event fn, make fields private * macOS: in view, set preservesContentInLiveResize to NO * macOS: add NSTrackingArea, cursor enter/exit events, better window init * macOS: remove unused WindowState.size field * macOS: acceptFirstMouse = YES in view * macOS: rename macro mouse_button_extern_fn to mouse_simple_extern_fn * macOS: remove key event handling, it will be implemented differently * macOS: trigger CursorMoved on right and middle mouse drag * macOS: run NSEvent.setMouseCoalescingEnabled(NO) * macOS: clean up * macOS: non-parented mode: don't "activate ignoring other apps" This is rarely necessary according to https://developer.apple.com/documentation/appkit/nsapplication/1428468-activate and I don't see any reason why we would need to do it. * macOS: call NSApp() before doing more work in non-parented mode * macOS: don't attempt to declare NSView subclass multiple times * macOS: add random suffix to name of NSView subclass to prevent issues * macOS: send tracking area options as a usize (objc UInt) * macOS: use UUID for class name suffix * macOS: fix view_will_move_to_window super call * macOS: drop WindowState when our NSView is released * macOS: in Window::open, autorelease an NSString that was allocated * macOS: delete our view class when the view is released * Upgrade cocoa dependency to version 0.24.0 * macOS: reorder some code in view.rs * macOS: mark WindowState::from_field as unsafe, update doc comment * macOS: in HasRawWindowHandle impl, use unwrap_or for ns_window
This commit is contained in:
parent
cd5c91f8a8
commit
2167a091fa
|
@ -7,6 +7,8 @@ authors = [
|
|||
"Mirko Covizzi <mrkcvzz@gmail.com>",
|
||||
"Micah Johnston <micah@glowcoil.com>",
|
||||
"Billy Messenger <billydm@protonmail.com>",
|
||||
"Anton Lazarev <https://antonok.com>",
|
||||
"Joakim Frostegård <joakim.frostegard@gmail.com>",
|
||||
]
|
||||
edition = "2018"
|
||||
|
||||
|
@ -25,5 +27,6 @@ nix = "0.18"
|
|||
winapi = { version = "0.3.8", features = ["libloaderapi", "winuser", "windef", "minwindef", "guiddef", "combaseapi", "wingdi", "errhandlingapi"] }
|
||||
|
||||
[target.'cfg(target_os="macos")'.dependencies]
|
||||
cocoa = "0.23.0"
|
||||
cocoa = "0.24.0"
|
||||
objc = "0.2.7"
|
||||
uuid = { version = "0.8", features = ["v4"] }
|
|
@ -1,2 +1,4 @@
|
|||
mod window;
|
||||
mod view;
|
||||
|
||||
pub use window::*;
|
||||
|
|
336
src/macos/view.rs
Normal file
336
src/macos/view.rs
Normal file
|
@ -0,0 +1,336 @@
|
|||
use std::ffi::c_void;
|
||||
use std::sync::Arc;
|
||||
|
||||
use cocoa::appkit::{NSEvent, NSView};
|
||||
use cocoa::base::{id, nil, BOOL, YES, NO};
|
||||
use cocoa::foundation::{NSArray, NSPoint, NSRect, NSSize};
|
||||
|
||||
use objc::{
|
||||
class,
|
||||
declare::ClassDecl,
|
||||
msg_send,
|
||||
runtime::{Class, Object, Sel},
|
||||
sel, sel_impl,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
Event, MouseButton, MouseEvent, Point, WindowHandler,
|
||||
WindowOpenOptions
|
||||
};
|
||||
use crate::MouseEvent::{ButtonPressed, ButtonReleased};
|
||||
|
||||
use super::window::{WindowState, WINDOW_STATE_IVAR_NAME};
|
||||
|
||||
|
||||
pub(super) unsafe fn create_view<H: WindowHandler>(
|
||||
window_options: &WindowOpenOptions,
|
||||
) -> id {
|
||||
let class = create_view_class::<H>();
|
||||
|
||||
let view: id = msg_send![class, alloc];
|
||||
|
||||
let size = window_options.size;
|
||||
|
||||
view.initWithFrame_(NSRect::new(
|
||||
NSPoint::new(0., 0.),
|
||||
NSSize::new(size.width, size.height),
|
||||
));
|
||||
|
||||
view
|
||||
}
|
||||
|
||||
|
||||
unsafe fn create_view_class<H: WindowHandler>() -> &'static Class {
|
||||
// Use unique class names so that there are no conflicts between different
|
||||
// instances. The class is deleted when the view is released. Previously,
|
||||
// the class was stored in a OnceCell after creation. This way, we didn't
|
||||
// have to recreate it each time a view was opened, but now we don't leave
|
||||
// any class definitions lying around when the plugin is closed.
|
||||
let class_name = format!("BaseviewNSView_{}", Uuid::new_v4().to_simple());
|
||||
let mut class = ClassDecl::new(&class_name, class!(NSView)).unwrap();
|
||||
|
||||
class.add_method(
|
||||
sel!(acceptsFirstResponder),
|
||||
property_yes::<H> as extern "C" fn(&Object, Sel) -> BOOL
|
||||
);
|
||||
class.add_method(
|
||||
sel!(isFlipped),
|
||||
property_yes::<H> as extern "C" fn(&Object, Sel) -> BOOL
|
||||
);
|
||||
class.add_method(
|
||||
sel!(preservesContentInLiveResize),
|
||||
property_no::<H> as extern "C" fn(&Object, Sel) -> BOOL
|
||||
);
|
||||
class.add_method(
|
||||
sel!(acceptsFirstMouse:),
|
||||
accepts_first_mouse::<H> as extern "C" fn(&Object, Sel, id) -> BOOL
|
||||
);
|
||||
|
||||
class.add_method(
|
||||
sel!(release),
|
||||
release::<H> as extern "C" fn(&Object, Sel)
|
||||
);
|
||||
class.add_method(
|
||||
sel!(viewWillMoveToWindow:),
|
||||
view_will_move_to_window::<H> as extern "C" fn(&Object, Sel, id)
|
||||
);
|
||||
class.add_method(
|
||||
sel!(updateTrackingAreas:),
|
||||
update_tracking_areas::<H> as extern "C" fn(&Object, Sel, id)
|
||||
);
|
||||
|
||||
class.add_method(
|
||||
sel!(mouseMoved:),
|
||||
mouse_moved::<H> as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
class.add_method(
|
||||
sel!(mouseDragged:),
|
||||
mouse_moved::<H> as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
class.add_method(
|
||||
sel!(rightMouseDragged:),
|
||||
mouse_moved::<H> as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
class.add_method(
|
||||
sel!(otherMouseDragged:),
|
||||
mouse_moved::<H> as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
|
||||
class.add_method(
|
||||
sel!(mouseEntered:),
|
||||
mouse_entered::<H> as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
|
||||
class.add_method(
|
||||
sel!(mouseExited:),
|
||||
mouse_exited::<H> as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
|
||||
class.add_method(
|
||||
sel!(mouseDown:),
|
||||
left_mouse_down::<H> as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
class.add_method(
|
||||
sel!(mouseUp:),
|
||||
left_mouse_up::<H> as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
|
||||
class.add_method(
|
||||
sel!(rightMouseDown:),
|
||||
right_mouse_down::<H> as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
class.add_method(
|
||||
sel!(rightMouseUp:),
|
||||
right_mouse_up::<H> as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
|
||||
class.add_method(
|
||||
sel!(otherMouseDown:),
|
||||
middle_mouse_down::<H> as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
class.add_method(
|
||||
sel!(otherMouseUp:),
|
||||
middle_mouse_up::<H> as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
|
||||
class.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR_NAME);
|
||||
|
||||
class.register()
|
||||
}
|
||||
|
||||
|
||||
extern "C" fn property_yes<H: WindowHandler>(
|
||||
_this: &Object,
|
||||
_sel: Sel,
|
||||
) -> BOOL {
|
||||
YES
|
||||
}
|
||||
|
||||
|
||||
extern "C" fn property_no<H: WindowHandler>(
|
||||
_this: &Object,
|
||||
_sel: Sel,
|
||||
) -> BOOL {
|
||||
YES
|
||||
}
|
||||
|
||||
|
||||
extern "C" fn accepts_first_mouse<H: WindowHandler>(
|
||||
_this: &Object,
|
||||
_sel: Sel,
|
||||
_event: id
|
||||
) -> BOOL {
|
||||
YES
|
||||
}
|
||||
|
||||
|
||||
extern "C" fn release<H: WindowHandler>(this: &Object, _sel: Sel) {
|
||||
unsafe {
|
||||
let superclass = msg_send![this, superclass];
|
||||
|
||||
let () = msg_send![super(this, superclass), release];
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let retain_count: usize = msg_send![this, retainCount];
|
||||
|
||||
if retain_count == 1 {
|
||||
let state_ptr: *mut c_void = *this.get_ivar(
|
||||
WINDOW_STATE_IVAR_NAME
|
||||
);
|
||||
|
||||
// Drop WindowState
|
||||
Arc::from_raw(state_ptr as *mut WindowState<H>);
|
||||
|
||||
// Delete class
|
||||
let class = msg_send![this, class];
|
||||
::objc::runtime::objc_disposeClassPair(class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Init/reinit tracking area
|
||||
///
|
||||
/// Info:
|
||||
/// https://developer.apple.com/documentation/appkit/nstrackingarea
|
||||
/// https://developer.apple.com/documentation/appkit/nstrackingarea/options
|
||||
/// https://developer.apple.com/documentation/appkit/nstrackingareaoptions
|
||||
unsafe fn reinit_tracking_area(this: &Object, tracking_area: *mut Object){
|
||||
let options: usize = {
|
||||
let mouse_entered_and_exited = 0x01;
|
||||
let tracking_mouse_moved = 0x02;
|
||||
let tracking_cursor_update = 0x04;
|
||||
let tracking_active_in_active_app = 0x40;
|
||||
let tracking_in_visible_rect = 0x200;
|
||||
let tracking_enabled_during_mouse_drag = 0x400;
|
||||
|
||||
mouse_entered_and_exited | tracking_mouse_moved |
|
||||
tracking_cursor_update | tracking_active_in_active_app |
|
||||
tracking_in_visible_rect | tracking_enabled_during_mouse_drag
|
||||
};
|
||||
|
||||
let bounds: NSRect = msg_send![this, bounds];
|
||||
|
||||
*tracking_area = msg_send![tracking_area,
|
||||
initWithRect:bounds
|
||||
options:options
|
||||
owner:this
|
||||
userInfo:nil
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
extern "C" fn view_will_move_to_window<H: WindowHandler>(
|
||||
this: &Object,
|
||||
_self: Sel,
|
||||
new_window: id
|
||||
){
|
||||
unsafe {
|
||||
let tracking_areas: *mut Object = msg_send![this, trackingAreas];
|
||||
let tracking_area_count = NSArray::count(tracking_areas);
|
||||
|
||||
let _: () = msg_send![class!(NSEvent), setMouseCoalescingEnabled:NO];
|
||||
|
||||
if new_window == nil {
|
||||
if tracking_area_count != 0 {
|
||||
let tracking_area = NSArray::objectAtIndex(tracking_areas, 0);
|
||||
|
||||
|
||||
let _: () = msg_send![this, removeTrackingArea:tracking_area];
|
||||
let _: () = msg_send![tracking_area, release];
|
||||
}
|
||||
|
||||
} else {
|
||||
if tracking_area_count == 0 {
|
||||
let class = Class::get("NSTrackingArea").unwrap();
|
||||
|
||||
let tracking_area: *mut Object = msg_send![class, alloc];
|
||||
|
||||
reinit_tracking_area(this, tracking_area);
|
||||
|
||||
let _: () = msg_send![this, addTrackingArea:tracking_area];
|
||||
}
|
||||
|
||||
let _: () = msg_send![new_window, setAcceptsMouseMovedEvents:YES];
|
||||
let _: () = msg_send![new_window, makeFirstResponder:this];
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let superclass = msg_send![this, superclass];
|
||||
|
||||
let () = msg_send![super(this, superclass), viewWillMoveToWindow:new_window];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extern "C" fn update_tracking_areas<H: WindowHandler>(
|
||||
this: &Object,
|
||||
_self: Sel,
|
||||
_: id
|
||||
){
|
||||
unsafe {
|
||||
let tracking_areas: *mut Object = msg_send![this, trackingAreas];
|
||||
let tracking_area = NSArray::objectAtIndex(tracking_areas, 0);
|
||||
|
||||
reinit_tracking_area(this, tracking_area);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extern "C" fn mouse_moved<H: WindowHandler>(
|
||||
this: &Object,
|
||||
_sel: Sel,
|
||||
event: id
|
||||
){
|
||||
let point: NSPoint = unsafe {
|
||||
let point = NSEvent::locationInWindow(event);
|
||||
|
||||
msg_send![this, convertPoint:point fromView:nil]
|
||||
};
|
||||
|
||||
let position = Point {
|
||||
x: point.x,
|
||||
y: point.y
|
||||
};
|
||||
|
||||
let event = Event::Mouse(MouseEvent::CursorMoved { position });
|
||||
|
||||
let state: &mut WindowState<H> = unsafe {
|
||||
WindowState::from_field(this)
|
||||
};
|
||||
|
||||
state.trigger_event(event);
|
||||
}
|
||||
|
||||
|
||||
macro_rules! mouse_simple_extern_fn {
|
||||
($fn:ident, $event:expr) => {
|
||||
extern "C" fn $fn<H: WindowHandler>(
|
||||
this: &Object,
|
||||
_sel: Sel,
|
||||
_event: id,
|
||||
){
|
||||
let state: &mut WindowState<H> = unsafe {
|
||||
WindowState::from_field(this)
|
||||
};
|
||||
|
||||
state.trigger_event(Event::Mouse($event));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
mouse_simple_extern_fn!(left_mouse_down, ButtonPressed(MouseButton::Left));
|
||||
mouse_simple_extern_fn!(left_mouse_up, ButtonReleased(MouseButton::Left));
|
||||
|
||||
mouse_simple_extern_fn!(right_mouse_down, ButtonPressed(MouseButton::Right));
|
||||
mouse_simple_extern_fn!(right_mouse_up, ButtonReleased(MouseButton::Right));
|
||||
|
||||
mouse_simple_extern_fn!(middle_mouse_down, ButtonPressed(MouseButton::Middle));
|
||||
mouse_simple_extern_fn!(middle_mouse_up, ButtonReleased(MouseButton::Middle));
|
||||
|
||||
mouse_simple_extern_fn!(mouse_entered, MouseEvent::CursorEntered);
|
||||
mouse_simple_extern_fn!(mouse_exited, MouseEvent::CursorLeft);
|
|
@ -1,33 +1,52 @@
|
|||
/// macOS window handling
|
||||
///
|
||||
/// Inspired by implementation in https://github.com/antonok-edm/vst_window
|
||||
|
||||
use std::ffi::c_void;
|
||||
use std::sync::Arc;
|
||||
|
||||
use cocoa::appkit::{
|
||||
NSApp, NSApplication, NSApplicationActivateIgnoringOtherApps,
|
||||
NSApplicationActivationPolicyRegular, NSBackingStoreBuffered, NSRunningApplication, NSView,
|
||||
NSWindow, NSWindowStyleMask,
|
||||
NSApp, NSApplication, NSApplicationActivationPolicyRegular,
|
||||
NSBackingStoreBuffered, NSWindow, NSWindowStyleMask,
|
||||
};
|
||||
use cocoa::base::{id, nil, NO};
|
||||
use cocoa::foundation::{NSAutoreleasePool, NSPoint, NSRect, NSSize, NSString};
|
||||
|
||||
use objc::{msg_send, runtime::Object, sel, sel_impl};
|
||||
|
||||
use raw_window_handle::{macos::MacOSHandle, HasRawWindowHandle, RawWindowHandle};
|
||||
|
||||
use crate::{
|
||||
Event, KeyboardEvent, MouseButton, MouseEvent, ScrollDelta, WindowEvent, WindowHandler,
|
||||
WindowOpenOptions, WindowScalePolicy, WindowInfo,
|
||||
Event, Parent, WindowHandler, WindowOpenOptions, WindowScalePolicy,
|
||||
WindowInfo
|
||||
};
|
||||
|
||||
use super::view::create_view;
|
||||
|
||||
|
||||
/// Name of the field used to store the `WindowState` pointer in the custom
|
||||
/// view class.
|
||||
pub(super) const WINDOW_STATE_IVAR_NAME: &str = "WINDOW_STATE_IVAR_NAME";
|
||||
|
||||
|
||||
pub struct Window {
|
||||
ns_window: id,
|
||||
/// Only set if we created the parent window, i.e. we are running in
|
||||
/// parentless mode
|
||||
ns_window: Option<id>,
|
||||
/// Our subclassed NSView
|
||||
ns_view: id,
|
||||
}
|
||||
|
||||
|
||||
pub struct WindowHandle;
|
||||
|
||||
|
||||
impl WindowHandle {
|
||||
pub fn app_run_blocking(self) {
|
||||
unsafe {
|
||||
let app = NSApp();
|
||||
app.setActivationPolicy_(NSApplicationActivationPolicyRegular);
|
||||
app.run();
|
||||
// Get reference to already created shared NSApplication object
|
||||
// and run the main loop
|
||||
NSApp().run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,62 +57,162 @@ impl Window {
|
|||
B: FnOnce(&mut Window) -> H,
|
||||
B: Send + 'static
|
||||
{
|
||||
let _pool = unsafe { NSAutoreleasePool::new(nil) };
|
||||
|
||||
let mut window = match options.parent {
|
||||
Parent::WithParent(parent) => {
|
||||
if let RawWindowHandle::MacOS(handle) = parent {
|
||||
let ns_view = handle.ns_view as *mut objc::runtime::Object;
|
||||
|
||||
unsafe {
|
||||
let subview = create_view::<H>(&options);
|
||||
|
||||
let _: id = msg_send![ns_view, addSubview: subview];
|
||||
|
||||
Window {
|
||||
ns_window: None,
|
||||
ns_view: subview,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic!("Not a macOS window");
|
||||
}
|
||||
},
|
||||
Parent::AsIfParented => {
|
||||
let ns_view = unsafe {
|
||||
create_view::<H>(&options)
|
||||
};
|
||||
|
||||
Window {
|
||||
ns_window: None,
|
||||
ns_view,
|
||||
}
|
||||
},
|
||||
Parent::None => {
|
||||
// It seems prudent to run NSApp() here before doing other
|
||||
// work. It runs [NSApplication sharedApplication], which is
|
||||
// what is run at the very start of the Xcode-generated main
|
||||
// function of a cocoa app according to:
|
||||
// https://developer.apple.com/documentation/appkit/nsapplication
|
||||
unsafe {
|
||||
let app = NSApp();
|
||||
app.setActivationPolicy_(
|
||||
NSApplicationActivationPolicyRegular
|
||||
);
|
||||
}
|
||||
|
||||
let scaling = match options.scale {
|
||||
WindowScalePolicy::ScaleFactor(scale) => scale,
|
||||
WindowScalePolicy::SystemScaleFactor => {
|
||||
get_scaling().unwrap_or(1.0)
|
||||
},
|
||||
};
|
||||
|
||||
let window_info = WindowInfo::from_logical_size(
|
||||
options.size,
|
||||
scaling
|
||||
);
|
||||
|
||||
let rect = NSRect::new(
|
||||
NSPoint::new(0.0, 0.0),
|
||||
NSSize::new(
|
||||
window_info.logical_size().width as f64,
|
||||
window_info.logical_size().height as f64
|
||||
),
|
||||
);
|
||||
|
||||
unsafe {
|
||||
let ns_window = NSWindow::alloc(nil)
|
||||
.initWithContentRect_styleMask_backing_defer_(
|
||||
rect,
|
||||
NSWindowStyleMask::NSTitledWindowMask,
|
||||
NSBackingStoreBuffered,
|
||||
NO,
|
||||
)
|
||||
.autorelease();
|
||||
ns_window.center();
|
||||
|
||||
let title = NSString::alloc(nil)
|
||||
.init_str(&options.title)
|
||||
.autorelease();
|
||||
ns_window.setTitle_(title);
|
||||
|
||||
ns_window.makeKeyAndOrderFront_(nil);
|
||||
|
||||
let subview = create_view::<H>(&options);
|
||||
|
||||
ns_window.setContentView_(subview);
|
||||
|
||||
Window {
|
||||
ns_window: Some(ns_window),
|
||||
ns_view: subview,
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let window_handler = build(&mut window);
|
||||
|
||||
let window_state_arc = Arc::new(WindowState {
|
||||
window,
|
||||
window_handler,
|
||||
});
|
||||
|
||||
let window_state_pointer = Arc::into_raw(
|
||||
window_state_arc.clone()
|
||||
) as *mut c_void;
|
||||
|
||||
unsafe {
|
||||
let _pool = NSAutoreleasePool::new(nil);
|
||||
|
||||
let scaling = match options.scale {
|
||||
WindowScalePolicy::SystemScaleFactor => get_scaling().unwrap_or(1.0),
|
||||
WindowScalePolicy::ScaleFactor(scale) => scale
|
||||
};
|
||||
|
||||
let window_info = WindowInfo::from_logical_size(options.size, scaling);
|
||||
|
||||
let rect = NSRect::new(
|
||||
NSPoint::new(0.0, 0.0),
|
||||
NSSize::new(
|
||||
window_info.logical_size().width as f64,
|
||||
window_info.logical_size().height as f64
|
||||
),
|
||||
(*window_state_arc.window.ns_view).set_ivar(
|
||||
WINDOW_STATE_IVAR_NAME,
|
||||
window_state_pointer
|
||||
);
|
||||
|
||||
let ns_window = NSWindow::alloc(nil)
|
||||
.initWithContentRect_styleMask_backing_defer_(
|
||||
rect,
|
||||
NSWindowStyleMask::NSTitledWindowMask,
|
||||
NSBackingStoreBuffered,
|
||||
NO,
|
||||
)
|
||||
.autorelease();
|
||||
ns_window.center();
|
||||
ns_window.setTitle_(NSString::alloc(nil).init_str(&options.title));
|
||||
ns_window.makeKeyAndOrderFront_(nil);
|
||||
|
||||
let ns_view = NSView::alloc(nil).init();
|
||||
ns_window.setContentView_(ns_view);
|
||||
|
||||
let mut window = Window { ns_window, ns_view };
|
||||
|
||||
let handler = build(&mut window);
|
||||
|
||||
// FIXME: only do this in the unparented case
|
||||
let current_app = NSRunningApplication::currentApplication(nil);
|
||||
current_app.activateWithOptions_(NSApplicationActivateIgnoringOtherApps);
|
||||
|
||||
WindowHandle
|
||||
}
|
||||
|
||||
WindowHandle
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub(super) struct WindowState<H: WindowHandler> {
|
||||
window: Window,
|
||||
window_handler: H,
|
||||
}
|
||||
|
||||
|
||||
impl <H: WindowHandler>WindowState<H> {
|
||||
/// Returns a mutable reference to a WindowState from an Objective-C field
|
||||
///
|
||||
/// Don't use this to create two simulataneous references to a single
|
||||
/// WindowState. Apparently, macOS blocks for the duration of an event,
|
||||
/// callback, meaning that this shouldn't be a problem in practice.
|
||||
pub(super) unsafe fn from_field(obj: &Object) -> &mut Self {
|
||||
let state_ptr: *mut c_void = *obj.get_ivar(WINDOW_STATE_IVAR_NAME);
|
||||
|
||||
&mut *(state_ptr as *mut Self)
|
||||
}
|
||||
|
||||
pub(super) fn trigger_event(&mut self, event: Event){
|
||||
self.window_handler.on_event(&mut self.window, event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unsafe impl HasRawWindowHandle for Window {
|
||||
fn raw_window_handle(&self) -> RawWindowHandle {
|
||||
let ns_window = self.ns_window.unwrap_or(
|
||||
::std::ptr::null_mut()
|
||||
) as *mut c_void;
|
||||
|
||||
RawWindowHandle::MacOS(MacOSHandle {
|
||||
ns_window: self.ns_window as *mut c_void,
|
||||
ns_window,
|
||||
ns_view: self.ns_view as *mut c_void,
|
||||
..MacOSHandle::empty()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn get_scaling() -> Option<f64> {
|
||||
// TODO: find system scaling
|
||||
None
|
||||
|
|
Loading…
Reference in a new issue