More work on Application methods

This commit is contained in:
Ryan McGrath 2020-03-20 13:12:01 -07:00
parent f45c86743b
commit e50bb25e9f
No known key found for this signature in database
GPG key ID: 811674B62B666830
2 changed files with 102 additions and 24 deletions

View file

@ -29,3 +29,30 @@ impl From<TerminateResponse> for NSUInteger {
} }
} }
} }
/// Used for responding to open/print/copy requests.
/// You only really need this for calling `App::reply_to_open_or_print()`.
/// The name is unfortunate, but it covers a variety of things, and by keeping it closer to the
/// `NSApplication` documentation it may help some poor soul who needs to find information about
/// it.
#[derive(Copy, Clone, Debug)]
pub enum AppDelegateResponse {
/// Cancelled.
Cancelled,
/// Success.
Success,
/// Failed.
Failure
}
impl From<AppDelegateResponse> for NSUInteger {
fn from(response: AppDelegateResponse) -> Self {
match response {
AppDelegateResponse::Cancelled => 1,
AppDelegateResponse::Success => 0,
AppDelegateResponse::Failure => 2
}
}
}

View file

@ -5,7 +5,7 @@ use objc_id::Id;
use objc::runtime::Object; use objc::runtime::Object;
use objc::{class, msg_send, sel, sel_impl}; use objc::{class, msg_send, sel, sel_impl};
use crate::foundation::{id, AutoReleasePool}; use crate::foundation::{id, YES, NO, NSUInteger, AutoReleasePool};
use crate::constants::APP_PTR; use crate::constants::APP_PTR;
use crate::menu::Menu; use crate::menu::Menu;
@ -16,10 +16,18 @@ mod delegate;
use delegate::register_app_delegate_class; use delegate::register_app_delegate_class;
pub mod enums; pub mod enums;
pub use enums::AppDelegateResponse;
pub mod traits; pub mod traits;
pub use traits::{AppDelegate, Dispatcher}; pub use traits::{AppDelegate, Dispatcher};
/// A handler to make some boilerplate less annoying.
#[inline]
fn shared_application<F: Fn(id)>(handler: F) {
let app: id = unsafe { msg_send![register_app_class(), sharedApplication] };
handler(app);
}
/// A wrapper for `NSApplication`. It holds (retains) pointers for the Objective-C runtime, /// A wrapper for `NSApplication`. It holds (retains) pointers for the Objective-C runtime,
/// which is where our application instance lives. It also injects an `NSObject` subclass, /// which is where our application instance lives. It also injects an `NSObject` subclass,
/// which acts as the Delegate, looping back into our Vaulthund shared application. /// which acts as the Delegate, looping back into our Vaulthund shared application.
@ -31,29 +39,6 @@ pub struct App<T = (), M = ()> {
_t: std::marker::PhantomData<M> _t: std::marker::PhantomData<M>
} }
impl App {
/// Sets a set of `Menu`'s as the top level Menu for the current application. Note that behind
/// the scenes, Cocoa/AppKit make a copy of the menu you pass in - so we don't retain it, and
/// you shouldn't bother to either.
pub fn set_menu(menus: Vec<Menu>) {
unsafe {
let menu_cls = class!(NSMenu);
let main_menu: id = msg_send![menu_cls, new];
let item_cls = class!(NSMenuItem);
for menu in menus.iter() {
let item: id = msg_send![item_cls, new];
let _: () = msg_send![item, setSubmenu:&*menu.inner];
let _: () = msg_send![main_menu, addItem:item];
}
let cls = class!(RSTApplication);
let shared_app: id = msg_send![cls, sharedApplication];
let _: () = msg_send![shared_app, setMainMenu:main_menu];
}
}
}
impl<T> App<T> where T: AppDelegate + 'static { impl<T> App<T> where T: AppDelegate + 'static {
/// Creates an NSAutoReleasePool, configures various NSApplication properties (e.g, activation /// Creates an NSAutoReleasePool, configures various NSApplication properties (e.g, activation
/// policies), injects an `NSObject` delegate wrapper, and retains everything on the /// policies), injects an `NSObject` delegate wrapper, and retains everything on the
@ -103,6 +88,7 @@ impl<T> App<T> where T: AppDelegate + 'static {
} }
} }
// This is a hack and should be replaced with an actual messaging pipeline at some point. :)
impl<T, M> App<T, M> where M: Send + Sync + 'static, T: AppDelegate + Dispatcher<Message = M> { impl<T, M> App<T, M> where M: Send + Sync + 'static, T: AppDelegate + Dispatcher<Message = M> {
/// Dispatches a message by grabbing the `sharedApplication`, getting ahold of the delegate, /// Dispatches a message by grabbing the `sharedApplication`, getting ahold of the delegate,
/// and passing back through there. All messages are currently dispatched on the main thread. /// and passing back through there. All messages are currently dispatched on the main thread.
@ -118,3 +104,68 @@ impl<T, M> App<T, M> where M: Send + Sync + 'static, T: AppDelegate + Dispatcher
}); });
} }
} }
impl App {
/// Registers for remote notifications from APNS.
pub fn register_for_remote_notifications() {
shared_application(|app| unsafe {
let _: () = msg_send![app, registerForRemoteNotifications];
});
}
/// Unregisters for remote notifications from APNS.
pub fn unregister_for_remote_notifications() {
shared_application(|app| unsafe {
let _: () = msg_send![app, unregisterForRemoteNotifications];
});
}
/// Sets whether this application should relaunch at login.
pub fn set_relaunch_on_login(relaunch: bool) {
shared_application(|app| unsafe {
if relaunch {
let _: () = msg_send![app, enableRelaunchOnLogin];
} else {
let _: () = msg_send![app, disableRelaunchOnLogin];
}
});
}
/// Respond to a termination request. Generally done after returning `TerminateResponse::Later`
/// from your trait implementation of `should_terminate()`.
pub fn reply_to_termination_request(should_terminate: bool) {
shared_application(|app| unsafe {
let _: () = msg_send![app, replyToApplicationShouldTerminate:match should_terminate {
true => YES,
false => NO
}];
});
}
/// An optional call that you can use for certain scenarios surrounding opening/printing files.
pub fn reply_to_open_or_print(response: AppDelegateResponse) {
shared_application(|app| unsafe {
let r: NSUInteger = response.into();
let _: () = msg_send![app, replyToOpenOrPrint:r];
});
}
/// Sets a set of `Menu`'s as the top level Menu for the current application. Note that behind
/// the scenes, Cocoa/AppKit make a copy of the menu you pass in - so we don't retain it, and
/// you shouldn't bother to either.
pub fn set_menu(menus: Vec<Menu>) {
shared_application(|app| unsafe {
let menu_cls = class!(NSMenu);
let main_menu: id = msg_send![menu_cls, new];
let item_cls = class!(NSMenuItem);
for menu in menus.iter() {
let item: id = msg_send![item_cls, new];
let _: () = msg_send![item, setSubmenu:&*menu.inner];
let _: () = msg_send![main_menu, addItem:item];
}
let _: () = msg_send![app, setMainMenu:main_menu];
});
}
}