mirror of
https://github.com/tauri-apps/tao.git
synced 2026-01-31 00:35:16 +01:00
refactor: Use objc2 (#1049)
This commit is contained in:
5
.changes/objc2.md
Normal file
5
.changes/objc2.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
tao: patch
|
||||
---
|
||||
|
||||
Use `objc2`.
|
||||
35
Cargo.toml
35
Cargo.toml
@@ -104,10 +104,41 @@ ndk-context = "0.1"
|
||||
tao-macros = { version = "0.1.0", path = "./tao-macros" }
|
||||
|
||||
[target."cfg(any(target_os = \"ios\", target_os = \"macos\"))".dependencies]
|
||||
objc = "0.2"
|
||||
objc2 = "0.5"
|
||||
|
||||
[target."cfg(target_os = \"macos\")".dependencies]
|
||||
cocoa = "0.26"
|
||||
objc2-foundation = { version = "0.2", default-features = false, features = [
|
||||
"std",
|
||||
"NSArray",
|
||||
"NSAttributedString",
|
||||
"NSAutoreleasePool",
|
||||
"NSDate",
|
||||
"NSDictionary",
|
||||
"NSEnumerator",
|
||||
"NSGeometry",
|
||||
"NSObjCRuntime",
|
||||
"NSRange",
|
||||
"NSString",
|
||||
"NSThread",
|
||||
"NSURL",
|
||||
] }
|
||||
objc2-app-kit = { version = "0.2", default-features = false, features = [
|
||||
"std",
|
||||
"NSApplication",
|
||||
"NSButton",
|
||||
"NSColor",
|
||||
"NSControl",
|
||||
"NSEvent",
|
||||
"NSGraphics",
|
||||
"NSImage",
|
||||
"NSOpenGLView",
|
||||
"NSPasteboard",
|
||||
"NSResponder",
|
||||
"NSRunningApplication",
|
||||
"NSScreen",
|
||||
"NSView",
|
||||
"NSWindow",
|
||||
] }
|
||||
core-foundation = "0.10"
|
||||
core-graphics = "0.24"
|
||||
dispatch = "0.2"
|
||||
|
||||
@@ -37,8 +37,7 @@
|
||||
//! describes what happens in what order.
|
||||
//!
|
||||
//! [event_loop_run]: crate::event_loop::EventLoop::run
|
||||
use std::path::PathBuf;
|
||||
use std::time::Instant;
|
||||
use std::{path::PathBuf, time::Instant};
|
||||
|
||||
use crate::{
|
||||
dpi::{PhysicalPosition, PhysicalSize},
|
||||
|
||||
@@ -167,8 +167,8 @@ extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
#[macro_use]
|
||||
extern crate objc;
|
||||
#[macro_use(class, msg_send, sel)]
|
||||
extern crate objc2;
|
||||
|
||||
pub use dpi;
|
||||
|
||||
|
||||
@@ -363,7 +363,10 @@ impl MonitorHandleExtMacOS for MonitorHandle {
|
||||
}
|
||||
|
||||
fn ns_screen(&self) -> Option<*mut c_void> {
|
||||
self.inner.ns_screen().map(|s| s as *mut c_void)
|
||||
self
|
||||
.inner
|
||||
.ns_screen()
|
||||
.map(|s| objc2::rc::Retained::into_raw(s) as *mut c_void)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,36 +391,35 @@ pub trait EventLoopWindowTargetExtMacOS {
|
||||
|
||||
impl<T> EventLoopWindowTargetExtMacOS for EventLoopWindowTarget<T> {
|
||||
fn hide_application(&self) {
|
||||
let cls = objc::runtime::Class::get("NSApplication").unwrap();
|
||||
let app: cocoa::base::id = unsafe { msg_send![cls, sharedApplication] };
|
||||
unsafe { msg_send![app, hide: 0] }
|
||||
// TODO: Safety.
|
||||
let mtm = unsafe { objc2_foundation::MainThreadMarker::new_unchecked() };
|
||||
objc2_app_kit::NSApplication::sharedApplication(mtm).hide(None)
|
||||
}
|
||||
|
||||
fn show_application(&self) {
|
||||
let cls = objc::runtime::Class::get("NSApplication").unwrap();
|
||||
let app: cocoa::base::id = unsafe { msg_send![cls, sharedApplication] };
|
||||
unsafe { msg_send![app, unhide: 0] }
|
||||
// TODO: Safety.
|
||||
let mtm = unsafe { objc2_foundation::MainThreadMarker::new_unchecked() };
|
||||
unsafe { objc2_app_kit::NSApplication::sharedApplication(mtm).unhide(None) }
|
||||
}
|
||||
|
||||
fn hide_other_applications(&self) {
|
||||
let cls = objc::runtime::Class::get("NSApplication").unwrap();
|
||||
let app: cocoa::base::id = unsafe { msg_send![cls, sharedApplication] };
|
||||
unsafe { msg_send![app, hideOtherApplications: 0] }
|
||||
// TODO: Safety.
|
||||
let mtm = unsafe { objc2_foundation::MainThreadMarker::new_unchecked() };
|
||||
objc2_app_kit::NSApplication::sharedApplication(mtm).hideOtherApplications(None)
|
||||
}
|
||||
|
||||
fn set_activation_policy_at_runtime(&self, activation_policy: ActivationPolicy) {
|
||||
use cocoa::appkit;
|
||||
|
||||
let cls = objc::runtime::Class::get("NSApplication").unwrap();
|
||||
let app: cocoa::base::id = unsafe { msg_send![cls, sharedApplication] };
|
||||
use objc2_app_kit::NSApplicationActivationPolicy;
|
||||
|
||||
let ns_activation_policy = match activation_policy {
|
||||
ActivationPolicy::Regular => appkit::NSApplicationActivationPolicyRegular,
|
||||
ActivationPolicy::Accessory => appkit::NSApplicationActivationPolicyAccessory,
|
||||
ActivationPolicy::Prohibited => appkit::NSApplicationActivationPolicyProhibited,
|
||||
ActivationPolicy::Regular => NSApplicationActivationPolicy::Regular,
|
||||
ActivationPolicy::Accessory => NSApplicationActivationPolicy::Accessory,
|
||||
ActivationPolicy::Prohibited => NSApplicationActivationPolicy::Prohibited,
|
||||
};
|
||||
|
||||
unsafe { msg_send![app, setActivationPolicy: ns_activation_policy] }
|
||||
// TODO: Safety.
|
||||
let mtm = unsafe { objc2_foundation::MainThreadMarker::new_unchecked() };
|
||||
objc2_app_kit::NSApplication::sharedApplication(mtm).setActivationPolicy(ns_activation_policy);
|
||||
}
|
||||
|
||||
fn set_badge_label(&self, label: Option<String>) {
|
||||
|
||||
@@ -13,7 +13,7 @@ use std::{
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use objc::runtime::{BOOL, YES};
|
||||
use objc2::runtime::AnyObject;
|
||||
|
||||
use crate::{
|
||||
dpi::LogicalSize,
|
||||
@@ -475,10 +475,7 @@ impl AppState {
|
||||
// retains window
|
||||
pub unsafe fn set_key_window(window: id) {
|
||||
bug_assert!(
|
||||
{
|
||||
let is_window: BOOL = msg_send![window, isKindOfClass: class!(UIWindow)];
|
||||
is_window == YES
|
||||
},
|
||||
msg_send![window, isKindOfClass: class!(UIWindow)],
|
||||
"set_key_window called with an incorrect type"
|
||||
);
|
||||
let mut this = AppState::get_mut();
|
||||
@@ -505,10 +502,7 @@ pub unsafe fn set_key_window(window: id) {
|
||||
// retains window
|
||||
pub unsafe fn queue_gl_or_metal_redraw(window: id) {
|
||||
bug_assert!(
|
||||
{
|
||||
let is_window: BOOL = msg_send![window, isKindOfClass: class!(UIWindow)];
|
||||
is_window == YES
|
||||
},
|
||||
msg_send![window, isKindOfClass: class!(UIWindow)],
|
||||
"set_key_window called with an incorrect type"
|
||||
);
|
||||
let mut this = AppState::get_mut();
|
||||
@@ -582,7 +576,7 @@ pub unsafe fn did_finish_launching() {
|
||||
let () = msg_send![window, setScreen: screen];
|
||||
let () = msg_send![screen, release];
|
||||
let controller: id = msg_send![window, rootViewController];
|
||||
let () = msg_send![window, setRootViewController:ptr::null::<()>()];
|
||||
let () = msg_send![window, setRootViewController: ptr::null::<AnyObject>()];
|
||||
let () = msg_send![window, setRootViewController: controller];
|
||||
let () = msg_send![window, makeKeyAndVisible];
|
||||
}
|
||||
@@ -1019,7 +1013,7 @@ pub fn os_capabilities() -> OSCapabilities {
|
||||
static ref OS_CAPABILITIES: OSCapabilities = {
|
||||
let version: NSOperatingSystemVersion = unsafe {
|
||||
let process_info: id = msg_send![class!(NSProcessInfo), processInfo];
|
||||
let atleast_ios_8: BOOL = msg_send![
|
||||
let atleast_ios_8: bool = msg_send![
|
||||
process_info,
|
||||
respondsToSelector: sel!(operatingSystemVersion)
|
||||
];
|
||||
@@ -1031,7 +1025,7 @@ pub fn os_capabilities() -> OSCapabilities {
|
||||
//
|
||||
// The minimum required iOS version is likely to grow in the future.
|
||||
assert!(
|
||||
atleast_ios_8 == YES,
|
||||
atleast_ios_8,
|
||||
"`tao` requires iOS version 8 or greater"
|
||||
);
|
||||
msg_send![process_info, operatingSystemVersion]
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
use objc::runtime::{Class, Object};
|
||||
use objc::{msg_send, sel, sel_impl};
|
||||
use objc2::{
|
||||
msg_send,
|
||||
runtime::{AnyClass, AnyObject},
|
||||
};
|
||||
|
||||
pub fn set_badge_count(count: i32) {
|
||||
unsafe {
|
||||
let ui_application = Class::get("UIApplication").expect("Failed to get UIApplication class");
|
||||
let app: *mut Object = msg_send![ui_application, sharedApplication];
|
||||
let ui_application = AnyClass::get("UIApplication").expect("Failed to get UIApplication class");
|
||||
let app: *mut AnyObject = msg_send![ui_application, sharedApplication];
|
||||
let _: () = msg_send![app, setApplicationIconBadgeNumber:count];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
F: 'static + FnMut(Event<'_, T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
|
||||
{
|
||||
unsafe {
|
||||
let application: *mut c_void = msg_send![class!(UIApplication), sharedApplication];
|
||||
let application: id = msg_send![class!(UIApplication), sharedApplication];
|
||||
assert_eq!(
|
||||
application,
|
||||
ptr::null_mut(),
|
||||
|
||||
@@ -6,16 +6,26 @@
|
||||
|
||||
use std::{convert::TryInto, ffi::CString, ops::BitOr, os::raw::*};
|
||||
|
||||
use objc::{runtime::Object, Encode, Encoding};
|
||||
use objc2::{
|
||||
encode::{Encode, Encoding},
|
||||
runtime::{AnyObject, Bool},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
dpi::LogicalSize,
|
||||
platform::ios::{Idiom, ScreenEdge, ValidOrientations},
|
||||
};
|
||||
|
||||
pub type id = *mut Object;
|
||||
pub type id = *mut AnyObject;
|
||||
pub const nil: id = 0 as id;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type BOOL = Bool;
|
||||
#[allow(deprecated)]
|
||||
pub const YES: Bool = Bool::YES;
|
||||
#[allow(deprecated)]
|
||||
pub const NO: Bool = Bool::NO;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
pub type CGFloat = f32;
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
@@ -32,6 +42,17 @@ pub struct NSOperatingSystemVersion {
|
||||
pub patch: NSInteger,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSOperatingSystemVersion {
|
||||
const ENCODING: Encoding = Encoding::Struct(
|
||||
"?",
|
||||
&[
|
||||
NSInteger::ENCODING,
|
||||
NSInteger::ENCODING,
|
||||
NSInteger::ENCODING,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct CGPoint {
|
||||
@@ -39,6 +60,10 @@ pub struct CGPoint {
|
||||
pub y: CGFloat,
|
||||
}
|
||||
|
||||
unsafe impl Encode for CGPoint {
|
||||
const ENCODING: Encoding = Encoding::Struct("CGPoint", &[CGFloat::ENCODING, CGFloat::ENCODING]);
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct CGSize {
|
||||
@@ -55,6 +80,10 @@ impl CGSize {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Encode for CGSize {
|
||||
const ENCODING: Encoding = Encoding::Struct("CGSize", &[CGFloat::ENCODING, CGFloat::ENCODING]);
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct CGRect {
|
||||
@@ -69,18 +98,9 @@ impl CGRect {
|
||||
}
|
||||
|
||||
unsafe impl Encode for CGRect {
|
||||
fn encode() -> Encoding {
|
||||
unsafe {
|
||||
if cfg!(target_pointer_width = "32") {
|
||||
Encoding::from_str("{CGRect={CGPoint=ff}{CGSize=ff}}")
|
||||
} else if cfg!(target_pointer_width = "64") {
|
||||
Encoding::from_str("{CGRect={CGPoint=dd}{CGSize=dd}}")
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
const ENCODING: Encoding = Encoding::Struct("CGRect", &[CGPoint::ENCODING, CGSize::ENCODING]);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
#[repr(isize)]
|
||||
@@ -92,6 +112,10 @@ pub enum UITouchPhase {
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
unsafe impl Encode for UITouchPhase {
|
||||
const ENCODING: Encoding = isize::ENCODING;
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[allow(dead_code)]
|
||||
#[repr(isize)]
|
||||
@@ -101,6 +125,10 @@ pub enum UIForceTouchCapability {
|
||||
Available,
|
||||
}
|
||||
|
||||
unsafe impl Encode for UIForceTouchCapability {
|
||||
const ENCODING: Encoding = isize::ENCODING;
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[allow(dead_code)]
|
||||
#[repr(isize)]
|
||||
@@ -110,6 +138,10 @@ pub enum UITouchType {
|
||||
Pencil,
|
||||
}
|
||||
|
||||
unsafe impl Encode for UITouchType {
|
||||
const ENCODING: Encoding = isize::ENCODING;
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UIEdgeInsets {
|
||||
@@ -119,14 +151,24 @@ pub struct UIEdgeInsets {
|
||||
pub right: CGFloat,
|
||||
}
|
||||
|
||||
unsafe impl Encode for UIEdgeInsets {
|
||||
const ENCODING: Encoding = Encoding::Struct(
|
||||
"UIEdgeInsets",
|
||||
&[
|
||||
CGFloat::ENCODING,
|
||||
CGFloat::ENCODING,
|
||||
CGFloat::ENCODING,
|
||||
CGFloat::ENCODING,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct UIUserInterfaceIdiom(NSInteger);
|
||||
|
||||
unsafe impl Encode for UIUserInterfaceIdiom {
|
||||
fn encode() -> Encoding {
|
||||
NSInteger::encode()
|
||||
}
|
||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
||||
}
|
||||
|
||||
impl UIUserInterfaceIdiom {
|
||||
@@ -167,9 +209,7 @@ impl Into<Idiom> for UIUserInterfaceIdiom {
|
||||
pub struct UIInterfaceOrientationMask(NSUInteger);
|
||||
|
||||
unsafe impl Encode for UIInterfaceOrientationMask {
|
||||
fn encode() -> Encoding {
|
||||
NSUInteger::encode()
|
||||
}
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
|
||||
impl UIInterfaceOrientationMask {
|
||||
@@ -217,9 +257,7 @@ impl UIInterfaceOrientationMask {
|
||||
pub struct UIRectEdge(NSUInteger);
|
||||
|
||||
unsafe impl Encode for UIRectEdge {
|
||||
fn encode() -> Encoding {
|
||||
NSUInteger::encode()
|
||||
}
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
|
||||
impl From<ScreenEdge> for UIRectEdge {
|
||||
@@ -245,9 +283,7 @@ impl Into<ScreenEdge> for UIRectEdge {
|
||||
pub struct UIScreenOverscanCompensation(NSInteger);
|
||||
|
||||
unsafe impl Encode for UIScreenOverscanCompensation {
|
||||
fn encode() -> Encoding {
|
||||
NSInteger::encode()
|
||||
}
|
||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
@@ -378,7 +414,7 @@ pub trait NSStringRust: Sized {
|
||||
|
||||
impl NSStringRust for id {
|
||||
unsafe fn initWithUTF8String_(self, c_string: *const c_char) -> id {
|
||||
msg_send![self, initWithUTF8String: c_string as id]
|
||||
msg_send![self, initWithUTF8String: c_string]
|
||||
}
|
||||
|
||||
unsafe fn stringByAppendingString_(self, other: id) -> id {
|
||||
|
||||
@@ -66,8 +66,8 @@
|
||||
// window size/position.
|
||||
macro_rules! assert_main_thread {
|
||||
($($t:tt)*) => {
|
||||
let is_main_thread: ::objc::runtime::BOOL = msg_send!(class!(NSThread), isMainThread);
|
||||
if is_main_thread == ::objc::runtime::NO {
|
||||
let is_main_thread: bool = msg_send![class!(NSThread), isMainThread];
|
||||
if !is_main_thread {
|
||||
panic!($($t)*);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -192,7 +192,7 @@ impl MonitorHandle {
|
||||
pub fn retained_new(uiscreen: id) -> MonitorHandle {
|
||||
unsafe {
|
||||
assert_main_thread!("`MonitorHandle` can only be cloned on the main thread on iOS");
|
||||
let () = msg_send![uiscreen, retain];
|
||||
let _: id = msg_send![uiscreen, retain];
|
||||
}
|
||||
MonitorHandle {
|
||||
inner: Inner { uiscreen },
|
||||
|
||||
@@ -4,10 +4,7 @@
|
||||
|
||||
use std::{collections::HashMap, ffi::c_char};
|
||||
|
||||
use objc::{
|
||||
declare::ClassDecl,
|
||||
runtime::{Class, Object, Sel, BOOL, NO, YES},
|
||||
};
|
||||
use objc2::runtime::{AnyClass as Class, AnyObject as Object, ClassBuilder as ClassDecl, Sel};
|
||||
|
||||
use crate::{
|
||||
dpi::PhysicalPosition,
|
||||
@@ -18,7 +15,7 @@ use crate::{
|
||||
event_loop::{self, EventProxy, EventWrapper},
|
||||
ffi::{
|
||||
id, nil, CGFloat, CGPoint, CGRect, UIForceTouchCapability, UIInterfaceOrientationMask,
|
||||
UIRectEdge, UITouchPhase, UITouchType,
|
||||
UIRectEdge, UITouchPhase, UITouchType, BOOL, NO, YES,
|
||||
},
|
||||
window::PlatformSpecificWindowBuilderAttributes,
|
||||
DeviceId,
|
||||
@@ -52,8 +49,9 @@ macro_rules! add_property {
|
||||
let setter = if $capability {
|
||||
#[allow(non_snake_case)]
|
||||
extern "C" fn $setter_name($object: &mut Object, _: Sel, value: $t) {
|
||||
#[allow(deprecated)] // TODO: define_class!
|
||||
unsafe {
|
||||
$object.set_ivar::<$t>(VAR_NAME, value);
|
||||
*$object.get_mut_ivar::<$t>(VAR_NAME) = value;
|
||||
}
|
||||
$after_set
|
||||
}
|
||||
@@ -61,8 +59,9 @@ macro_rules! add_property {
|
||||
} else {
|
||||
#[allow(non_snake_case)]
|
||||
extern "C" fn $setter_name($object: &mut Object, _: Sel, value: $t) {
|
||||
#[allow(deprecated)] // TODO: define_class!
|
||||
unsafe {
|
||||
$object.set_ivar::<$t>(VAR_NAME, value);
|
||||
*$object.get_mut_ivar::<$t>(VAR_NAME) = value;
|
||||
}
|
||||
$err(&app_state::os_capabilities(), "ignoring")
|
||||
}
|
||||
@@ -70,15 +69,16 @@ macro_rules! add_property {
|
||||
};
|
||||
#[allow(non_snake_case)]
|
||||
extern "C" fn $getter_name($object: &Object, _: Sel) -> $t {
|
||||
#[allow(deprecated)] // TODO: define_class!
|
||||
unsafe { *$object.get_ivar::<$t>(VAR_NAME) }
|
||||
}
|
||||
$decl.add_method(
|
||||
sel!($setter_name:),
|
||||
setter as extern "C" fn(&mut Object, Sel, $t),
|
||||
setter as extern "C" fn(_, _, _),
|
||||
);
|
||||
$decl.add_method(
|
||||
sel!($getter_name),
|
||||
$getter_name as extern "C" fn(&Object, Sel) -> $t,
|
||||
$getter_name as extern "C" fn(_, _) -> _,
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -97,11 +97,8 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
|
||||
|
||||
classes.entry(root_view_class).or_insert_with(move || {
|
||||
let uiview_class = class!(UIView);
|
||||
let is_uiview: BOOL = msg_send![root_view_class, isSubclassOfClass: uiview_class];
|
||||
assert_eq!(
|
||||
is_uiview, YES,
|
||||
"`root_view_class` must inherit from `UIView`"
|
||||
);
|
||||
let is_uiview: bool = msg_send![root_view_class, isSubclassOfClass: uiview_class];
|
||||
assert!(is_uiview, "`root_view_class` must inherit from `UIView`");
|
||||
|
||||
extern "C" fn draw_rect(object: &Object, _: Sel, rect: CGRect) {
|
||||
unsafe {
|
||||
@@ -155,7 +152,7 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
|
||||
}
|
||||
|
||||
extern "C" fn set_content_scale_factor(
|
||||
object: &mut Object,
|
||||
object: &Object,
|
||||
_: Sel,
|
||||
untrusted_scale_factor: CGFloat,
|
||||
) {
|
||||
@@ -286,34 +283,28 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
|
||||
let mut decl = ClassDecl::new(&format!("TaoUIView{}", ID), root_view_class)
|
||||
.expect("Failed to declare class `TaoUIView`");
|
||||
ID += 1;
|
||||
decl.add_method(
|
||||
sel!(drawRect:),
|
||||
draw_rect as extern "C" fn(&Object, Sel, CGRect),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(layoutSubviews),
|
||||
layout_subviews as extern "C" fn(&Object, Sel),
|
||||
);
|
||||
decl.add_method(sel!(drawRect:), draw_rect as extern "C" fn(_, _, _));
|
||||
decl.add_method(sel!(layoutSubviews), layout_subviews as extern "C" fn(_, _));
|
||||
decl.add_method(
|
||||
sel!(setContentScaleFactor:),
|
||||
set_content_scale_factor as extern "C" fn(&mut Object, Sel, CGFloat),
|
||||
set_content_scale_factor as extern "C" fn(_, _, _),
|
||||
);
|
||||
|
||||
decl.add_method(
|
||||
sel!(touchesBegan:withEvent:),
|
||||
handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
|
||||
handle_touches as extern "C" fn(_, _, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(touchesMoved:withEvent:),
|
||||
handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
|
||||
handle_touches as extern "C" fn(_, _, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(touchesEnded:withEvent:),
|
||||
handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
|
||||
handle_touches as extern "C" fn(_, _, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(touchesCancelled:withEvent:),
|
||||
handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
|
||||
handle_touches as extern "C" fn(_, _, _, _),
|
||||
);
|
||||
|
||||
decl.register()
|
||||
@@ -336,7 +327,7 @@ unsafe fn get_view_controller_class() -> &'static Class {
|
||||
.expect("Failed to declare class `TaoUIViewController`");
|
||||
decl.add_method(
|
||||
sel!(shouldAutorotate),
|
||||
should_autorotate as extern "C" fn(&Object, Sel) -> BOOL,
|
||||
should_autorotate as extern "C" fn(_, _) -> _,
|
||||
);
|
||||
add_property! {
|
||||
decl,
|
||||
@@ -419,11 +410,11 @@ unsafe fn get_window_class() -> &'static Class {
|
||||
ClassDecl::new("TaoUIWindow", uiwindow_class).expect("Failed to declare class `TaoUIWindow`");
|
||||
decl.add_method(
|
||||
sel!(becomeKeyWindow),
|
||||
become_key_window as extern "C" fn(&Object, Sel),
|
||||
become_key_window as extern "C" fn(_, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(resignKeyWindow),
|
||||
resign_key_window as extern "C" fn(&Object, Sel),
|
||||
resign_key_window as extern "C" fn(_, _),
|
||||
);
|
||||
|
||||
CLASS = Some(decl.register());
|
||||
@@ -548,7 +539,7 @@ pub unsafe fn create_window(
|
||||
}
|
||||
|
||||
pub fn create_delegate_class() {
|
||||
extern "C" fn did_finish_launching(_: &mut Object, _: Sel, _: id, _: id) -> BOOL {
|
||||
extern "C" fn did_finish_launching(_: &Object, _: Sel, _: id, _: id) -> BOOL {
|
||||
unsafe {
|
||||
app_state::did_finish_launching();
|
||||
}
|
||||
@@ -576,7 +567,7 @@ pub fn create_delegate_class() {
|
||||
// custom URL schemes
|
||||
// https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app
|
||||
extern "C" fn application_open_url(
|
||||
_self: &mut Object,
|
||||
_self: &Object,
|
||||
_cmd: Sel,
|
||||
_app: id,
|
||||
url: id,
|
||||
@@ -590,7 +581,7 @@ pub fn create_delegate_class() {
|
||||
// universal links
|
||||
// https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app
|
||||
extern "C" fn application_continue(
|
||||
_: &mut Object,
|
||||
_: &Object,
|
||||
_: Sel,
|
||||
_application: id,
|
||||
user_activity: id,
|
||||
@@ -630,8 +621,8 @@ pub fn create_delegate_class() {
|
||||
if window == nil {
|
||||
break;
|
||||
}
|
||||
let is_tao_window: BOOL = msg_send![window, isKindOfClass: class!(TaoUIWindow)];
|
||||
if is_tao_window == YES {
|
||||
let is_tao_window: bool = msg_send![window, isKindOfClass: class!(TaoUIWindow)];
|
||||
if is_tao_window {
|
||||
events.push(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id: RootWindowId(window.into()),
|
||||
event: WindowEvent::Destroyed,
|
||||
@@ -650,39 +641,39 @@ pub fn create_delegate_class() {
|
||||
unsafe {
|
||||
decl.add_method(
|
||||
sel!(application:didFinishLaunchingWithOptions:),
|
||||
did_finish_launching as extern "C" fn(&mut Object, Sel, id, id) -> BOOL,
|
||||
did_finish_launching as extern "C" fn(_, _, _, _) -> _,
|
||||
);
|
||||
|
||||
decl.add_method(
|
||||
sel!(application:openURL:options:),
|
||||
application_open_url as extern "C" fn(&mut Object, Sel, id, id, id) -> BOOL,
|
||||
application_open_url as extern "C" fn(_, _, _, _, _) -> _,
|
||||
);
|
||||
|
||||
decl.add_method(
|
||||
sel!(application:continueUserActivity:restorationHandler:),
|
||||
application_continue as extern "C" fn(&mut Object, Sel, id, id, id) -> BOOL,
|
||||
application_continue as extern "C" fn(_, _, _, _, _) -> _,
|
||||
);
|
||||
|
||||
decl.add_method(
|
||||
sel!(applicationDidBecomeActive:),
|
||||
did_become_active as extern "C" fn(&Object, Sel, id),
|
||||
did_become_active as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(applicationWillResignActive:),
|
||||
will_resign_active as extern "C" fn(&Object, Sel, id),
|
||||
will_resign_active as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(applicationWillEnterForeground:),
|
||||
will_enter_foreground as extern "C" fn(&Object, Sel, id),
|
||||
will_enter_foreground as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(applicationDidEnterBackground:),
|
||||
did_enter_background as extern "C" fn(&Object, Sel, id),
|
||||
did_enter_background as extern "C" fn(_, _, _),
|
||||
);
|
||||
|
||||
decl.add_method(
|
||||
sel!(applicationWillTerminate:),
|
||||
will_terminate as extern "C" fn(&Object, Sel, id),
|
||||
will_terminate as extern "C" fn(_, _, _),
|
||||
);
|
||||
|
||||
decl.register();
|
||||
|
||||
@@ -7,7 +7,7 @@ use std::{
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
use objc::runtime::{Class, Object, BOOL, NO, YES};
|
||||
use objc2::runtime::{AnyClass, AnyObject};
|
||||
|
||||
use crate::{
|
||||
dpi::{self, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
|
||||
@@ -21,7 +21,7 @@ use crate::{
|
||||
event_loop::{self, EventProxy, EventWrapper},
|
||||
ffi::{
|
||||
id, CGFloat, CGPoint, CGRect, CGSize, UIEdgeInsets, UIInterfaceOrientationMask, UIRectEdge,
|
||||
UIScreenOverscanCompensation,
|
||||
UIScreenOverscanCompensation, NO, YES,
|
||||
},
|
||||
monitor, set_badge_count, view, EventLoopWindowTarget, MonitorHandle,
|
||||
},
|
||||
@@ -522,11 +522,11 @@ impl Window {
|
||||
let view = view::create_view(&window_attributes, &platform_attributes, frame.clone());
|
||||
|
||||
let gl_or_metal_backed = {
|
||||
let view_class: id = msg_send![view, class];
|
||||
let layer_class: id = msg_send![view_class, layerClass];
|
||||
let is_metal: BOOL = msg_send![layer_class, isSubclassOfClass: class!(CAMetalLayer)];
|
||||
let is_gl: BOOL = msg_send![layer_class, isSubclassOfClass: class!(CAEAGLLayer)];
|
||||
is_metal == YES || is_gl == YES
|
||||
let view_class: *const AnyClass = msg_send![view, class];
|
||||
let layer_class: *const AnyClass = msg_send![view_class, layerClass];
|
||||
let is_metal: bool = msg_send![layer_class, isSubclassOfClass: class!(CAMetalLayer)];
|
||||
let is_gl: bool = msg_send![layer_class, isSubclassOfClass: class!(CAEAGLLayer)];
|
||||
is_metal || is_gl
|
||||
};
|
||||
|
||||
let view_controller =
|
||||
@@ -740,16 +740,16 @@ impl WindowId {
|
||||
unsafe impl Send for WindowId {}
|
||||
unsafe impl Sync for WindowId {}
|
||||
|
||||
impl From<&Object> for WindowId {
|
||||
fn from(window: &Object) -> WindowId {
|
||||
impl From<&AnyObject> for WindowId {
|
||||
fn from(window: &AnyObject) -> WindowId {
|
||||
WindowId {
|
||||
window: window as *const _ as _,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&mut Object> for WindowId {
|
||||
fn from(window: &mut Object) -> WindowId {
|
||||
impl From<&mut AnyObject> for WindowId {
|
||||
fn from(window: &mut AnyObject) -> WindowId {
|
||||
WindowId {
|
||||
window: window as _,
|
||||
}
|
||||
@@ -764,7 +764,7 @@ impl From<id> for WindowId {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PlatformSpecificWindowBuilderAttributes {
|
||||
pub root_view_class: &'static Class,
|
||||
pub root_view_class: &'static AnyClass,
|
||||
pub scale_factor: Option<f64>,
|
||||
pub valid_orientations: ValidOrientations,
|
||||
pub prefers_home_indicator_hidden: bool,
|
||||
|
||||
@@ -4,16 +4,10 @@
|
||||
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use cocoa::{
|
||||
appkit::{self, NSEvent},
|
||||
base::id,
|
||||
};
|
||||
use objc::{
|
||||
declare::ClassDecl,
|
||||
runtime::{Class, Object, Sel},
|
||||
};
|
||||
use objc2::runtime::{AnyClass as Class, AnyObject as Object, ClassBuilder as ClassDecl, Sel};
|
||||
use objc2_app_kit::{self as appkit, NSEvent, NSEventType};
|
||||
|
||||
use super::{app_state::AppState, event::EventWrapper, util, DEVICE_ID};
|
||||
use super::{app_state::AppState, event::EventWrapper, ffi::id, util, DEVICE_ID};
|
||||
use crate::event::{DeviceEvent, ElementState, Event};
|
||||
|
||||
pub struct AppClass(pub *const Class);
|
||||
@@ -25,10 +19,7 @@ lazy_static! {
|
||||
let superclass = class!(NSApplication);
|
||||
let mut decl = ClassDecl::new("TaoApp", superclass).unwrap();
|
||||
|
||||
decl.add_method(
|
||||
sel!(sendEvent:),
|
||||
send_event as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
decl.add_method(sel!(sendEvent:), send_event as extern "C" fn(_, _, _));
|
||||
|
||||
AppClass(decl.register())
|
||||
};
|
||||
@@ -37,17 +28,17 @@ lazy_static! {
|
||||
// Normally, holding Cmd + any key never sends us a `keyUp` event for that key.
|
||||
// Overriding `sendEvent:` like this fixes that. (https://stackoverflow.com/a/15294196)
|
||||
// Fun fact: Firefox still has this bug! (https://bugzilla.mozilla.org/show_bug.cgi?id=1299553)
|
||||
extern "C" fn send_event(this: &Object, _sel: Sel, event: id) {
|
||||
extern "C" fn send_event(this: &Object, _sel: Sel, event: &NSEvent) {
|
||||
unsafe {
|
||||
// For posterity, there are some undocumented event types
|
||||
// (https://github.com/servo/cocoa-rs/issues/155)
|
||||
// but that doesn't really matter here.
|
||||
let event_type = event.eventType();
|
||||
let event_type = event.r#type();
|
||||
let modifier_flags = event.modifierFlags();
|
||||
if event_type == appkit::NSKeyUp
|
||||
&& util::has_flag(
|
||||
modifier_flags,
|
||||
appkit::NSEventModifierFlags::NSCommandKeyMask,
|
||||
appkit::NSEventModifierFlags::NSEventModifierFlagCommand,
|
||||
)
|
||||
{
|
||||
let key_window: id = msg_send![this, keyWindow];
|
||||
@@ -60,13 +51,13 @@ extern "C" fn send_event(this: &Object, _sel: Sel, event: id) {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn maybe_dispatch_device_event(event: id) {
|
||||
let event_type = event.eventType();
|
||||
unsafe fn maybe_dispatch_device_event(event: &NSEvent) {
|
||||
let event_type = event.r#type();
|
||||
match event_type {
|
||||
appkit::NSMouseMoved
|
||||
| appkit::NSLeftMouseDragged
|
||||
| appkit::NSOtherMouseDragged
|
||||
| appkit::NSRightMouseDragged => {
|
||||
NSEventType::MouseMoved
|
||||
| NSEventType::LeftMouseDragged
|
||||
| NSEventType::OtherMouseDragged
|
||||
| NSEventType::RightMouseDragged => {
|
||||
let mut events = VecDeque::with_capacity(3);
|
||||
|
||||
let delta_x = event.deltaX() as f64;
|
||||
@@ -103,7 +94,7 @@ unsafe fn maybe_dispatch_device_event(event: id) {
|
||||
|
||||
AppState::queue_events(events);
|
||||
}
|
||||
appkit::NSLeftMouseDown | appkit::NSRightMouseDown | appkit::NSOtherMouseDown => {
|
||||
NSEventType::LeftMouseDown | NSEventType::RightMouseDown | NSEventType::OtherMouseDown => {
|
||||
let mut events = VecDeque::with_capacity(1);
|
||||
|
||||
events.push_back(EventWrapper::StaticEvent(Event::DeviceEvent {
|
||||
@@ -116,7 +107,7 @@ unsafe fn maybe_dispatch_device_event(event: id) {
|
||||
|
||||
AppState::queue_events(events);
|
||||
}
|
||||
appkit::NSLeftMouseUp | appkit::NSRightMouseUp | appkit::NSOtherMouseUp => {
|
||||
NSEventType::LeftMouseUp | NSEventType::RightMouseUp | NSEventType::OtherMouseUp => {
|
||||
let mut events = VecDeque::with_capacity(1);
|
||||
|
||||
events.push_back(EventWrapper::StaticEvent(Event::DeviceEvent {
|
||||
|
||||
@@ -2,24 +2,21 @@
|
||||
// Copyright 2021-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{platform::macos::ActivationPolicy, platform_impl::platform::app_state::AppState};
|
||||
use crate::{
|
||||
platform::macos::ActivationPolicy,
|
||||
platform_impl::platform::{
|
||||
app_state::AppState,
|
||||
ffi::{id, BOOL, YES},
|
||||
},
|
||||
};
|
||||
|
||||
use cocoa::{
|
||||
base::{id, NO},
|
||||
foundation::NSString,
|
||||
};
|
||||
use objc::{
|
||||
declare::ClassDecl,
|
||||
runtime::{Class, Object, Sel, BOOL},
|
||||
};
|
||||
use objc2::runtime::{AnyClass as Class, AnyObject as Object, ClassBuilder as ClassDecl, Sel};
|
||||
use objc2_foundation::{NSArray, NSURL};
|
||||
use std::{
|
||||
cell::{RefCell, RefMut},
|
||||
os::raw::c_void,
|
||||
};
|
||||
|
||||
use cocoa::foundation::{NSArray, NSURL};
|
||||
use std::ffi::CStr;
|
||||
|
||||
static AUX_DELEGATE_STATE_NAME: &str = "auxState";
|
||||
|
||||
pub struct AuxDelegateState {
|
||||
@@ -40,28 +37,28 @@ lazy_static! {
|
||||
let superclass = class!(NSResponder);
|
||||
let mut decl = ClassDecl::new("TaoAppDelegateParent", superclass).unwrap();
|
||||
|
||||
decl.add_class_method(sel!(new), new as extern "C" fn(&Class, Sel) -> id);
|
||||
decl.add_method(sel!(dealloc), dealloc as extern "C" fn(&Object, Sel));
|
||||
decl.add_class_method(sel!(new), new as extern "C" fn(_, _) -> _);
|
||||
decl.add_method(sel!(dealloc), dealloc as extern "C" fn(_, _));
|
||||
|
||||
decl.add_method(
|
||||
sel!(applicationDidFinishLaunching:),
|
||||
did_finish_launching as extern "C" fn(&Object, Sel, id),
|
||||
did_finish_launching as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(applicationWillTerminate:),
|
||||
application_will_terminate as extern "C" fn(&Object, Sel, id),
|
||||
application_will_terminate as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(application:openURLs:),
|
||||
application_open_urls as extern "C" fn(&Object, Sel, id, id),
|
||||
application_open_urls as extern "C" fn(_, _, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(applicationShouldHandleReopen:hasVisibleWindows:),
|
||||
application_should_handle_reopen as extern "C" fn(&Object, Sel, id, BOOL) -> BOOL,
|
||||
application_should_handle_reopen as extern "C" fn(_, _, _, _) -> _,
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(applicationSupportsSecureRestorableState:),
|
||||
application_supports_secure_restorable_state as extern "C" fn(&Object, Sel, id) -> BOOL,
|
||||
application_supports_secure_restorable_state as extern "C" fn(_, _, _) -> _,
|
||||
);
|
||||
decl.add_ivar::<*mut c_void>(AUX_DELEGATE_STATE_NAME);
|
||||
|
||||
@@ -70,6 +67,7 @@ lazy_static! {
|
||||
}
|
||||
|
||||
/// Safety: Assumes that Object is an instance of APP_DELEGATE_CLASS
|
||||
#[allow(deprecated)] // TODO: Use define_class!
|
||||
pub unsafe fn get_aux_state_mut(this: &Object) -> RefMut<'_, AuxDelegateState> {
|
||||
let ptr: *mut c_void = *this.get_ivar(AUX_DELEGATE_STATE_NAME);
|
||||
// Watch out that this needs to be the correct type
|
||||
@@ -77,21 +75,21 @@ pub unsafe fn get_aux_state_mut(this: &Object) -> RefMut<'_, AuxDelegateState> {
|
||||
}
|
||||
|
||||
extern "C" fn new(class: &Class, _: Sel) -> id {
|
||||
#[allow(deprecated)] // TODO: Use define_class!
|
||||
unsafe {
|
||||
let this: id = msg_send![class, alloc];
|
||||
let this: id = msg_send![this, init];
|
||||
(*this).set_ivar(
|
||||
AUX_DELEGATE_STATE_NAME,
|
||||
*(*this).get_mut_ivar(AUX_DELEGATE_STATE_NAME) =
|
||||
Box::into_raw(Box::new(RefCell::new(AuxDelegateState {
|
||||
activation_policy: ActivationPolicy::Regular,
|
||||
activate_ignoring_other_apps: true,
|
||||
}))) as *mut c_void,
|
||||
);
|
||||
}))) as *mut c_void;
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn dealloc(this: &Object, _: Sel) {
|
||||
#[allow(deprecated)] // TODO: Use define_class!
|
||||
unsafe {
|
||||
let state_ptr: *mut c_void = *(this.get_ivar(AUX_DELEGATE_STATE_NAME));
|
||||
// As soon as the box is constructed it is immediately dropped, releasing the underlying
|
||||
@@ -112,16 +110,12 @@ extern "C" fn application_will_terminate(_: &Object, _: Sel, _: id) {
|
||||
trace!("Completed `applicationWillTerminate`");
|
||||
}
|
||||
|
||||
extern "C" fn application_open_urls(_: &Object, _: Sel, _: id, urls: id) -> () {
|
||||
extern "C" fn application_open_urls(_: &Object, _: Sel, _: id, urls: &NSArray<NSURL>) -> () {
|
||||
trace!("Trigger `application:openURLs:`");
|
||||
|
||||
let urls = unsafe {
|
||||
(0..urls.count())
|
||||
.map(|i| {
|
||||
url::Url::parse(
|
||||
&CStr::from_ptr(urls.objectAtIndex(i).absoluteString().UTF8String()).to_string_lossy(),
|
||||
)
|
||||
})
|
||||
.map(|i| url::Url::parse(&urls.objectAtIndex(i).absoluteString().unwrap().to_string()))
|
||||
.flatten()
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
@@ -137,7 +131,7 @@ extern "C" fn application_should_handle_reopen(
|
||||
has_visible_windows: BOOL,
|
||||
) -> BOOL {
|
||||
trace!("Triggered `applicationShouldHandleReopen`");
|
||||
AppState::reopen(has_visible_windows != NO);
|
||||
AppState::reopen(has_visible_windows.as_bool());
|
||||
trace!("Completed `applicationShouldHandleReopen`");
|
||||
has_visible_windows
|
||||
}
|
||||
@@ -145,5 +139,5 @@ extern "C" fn application_should_handle_reopen(
|
||||
extern "C" fn application_supports_secure_restorable_state(_: &Object, _: Sel, _: id) -> BOOL {
|
||||
trace!("Triggered `applicationSupportsSecureRestorableState`");
|
||||
trace!("Completed `applicationSupportsSecureRestorableState`");
|
||||
objc::runtime::YES
|
||||
YES
|
||||
}
|
||||
|
||||
@@ -16,12 +16,9 @@ use std::{
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use cocoa::{
|
||||
appkit::{NSApp, NSApplication, NSWindow},
|
||||
base::{id, nil},
|
||||
foundation::{NSAutoreleasePool, NSSize},
|
||||
};
|
||||
use objc::runtime::{Object, NO, YES};
|
||||
use objc2::{msg_send_id, rc::Retained, runtime::AnyObject as Object};
|
||||
use objc2_app_kit::{NSApp, NSApplication, NSApplicationActivationPolicy, NSWindow};
|
||||
use objc2_foundation::{MainThreadMarker, NSAutoreleasePool, NSSize};
|
||||
|
||||
use crate::{
|
||||
dpi::LogicalSize,
|
||||
@@ -33,8 +30,9 @@ use crate::{
|
||||
platform::{
|
||||
event::{EventProxy, EventWrapper},
|
||||
event_loop::{post_dummy_event, PanicInfo},
|
||||
ffi::{id, nil},
|
||||
observer::{CFRunLoopGetMain, CFRunLoopWakeUp, EventLoopWaker},
|
||||
util::{self, IdRef, Never},
|
||||
util::{self, Never},
|
||||
window::get_window_id,
|
||||
},
|
||||
},
|
||||
@@ -221,14 +219,14 @@ impl Handler {
|
||||
fn handle_scale_factor_changed_event(
|
||||
&self,
|
||||
callback: &mut Box<dyn EventHandler + 'static>,
|
||||
ns_window: IdRef,
|
||||
ns_window: &NSWindow,
|
||||
suggested_size: LogicalSize<f64>,
|
||||
scale_factor: f64,
|
||||
) {
|
||||
let mut size = suggested_size.to_physical(scale_factor);
|
||||
let old_size = size.clone();
|
||||
let event = Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(*ns_window)),
|
||||
window_id: WindowId(get_window_id(ns_window)),
|
||||
event: WindowEvent::ScaleFactorChanged {
|
||||
scale_factor,
|
||||
new_inner_size: &mut size,
|
||||
@@ -240,7 +238,7 @@ impl Handler {
|
||||
if old_size != size {
|
||||
let logical_size = size.to_logical(scale_factor);
|
||||
let size = NSSize::new(logical_size.width, logical_size.height);
|
||||
unsafe { NSWindow::setContentSize_(*ns_window, size) };
|
||||
NSWindow::setContentSize(ns_window, size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,7 +249,7 @@ impl Handler {
|
||||
suggested_size,
|
||||
scale_factor,
|
||||
} => {
|
||||
self.handle_scale_factor_changed_event(callback, ns_window, suggested_size, scale_factor)
|
||||
self.handle_scale_factor_changed_event(callback, &ns_window, suggested_size, scale_factor)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -285,14 +283,12 @@ impl AppState {
|
||||
pub fn launched(app_delegate: &Object) {
|
||||
apply_activation_policy(app_delegate);
|
||||
unsafe {
|
||||
let ns_app = NSApp();
|
||||
window_activation_hack(ns_app);
|
||||
let ignore = if get_aux_state_mut(app_delegate).activate_ignoring_other_apps {
|
||||
YES
|
||||
} else {
|
||||
NO
|
||||
};
|
||||
ns_app.activateIgnoringOtherApps_(ignore);
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
let ns_app = NSApp(mtm);
|
||||
window_activation_hack(&ns_app);
|
||||
let ignore = get_aux_state_mut(app_delegate).activate_ignoring_other_apps;
|
||||
#[allow(deprecated)]
|
||||
ns_app.activateIgnoringOtherApps(ignore);
|
||||
};
|
||||
HANDLER.set_ready();
|
||||
HANDLER.waker().start();
|
||||
@@ -399,12 +395,12 @@ impl AppState {
|
||||
HANDLER.set_in_callback(false);
|
||||
if HANDLER.should_exit() {
|
||||
unsafe {
|
||||
let app: id = NSApp();
|
||||
let pool = NSAutoreleasePool::new(nil);
|
||||
let () = msg_send![app, stop: nil];
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
let app = NSApp(mtm);
|
||||
let _pool = NSAutoreleasePool::new();
|
||||
let () = msg_send![&app, stop: nil];
|
||||
// To stop event loop immediately, we need to post some event here.
|
||||
post_dummy_event(app);
|
||||
pool.drain();
|
||||
post_dummy_event(&app);
|
||||
};
|
||||
}
|
||||
HANDLER.update_start_time();
|
||||
@@ -426,23 +422,24 @@ impl AppState {
|
||||
///
|
||||
/// If this becomes too bothersome to maintain, it can probably be removed
|
||||
/// without too much damage.
|
||||
unsafe fn window_activation_hack(ns_app: id) {
|
||||
unsafe fn window_activation_hack(ns_app: &NSApplication) {
|
||||
// Get the application's windows
|
||||
// TODO: Proper ordering of the windows
|
||||
let ns_windows: id = msg_send![ns_app, windows];
|
||||
let ns_enumerator: id = msg_send![ns_windows, objectEnumerator];
|
||||
loop {
|
||||
// Enumerate over the windows
|
||||
let ns_window: id = msg_send![ns_enumerator, nextObject];
|
||||
if ns_window == nil {
|
||||
let ns_window: Option<Retained<NSWindow>> = msg_send_id![ns_enumerator, nextObject];
|
||||
if ns_window.is_none() {
|
||||
break;
|
||||
}
|
||||
let ns_window = ns_window.unwrap();
|
||||
// And call `makeKeyAndOrderFront` if it was called on the window in `UnownedWindow::new`
|
||||
// This way we preserve the user's desired initial visiblity status
|
||||
// TODO: Also filter on the type/"level" of the window, and maybe other things?
|
||||
if ns_window.isVisible() == YES {
|
||||
if ns_window.isVisible() == true {
|
||||
trace!("Activating visible window");
|
||||
ns_window.makeKeyAndOrderFront_(nil);
|
||||
ns_window.makeKeyAndOrderFront(None);
|
||||
} else {
|
||||
trace!("Skipping activating invisible window");
|
||||
}
|
||||
@@ -450,16 +447,16 @@ unsafe fn window_activation_hack(ns_app: id) {
|
||||
}
|
||||
fn apply_activation_policy(app_delegate: &Object) {
|
||||
unsafe {
|
||||
use cocoa::appkit::NSApplicationActivationPolicy::*;
|
||||
let ns_app = NSApp();
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
let ns_app = NSApp(mtm);
|
||||
// We need to delay setting the activation policy and activating the app
|
||||
// until `applicationDidFinishLaunching` has been called. Otherwise the
|
||||
// menu bar won't be interactable.
|
||||
let act_pol = get_aux_state_mut(app_delegate).activation_policy;
|
||||
ns_app.setActivationPolicy_(match act_pol {
|
||||
ActivationPolicy::Regular => NSApplicationActivationPolicyRegular,
|
||||
ActivationPolicy::Accessory => NSApplicationActivationPolicyAccessory,
|
||||
ActivationPolicy::Prohibited => NSApplicationActivationPolicyProhibited,
|
||||
ns_app.setActivationPolicy(match act_pol {
|
||||
ActivationPolicy::Regular => NSApplicationActivationPolicy::Regular,
|
||||
ActivationPolicy::Accessory => NSApplicationActivationPolicy::Accessory,
|
||||
ActivationPolicy::Prohibited => NSApplicationActivationPolicy::Prohibited,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
use cocoa::{appkit::NSApp, base::nil, foundation::NSString};
|
||||
use super::ffi::id;
|
||||
use objc2_app_kit::NSApp;
|
||||
use objc2_foundation::{MainThreadMarker, NSString};
|
||||
|
||||
pub fn set_badge_label(label: Option<String>) {
|
||||
// SAFETY: TODO
|
||||
let mtm = unsafe { MainThreadMarker::new_unchecked() };
|
||||
unsafe {
|
||||
let label = match label {
|
||||
None => nil,
|
||||
Some(label) => NSString::alloc(nil).init_str(&label),
|
||||
None => None,
|
||||
Some(label) => Some(NSString::from_str(&label)),
|
||||
};
|
||||
let dock_tile: cocoa::base::id = msg_send![NSApp(), dockTile];
|
||||
let _: cocoa::base::id = msg_send![dock_tile, setBadgeLabel: label];
|
||||
let dock_tile: id = msg_send![&NSApp(mtm), dockTile];
|
||||
let _: () = msg_send![dock_tile, setBadgeLabel: label.as_deref()];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,21 +4,17 @@
|
||||
|
||||
use std::{collections::HashSet, ffi::c_void, os::raw::c_ushort, sync::Mutex};
|
||||
|
||||
use cocoa::{
|
||||
appkit::{NSEvent, NSEventModifierFlags},
|
||||
base::id,
|
||||
};
|
||||
use objc2::{msg_send_id, rc::Retained};
|
||||
use objc2_app_kit::{NSEvent, NSEventModifierFlags, NSWindow};
|
||||
|
||||
use core_foundation::{base::CFRelease, data::CFDataGetBytePtr};
|
||||
use objc2_foundation::NSString;
|
||||
|
||||
use crate::{
|
||||
dpi::LogicalSize,
|
||||
event::{ElementState, Event, KeyEvent},
|
||||
keyboard::{Key, KeyCode, KeyLocation, ModifiersState, NativeKeyCode},
|
||||
platform_impl::platform::{
|
||||
ffi,
|
||||
util::{ns_string_to_rust, IdRef, Never},
|
||||
},
|
||||
platform_impl::platform::{ffi, util::Never},
|
||||
};
|
||||
|
||||
lazy_static! {
|
||||
@@ -47,7 +43,7 @@ pub enum EventWrapper {
|
||||
pub enum EventProxy {
|
||||
#[non_exhaustive]
|
||||
DpiChangedProxy {
|
||||
ns_window: IdRef,
|
||||
ns_window: Retained<NSWindow>,
|
||||
suggested_size: LogicalSize<f64>,
|
||||
scale_factor: f64,
|
||||
},
|
||||
@@ -115,9 +111,10 @@ pub fn get_modifierless_char(scancode: u16) -> Key<'static> {
|
||||
Key::Character(insert_or_get_key_str(chars))
|
||||
}
|
||||
|
||||
fn get_logical_key_char(ns_event: id, modifierless_chars: &str) -> Key<'static> {
|
||||
let characters: id = unsafe { msg_send![ns_event, charactersIgnoringModifiers] };
|
||||
let string = unsafe { ns_string_to_rust(characters) };
|
||||
fn get_logical_key_char(ns_event: &NSEvent, modifierless_chars: &str) -> Key<'static> {
|
||||
let characters: Retained<NSString> =
|
||||
unsafe { msg_send_id![ns_event, charactersIgnoringModifiers] };
|
||||
let string = characters.to_string();
|
||||
if string.is_empty() {
|
||||
// Probably a dead key
|
||||
let first_char = modifierless_chars.chars().next();
|
||||
@@ -128,7 +125,7 @@ fn get_logical_key_char(ns_event: id, modifierless_chars: &str) -> Key<'static>
|
||||
|
||||
#[allow(clippy::unnecessary_unwrap)]
|
||||
pub fn create_key_event(
|
||||
ns_event: id,
|
||||
ns_event: &NSEvent,
|
||||
is_press: bool,
|
||||
is_repeat: bool,
|
||||
in_ime: bool,
|
||||
@@ -144,8 +141,8 @@ pub fn create_key_event(
|
||||
if key_override.is_some() {
|
||||
None
|
||||
} else {
|
||||
let characters: id = unsafe { msg_send![ns_event, characters] };
|
||||
let characters = unsafe { ns_string_to_rust(characters) };
|
||||
let characters: Retained<NSString> = unsafe { msg_send_id![ns_event, characters] };
|
||||
let characters = characters.to_string();
|
||||
if characters.is_empty() {
|
||||
None
|
||||
} else {
|
||||
@@ -168,8 +165,8 @@ pub fn create_key_event(
|
||||
key_without_modifiers = get_modifierless_char(scancode);
|
||||
|
||||
let modifiers = unsafe { NSEvent::modifierFlags(ns_event) };
|
||||
let has_alt = modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask);
|
||||
let has_ctrl = modifiers.contains(NSEventModifierFlags::NSControlKeyMask);
|
||||
let has_alt = modifiers.contains(NSEventModifierFlags::NSEventModifierFlagOption);
|
||||
let has_ctrl = modifiers.contains(NSEventModifierFlags::NSEventModifierFlagControl);
|
||||
if has_alt || has_ctrl || text_with_all_modifiers.is_none() || !is_press {
|
||||
let modifierless_chars = match key_without_modifiers.clone() {
|
||||
Key::Character(ch) => ch,
|
||||
@@ -308,29 +305,29 @@ pub fn extra_function_key_to_code(scancode: u16, string: &str) -> KeyCode {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event_mods(event: id) -> ModifiersState {
|
||||
pub fn event_mods(event: &NSEvent) -> ModifiersState {
|
||||
let flags = unsafe { NSEvent::modifierFlags(event) };
|
||||
let mut m = ModifiersState::empty();
|
||||
m.set(
|
||||
ModifiersState::SHIFT,
|
||||
flags.contains(NSEventModifierFlags::NSShiftKeyMask),
|
||||
flags.contains(NSEventModifierFlags::NSEventModifierFlagShift),
|
||||
);
|
||||
m.set(
|
||||
ModifiersState::CONTROL,
|
||||
flags.contains(NSEventModifierFlags::NSControlKeyMask),
|
||||
flags.contains(NSEventModifierFlags::NSEventModifierFlagControl),
|
||||
);
|
||||
m.set(
|
||||
ModifiersState::ALT,
|
||||
flags.contains(NSEventModifierFlags::NSAlternateKeyMask),
|
||||
flags.contains(NSEventModifierFlags::NSEventModifierFlagOption),
|
||||
);
|
||||
m.set(
|
||||
ModifiersState::SUPER,
|
||||
flags.contains(NSEventModifierFlags::NSCommandKeyMask),
|
||||
flags.contains(NSEventModifierFlags::NSEventModifierFlagCommand),
|
||||
);
|
||||
m
|
||||
}
|
||||
|
||||
pub fn get_scancode(event: cocoa::base::id) -> c_ushort {
|
||||
pub fn get_scancode(event: &NSEvent) -> c_ushort {
|
||||
// In AppKit, `keyCode` refers to the position (scancode) of a key rather than its character,
|
||||
// and there is no easy way to navtively retrieve the layout-dependent character.
|
||||
// In tao, we use keycode to refer to the key's character, and so this function aligns
|
||||
|
||||
@@ -14,12 +14,10 @@ use std::{
|
||||
rc::{Rc, Weak},
|
||||
};
|
||||
|
||||
use cocoa::{
|
||||
appkit::{NSApp, NSEventModifierFlags, NSEventSubtype, NSEventType::NSApplicationDefined},
|
||||
base::{id, nil, YES},
|
||||
foundation::{NSAutoreleasePool, NSInteger, NSPoint, NSTimeInterval},
|
||||
};
|
||||
use crossbeam_channel::{self as channel, Receiver, Sender};
|
||||
use objc2::{msg_send_id, rc::Retained};
|
||||
use objc2_app_kit::{NSApp, NSApplication, NSEventModifierFlags, NSEventSubtype, NSEventType};
|
||||
use objc2_foundation::{MainThreadMarker, NSAutoreleasePool, NSInteger, NSPoint, NSTimeInterval};
|
||||
use scopeguard::defer;
|
||||
|
||||
use crate::{
|
||||
@@ -33,6 +31,7 @@ use crate::{
|
||||
app::APP_CLASS,
|
||||
app_delegate::APP_DELEGATE_CLASS,
|
||||
app_state::AppState,
|
||||
ffi::{id, nil, YES},
|
||||
monitor::{self, MonitorHandle},
|
||||
observer::*,
|
||||
util::{self, IdRef},
|
||||
@@ -176,9 +175,8 @@ impl<T> EventLoop<T> {
|
||||
let app: id = msg_send![APP_CLASS.0, sharedApplication];
|
||||
|
||||
let delegate = IdRef::new(msg_send![APP_DELEGATE_CLASS.0, new]);
|
||||
let pool = NSAutoreleasePool::new(nil);
|
||||
let _pool = NSAutoreleasePool::new();
|
||||
let _: () = msg_send![app, setDelegate:*delegate];
|
||||
let _: () = msg_send![pool, drain];
|
||||
delegate
|
||||
};
|
||||
|
||||
@@ -222,11 +220,11 @@ impl<T> EventLoop<T> {
|
||||
|
||||
self._callback = Some(Rc::clone(&callback));
|
||||
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
|
||||
let exit_code = unsafe {
|
||||
let pool = NSAutoreleasePool::new(nil);
|
||||
defer!(pool.drain());
|
||||
let app = NSApp();
|
||||
assert_ne!(app, nil);
|
||||
let _pool = NSAutoreleasePool::new();
|
||||
let app = NSApp(mtm);
|
||||
|
||||
// A bit of juggling with the callback references to make sure
|
||||
// that `self.callback` is the only owner of the callback.
|
||||
@@ -234,7 +232,7 @@ impl<T> EventLoop<T> {
|
||||
mem::drop(callback);
|
||||
|
||||
AppState::set_callback(weak_cb, Rc::clone(&self.window_target));
|
||||
let () = msg_send![app, run];
|
||||
let () = msg_send![&app, run];
|
||||
|
||||
if let Some(panic) = self.panic_info.take() {
|
||||
drop(self._callback.take());
|
||||
@@ -253,17 +251,17 @@ impl<T> EventLoop<T> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn post_dummy_event(target: id) {
|
||||
pub unsafe fn post_dummy_event(target: &NSApplication) {
|
||||
let event_class = class!(NSEvent);
|
||||
let dummy_event: id = msg_send![
|
||||
event_class,
|
||||
otherEventWithType: NSApplicationDefined
|
||||
otherEventWithType: NSEventType::ApplicationDefined
|
||||
location: NSPoint::new(0.0, 0.0)
|
||||
modifierFlags: NSEventModifierFlags::empty()
|
||||
timestamp: 0 as NSTimeInterval
|
||||
windowNumber: 0 as NSInteger
|
||||
context: nil
|
||||
subtype: NSEventSubtype::NSWindowExposedEventType
|
||||
subtype: NSEventSubtype::WindowExposed
|
||||
data1: 0 as NSInteger
|
||||
data2: 0 as NSInteger
|
||||
];
|
||||
@@ -290,12 +288,12 @@ pub fn stop_app_on_panic<F: FnOnce() -> R + UnwindSafe, R>(
|
||||
}
|
||||
unsafe {
|
||||
let app_class = class!(NSApplication);
|
||||
let app: id = msg_send![app_class, sharedApplication];
|
||||
let () = msg_send![app, stop: nil];
|
||||
let app: Retained<NSApplication> = msg_send_id![app_class, sharedApplication];
|
||||
let () = msg_send![&app, stop: nil];
|
||||
|
||||
// Posting a dummy event to get `stop` to take effect immediately.
|
||||
// See: https://stackoverflow.com/questions/48041279/stopping-the-nsapplication-main-event-loop/48064752#48064752
|
||||
post_dummy_event(app);
|
||||
post_dummy_event(&app);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
@@ -11,13 +11,8 @@
|
||||
clippy::enum_variant_names
|
||||
)]
|
||||
|
||||
use std::ffi::c_void;
|
||||
use std::{ffi::c_void, ptr};
|
||||
|
||||
use cocoa::{
|
||||
appkit::CGPoint,
|
||||
base::id,
|
||||
foundation::{NSInteger, NSUInteger},
|
||||
};
|
||||
use core_foundation::{
|
||||
array::CFArrayRef, data::CFDataRef, dictionary::CFDictionaryRef, string::CFStringRef,
|
||||
uuid::CFUUIDRef,
|
||||
@@ -25,75 +20,27 @@ use core_foundation::{
|
||||
use core_graphics::{
|
||||
base::CGError,
|
||||
display::{boolean_t, CGDirectDisplayID, CGDisplayConfigRef},
|
||||
geometry::CGRect,
|
||||
geometry::{CGPoint, CGRect},
|
||||
};
|
||||
use objc2::{
|
||||
encode::{Encode, Encoding},
|
||||
runtime::{AnyObject, Bool},
|
||||
};
|
||||
use objc2_foundation::NSInteger;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type id = *mut AnyObject;
|
||||
pub const nil: id = ptr::null_mut();
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type BOOL = Bool;
|
||||
#[allow(deprecated)]
|
||||
pub const YES: Bool = Bool::YES;
|
||||
#[allow(deprecated)]
|
||||
pub const NO: Bool = Bool::NO;
|
||||
|
||||
pub const NSNotFound: NSInteger = NSInteger::max_value();
|
||||
|
||||
#[repr(C)]
|
||||
pub struct NSRange {
|
||||
pub location: NSUInteger,
|
||||
pub length: NSUInteger,
|
||||
}
|
||||
|
||||
impl NSRange {
|
||||
#[inline]
|
||||
pub fn new(location: NSUInteger, length: NSUInteger) -> NSRange {
|
||||
NSRange { location, length }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl objc::Encode for NSRange {
|
||||
fn encode() -> objc::Encoding {
|
||||
let encoding = format!(
|
||||
// TODO: Verify that this is correct
|
||||
"{{NSRange={}{}}}",
|
||||
NSUInteger::encode().as_str(),
|
||||
NSUInteger::encode().as_str(),
|
||||
);
|
||||
unsafe { objc::Encoding::from_str(&encoding) }
|
||||
}
|
||||
}
|
||||
|
||||
pub trait NSMutableAttributedString: Sized {
|
||||
unsafe fn alloc(_: Self) -> id {
|
||||
msg_send![class!(NSMutableAttributedString), alloc]
|
||||
}
|
||||
|
||||
unsafe fn init(self) -> id; // *mut NSMutableAttributedString
|
||||
unsafe fn initWithString(self, string: id) -> id;
|
||||
unsafe fn initWithAttributedString(self, string: id) -> id;
|
||||
|
||||
unsafe fn string(self) -> id; // *mut NSString
|
||||
unsafe fn mutableString(self) -> id; // *mut NSMutableString
|
||||
unsafe fn length(self) -> NSUInteger;
|
||||
}
|
||||
|
||||
impl NSMutableAttributedString for id {
|
||||
unsafe fn init(self) -> id {
|
||||
msg_send![self, init]
|
||||
}
|
||||
|
||||
unsafe fn initWithString(self, string: id) -> id {
|
||||
msg_send![self, initWithString: string]
|
||||
}
|
||||
|
||||
unsafe fn initWithAttributedString(self, string: id) -> id {
|
||||
msg_send![self, initWithAttributedString: string]
|
||||
}
|
||||
|
||||
unsafe fn string(self) -> id {
|
||||
msg_send![self, string]
|
||||
}
|
||||
|
||||
unsafe fn mutableString(self) -> id {
|
||||
msg_send![self, mutableString]
|
||||
}
|
||||
|
||||
unsafe fn length(self) -> NSUInteger {
|
||||
msg_send![self, length]
|
||||
}
|
||||
}
|
||||
|
||||
pub const kCGBaseWindowLevelKey: NSInteger = 0;
|
||||
pub const kCGMinimumWindowLevelKey: NSInteger = 1;
|
||||
pub const kCGDesktopWindowLevelKey: NSInteger = 2;
|
||||
@@ -130,6 +77,10 @@ pub enum NSWindowLevel {
|
||||
NSScreenSaverWindowLevel = kCGScreenSaverWindowLevelKey as _,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSWindowLevel {
|
||||
const ENCODING: Encoding = isize::ENCODING;
|
||||
}
|
||||
|
||||
pub type CGDisplayFadeInterval = f32;
|
||||
pub type CGDisplayReservationInterval = f32;
|
||||
pub type CGDisplayBlendFraction = f32;
|
||||
|
||||
@@ -25,7 +25,7 @@ use std::{fmt, ops::Deref, sync::Arc};
|
||||
|
||||
pub(crate) use self::event_loop::PlatformSpecificEventLoopAttributes;
|
||||
pub use self::{
|
||||
app_delegate::{get_aux_state_mut, AuxDelegateState},
|
||||
app_delegate::get_aux_state_mut,
|
||||
event::KeyEventExtra,
|
||||
event_loop::{EventLoop, EventLoopWindowTarget, Proxy as EventLoopProxy},
|
||||
keycode::{keycode_from_scancode, keycode_to_scancode},
|
||||
|
||||
@@ -4,25 +4,23 @@
|
||||
|
||||
use std::{collections::VecDeque, fmt};
|
||||
|
||||
use super::{
|
||||
ffi::{self, CGRectContainsPoint},
|
||||
util,
|
||||
};
|
||||
use super::ffi::{self, id, nil, CGRectContainsPoint};
|
||||
use crate::{
|
||||
dpi::{PhysicalPosition, PhysicalSize},
|
||||
monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode},
|
||||
};
|
||||
use cocoa::{
|
||||
appkit::{CGPoint, NSScreen},
|
||||
base::{id, nil},
|
||||
foundation::NSUInteger,
|
||||
};
|
||||
use core_foundation::{
|
||||
array::{CFArrayGetCount, CFArrayGetValueAtIndex},
|
||||
base::{CFRelease, TCFType},
|
||||
string::CFString,
|
||||
};
|
||||
use core_graphics::display::{CGDirectDisplayID, CGDisplay, CGDisplayBounds};
|
||||
use core_graphics::{
|
||||
display::{CGDirectDisplayID, CGDisplay, CGDisplayBounds},
|
||||
geometry::CGPoint,
|
||||
};
|
||||
use objc2::{msg_send_id, rc::Retained};
|
||||
use objc2_app_kit::NSScreen;
|
||||
use objc2_foundation::{MainThreadMarker, NSString, NSUInteger};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct VideoMode {
|
||||
@@ -237,7 +235,7 @@ impl MonitorHandle {
|
||||
Some(screen) => screen,
|
||||
None => return 1.0, // default to 1.0 when we can't find the screen
|
||||
};
|
||||
unsafe { NSScreen::backingScaleFactor(screen) as f64 }
|
||||
NSScreen::backingScaleFactor(&screen) as f64
|
||||
}
|
||||
|
||||
pub fn video_modes(&self) -> impl Iterator<Item = RootVideoMode> {
|
||||
@@ -313,16 +311,18 @@ impl MonitorHandle {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn ns_screen(&self) -> Option<id> {
|
||||
pub(crate) fn ns_screen(&self) -> Option<Retained<NSScreen>> {
|
||||
// SAFETY: TODO.
|
||||
let mtm = unsafe { MainThreadMarker::new_unchecked() };
|
||||
unsafe {
|
||||
let uuid = ffi::CGDisplayCreateUUIDFromDisplayID(self.0);
|
||||
let screens = NSScreen::screens(nil);
|
||||
let count: NSUInteger = msg_send![screens, count];
|
||||
let key = util::ns_string_id_ref("NSScreenNumber");
|
||||
let screens = NSScreen::screens(mtm);
|
||||
let count: NSUInteger = msg_send![&screens, count];
|
||||
let key = NSString::from_str("NSScreenNumber");
|
||||
for i in 0..count {
|
||||
let screen = msg_send![screens, objectAtIndex: i as NSUInteger];
|
||||
let device_description = NSScreen::deviceDescription(screen);
|
||||
let value: id = msg_send![device_description, objectForKey:*key];
|
||||
let screen: Retained<NSScreen> = msg_send_id![&screens, objectAtIndex: i as NSUInteger];
|
||||
let device_description = NSScreen::deviceDescription(&screen);
|
||||
let value: id = msg_send![&device_description, objectForKey: &*key];
|
||||
if value != nil {
|
||||
let other_native_id: NSUInteger = msg_send![value, unsignedIntegerValue];
|
||||
let other_uuid =
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
use std::sync::Once;
|
||||
|
||||
use cocoa::{
|
||||
base::{id, nil},
|
||||
foundation::{NSArray, NSPoint, NSRect, NSSize},
|
||||
};
|
||||
use objc::{
|
||||
declare::ClassDecl,
|
||||
runtime::{Class, Object, Sel, NO},
|
||||
use objc2::{
|
||||
msg_send_id,
|
||||
rc::Retained,
|
||||
runtime::{AnyClass as Class, AnyObject as Object, ClassBuilder as ClassDecl, Sel},
|
||||
};
|
||||
use objc2_foundation::{NSArray, NSInsetRect, NSPoint, NSRect, NSSize};
|
||||
|
||||
use super::ffi::{id, nil, NO};
|
||||
use crate::window::{ProgressBarState, ProgressState};
|
||||
|
||||
/// Set progress indicator in the Dock.
|
||||
@@ -30,8 +29,9 @@ pub fn set_progress_indicator(progress_state: ProgressBarState) {
|
||||
let _: () = msg_send![progress_indicator, setDoubleValue: progress];
|
||||
let _: () = msg_send![progress_indicator, setHidden: NO];
|
||||
}
|
||||
#[allow(deprecated)] // TODO: Use define_class!
|
||||
if let Some(state) = progress_state.state {
|
||||
(*progress_indicator).set_ivar("state", state as u8);
|
||||
*(*progress_indicator).get_mut_ivar("state") = state as u8;
|
||||
let _: () = msg_send![
|
||||
progress_indicator,
|
||||
setHidden: matches!(state, ProgressState::None)
|
||||
@@ -61,7 +61,7 @@ fn create_progress_indicator(ns_app: id, dock_tile: id) -> id {
|
||||
let progress_class = create_progress_indicator_class();
|
||||
let progress_indicator: id = msg_send![progress_class, alloc];
|
||||
let progress_indicator: id = msg_send![progress_indicator, initWithFrame: frame];
|
||||
let _: () = msg_send![progress_indicator, autorelease];
|
||||
let _: id = msg_send![progress_indicator, autorelease];
|
||||
|
||||
// set progress indicator to the dock tile
|
||||
let _: () = msg_send![image_view, addSubview: progress_indicator];
|
||||
@@ -76,13 +76,14 @@ fn get_exist_progress_indicator(dock_tile: id) -> Option<id> {
|
||||
if content_view == nil {
|
||||
return None;
|
||||
}
|
||||
let subviews: id /* NSArray */ = msg_send![content_view, subviews];
|
||||
if subviews == nil {
|
||||
let subviews: Option<Retained<NSArray>> = msg_send_id![content_view, subviews];
|
||||
if subviews.is_none() {
|
||||
return None;
|
||||
}
|
||||
let subviews = subviews.unwrap();
|
||||
|
||||
for idx in 0..subviews.count() {
|
||||
let subview: id = msg_send![subviews, objectAtIndex: idx];
|
||||
let subview: id = msg_send![&subviews, objectAtIndex: idx];
|
||||
|
||||
let is_progress_indicator: bool =
|
||||
msg_send![subview, isKindOfClass: class!(NSProgressIndicator)];
|
||||
@@ -102,10 +103,7 @@ fn create_progress_indicator_class() -> *const Class {
|
||||
let superclass = class!(NSProgressIndicator);
|
||||
let mut decl = ClassDecl::new("TaoProgressIndicator", superclass).unwrap();
|
||||
|
||||
decl.add_method(
|
||||
sel!(drawRect:),
|
||||
draw_progress_bar as extern "C" fn(&Object, _, NSRect),
|
||||
);
|
||||
decl.add_method(sel!(drawRect:), draw_progress_bar as extern "C" fn(_, _, _));
|
||||
|
||||
// progress bar states, follows ProgressState
|
||||
decl.add_ivar::<u8>("state");
|
||||
@@ -117,6 +115,7 @@ fn create_progress_indicator_class() -> *const Class {
|
||||
}
|
||||
|
||||
extern "C" fn draw_progress_bar(this: &Object, _: Sel, rect: NSRect) {
|
||||
#[allow(deprecated)] // TODO: Use define_class!
|
||||
unsafe {
|
||||
let bar = NSRect::new(
|
||||
NSPoint { x: 0.0, y: 4.0 },
|
||||
@@ -125,8 +124,8 @@ extern "C" fn draw_progress_bar(this: &Object, _: Sel, rect: NSRect) {
|
||||
height: 8.0,
|
||||
},
|
||||
);
|
||||
let bar_inner = bar.inset(0.5, 0.5);
|
||||
let mut bar_progress = bar.inset(1.0, 1.0);
|
||||
let bar_inner = NSInsetRect(bar, 0.5, 0.5);
|
||||
let mut bar_progress = NSInsetRect(bar, 1.0, 1.0);
|
||||
|
||||
// set progress width
|
||||
let current_progress: f64 = msg_send![this, doubleValue];
|
||||
|
||||
@@ -7,25 +7,22 @@ use std::{
|
||||
sync::{Mutex, Weak},
|
||||
};
|
||||
|
||||
use cocoa::{
|
||||
appkit::{CGFloat, NSScreen, NSWindow, NSWindowStyleMask},
|
||||
base::{id, nil},
|
||||
foundation::{NSPoint, NSSize, NSString},
|
||||
};
|
||||
use core_graphics::base::CGFloat;
|
||||
use dispatch::Queue;
|
||||
use objc::{
|
||||
rc::autoreleasepool,
|
||||
runtime::{BOOL, NO, YES},
|
||||
};
|
||||
use objc2::{rc::autoreleasepool, ClassType};
|
||||
use objc2_app_kit::{NSScreen, NSView, NSWindow, NSWindowStyleMask};
|
||||
use objc2_foundation::{MainThreadMarker, NSPoint, NSSize, NSString};
|
||||
|
||||
use crate::{
|
||||
dpi::LogicalSize,
|
||||
platform_impl::platform::{ffi, util::IdRef, window::SharedState},
|
||||
platform_impl::platform::{
|
||||
ffi::{self, id, NO, YES},
|
||||
window::SharedState,
|
||||
},
|
||||
};
|
||||
|
||||
pub fn is_main_thread() -> bool {
|
||||
let is: BOOL = unsafe { msg_send!(class!(NSThread), isMainThread) };
|
||||
is == YES
|
||||
unsafe { msg_send!(class!(NSThread), isMainThread) }
|
||||
}
|
||||
|
||||
// Unsafe wrapper type that allows us to dispatch things that aren't Send.
|
||||
@@ -51,72 +48,76 @@ fn run_on_main<R: Send>(f: impl FnOnce() -> R + Send) -> R {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn set_style_mask(ns_window: id, ns_view: id, mask: NSWindowStyleMask) {
|
||||
ns_window.setStyleMask_(mask);
|
||||
unsafe fn set_style_mask(ns_window: &NSWindow, ns_view: &NSView, mask: NSWindowStyleMask) {
|
||||
ns_window.setStyleMask(mask);
|
||||
// If we don't do this, key handling will break
|
||||
// (at least until the window is clicked again/etc.)
|
||||
ns_window.makeFirstResponder_(ns_view);
|
||||
ns_window.makeFirstResponder(Some(ns_view));
|
||||
}
|
||||
|
||||
// Always use this function instead of trying to modify `styleMask` directly!
|
||||
// `setStyleMask:` isn't thread-safe, so we have to use Grand Central Dispatch.
|
||||
// Otherwise, this would vomit out errors about not being on the main thread
|
||||
// and fail to do anything.
|
||||
pub unsafe fn set_style_mask_async(ns_window: id, ns_view: id, mask: NSWindowStyleMask) {
|
||||
let ns_window = MainThreadSafe(ns_window);
|
||||
let ns_view = MainThreadSafe(ns_view);
|
||||
pub unsafe fn set_style_mask_async(
|
||||
ns_window: &NSWindow,
|
||||
ns_view: &NSView,
|
||||
mask: NSWindowStyleMask,
|
||||
) {
|
||||
let ns_window = MainThreadSafe(ns_window.retain());
|
||||
let ns_view = MainThreadSafe(ns_view.retain());
|
||||
Queue::main().exec_async(move || {
|
||||
set_style_mask(*ns_window, *ns_view, mask);
|
||||
set_style_mask(&*ns_window, &*ns_view, mask);
|
||||
});
|
||||
}
|
||||
pub unsafe fn set_style_mask_sync(ns_window: id, ns_view: id, mask: NSWindowStyleMask) {
|
||||
pub unsafe fn set_style_mask_sync(ns_window: &NSWindow, ns_view: &NSView, mask: NSWindowStyleMask) {
|
||||
if is_main_thread() {
|
||||
set_style_mask(ns_window, ns_view, mask);
|
||||
} else {
|
||||
let ns_window = MainThreadSafe(ns_window);
|
||||
let ns_view = MainThreadSafe(ns_view);
|
||||
let ns_window = MainThreadSafe(ns_window.retain());
|
||||
let ns_view = MainThreadSafe(ns_view.retain());
|
||||
Queue::main().exec_sync(move || {
|
||||
set_style_mask(*ns_window, *ns_view, mask);
|
||||
set_style_mask(&*ns_window, &*ns_view, mask);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// `setContentSize:` isn't thread-safe either, though it doesn't log any errors
|
||||
// and just fails silently. Anyway, GCD to the rescue!
|
||||
pub unsafe fn set_content_size_async(ns_window: id, size: LogicalSize<f64>) {
|
||||
let ns_window = MainThreadSafe(ns_window);
|
||||
pub unsafe fn set_content_size_async(ns_window: &NSWindow, size: LogicalSize<f64>) {
|
||||
let ns_window = MainThreadSafe(ns_window.retain());
|
||||
Queue::main().exec_async(move || {
|
||||
ns_window.setContentSize_(NSSize::new(size.width as CGFloat, size.height as CGFloat));
|
||||
ns_window.setContentSize(NSSize::new(size.width as CGFloat, size.height as CGFloat));
|
||||
});
|
||||
}
|
||||
|
||||
// `setFrameTopLeftPoint:` isn't thread-safe, but fortunately has the courtesy
|
||||
// to log errors.
|
||||
pub unsafe fn set_frame_top_left_point_async(ns_window: id, point: NSPoint) {
|
||||
let ns_window = MainThreadSafe(ns_window);
|
||||
pub unsafe fn set_frame_top_left_point_async(ns_window: &NSWindow, point: NSPoint) {
|
||||
let ns_window = MainThreadSafe(ns_window.retain());
|
||||
Queue::main().exec_async(move || {
|
||||
ns_window.setFrameTopLeftPoint_(point);
|
||||
ns_window.setFrameTopLeftPoint(point);
|
||||
});
|
||||
}
|
||||
|
||||
// `setFrameTopLeftPoint:` isn't thread-safe, and fails silently.
|
||||
pub unsafe fn set_level_async(ns_window: id, level: ffi::NSWindowLevel) {
|
||||
let ns_window = MainThreadSafe(ns_window);
|
||||
pub unsafe fn set_level_async(ns_window: &NSWindow, level: ffi::NSWindowLevel) {
|
||||
let ns_window = MainThreadSafe(ns_window.retain());
|
||||
Queue::main().exec_async(move || {
|
||||
ns_window.setLevel_(level as _);
|
||||
ns_window.setLevel(level as _);
|
||||
});
|
||||
}
|
||||
|
||||
// `toggleFullScreen` is thread-safe, but our additional logic to account for
|
||||
// window styles isn't.
|
||||
pub unsafe fn toggle_full_screen_async(
|
||||
ns_window: id,
|
||||
ns_view: id,
|
||||
ns_window: &NSWindow,
|
||||
ns_view: &NSView,
|
||||
not_fullscreen: bool,
|
||||
shared_state: Weak<Mutex<SharedState>>,
|
||||
) {
|
||||
let ns_window = MainThreadSafe(ns_window);
|
||||
let ns_view = MainThreadSafe(ns_view);
|
||||
let ns_window = MainThreadSafe(ns_window.retain());
|
||||
let ns_view = MainThreadSafe(ns_view.retain());
|
||||
let shared_state = MainThreadSafe(shared_state);
|
||||
Queue::main().exec_async(move || {
|
||||
// `toggleFullScreen` doesn't work if the `StyleMask` is none, so we
|
||||
@@ -124,10 +125,9 @@ pub unsafe fn toggle_full_screen_async(
|
||||
// restored in `WindowDelegate::window_did_exit_fullscreen`.
|
||||
if not_fullscreen {
|
||||
let curr_mask = ns_window.styleMask();
|
||||
let required =
|
||||
NSWindowStyleMask::NSTitledWindowMask | NSWindowStyleMask::NSResizableWindowMask;
|
||||
let required = NSWindowStyleMask::Titled | NSWindowStyleMask::Resizable;
|
||||
if !curr_mask.contains(required) {
|
||||
set_style_mask(*ns_window, *ns_view, required);
|
||||
set_style_mask(&ns_window, &ns_view, required);
|
||||
if let Some(shared_state) = shared_state.upgrade() {
|
||||
trace!("Locked shared state in `toggle_full_screen_callback`");
|
||||
let mut shared_state_lock = shared_state.lock().unwrap();
|
||||
@@ -139,8 +139,8 @@ pub unsafe fn toggle_full_screen_async(
|
||||
// Window level must be restored from `CGShieldingWindowLevel()
|
||||
// + 1` back to normal in order for `toggleFullScreen` to do
|
||||
// anything
|
||||
ns_window.setLevel_(0);
|
||||
ns_window.toggleFullScreen_(nil);
|
||||
ns_window.setLevel(0);
|
||||
ns_window.toggleFullScreen(None);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -153,12 +153,12 @@ pub unsafe fn restore_display_mode_async(ns_screen: u32) {
|
||||
|
||||
// `setMaximized` is not thread-safe
|
||||
pub unsafe fn set_maximized_async(
|
||||
ns_window: id,
|
||||
ns_window: &NSWindow,
|
||||
is_zoomed: bool,
|
||||
maximized: bool,
|
||||
shared_state: Weak<Mutex<SharedState>>,
|
||||
) {
|
||||
let ns_window = MainThreadSafe(ns_window);
|
||||
let ns_window = MainThreadSafe(ns_window.retain());
|
||||
let shared_state = MainThreadSafe(shared_state);
|
||||
Queue::main().exec_async(move || {
|
||||
if let Some(shared_state) = shared_state.upgrade() {
|
||||
@@ -167,7 +167,7 @@ pub unsafe fn set_maximized_async(
|
||||
|
||||
// Save the standard frame sized if it is not zoomed
|
||||
if !is_zoomed {
|
||||
shared_state_lock.standard_frame = Some(NSWindow::frame(*ns_window));
|
||||
shared_state_lock.standard_frame = Some(NSWindow::frame(&ns_window));
|
||||
}
|
||||
|
||||
shared_state_lock.maximized = maximized;
|
||||
@@ -176,20 +176,21 @@ pub unsafe fn set_maximized_async(
|
||||
if shared_state_lock.fullscreen.is_some() {
|
||||
// Handle it in window_did_exit_fullscreen
|
||||
return;
|
||||
} else if curr_mask.contains(NSWindowStyleMask::NSResizableWindowMask)
|
||||
&& curr_mask.contains(NSWindowStyleMask::NSTitledWindowMask)
|
||||
} else if curr_mask.contains(NSWindowStyleMask::Resizable)
|
||||
&& curr_mask.contains(NSWindowStyleMask::Titled)
|
||||
{
|
||||
// Just use the native zoom if resizable
|
||||
ns_window.zoom_(nil);
|
||||
ns_window.zoom(None);
|
||||
} else {
|
||||
// if it's not resizable, we set the frame directly
|
||||
let new_rect = if maximized {
|
||||
let screen = NSScreen::mainScreen(nil);
|
||||
NSScreen::visibleFrame(screen)
|
||||
let mtm = MainThreadMarker::new_unchecked();
|
||||
let screen = NSScreen::mainScreen(mtm).unwrap();
|
||||
NSScreen::visibleFrame(&screen)
|
||||
} else {
|
||||
shared_state_lock.saved_standard_frame()
|
||||
};
|
||||
let _: () = msg_send![*ns_window, setFrame:new_rect display:NO animate: YES];
|
||||
let _: () = msg_send![&*ns_window, setFrame:new_rect display:NO animate: YES];
|
||||
}
|
||||
|
||||
trace!("Unlocked shared state in `set_maximized`");
|
||||
@@ -199,38 +200,38 @@ pub unsafe fn set_maximized_async(
|
||||
|
||||
// `orderOut:` isn't thread-safe. Calling it from another thread actually works,
|
||||
// but with an odd delay.
|
||||
pub unsafe fn order_out_sync(ns_window: id) {
|
||||
let ns_window = MainThreadSafe(ns_window);
|
||||
pub unsafe fn order_out_sync(ns_window: &NSWindow) {
|
||||
let ns_window = MainThreadSafe(ns_window.retain());
|
||||
run_on_main(move || {
|
||||
ns_window.orderOut_(nil);
|
||||
ns_window.orderOut(None);
|
||||
});
|
||||
}
|
||||
|
||||
// `makeKeyAndOrderFront:` isn't thread-safe. Calling it from another thread
|
||||
// actually works, but with an odd delay.
|
||||
pub unsafe fn make_key_and_order_front_sync(ns_window: id) {
|
||||
let ns_window = MainThreadSafe(ns_window);
|
||||
pub unsafe fn make_key_and_order_front_sync(ns_window: &NSWindow) {
|
||||
let ns_window = MainThreadSafe(ns_window.retain());
|
||||
run_on_main(move || {
|
||||
ns_window.makeKeyAndOrderFront_(nil);
|
||||
ns_window.makeKeyAndOrderFront(None);
|
||||
});
|
||||
}
|
||||
|
||||
// `setTitle:` isn't thread-safe. Calling it from another thread invalidates the
|
||||
// window drag regions, which throws an exception when not done in the main
|
||||
// thread
|
||||
pub unsafe fn set_title_async(ns_window: id, title: String) {
|
||||
let ns_window = MainThreadSafe(ns_window);
|
||||
pub unsafe fn set_title_async(ns_window: &NSWindow, title: String) {
|
||||
let ns_window = MainThreadSafe(ns_window.retain());
|
||||
Queue::main().exec_async(move || {
|
||||
let title = IdRef::new(NSString::alloc(nil).init_str(&title));
|
||||
ns_window.setTitle_(*title);
|
||||
let title = NSString::from_str(&title);
|
||||
ns_window.setTitle(&title);
|
||||
});
|
||||
}
|
||||
|
||||
// `setFocus:` isn't thread-safe.
|
||||
pub unsafe fn set_focus(ns_window: id) {
|
||||
let ns_window = MainThreadSafe(ns_window);
|
||||
pub unsafe fn set_focus(ns_window: &NSWindow) {
|
||||
let ns_window = MainThreadSafe(ns_window.retain());
|
||||
run_on_main(move || {
|
||||
ns_window.makeKeyAndOrderFront_(nil);
|
||||
ns_window.makeKeyAndOrderFront(None);
|
||||
let app: id = msg_send![class!(NSApplication), sharedApplication];
|
||||
let () = msg_send![app, activateIgnoringOtherApps: YES];
|
||||
});
|
||||
@@ -238,22 +239,19 @@ pub unsafe fn set_focus(ns_window: id) {
|
||||
|
||||
// `close:` is thread-safe, but we want the event to be triggered from the main
|
||||
// thread. Though, it's a good idea to look into that more...
|
||||
//
|
||||
// ArturKovacs: It's important that this operation keeps the underlying window alive
|
||||
// through the `IdRef` because otherwise it would dereference free'd memory
|
||||
pub unsafe fn close_async(ns_window: IdRef) {
|
||||
let ns_window = MainThreadSafe(ns_window);
|
||||
pub unsafe fn close_async(ns_window: &NSWindow) {
|
||||
let ns_window = MainThreadSafe(ns_window.retain());
|
||||
run_on_main(move || {
|
||||
autoreleasepool(move || {
|
||||
autoreleasepool(move |_| {
|
||||
ns_window.close();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// `setIgnoresMouseEvents_:` isn't thread-safe, and fails silently.
|
||||
pub unsafe fn set_ignore_mouse_events(ns_window: id, ignore: bool) {
|
||||
let ns_window = MainThreadSafe(ns_window);
|
||||
pub unsafe fn set_ignore_mouse_events(ns_window: &NSWindow, ignore: bool) {
|
||||
let ns_window = MainThreadSafe(ns_window.retain());
|
||||
Queue::main().exec_async(move || {
|
||||
ns_window.setIgnoresMouseEvents_(if ignore { YES } else { NO });
|
||||
ns_window.setIgnoresMouseEvents(ignore);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,13 +2,16 @@
|
||||
// Copyright 2021-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cocoa::{
|
||||
appkit::NSImage,
|
||||
base::{id, nil},
|
||||
foundation::{NSDictionary, NSPoint, NSString},
|
||||
use crate::platform_impl::platform::ffi::{id, nil, NO};
|
||||
use objc2::{
|
||||
msg_send_id,
|
||||
rc::Retained,
|
||||
runtime::{AnyObject, Sel},
|
||||
ClassType,
|
||||
};
|
||||
use objc::runtime::{Sel, NO};
|
||||
use std::{cell::RefCell, ptr::null_mut};
|
||||
use objc2_app_kit::NSImage;
|
||||
use objc2_foundation::{NSDictionary, NSPoint, NSString};
|
||||
use std::{cell::RefCell, ffi::c_void, ptr::null_mut};
|
||||
|
||||
use crate::window::CursorIcon;
|
||||
|
||||
@@ -108,26 +111,32 @@ impl Cursor {
|
||||
// instead you'll just get them all in a column.
|
||||
pub unsafe fn load_webkit_cursor(cursor_name: &str) -> id {
|
||||
static CURSOR_ROOT: &str = "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors";
|
||||
let cursor_root = NSString::alloc(nil).init_str(CURSOR_ROOT);
|
||||
let cursor_name = NSString::alloc(nil).init_str(cursor_name);
|
||||
let cursor_pdf = NSString::alloc(nil).init_str("cursor.pdf");
|
||||
let cursor_plist = NSString::alloc(nil).init_str("info.plist");
|
||||
let key_x = NSString::alloc(nil).init_str("hotx");
|
||||
let key_y = NSString::alloc(nil).init_str("hoty");
|
||||
let cursor_root = NSString::from_str(CURSOR_ROOT);
|
||||
let cursor_name = NSString::from_str(cursor_name);
|
||||
let cursor_pdf = NSString::from_str("cursor.pdf");
|
||||
let cursor_plist = NSString::from_str("info.plist");
|
||||
let key_x = NSString::from_str("hotx");
|
||||
let key_y = NSString::from_str("hoty");
|
||||
|
||||
let cursor_path: id = msg_send![cursor_root, stringByAppendingPathComponent: cursor_name];
|
||||
let pdf_path: id = msg_send![cursor_path, stringByAppendingPathComponent: cursor_pdf];
|
||||
let info_path: id = msg_send![cursor_path, stringByAppendingPathComponent: cursor_plist];
|
||||
let cursor_path: Retained<NSString> =
|
||||
msg_send_id![&cursor_root, stringByAppendingPathComponent: &*cursor_name];
|
||||
let pdf_path: Retained<NSString> =
|
||||
msg_send_id![&cursor_path, stringByAppendingPathComponent: &*cursor_pdf];
|
||||
let info_path: Retained<NSString> =
|
||||
msg_send_id![&cursor_path, stringByAppendingPathComponent: &*cursor_plist];
|
||||
|
||||
let image = NSImage::alloc(nil).initByReferencingFile_(pdf_path);
|
||||
let info = NSDictionary::dictionaryWithContentsOfFile_(nil, info_path);
|
||||
let x = info.valueForKey_(key_x);
|
||||
let y = info.valueForKey_(key_y);
|
||||
let point = NSPoint::new(msg_send![x, doubleValue], msg_send![y, doubleValue]);
|
||||
let image = NSImage::initByReferencingFile(NSImage::alloc(), &pdf_path).unwrap();
|
||||
#[allow(deprecated)]
|
||||
let info =
|
||||
NSDictionary::<AnyObject, AnyObject>::dictionaryWithContentsOfFile(&info_path).unwrap();
|
||||
let x = info.objectForKey(&key_x).unwrap();
|
||||
let y = info.objectForKey(&key_y).unwrap();
|
||||
let point = NSPoint::new(msg_send![&x, doubleValue], msg_send![&y, doubleValue]);
|
||||
let cursor: id = msg_send![class!(NSCursor), alloc];
|
||||
msg_send![cursor,
|
||||
initWithImage:image
|
||||
hotSpot:point
|
||||
msg_send![
|
||||
cursor,
|
||||
initWithImage:&*image,
|
||||
hotSpot:point,
|
||||
]
|
||||
}
|
||||
|
||||
@@ -150,10 +159,11 @@ pub unsafe fn invisible_cursor() -> id {
|
||||
CURSOR_OBJECT.with(|cursor_obj| {
|
||||
if *cursor_obj.borrow() == nil {
|
||||
// Create a cursor from `CURSOR_BYTES`
|
||||
let cursor_data: id = msg_send![class!(NSData),
|
||||
dataWithBytesNoCopy:CURSOR_BYTES as *const [u8]
|
||||
length:CURSOR_BYTES.len()
|
||||
freeWhenDone:NO
|
||||
let cursor_data: id = msg_send![
|
||||
class!(NSData),
|
||||
dataWithBytesNoCopy:CURSOR_BYTES.as_ptr().cast::<c_void>(),
|
||||
length:CURSOR_BYTES.len(),
|
||||
freeWhenDone:NO,
|
||||
];
|
||||
|
||||
let ns_image: id = msg_send![class!(NSImage), alloc];
|
||||
|
||||
@@ -7,26 +7,20 @@ mod cursor;
|
||||
|
||||
pub use self::{cursor::*, r#async::*};
|
||||
|
||||
use std::{
|
||||
ops::{BitAnd, Deref},
|
||||
slice, str,
|
||||
};
|
||||
use std::ops::{BitAnd, Deref};
|
||||
|
||||
use cocoa::{
|
||||
appkit::{NSApp, NSWindowStyleMask},
|
||||
base::{id, nil},
|
||||
foundation::{NSAutoreleasePool, NSPoint, NSRect, NSString, NSUInteger},
|
||||
};
|
||||
use core_graphics::display::CGDisplay;
|
||||
use objc::{
|
||||
use objc2::{
|
||||
class,
|
||||
runtime::{Class, Object, Sel, BOOL, YES},
|
||||
runtime::{AnyClass as Class, AnyObject as Object, Sel},
|
||||
};
|
||||
use objc2_app_kit::{NSApp, NSView, NSWindow, NSWindowStyleMask};
|
||||
use objc2_foundation::{MainThreadMarker, NSAutoreleasePool, NSPoint, NSRange, NSRect, NSUInteger};
|
||||
|
||||
use crate::{
|
||||
dpi::{LogicalPosition, PhysicalPosition},
|
||||
error::ExternalError,
|
||||
platform_impl::platform::ffi,
|
||||
platform_impl::platform::ffi::{self, id, nil, BOOL, YES},
|
||||
};
|
||||
|
||||
// Replace with `!` once stable
|
||||
@@ -40,7 +34,7 @@ where
|
||||
bitset & flag == flag
|
||||
}
|
||||
|
||||
pub const EMPTY_RANGE: ffi::NSRange = ffi::NSRange {
|
||||
pub const EMPTY_RANGE: NSRange = NSRange {
|
||||
location: ffi::NSNotFound as NSUInteger,
|
||||
length: 0,
|
||||
};
|
||||
@@ -60,23 +54,14 @@ impl IdRef {
|
||||
}
|
||||
IdRef(inner)
|
||||
}
|
||||
|
||||
pub fn non_nil(self) -> Option<IdRef> {
|
||||
if self.0 == nil {
|
||||
None
|
||||
} else {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for IdRef {
|
||||
fn drop(&mut self) {
|
||||
if self.0 != nil {
|
||||
unsafe {
|
||||
let pool = NSAutoreleasePool::new(nil);
|
||||
let _pool = NSAutoreleasePool::new();
|
||||
let () = msg_send![self.0, release];
|
||||
pool.drain();
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -120,23 +105,12 @@ pub fn cursor_position() -> Result<PhysicalPosition<f64>, ExternalError> {
|
||||
Ok(point.to_physical(super::monitor::primary_monitor().scale_factor()))
|
||||
}
|
||||
|
||||
pub unsafe fn ns_string_id_ref(s: &str) -> IdRef {
|
||||
IdRef::new(NSString::alloc(nil).init_str(s))
|
||||
}
|
||||
|
||||
/// Copies the contents of the ns string into a `String` which gets returned.
|
||||
pub unsafe fn ns_string_to_rust(ns_string: id) -> String {
|
||||
let slice = slice::from_raw_parts(ns_string.UTF8String() as *mut u8, ns_string.len());
|
||||
let string = str::from_utf8_unchecked(slice);
|
||||
string.to_owned()
|
||||
}
|
||||
|
||||
pub unsafe fn superclass<'a>(this: &'a Object) -> &'a Class {
|
||||
let superclass: *const Class = msg_send![this, superclass];
|
||||
&*superclass
|
||||
}
|
||||
|
||||
pub unsafe fn create_input_context(view: id) -> IdRef {
|
||||
pub unsafe fn create_input_context(view: &NSView) -> IdRef {
|
||||
let input_context: id = msg_send![class!(NSTextInputContext), alloc];
|
||||
let input_context: id = msg_send![input_context, initWithClient: view];
|
||||
IdRef::new(input_context)
|
||||
@@ -144,23 +118,28 @@ pub unsafe fn create_input_context(view: id) -> IdRef {
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub unsafe fn open_emoji_picker() {
|
||||
let () = msg_send![NSApp(), orderFrontCharacterPalette: nil];
|
||||
// SAFETY: TODO
|
||||
let mtm = unsafe { MainThreadMarker::new_unchecked() };
|
||||
let () = msg_send![&NSApp(mtm), orderFrontCharacterPalette: nil];
|
||||
}
|
||||
|
||||
pub extern "C" fn yes(_: &Object, _: Sel) -> BOOL {
|
||||
YES
|
||||
}
|
||||
|
||||
pub unsafe fn toggle_style_mask(window: id, view: id, mask: NSWindowStyleMask, on: bool) {
|
||||
use cocoa::appkit::NSWindow;
|
||||
|
||||
pub unsafe fn toggle_style_mask(
|
||||
window: &NSWindow,
|
||||
view: &NSView,
|
||||
mask: NSWindowStyleMask,
|
||||
on: bool,
|
||||
) {
|
||||
let current_style_mask = window.styleMask();
|
||||
if on {
|
||||
window.setStyleMask_(current_style_mask | mask);
|
||||
window.setStyleMask(current_style_mask | mask);
|
||||
} else {
|
||||
window.setStyleMask_(current_style_mask & (!mask));
|
||||
window.setStyleMask(current_style_mask & (!mask));
|
||||
}
|
||||
|
||||
// If we don't do this, key handling will break. Therefore, never call `setStyleMask` directly!
|
||||
window.makeFirstResponder_(view);
|
||||
window.makeFirstResponder(Some(view));
|
||||
}
|
||||
|
||||
@@ -1,23 +1,31 @@
|
||||
// Copyright 2014-2021 The winit contributors
|
||||
// Copyright 2021-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#![allow(unused_unsafe)]
|
||||
#![allow(deprecated)] // TODO: Use define_class!
|
||||
|
||||
use std::{
|
||||
boxed::Box,
|
||||
collections::{HashSet, VecDeque},
|
||||
os::raw::*,
|
||||
ptr, slice, str,
|
||||
ptr,
|
||||
sync::{Arc, Mutex, Weak},
|
||||
};
|
||||
|
||||
use cocoa::{
|
||||
appkit::{NSApp, NSEvent, NSEventModifierFlags, NSEventPhase, NSView, NSWindow, NSWindowButton},
|
||||
base::{id, nil},
|
||||
foundation::{NSInteger, NSPoint, NSRect, NSSize, NSString, NSUInteger},
|
||||
use objc2::{
|
||||
msg_send_id,
|
||||
rc::Retained,
|
||||
runtime::{
|
||||
AnyClass as Class, AnyObject as Object, AnyProtocol as Protocol, ClassBuilder as ClassDecl, Sel,
|
||||
},
|
||||
ClassType,
|
||||
};
|
||||
use objc::{
|
||||
declare::ClassDecl,
|
||||
runtime::{Class, Object, Protocol, Sel, BOOL, NO, YES},
|
||||
use objc2_app_kit::{
|
||||
NSApp, NSEvent, NSEventModifierFlags, NSEventPhase, NSView, NSWindow, NSWindowButton,
|
||||
};
|
||||
use objc2_foundation::{
|
||||
MainThreadMarker, NSAttributedString, NSInteger, NSMutableAttributedString, NSPoint, NSRange,
|
||||
NSRect, NSSize, NSString, NSUInteger,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -30,7 +38,7 @@ use crate::{
|
||||
app_state::AppState,
|
||||
event::{code_to_key, create_key_event, event_mods, get_scancode, EventWrapper},
|
||||
ffi::*,
|
||||
util::{self, IdRef},
|
||||
util::{self},
|
||||
window::get_window_id,
|
||||
DEVICE_ID,
|
||||
},
|
||||
@@ -52,7 +60,7 @@ impl Default for CursorState {
|
||||
}
|
||||
|
||||
pub(super) struct ViewState {
|
||||
ns_window: id,
|
||||
ns_window: objc2::rc::Weak<NSWindow>,
|
||||
pub cursor_state: Arc<Mutex<CursorState>>,
|
||||
ime_spot: Option<(f64, f64)>,
|
||||
|
||||
@@ -75,15 +83,15 @@ pub(super) struct ViewState {
|
||||
|
||||
impl ViewState {
|
||||
fn get_scale_factor(&self) -> f64 {
|
||||
(unsafe { NSWindow::backingScaleFactor(self.ns_window) }) as f64
|
||||
(unsafe { NSWindow::backingScaleFactor(&self.ns_window.load().unwrap()) }) as f64
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_view(ns_window: id) -> (IdRef, Weak<Mutex<CursorState>>) {
|
||||
pub fn new_view(ns_window: &NSWindow) -> (Option<Retained<NSView>>, Weak<Mutex<CursorState>>) {
|
||||
let cursor_state = Default::default();
|
||||
let cursor_access = Arc::downgrade(&cursor_state);
|
||||
let state = ViewState {
|
||||
ns_window,
|
||||
ns_window: objc2::rc::Weak::from(ns_window),
|
||||
cursor_state,
|
||||
ime_spot: None,
|
||||
in_ime_preedit: false,
|
||||
@@ -97,19 +105,18 @@ pub fn new_view(ns_window: id) -> (IdRef, Weak<Mutex<CursorState>>) {
|
||||
unsafe {
|
||||
// This is free'd in `dealloc`
|
||||
let state_ptr = Box::into_raw(Box::new(state)) as *mut c_void;
|
||||
let ns_view: id = msg_send![VIEW_CLASS.0, alloc];
|
||||
(
|
||||
IdRef::new(msg_send![ns_view, initWithTao: state_ptr]),
|
||||
cursor_access,
|
||||
)
|
||||
let ns_view = msg_send_id![VIEW_CLASS.0, alloc];
|
||||
(msg_send_id![ns_view, initWithTao: state_ptr], cursor_access)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn set_ime_position(ns_view: id, input_context: id, x: f64, y: f64) {
|
||||
let state_ptr: *mut c_void = *(*ns_view).get_mut_ivar("taoState");
|
||||
pub unsafe fn set_ime_position(ns_view: &NSView, input_context: id, x: f64, y: f64) {
|
||||
let state_ptr: *mut c_void = *ns_view.get_ivar("taoState");
|
||||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
let content_rect =
|
||||
NSWindow::contentRectForFrameRect_(state.ns_window, NSWindow::frame(state.ns_window));
|
||||
let content_rect = NSWindow::contentRectForFrameRect(
|
||||
&state.ns_window.load().unwrap(),
|
||||
NSWindow::frame(&state.ns_window.load().unwrap()),
|
||||
);
|
||||
let base_x = content_rect.origin.x as f64;
|
||||
let base_y = (content_rect.origin.y + content_rect.size.height) as f64;
|
||||
state.ime_spot = Some((base_x + x, base_y - y));
|
||||
@@ -123,17 +130,7 @@ fn is_arrow_key(keycode: KeyCode) -> bool {
|
||||
)
|
||||
}
|
||||
|
||||
/// `view` must be the reference to the `TaoView` class
|
||||
///
|
||||
/// Returns the mutable reference to the `markedText` field.
|
||||
unsafe fn clear_marked_text(view: &mut Object) -> &mut id {
|
||||
let marked_text_ref: &mut id = view.get_mut_ivar("markedText");
|
||||
let () = msg_send![(*marked_text_ref), release];
|
||||
*marked_text_ref = NSMutableAttributedString::alloc(nil);
|
||||
marked_text_ref
|
||||
}
|
||||
|
||||
struct ViewClass(*const Class);
|
||||
struct ViewClass(&'static Class);
|
||||
unsafe impl Send for ViewClass {}
|
||||
unsafe impl Sync for ViewClass {}
|
||||
|
||||
@@ -141,161 +138,121 @@ lazy_static! {
|
||||
static ref VIEW_CLASS: ViewClass = unsafe {
|
||||
let superclass = class!(NSView);
|
||||
let mut decl = ClassDecl::new("TaoView", superclass).unwrap();
|
||||
decl.add_method(sel!(dealloc), dealloc as extern "C" fn(&Object, Sel));
|
||||
decl.add_method(sel!(dealloc), dealloc as extern "C" fn(_, _));
|
||||
decl.add_method(
|
||||
sel!(initWithTao:),
|
||||
init_with_tao as extern "C" fn(&Object, Sel, *mut c_void) -> id,
|
||||
init_with_tao as extern "C" fn(_, _, _) -> _,
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(viewDidMoveToWindow),
|
||||
view_did_move_to_window as extern "C" fn(&Object, Sel),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(drawRect:),
|
||||
draw_rect as extern "C" fn(&Object, Sel, NSRect),
|
||||
view_did_move_to_window as extern "C" fn(_, _),
|
||||
);
|
||||
decl.add_method(sel!(drawRect:), draw_rect as extern "C" fn(_, _, _));
|
||||
decl.add_method(
|
||||
sel!(acceptsFirstResponder),
|
||||
accepts_first_responder as extern "C" fn(&Object, Sel) -> BOOL,
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(touchBar),
|
||||
touch_bar as extern "C" fn(&Object, Sel) -> BOOL,
|
||||
accepts_first_responder as extern "C" fn(_, _) -> _,
|
||||
);
|
||||
decl.add_method(sel!(touchBar), touch_bar as extern "C" fn(_, _) -> _);
|
||||
decl.add_method(
|
||||
sel!(resetCursorRects),
|
||||
reset_cursor_rects as extern "C" fn(&Object, Sel),
|
||||
reset_cursor_rects as extern "C" fn(_, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(hasMarkedText),
|
||||
has_marked_text as extern "C" fn(&Object, Sel) -> BOOL,
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(markedRange),
|
||||
marked_range as extern "C" fn(&Object, Sel) -> NSRange,
|
||||
has_marked_text as extern "C" fn(_, _) -> _,
|
||||
);
|
||||
decl.add_method(sel!(markedRange), marked_range as extern "C" fn(_, _) -> _);
|
||||
decl.add_method(
|
||||
sel!(selectedRange),
|
||||
selected_range as extern "C" fn(&Object, Sel) -> NSRange,
|
||||
selected_range as extern "C" fn(_, _) -> _,
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(setMarkedText:selectedRange:replacementRange:),
|
||||
set_marked_text as extern "C" fn(&mut Object, Sel, id, NSRange, NSRange),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(unmarkText),
|
||||
unmark_text as extern "C" fn(&mut Object, Sel),
|
||||
set_marked_text as extern "C" fn(_, _, _, _, _),
|
||||
);
|
||||
decl.add_method(sel!(unmarkText), unmark_text as extern "C" fn(_, _));
|
||||
decl.add_method(
|
||||
sel!(validAttributesForMarkedText),
|
||||
valid_attributes_for_marked_text as extern "C" fn(&Object, Sel) -> id,
|
||||
valid_attributes_for_marked_text as extern "C" fn(_, _) -> _,
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(attributedSubstringForProposedRange:actualRange:),
|
||||
attributed_substring_for_proposed_range
|
||||
as extern "C" fn(&Object, Sel, NSRange, *mut c_void) -> id,
|
||||
attributed_substring_for_proposed_range as extern "C" fn(_, _, _, _) -> _,
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(insertText:replacementRange:),
|
||||
insert_text as extern "C" fn(&Object, Sel, id, NSRange),
|
||||
insert_text as extern "C" fn(_, _, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(characterIndexForPoint:),
|
||||
character_index_for_point as extern "C" fn(&Object, Sel, NSPoint) -> NSUInteger,
|
||||
character_index_for_point as extern "C" fn(_, _, _) -> _,
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(firstRectForCharacterRange:actualRange:),
|
||||
first_rect_for_character_range as extern "C" fn(&Object, Sel, NSRange, *mut c_void) -> NSRect,
|
||||
first_rect_for_character_range as extern "C" fn(_, _, _, _) -> _,
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(doCommandBySelector:),
|
||||
do_command_by_selector as extern "C" fn(&Object, Sel, Sel),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(keyDown:),
|
||||
key_down as extern "C" fn(&mut Object, Sel, id),
|
||||
);
|
||||
decl.add_method(sel!(keyUp:), key_up as extern "C" fn(&Object, Sel, id));
|
||||
decl.add_method(
|
||||
sel!(flagsChanged:),
|
||||
flags_changed as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(insertTab:),
|
||||
insert_tab as extern "C" fn(&Object, Sel, id),
|
||||
do_command_by_selector as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(sel!(keyDown:), key_down as extern "C" fn(_, _, _));
|
||||
decl.add_method(sel!(keyUp:), key_up as extern "C" fn(_, _, _));
|
||||
decl.add_method(sel!(flagsChanged:), flags_changed as extern "C" fn(_, _, _));
|
||||
decl.add_method(sel!(insertTab:), insert_tab as extern "C" fn(_, _, _));
|
||||
decl.add_method(
|
||||
sel!(insertBackTab:),
|
||||
insert_back_tab as extern "C" fn(&Object, Sel, id),
|
||||
insert_back_tab as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(mouseDown:),
|
||||
mouse_down as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
decl.add_method(sel!(mouseUp:), mouse_up as extern "C" fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(mouseDown:), mouse_down as extern "C" fn(_, _, _));
|
||||
decl.add_method(sel!(mouseUp:), mouse_up as extern "C" fn(_, _, _));
|
||||
decl.add_method(
|
||||
sel!(rightMouseDown:),
|
||||
right_mouse_down as extern "C" fn(&Object, Sel, id),
|
||||
right_mouse_down as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(rightMouseUp:),
|
||||
right_mouse_up as extern "C" fn(&Object, Sel, id),
|
||||
right_mouse_up as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(otherMouseDown:),
|
||||
other_mouse_down as extern "C" fn(&Object, Sel, id),
|
||||
other_mouse_down as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(otherMouseUp:),
|
||||
other_mouse_up as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(mouseMoved:),
|
||||
mouse_moved as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(mouseDragged:),
|
||||
mouse_dragged as extern "C" fn(&Object, Sel, id),
|
||||
other_mouse_up as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(sel!(mouseMoved:), mouse_moved as extern "C" fn(_, _, _));
|
||||
decl.add_method(sel!(mouseDragged:), mouse_dragged as extern "C" fn(_, _, _));
|
||||
decl.add_method(
|
||||
sel!(rightMouseDragged:),
|
||||
right_mouse_dragged as extern "C" fn(&Object, Sel, id),
|
||||
right_mouse_dragged as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(otherMouseDragged:),
|
||||
other_mouse_dragged as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(mouseEntered:),
|
||||
mouse_entered as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(mouseExited:),
|
||||
mouse_exited as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(scrollWheel:),
|
||||
scroll_wheel as extern "C" fn(&Object, Sel, id),
|
||||
other_mouse_dragged as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(sel!(mouseEntered:), mouse_entered as extern "C" fn(_, _, _));
|
||||
decl.add_method(sel!(mouseExited:), mouse_exited as extern "C" fn(_, _, _));
|
||||
decl.add_method(sel!(scrollWheel:), scroll_wheel as extern "C" fn(_, _, _));
|
||||
decl.add_method(
|
||||
sel!(pressureChangeWithEvent:),
|
||||
pressure_change_with_event as extern "C" fn(&Object, Sel, id),
|
||||
pressure_change_with_event as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(_wantsKeyDownForEvent:),
|
||||
wants_key_down_for_event as extern "C" fn(&Object, Sel, id) -> BOOL,
|
||||
wants_key_down_for_event as extern "C" fn(_, _, _) -> _,
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(cancelOperation:),
|
||||
cancel_operation as extern "C" fn(&Object, Sel, id),
|
||||
cancel_operation as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(frameDidChange:),
|
||||
frame_did_change as extern "C" fn(&Object, Sel, id),
|
||||
frame_did_change as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(acceptsFirstMouse:),
|
||||
accepts_first_mouse as extern "C" fn(&Object, Sel, id) -> BOOL,
|
||||
accepts_first_mouse as extern "C" fn(_, _, _) -> _,
|
||||
);
|
||||
decl.add_ivar::<*mut c_void>("taoState");
|
||||
decl.add_ivar::<id>("markedText");
|
||||
@@ -308,7 +265,7 @@ lazy_static! {
|
||||
extern "C" fn dealloc(this: &Object, _sel: Sel) {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("taoState");
|
||||
let marked_text: id = *this.get_ivar("markedText");
|
||||
let marked_text: *mut NSMutableAttributedString = *this.get_ivar("markedText");
|
||||
let _: () = msg_send![marked_text, release];
|
||||
drop(Box::from_raw(state as *mut ViewState));
|
||||
}
|
||||
@@ -318,19 +275,18 @@ extern "C" fn init_with_tao(this: &Object, _sel: Sel, state: *mut c_void) -> id
|
||||
unsafe {
|
||||
let this: id = msg_send![this, init];
|
||||
if this != nil {
|
||||
(*this).set_ivar("taoState", state);
|
||||
let marked_text =
|
||||
<id as NSMutableAttributedString>::init(NSMutableAttributedString::alloc(nil));
|
||||
(*this).set_ivar("markedText", marked_text);
|
||||
*(*this).get_mut_ivar("taoState") = state;
|
||||
let marked_text = Retained::into_raw(NSMutableAttributedString::new());
|
||||
*(*this).get_mut_ivar("markedText") = marked_text;
|
||||
let _: () = msg_send![this, setPostsFrameChangedNotifications: YES];
|
||||
|
||||
let notification_center: &Object = msg_send![class!(NSNotificationCenter), defaultCenter];
|
||||
let notification_name = NSString::alloc(nil).init_str("NSViewFrameDidChangeNotification");
|
||||
let notification_name = NSString::from_str("NSViewFrameDidChangeNotification");
|
||||
let _: () = msg_send![
|
||||
notification_center,
|
||||
addObserver: this
|
||||
selector: sel!(frameDidChange:)
|
||||
name: notification_name
|
||||
name: &*notification_name
|
||||
object: this
|
||||
];
|
||||
}
|
||||
@@ -387,11 +343,11 @@ extern "C" fn draw_rect(this: &Object, _sel: Sel, rect: NSRect) {
|
||||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
|
||||
if let Some(position) = state.traffic_light_inset {
|
||||
let window = state.ns_window;
|
||||
inset_traffic_lights(window, position);
|
||||
let window = state.ns_window.load().unwrap();
|
||||
inset_traffic_lights(&window, position);
|
||||
}
|
||||
|
||||
AppState::handle_redraw(WindowId(get_window_id(state.ns_window)));
|
||||
AppState::handle_redraw(WindowId(get_window_id(&state.ns_window.load().unwrap())));
|
||||
|
||||
let superclass = util::superclass(this);
|
||||
let () = msg_send![super(this, superclass), drawRect: rect];
|
||||
@@ -405,8 +361,8 @@ extern "C" fn accepts_first_responder(_this: &Object, _sel: Sel) -> BOOL {
|
||||
// This is necessary to prevent a beefy terminal error on MacBook Pros:
|
||||
// IMKInputSession [0x7fc573576ff0 presentFunctionRowItemTextInputViewWithEndpoint:completionHandler:] : [self textInputContext]=0x7fc573558e10 *NO* NSRemoteViewController to client, NSError=Error Domain=NSCocoaErrorDomain Code=4099 "The connection from pid 0 was invalidated from this process." UserInfo={NSDebugDescription=The connection from pid 0 was invalidated from this process.}, com.apple.inputmethod.EmojiFunctionRowItem
|
||||
// TODO: Add an API extension for using `NSTouchBar`
|
||||
extern "C" fn touch_bar(_this: &Object, _sel: Sel) -> BOOL {
|
||||
NO
|
||||
extern "C" fn touch_bar(_this: &Object, _sel: Sel) -> id {
|
||||
nil
|
||||
}
|
||||
|
||||
extern "C" fn reset_cursor_rects(this: &Object, _sel: Sel) {
|
||||
@@ -434,16 +390,16 @@ extern "C" fn reset_cursor_rects(this: &Object, _sel: Sel) {
|
||||
extern "C" fn has_marked_text(this: &Object, _sel: Sel) -> BOOL {
|
||||
unsafe {
|
||||
trace!("Triggered `hasMarkedText`");
|
||||
let marked_text: id = *this.get_ivar("markedText");
|
||||
let marked_text: &NSMutableAttributedString = *this.get_ivar("markedText");
|
||||
trace!("Completed `hasMarkedText`");
|
||||
(marked_text.length() > 0) as BOOL
|
||||
(marked_text.length() > 0).into()
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn marked_range(this: &Object, _sel: Sel) -> NSRange {
|
||||
unsafe {
|
||||
trace!("Triggered `markedRange`");
|
||||
let marked_text: id = *this.get_ivar("markedText");
|
||||
let marked_text: &NSMutableAttributedString = *this.get_ivar("markedText");
|
||||
let length = marked_text.length();
|
||||
trace!("Completed `markedRange`");
|
||||
if length > 0 {
|
||||
@@ -472,13 +428,21 @@ extern "C" fn set_marked_text(
|
||||
) {
|
||||
trace!("Triggered `setMarkedText`");
|
||||
unsafe {
|
||||
let marked_text_ref = clear_marked_text(this);
|
||||
let has_attr: BOOL = msg_send![string, isKindOfClass: class!(NSAttributedString)];
|
||||
if has_attr != NO {
|
||||
marked_text_ref.initWithAttributedString(string);
|
||||
let has_attr: bool = msg_send![string, isKindOfClass: class!(NSAttributedString)];
|
||||
let marked_text = if has_attr {
|
||||
NSMutableAttributedString::initWithAttributedString(
|
||||
NSMutableAttributedString::alloc(),
|
||||
&*(string as *const NSAttributedString),
|
||||
)
|
||||
} else {
|
||||
marked_text_ref.initWithString(string);
|
||||
NSMutableAttributedString::initWithString(
|
||||
NSMutableAttributedString::alloc(),
|
||||
&*(string as *const NSString),
|
||||
)
|
||||
};
|
||||
let marked_text_ref: &mut *mut NSMutableAttributedString = this.get_mut_ivar("markedText");
|
||||
let () = msg_send![(*marked_text_ref), release];
|
||||
*marked_text_ref = Retained::into_raw(marked_text);
|
||||
|
||||
let state_ptr: *mut c_void = *this.get_ivar("taoState");
|
||||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
@@ -491,7 +455,9 @@ extern "C" fn set_marked_text(
|
||||
extern "C" fn unmark_text(this: &mut Object, _sel: Sel) {
|
||||
trace!("Triggered `unmarkText`");
|
||||
unsafe {
|
||||
clear_marked_text(this);
|
||||
let marked_text_ref: &mut *mut NSMutableAttributedString = this.get_mut_ivar("markedText");
|
||||
let () = msg_send![(*marked_text_ref), release];
|
||||
*marked_text_ref = Retained::into_raw(NSMutableAttributedString::new());
|
||||
let input_context: id = msg_send![this, inputContext];
|
||||
let _: () = msg_send![input_context, discardMarkedText];
|
||||
}
|
||||
@@ -532,8 +498,10 @@ extern "C" fn first_rect_for_character_range(
|
||||
let state_ptr: *mut c_void = *this.get_ivar("taoState");
|
||||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
let (x, y) = state.ime_spot.unwrap_or_else(|| {
|
||||
let content_rect =
|
||||
NSWindow::contentRectForFrameRect_(state.ns_window, NSWindow::frame(state.ns_window));
|
||||
let content_rect = NSWindow::contentRectForFrameRect(
|
||||
&state.ns_window.load().unwrap(),
|
||||
NSWindow::frame(&state.ns_window.load().unwrap()),
|
||||
);
|
||||
let x = content_rect.origin.x;
|
||||
let y = util::bottom_left_to_top_left(content_rect);
|
||||
(x, y)
|
||||
@@ -543,14 +511,19 @@ extern "C" fn first_rect_for_character_range(
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_range: NSRange) {
|
||||
extern "C" fn insert_text(
|
||||
this: &Object,
|
||||
_sel: Sel,
|
||||
string: &NSString,
|
||||
_replacement_range: NSRange,
|
||||
) {
|
||||
trace!("Triggered `insertText`");
|
||||
unsafe {
|
||||
let state_ptr: *mut c_void = *this.get_ivar("taoState");
|
||||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
|
||||
let has_attr: BOOL = msg_send![string, isKindOfClass: class!(NSAttributedString)];
|
||||
let characters = if has_attr != NO {
|
||||
let has_attr: bool = msg_send![string, isKindOfClass: class!(NSAttributedString)];
|
||||
let characters = if has_attr {
|
||||
// This is a *mut NSAttributedString
|
||||
msg_send![string, string]
|
||||
} else {
|
||||
@@ -558,8 +531,8 @@ extern "C" fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_ran
|
||||
string
|
||||
};
|
||||
|
||||
let slice = slice::from_raw_parts(characters.UTF8String() as *const c_uchar, characters.len());
|
||||
let string: String = str::from_utf8_unchecked(slice)
|
||||
let string: String = characters
|
||||
.to_string()
|
||||
.chars()
|
||||
.filter(|c| !is_corporate_character(*c))
|
||||
.collect();
|
||||
@@ -569,7 +542,7 @@ extern "C" fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_ran
|
||||
//let event: id = msg_send![NSApp(), currentEvent];
|
||||
|
||||
AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(state.ns_window)),
|
||||
window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())),
|
||||
event: WindowEvent::ReceivedImeText(string),
|
||||
}));
|
||||
if state.in_ime_preedit {
|
||||
@@ -598,7 +571,7 @@ extern "C" fn do_command_by_selector(_this: &Object, _sel: Sel, _command: Sel) {
|
||||
// // 1) as a reminder for how `doCommandBySelector` works
|
||||
// // 2) to make our use of carriage return explicit
|
||||
// events.push_back(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
// window_id: WindowId(get_window_id(state.ns_window)),
|
||||
// window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())),
|
||||
// event: WindowEvent::ReceivedCharacter('\r'),
|
||||
// }));
|
||||
// } else {
|
||||
@@ -609,7 +582,7 @@ extern "C" fn do_command_by_selector(_this: &Object, _sel: Sel, _command: Sel) {
|
||||
// .filter(|c| !is_corporate_character(*c))
|
||||
// {
|
||||
// events.push_back(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
// window_id: WindowId(get_window_id(state.ns_window)),
|
||||
// window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())),
|
||||
// event: WindowEvent::ReceivedCharacter(character),
|
||||
// }));
|
||||
// }
|
||||
@@ -679,45 +652,46 @@ fn is_corporate_character(c: char) -> bool {
|
||||
// }
|
||||
|
||||
// Update `state.modifiers` if `event` has something different
|
||||
fn update_potentially_stale_modifiers(state: &mut ViewState, event: id) {
|
||||
fn update_potentially_stale_modifiers(state: &mut ViewState, event: &NSEvent) {
|
||||
let event_modifiers = event_mods(event);
|
||||
if state.modifiers != event_modifiers {
|
||||
state.modifiers = event_modifiers;
|
||||
|
||||
AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(state.ns_window)),
|
||||
window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())),
|
||||
event: WindowEvent::ModifiersChanged(state.modifiers),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn key_down(this: &mut Object, _sel: Sel, event: id) {
|
||||
extern "C" fn key_down(this: &mut Object, _sel: Sel, event: &NSEvent) {
|
||||
trace!("Triggered `keyDown`");
|
||||
unsafe {
|
||||
let state_ptr: *mut c_void = *this.get_ivar("taoState");
|
||||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
let window_id = WindowId(get_window_id(state.ns_window));
|
||||
let window_id = WindowId(get_window_id(&state.ns_window.load().unwrap()));
|
||||
|
||||
// keyboard refactor: don't seems to be needed anymore
|
||||
// let characters = get_characters(event, false);
|
||||
//state.raw_characters = Some(characters);
|
||||
|
||||
let is_repeat: BOOL = msg_send![event, isARepeat];
|
||||
let is_repeat = is_repeat == YES;
|
||||
let is_repeat: bool = msg_send![event, isARepeat];
|
||||
|
||||
update_potentially_stale_modifiers(state, event);
|
||||
|
||||
let pass_along = !is_repeat || !state.is_key_down;
|
||||
if pass_along {
|
||||
// See below for why we do this.
|
||||
clear_marked_text(this);
|
||||
let marked_text_ref: &mut *mut NSMutableAttributedString = this.get_mut_ivar("markedText");
|
||||
let () = msg_send![(*marked_text_ref), release];
|
||||
*marked_text_ref = Retained::into_raw(NSMutableAttributedString::new());
|
||||
state.key_triggered_ime = false;
|
||||
|
||||
// Some keys (and only *some*, with no known reason) don't trigger `insertText`, while others do...
|
||||
// So, we don't give repeats the opportunity to trigger that, since otherwise our hack will cause some
|
||||
// keys to generate twice as many characters.
|
||||
let array: id = msg_send![class!(NSArray), arrayWithObject: event];
|
||||
let () = msg_send![this, interpretKeyEvents: array];
|
||||
let () = msg_send![&*this, interpretKeyEvents: array];
|
||||
}
|
||||
// The `interpretKeyEvents` above, may invoke `set_marked_text` or `insert_text`,
|
||||
// if the event corresponds to an IME event.
|
||||
@@ -751,7 +725,7 @@ extern "C" fn key_down(this: &mut Object, _sel: Sel, event: id) {
|
||||
trace!("Completed `keyDown`");
|
||||
}
|
||||
|
||||
extern "C" fn key_up(this: &Object, _sel: Sel, event: id) {
|
||||
extern "C" fn key_up(this: &Object, _sel: Sel, event: &NSEvent) {
|
||||
trace!("Triggered `keyUp`");
|
||||
unsafe {
|
||||
let state_ptr: *mut c_void = *this.get_ivar("taoState");
|
||||
@@ -762,7 +736,7 @@ extern "C" fn key_up(this: &Object, _sel: Sel, event: id) {
|
||||
update_potentially_stale_modifiers(state, event);
|
||||
|
||||
let window_event = Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(state.ns_window)),
|
||||
window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())),
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
event: create_key_event(event, false, false, false, None),
|
||||
@@ -774,7 +748,7 @@ extern "C" fn key_up(this: &Object, _sel: Sel, event: id) {
|
||||
trace!("Completed `keyUp`");
|
||||
}
|
||||
|
||||
extern "C" fn flags_changed(this: &Object, _sel: Sel, ns_event: id) {
|
||||
extern "C" fn flags_changed(this: &Object, _sel: Sel, ns_event: &NSEvent) {
|
||||
use KeyCode::{
|
||||
AltLeft, AltRight, ControlLeft, ControlRight, ShiftLeft, ShiftRight, SuperLeft, SuperRight,
|
||||
};
|
||||
@@ -844,46 +818,46 @@ extern "C" fn flags_changed(this: &Object, _sel: Sel, ns_event: id) {
|
||||
}
|
||||
process_event!(
|
||||
ModifiersState::SHIFT,
|
||||
NSEventModifierFlags::NSShiftKeyMask,
|
||||
NSEventModifierFlags::NSEventModifierFlagShift,
|
||||
ShiftLeft
|
||||
);
|
||||
process_event!(
|
||||
ModifiersState::SHIFT,
|
||||
NSEventModifierFlags::NSShiftKeyMask,
|
||||
NSEventModifierFlags::NSEventModifierFlagShift,
|
||||
ShiftRight
|
||||
);
|
||||
process_event!(
|
||||
ModifiersState::CONTROL,
|
||||
NSEventModifierFlags::NSControlKeyMask,
|
||||
NSEventModifierFlags::NSEventModifierFlagControl,
|
||||
ControlLeft
|
||||
);
|
||||
process_event!(
|
||||
ModifiersState::CONTROL,
|
||||
NSEventModifierFlags::NSControlKeyMask,
|
||||
NSEventModifierFlags::NSEventModifierFlagControl,
|
||||
ControlRight
|
||||
);
|
||||
process_event!(
|
||||
ModifiersState::ALT,
|
||||
NSEventModifierFlags::NSAlternateKeyMask,
|
||||
NSEventModifierFlags::NSEventModifierFlagOption,
|
||||
AltLeft
|
||||
);
|
||||
process_event!(
|
||||
ModifiersState::ALT,
|
||||
NSEventModifierFlags::NSAlternateKeyMask,
|
||||
NSEventModifierFlags::NSEventModifierFlagOption,
|
||||
AltRight
|
||||
);
|
||||
process_event!(
|
||||
ModifiersState::SUPER,
|
||||
NSEventModifierFlags::NSCommandKeyMask,
|
||||
NSEventModifierFlags::NSEventModifierFlagCommand,
|
||||
SuperLeft
|
||||
);
|
||||
process_event!(
|
||||
ModifiersState::SUPER,
|
||||
NSEventModifierFlags::NSCommandKeyMask,
|
||||
NSEventModifierFlags::NSEventModifierFlagCommand,
|
||||
SuperRight
|
||||
);
|
||||
|
||||
let window_id = WindowId(get_window_id(state.ns_window));
|
||||
let window_id = WindowId(get_window_id(&state.ns_window.load().unwrap()));
|
||||
|
||||
for event in events {
|
||||
AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
@@ -927,21 +901,22 @@ extern "C" fn insert_back_tab(this: &Object, _sel: Sel, _sender: id) {
|
||||
extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
|
||||
trace!("Triggered `cancelOperation`");
|
||||
unsafe {
|
||||
let mtm = MainThreadMarker::new_unchecked();
|
||||
let state_ptr: *mut c_void = *this.get_ivar("taoState");
|
||||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
|
||||
let event: id = msg_send![NSApp(), currentEvent];
|
||||
update_potentially_stale_modifiers(state, event);
|
||||
let event: Retained<NSEvent> = msg_send_id![&NSApp(mtm), currentEvent];
|
||||
update_potentially_stale_modifiers(state, &event);
|
||||
|
||||
let scancode = 0x2f;
|
||||
let key = KeyCode::from_scancode(scancode);
|
||||
debug_assert_eq!(key, KeyCode::Period);
|
||||
|
||||
let window_event = Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(state.ns_window)),
|
||||
window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())),
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
event: create_key_event(event, true, false, false, Some(key)),
|
||||
event: create_key_event(&event, true, false, false, Some(key)),
|
||||
is_synthetic: false,
|
||||
},
|
||||
};
|
||||
@@ -950,7 +925,7 @@ extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
|
||||
trace!("Completed `cancelOperation`");
|
||||
}
|
||||
|
||||
fn mouse_click(this: &Object, event: id, button: MouseButton, button_state: ElementState) {
|
||||
fn mouse_click(this: &Object, event: &NSEvent, button: MouseButton, button_state: ElementState) {
|
||||
unsafe {
|
||||
let state_ptr: *mut c_void = *this.get_ivar("taoState");
|
||||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
@@ -958,7 +933,7 @@ fn mouse_click(this: &Object, event: id, button: MouseButton, button_state: Elem
|
||||
update_potentially_stale_modifiers(state, event);
|
||||
|
||||
let window_event = Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(state.ns_window)),
|
||||
window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())),
|
||||
event: WindowEvent::MouseInput {
|
||||
device_id: DEVICE_ID,
|
||||
state: button_state,
|
||||
@@ -971,47 +946,44 @@ fn mouse_click(this: &Object, event: id, button: MouseButton, button_state: Elem
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn mouse_down(this: &Object, _sel: Sel, event: id) {
|
||||
extern "C" fn mouse_down(this: &NSView, _sel: Sel, event: &NSEvent) {
|
||||
mouse_motion(this, event);
|
||||
mouse_click(this, event, MouseButton::Left, ElementState::Pressed);
|
||||
}
|
||||
|
||||
extern "C" fn mouse_up(this: &Object, _sel: Sel, event: id) {
|
||||
extern "C" fn mouse_up(this: &NSView, _sel: Sel, event: &NSEvent) {
|
||||
mouse_motion(this, event);
|
||||
mouse_click(this, event, MouseButton::Left, ElementState::Released);
|
||||
}
|
||||
|
||||
extern "C" fn right_mouse_down(this: &Object, _sel: Sel, event: id) {
|
||||
extern "C" fn right_mouse_down(this: &NSView, _sel: Sel, event: &NSEvent) {
|
||||
mouse_motion(this, event);
|
||||
mouse_click(this, event, MouseButton::Right, ElementState::Pressed);
|
||||
}
|
||||
|
||||
extern "C" fn right_mouse_up(this: &Object, _sel: Sel, event: id) {
|
||||
extern "C" fn right_mouse_up(this: &NSView, _sel: Sel, event: &NSEvent) {
|
||||
mouse_motion(this, event);
|
||||
mouse_click(this, event, MouseButton::Right, ElementState::Released);
|
||||
}
|
||||
|
||||
extern "C" fn other_mouse_down(this: &Object, _sel: Sel, event: id) {
|
||||
extern "C" fn other_mouse_down(this: &NSView, _sel: Sel, event: &NSEvent) {
|
||||
mouse_motion(this, event);
|
||||
mouse_click(this, event, MouseButton::Middle, ElementState::Pressed);
|
||||
}
|
||||
|
||||
extern "C" fn other_mouse_up(this: &Object, _sel: Sel, event: id) {
|
||||
extern "C" fn other_mouse_up(this: &NSView, _sel: Sel, event: &NSEvent) {
|
||||
mouse_motion(this, event);
|
||||
mouse_click(this, event, MouseButton::Middle, ElementState::Released);
|
||||
}
|
||||
|
||||
fn mouse_motion(this: &Object, event: id) {
|
||||
fn mouse_motion(this: &NSView, event: &NSEvent) {
|
||||
unsafe {
|
||||
let state_ptr: *mut c_void = *this.get_ivar("taoState");
|
||||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
|
||||
// We have to do this to have access to the `NSView` trait...
|
||||
let view: id = this as *const _ as *mut _;
|
||||
|
||||
let window_point = event.locationInWindow();
|
||||
let view_point = view.convertPoint_fromView_(window_point, nil);
|
||||
let view_rect = NSView::frame(view);
|
||||
let view_point = this.convertPoint_fromView(window_point, None);
|
||||
let view_rect = NSView::frame(this);
|
||||
|
||||
if view_point.x.is_sign_negative()
|
||||
|| view_point.y.is_sign_negative()
|
||||
@@ -1032,7 +1004,7 @@ fn mouse_motion(this: &Object, event: id) {
|
||||
update_potentially_stale_modifiers(state, event);
|
||||
|
||||
let window_event = Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(state.ns_window)),
|
||||
window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())),
|
||||
event: WindowEvent::CursorMoved {
|
||||
device_id: DEVICE_ID,
|
||||
position: logical_position.to_physical(state.get_scale_factor()),
|
||||
@@ -1044,19 +1016,19 @@ fn mouse_motion(this: &Object, event: id) {
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn mouse_moved(this: &Object, _sel: Sel, event: id) {
|
||||
extern "C" fn mouse_moved(this: &NSView, _sel: Sel, event: &NSEvent) {
|
||||
mouse_motion(this, event);
|
||||
}
|
||||
|
||||
extern "C" fn mouse_dragged(this: &Object, _sel: Sel, event: id) {
|
||||
extern "C" fn mouse_dragged(this: &NSView, _sel: Sel, event: &NSEvent) {
|
||||
mouse_motion(this, event);
|
||||
}
|
||||
|
||||
extern "C" fn right_mouse_dragged(this: &Object, _sel: Sel, event: id) {
|
||||
extern "C" fn right_mouse_dragged(this: &NSView, _sel: Sel, event: &NSEvent) {
|
||||
mouse_motion(this, event);
|
||||
}
|
||||
|
||||
extern "C" fn other_mouse_dragged(this: &Object, _sel: Sel, event: id) {
|
||||
extern "C" fn other_mouse_dragged(this: &NSView, _sel: Sel, event: &NSEvent) {
|
||||
mouse_motion(this, event);
|
||||
}
|
||||
|
||||
@@ -1067,7 +1039,7 @@ extern "C" fn mouse_entered(this: &Object, _sel: Sel, _event: id) {
|
||||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
|
||||
let enter_event = Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(state.ns_window)),
|
||||
window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())),
|
||||
event: WindowEvent::CursorEntered {
|
||||
device_id: DEVICE_ID,
|
||||
},
|
||||
@@ -1085,7 +1057,7 @@ extern "C" fn mouse_exited(this: &Object, _sel: Sel, _event: id) {
|
||||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
|
||||
let window_event = Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(state.ns_window)),
|
||||
window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())),
|
||||
event: WindowEvent::CursorLeft {
|
||||
device_id: DEVICE_ID,
|
||||
},
|
||||
@@ -1096,7 +1068,7 @@ extern "C" fn mouse_exited(this: &Object, _sel: Sel, _event: id) {
|
||||
trace!("Completed `mouseExited`");
|
||||
}
|
||||
|
||||
extern "C" fn scroll_wheel(this: &Object, _sel: Sel, event: id) {
|
||||
extern "C" fn scroll_wheel(this: &NSView, _sel: Sel, event: &NSEvent) {
|
||||
trace!("Triggered `scrollWheel`");
|
||||
|
||||
mouse_motion(this, event);
|
||||
@@ -1108,7 +1080,7 @@ extern "C" fn scroll_wheel(this: &Object, _sel: Sel, event: id) {
|
||||
let delta = {
|
||||
// macOS horizontal sign convention is the inverse of tao.
|
||||
let (x, y) = (event.scrollingDeltaX() * -1.0, event.scrollingDeltaY());
|
||||
if event.hasPreciseScrollingDeltas() == YES {
|
||||
if event.hasPreciseScrollingDeltas() {
|
||||
let delta = LogicalPosition::new(x, y).to_physical(state.get_scale_factor());
|
||||
MouseScrollDelta::PixelDelta(delta)
|
||||
} else {
|
||||
@@ -1116,8 +1088,8 @@ extern "C" fn scroll_wheel(this: &Object, _sel: Sel, event: id) {
|
||||
}
|
||||
};
|
||||
let phase = match event.phase() {
|
||||
NSEventPhase::NSEventPhaseMayBegin | NSEventPhase::NSEventPhaseBegan => TouchPhase::Started,
|
||||
NSEventPhase::NSEventPhaseEnded => TouchPhase::Ended,
|
||||
NSEventPhase::MayBegin | NSEventPhase::Began => TouchPhase::Started,
|
||||
NSEventPhase::Ended => TouchPhase::Ended,
|
||||
_ => TouchPhase::Moved,
|
||||
};
|
||||
|
||||
@@ -1132,7 +1104,7 @@ extern "C" fn scroll_wheel(this: &Object, _sel: Sel, event: id) {
|
||||
update_potentially_stale_modifiers(state, event);
|
||||
|
||||
let window_event = Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(state.ns_window)),
|
||||
window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())),
|
||||
event: WindowEvent::MouseWheel {
|
||||
device_id: DEVICE_ID,
|
||||
delta,
|
||||
@@ -1147,7 +1119,7 @@ extern "C" fn scroll_wheel(this: &Object, _sel: Sel, event: id) {
|
||||
trace!("Completed `scrollWheel`");
|
||||
}
|
||||
|
||||
extern "C" fn pressure_change_with_event(this: &Object, _sel: Sel, event: id) {
|
||||
extern "C" fn pressure_change_with_event(this: &NSView, _sel: Sel, event: &NSEvent) {
|
||||
trace!("Triggered `pressureChangeWithEvent`");
|
||||
|
||||
mouse_motion(this, event);
|
||||
@@ -1160,11 +1132,11 @@ extern "C" fn pressure_change_with_event(this: &Object, _sel: Sel, event: id) {
|
||||
let stage = event.stage();
|
||||
|
||||
let window_event = Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(state.ns_window)),
|
||||
window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())),
|
||||
event: WindowEvent::TouchpadPressure {
|
||||
device_id: DEVICE_ID,
|
||||
pressure,
|
||||
stage,
|
||||
stage: stage as i64,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1184,27 +1156,33 @@ extern "C" fn accepts_first_mouse(_this: &Object, _sel: Sel, _event: id) -> BOOL
|
||||
YES
|
||||
}
|
||||
|
||||
pub unsafe fn inset_traffic_lights<W: NSWindow + Copy>(window: W, position: LogicalPosition<f64>) {
|
||||
pub unsafe fn inset_traffic_lights(window: &NSWindow, position: LogicalPosition<f64>) {
|
||||
let (x, y) = (position.x, position.y);
|
||||
|
||||
let close = window.standardWindowButton_(NSWindowButton::NSWindowCloseButton);
|
||||
let miniaturize = window.standardWindowButton_(NSWindowButton::NSWindowMiniaturizeButton);
|
||||
let zoom = window.standardWindowButton_(NSWindowButton::NSWindowZoomButton);
|
||||
let close = window
|
||||
.standardWindowButton(NSWindowButton::NSWindowCloseButton)
|
||||
.unwrap();
|
||||
let miniaturize = window
|
||||
.standardWindowButton(NSWindowButton::NSWindowMiniaturizeButton)
|
||||
.unwrap();
|
||||
let zoom = window
|
||||
.standardWindowButton(NSWindowButton::NSWindowZoomButton)
|
||||
.unwrap();
|
||||
|
||||
let title_bar_container_view = close.superview().superview();
|
||||
let title_bar_container_view = close.superview().unwrap().superview().unwrap();
|
||||
|
||||
let close_rect = NSView::frame(close);
|
||||
let close_rect = NSView::frame(&close);
|
||||
let title_bar_frame_height = close_rect.size.height + y;
|
||||
let mut title_bar_rect = NSView::frame(title_bar_container_view);
|
||||
let mut title_bar_rect = NSView::frame(&title_bar_container_view);
|
||||
title_bar_rect.size.height = title_bar_frame_height;
|
||||
title_bar_rect.origin.y = window.frame().size.height - title_bar_frame_height;
|
||||
let _: () = msg_send![title_bar_container_view, setFrame: title_bar_rect];
|
||||
let _: () = msg_send![&title_bar_container_view, setFrame: title_bar_rect];
|
||||
|
||||
let window_buttons = vec![close, miniaturize, zoom];
|
||||
let space_between = NSView::frame(miniaturize).origin.x - close_rect.origin.x;
|
||||
let window_buttons = vec![close, miniaturize.clone(), zoom];
|
||||
let space_between = NSView::frame(&miniaturize).origin.x - close_rect.origin.x;
|
||||
|
||||
for (i, button) in window_buttons.into_iter().enumerate() {
|
||||
let mut rect = NSView::frame(button);
|
||||
let mut rect = NSView::frame(&button);
|
||||
rect.origin.x = x + (i as f64 * space_between);
|
||||
button.setFrameOrigin(rect.origin);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,19 +4,20 @@
|
||||
|
||||
use std::{
|
||||
f64,
|
||||
ffi::CStr,
|
||||
os::raw::c_void,
|
||||
sync::{Arc, Weak},
|
||||
};
|
||||
|
||||
use cocoa::{
|
||||
appkit::{self, NSApplicationPresentationOptions, NSView, NSWindow},
|
||||
base::{id, nil},
|
||||
foundation::{NSAutoreleasePool, NSString, NSUInteger},
|
||||
use objc2::{
|
||||
msg_send_id,
|
||||
rc::Retained,
|
||||
runtime::{AnyClass as Class, AnyObject as Object, ClassBuilder as ClassDecl, Sel},
|
||||
};
|
||||
use objc::{
|
||||
declare::ClassDecl,
|
||||
runtime::{Class, Object, Sel, BOOL, NO, YES},
|
||||
use objc2_app_kit::{
|
||||
self as appkit, NSApplicationPresentationOptions, NSPasteboard, NSView, NSWindow,
|
||||
};
|
||||
use objc2_foundation::{NSArray, NSAutoreleasePool, NSString, NSUInteger};
|
||||
|
||||
use crate::{
|
||||
dpi::{LogicalPosition, LogicalSize},
|
||||
@@ -25,6 +26,7 @@ use crate::{
|
||||
platform_impl::platform::{
|
||||
app_state::AppState,
|
||||
event::{EventProxy, EventWrapper},
|
||||
ffi::{id, nil, BOOL, NO, YES},
|
||||
util::{self, IdRef},
|
||||
view::ViewState,
|
||||
window::{get_ns_theme, get_window_id, UnownedWindow},
|
||||
@@ -33,10 +35,10 @@ use crate::{
|
||||
};
|
||||
|
||||
pub struct WindowDelegateState {
|
||||
ns_window: IdRef, // never changes
|
||||
ns_window: Retained<NSWindow>, // never changes
|
||||
// We keep this ns_view because we still need its view state for some extern function
|
||||
// like didResignKey
|
||||
ns_view: IdRef, // never changes
|
||||
ns_view: Retained<NSView>, // never changes
|
||||
|
||||
window: Weak<UnownedWindow>,
|
||||
|
||||
@@ -77,8 +79,8 @@ impl WindowDelegateState {
|
||||
delegate_state
|
||||
}
|
||||
|
||||
fn ns_view(&self) -> id {
|
||||
unsafe { (*self.ns_window).contentView() }
|
||||
fn ns_view(&self) -> Retained<NSView> {
|
||||
self.ns_window.contentView().unwrap()
|
||||
}
|
||||
|
||||
fn with_window<F, T>(&mut self, callback: F) -> Option<T>
|
||||
@@ -90,7 +92,7 @@ impl WindowDelegateState {
|
||||
|
||||
pub fn emit_event(&mut self, event: WindowEvent<'static>) {
|
||||
let event = Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(*self.ns_window)),
|
||||
window_id: WindowId(get_window_id(&self.ns_window)),
|
||||
event,
|
||||
};
|
||||
AppState::queue_event(EventWrapper::StaticEvent(event));
|
||||
@@ -104,7 +106,7 @@ impl WindowDelegateState {
|
||||
|
||||
self.previous_scale_factor = scale_factor;
|
||||
let wrapper = EventWrapper::EventProxy(EventProxy::DpiChangedProxy {
|
||||
ns_window: IdRef::retain(*self.ns_window),
|
||||
ns_window: self.ns_window.clone(),
|
||||
suggested_size: self.view_size(),
|
||||
scale_factor,
|
||||
});
|
||||
@@ -112,7 +114,7 @@ impl WindowDelegateState {
|
||||
}
|
||||
|
||||
pub fn emit_resize_event(&mut self) {
|
||||
let rect = unsafe { NSView::frame(self.ns_view()) };
|
||||
let rect = NSView::frame(&self.ns_view());
|
||||
let scale_factor = self.get_scale_factor();
|
||||
let logical_size = LogicalSize::new(rect.size.width as f64, rect.size.height as f64);
|
||||
let size = logical_size.to_physical(scale_factor);
|
||||
@@ -120,7 +122,7 @@ impl WindowDelegateState {
|
||||
}
|
||||
|
||||
fn emit_move_event(&mut self) {
|
||||
let rect = unsafe { NSWindow::frame(*self.ns_window) };
|
||||
let rect = NSWindow::frame(&self.ns_window);
|
||||
let x = rect.origin.x as f64;
|
||||
let y = util::bottom_left_to_top_left(rect);
|
||||
let moved = self.previous_position != Some((x, y));
|
||||
@@ -133,11 +135,11 @@ impl WindowDelegateState {
|
||||
}
|
||||
|
||||
fn get_scale_factor(&self) -> f64 {
|
||||
(unsafe { NSWindow::backingScaleFactor(*self.ns_window) }) as f64
|
||||
NSWindow::backingScaleFactor(&self.ns_window) as f64
|
||||
}
|
||||
|
||||
fn view_size(&self) -> LogicalSize<f64> {
|
||||
let ns_size = unsafe { NSView::frame(self.ns_view()).size };
|
||||
let ns_size = NSView::frame(&self.ns_view()).size;
|
||||
LogicalSize::new(ns_size.width as f64, ns_size.height as f64)
|
||||
}
|
||||
}
|
||||
@@ -161,102 +163,101 @@ lazy_static! {
|
||||
let superclass = class!(NSResponder);
|
||||
let mut decl = ClassDecl::new("TaoWindowDelegate", superclass).unwrap();
|
||||
|
||||
decl.add_method(sel!(dealloc), dealloc as extern "C" fn(&Object, Sel));
|
||||
decl.add_method(sel!(dealloc), dealloc as extern "C" fn(_, _));
|
||||
decl.add_method(
|
||||
sel!(initWithTao:),
|
||||
init_with_tao as extern "C" fn(&Object, Sel, *mut c_void) -> id,
|
||||
init_with_tao as extern "C" fn(_, _, _) -> _,
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(markIsCheckingZoomedIn),
|
||||
mark_is_checking_zoomed_in as extern "C" fn(&Object, Sel),
|
||||
mark_is_checking_zoomed_in as extern "C" fn(_, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(clearIsCheckingZoomedIn),
|
||||
clear_is_checking_zoomed_in as extern "C" fn(&Object, Sel),
|
||||
clear_is_checking_zoomed_in as extern "C" fn(_, _),
|
||||
);
|
||||
|
||||
decl.add_method(
|
||||
sel!(windowShouldClose:),
|
||||
window_should_close as extern "C" fn(&Object, Sel, id) -> BOOL,
|
||||
window_should_close as extern "C" fn(_, _, _) -> _,
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowWillClose:),
|
||||
window_will_close as extern "C" fn(&Object, Sel, id),
|
||||
window_will_close as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowDidResize:),
|
||||
window_did_resize as extern "C" fn(&Object, Sel, id),
|
||||
window_did_resize as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowDidMove:),
|
||||
window_did_move as extern "C" fn(&Object, Sel, id),
|
||||
window_did_move as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowDidChangeBackingProperties:),
|
||||
window_did_change_backing_properties as extern "C" fn(&Object, Sel, id),
|
||||
window_did_change_backing_properties as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowDidBecomeKey:),
|
||||
window_did_become_key as extern "C" fn(&Object, Sel, id),
|
||||
window_did_become_key as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowDidResignKey:),
|
||||
window_did_resign_key as extern "C" fn(&Object, Sel, id),
|
||||
window_did_resign_key as extern "C" fn(_, _, _),
|
||||
);
|
||||
|
||||
decl.add_method(
|
||||
sel!(draggingEntered:),
|
||||
dragging_entered as extern "C" fn(&Object, Sel, id) -> BOOL,
|
||||
dragging_entered as extern "C" fn(_, _, _) -> _,
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(prepareForDragOperation:),
|
||||
prepare_for_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL,
|
||||
prepare_for_drag_operation as extern "C" fn(_, _, _) -> _,
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(performDragOperation:),
|
||||
perform_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL,
|
||||
perform_drag_operation as extern "C" fn(_, _, _) -> _,
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(concludeDragOperation:),
|
||||
conclude_drag_operation as extern "C" fn(&Object, Sel, id),
|
||||
conclude_drag_operation as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(draggingExited:),
|
||||
dragging_exited as extern "C" fn(&Object, Sel, id),
|
||||
dragging_exited as extern "C" fn(_, _, _),
|
||||
);
|
||||
|
||||
decl.add_method(
|
||||
sel!(window:willUseFullScreenPresentationOptions:),
|
||||
window_will_use_fullscreen_presentation_options
|
||||
as extern "C" fn(&Object, Sel, id, NSUInteger) -> NSUInteger,
|
||||
window_will_use_fullscreen_presentation_options as extern "C" fn(_, _, _, _) -> _,
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowDidEnterFullScreen:),
|
||||
window_did_enter_fullscreen as extern "C" fn(&Object, Sel, id),
|
||||
window_did_enter_fullscreen as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowWillEnterFullScreen:),
|
||||
window_will_enter_fullscreen as extern "C" fn(&Object, Sel, id),
|
||||
window_will_enter_fullscreen as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowDidExitFullScreen:),
|
||||
window_did_exit_fullscreen as extern "C" fn(&Object, Sel, id),
|
||||
window_did_exit_fullscreen as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowWillExitFullScreen:),
|
||||
window_will_exit_fullscreen as extern "C" fn(&Object, Sel, id),
|
||||
window_will_exit_fullscreen as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowDidFailToEnterFullScreen:),
|
||||
window_did_fail_to_enter_fullscreen as extern "C" fn(&Object, Sel, id),
|
||||
window_did_fail_to_enter_fullscreen as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(effectiveAppearanceDidChange:),
|
||||
effective_appearance_did_change as extern "C" fn(&Object, Sel, id),
|
||||
effective_appearance_did_change as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(effectiveAppearanceDidChangedOnMainThread:),
|
||||
effective_appearance_did_changed_on_main_thread as extern "C" fn(&Object, Sel, id),
|
||||
effective_appearance_did_changed_on_main_thread as extern "C" fn(_, _, _),
|
||||
);
|
||||
|
||||
decl.add_ivar::<*mut c_void>("taoState");
|
||||
@@ -267,6 +268,7 @@ lazy_static! {
|
||||
// This function is definitely unsafe, but labeling that would increase
|
||||
// boilerplate and wouldn't really clarify anything...
|
||||
fn with_state<F: FnOnce(&mut WindowDelegateState) -> T, T>(this: &Object, callback: F) {
|
||||
#[allow(deprecated)] // TODO: Use define_class!
|
||||
let state_ptr = unsafe {
|
||||
let state_ptr: *mut c_void = *this.get_ivar("taoState");
|
||||
&mut *(state_ptr as *mut WindowDelegateState)
|
||||
@@ -281,23 +283,24 @@ extern "C" fn dealloc(this: &Object, _sel: Sel) {
|
||||
}
|
||||
|
||||
extern "C" fn init_with_tao(this: &Object, _sel: Sel, state: *mut c_void) -> id {
|
||||
#[allow(deprecated)] // TODO: Use define_class!
|
||||
unsafe {
|
||||
let this: id = msg_send![this, init];
|
||||
if this != nil {
|
||||
(*this).set_ivar("taoState", state);
|
||||
*(*this).get_mut_ivar("taoState") = state;
|
||||
with_state(&*this, |state| {
|
||||
let () = msg_send![*state.ns_window, setDelegate: this];
|
||||
let () = msg_send![&state.ns_window, setDelegate: this];
|
||||
});
|
||||
}
|
||||
|
||||
let notification_center: &Object =
|
||||
msg_send![class!(NSDistributedNotificationCenter), defaultCenter];
|
||||
let notification_name = NSString::alloc(nil).init_str("AppleInterfaceThemeChangedNotification");
|
||||
let notification_name = NSString::from_str("AppleInterfaceThemeChangedNotification");
|
||||
let _: () = msg_send![
|
||||
notification_center,
|
||||
addObserver: this
|
||||
selector: sel!(effectiveAppearanceDidChange:)
|
||||
name: notification_name
|
||||
name: &*notification_name
|
||||
object: nil
|
||||
];
|
||||
|
||||
@@ -328,11 +331,10 @@ extern "C" fn window_will_close(this: &Object, _: Sel, _: id) {
|
||||
trace!("Triggered `windowWillClose:`");
|
||||
with_state(this, |state| unsafe {
|
||||
// `setDelegate:` retains the previous value and then autoreleases it
|
||||
let pool = NSAutoreleasePool::new(nil);
|
||||
let _pool = NSAutoreleasePool::new();
|
||||
// Since El Capitan, we need to be careful that delegate methods can't
|
||||
// be called after the window closes.
|
||||
let () = msg_send![*state.ns_window, setDelegate: nil];
|
||||
pool.drain();
|
||||
let () = msg_send![&state.ns_window, setDelegate: nil];
|
||||
state.emit_event(WindowEvent::Destroyed);
|
||||
});
|
||||
trace!("Completed `windowWillClose:`");
|
||||
@@ -390,8 +392,9 @@ extern "C" fn window_did_resign_key(this: &Object, _: Sel, _: id) {
|
||||
// Here we (very unsafely) acquire the taoState (a ViewState) from the
|
||||
// Object referenced by state.ns_view (an IdRef, which is dereferenced
|
||||
// to an id)
|
||||
#[allow(deprecated)] // TODO: Use define_class!
|
||||
let view_state: &mut ViewState = unsafe {
|
||||
let ns_view: &Object = (*state.ns_view).as_ref().expect("failed to deref");
|
||||
let ns_view: &Object = &state.ns_view;
|
||||
let state_ptr: *mut c_void = *ns_view.get_ivar("taoState");
|
||||
&mut *(state_ptr as *mut ViewState)
|
||||
};
|
||||
@@ -411,17 +414,17 @@ extern "C" fn window_did_resign_key(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn dragging_entered(this: &Object, _: Sel, sender: id) -> BOOL {
|
||||
trace!("Triggered `draggingEntered:`");
|
||||
|
||||
use cocoa::{appkit::NSPasteboard, foundation::NSFastEnumeration};
|
||||
use std::path::PathBuf;
|
||||
|
||||
let pb: id = unsafe { msg_send![sender, draggingPasteboard] };
|
||||
let filenames = unsafe { NSPasteboard::propertyListForType(pb, appkit::NSFilenamesPboardType) };
|
||||
let pb: Retained<NSPasteboard> = unsafe { msg_send_id![sender, draggingPasteboard] };
|
||||
let filenames =
|
||||
unsafe { NSPasteboard::propertyListForType(&pb, appkit::NSFilenamesPboardType) }.unwrap();
|
||||
|
||||
for file in unsafe { filenames.iter() } {
|
||||
use std::ffi::CStr;
|
||||
for file in unsafe { Retained::cast::<NSArray>(filenames) } {
|
||||
let file = unsafe { Retained::cast::<NSString>(file) };
|
||||
|
||||
unsafe {
|
||||
let f = NSString::UTF8String(file);
|
||||
let f = NSString::UTF8String(&file);
|
||||
let path = CStr::from_ptr(f).to_string_lossy().into_owned();
|
||||
|
||||
with_state(this, |state| {
|
||||
@@ -445,17 +448,17 @@ extern "C" fn prepare_for_drag_operation(_: &Object, _: Sel, _: id) -> BOOL {
|
||||
extern "C" fn perform_drag_operation(this: &Object, _: Sel, sender: id) -> BOOL {
|
||||
trace!("Triggered `performDragOperation:`");
|
||||
|
||||
use cocoa::{appkit::NSPasteboard, foundation::NSFastEnumeration};
|
||||
use std::path::PathBuf;
|
||||
|
||||
let pb: id = unsafe { msg_send![sender, draggingPasteboard] };
|
||||
let filenames = unsafe { NSPasteboard::propertyListForType(pb, appkit::NSFilenamesPboardType) };
|
||||
let pb: Retained<NSPasteboard> = unsafe { msg_send_id![sender, draggingPasteboard] };
|
||||
let filenames =
|
||||
unsafe { NSPasteboard::propertyListForType(&pb, appkit::NSFilenamesPboardType) }.unwrap();
|
||||
|
||||
for file in unsafe { filenames.iter() } {
|
||||
use std::ffi::CStr;
|
||||
for file in unsafe { Retained::cast::<NSArray>(filenames) } {
|
||||
let file = unsafe { Retained::cast::<NSString>(file) };
|
||||
|
||||
unsafe {
|
||||
let f = NSString::UTF8String(file);
|
||||
let f = NSString::UTF8String(&file);
|
||||
let path = CStr::from_ptr(f).to_string_lossy().into_owned();
|
||||
|
||||
with_state(this, |state| {
|
||||
@@ -634,7 +637,8 @@ extern "C" fn window_did_fail_to_enter_fullscreen(this: &Object, _: Sel, _: id)
|
||||
});
|
||||
if state.initial_fullscreen {
|
||||
let _: () = unsafe {
|
||||
msg_send![*state.ns_window,
|
||||
msg_send![
|
||||
&state.ns_window,
|
||||
performSelector:sel!(toggleFullScreen:)
|
||||
withObject:nil
|
||||
afterDelay: 0.5
|
||||
|
||||
Reference in New Issue
Block a user