Wrap Foundation ourselves, because there's some considerations down the road and it's easier to do this now.

This commit is contained in:
Ryan McGrath 2020-03-17 16:55:09 -07:00
parent 66ffd83db0
commit c16dad564e
No known key found for this signature in database
GPG key ID: 811674B62B666830
43 changed files with 613 additions and 431 deletions

View file

@ -6,16 +6,12 @@ edition = "2018"
build = "build.rs" build = "build.rs"
[dependencies] [dependencies]
appkit-derive = { path = "../derives" }
block = "0.1.6" block = "0.1.6"
cocoa = "0.20.0"
core-foundation = "0.7"
core-graphics = "0.19.0"
dispatch = "0.2.0" dispatch = "0.2.0"
lazy_static = "1" libc = "0.2"
objc = "0.2.7" objc = "0.2.7"
objc_id = "0.1.1" objc_id = "0.1.1"
uuid = { version = "0.8", features = ["v4"] } #uuid = { version = "0.8", features = ["v4"] }
url = "2.1.1" url = "2.1.1"
[features] [features]

View file

@ -5,6 +5,9 @@
fn main() { fn main() {
if std::env::var("TARGET").unwrap().contains("-apple") { if std::env::var("TARGET").unwrap().contains("-apple") {
println!("cargo:rustc-link-lib=framework=Foundation");
println!("cargo:rustc-link-lib=framework=CoreGraphics");
println!("cargo:rustc-link-lib=framework=Security"); println!("cargo:rustc-link-lib=framework=Security");
println!("cargo:rustc-link-lib=framework=WebKit"); println!("cargo:rustc-link-lib=framework=WebKit");

View file

@ -1,46 +1,37 @@
//! A wrapper for `NSAlert`. Currently doesn't cover everything possible for this class, as it was //! A wrapper for `NSAlert`. Currently doesn't cover everything possible for this class, as it was
//! built primarily for debugging uses. Feel free to extend via pull requests or something. //! built primarily for debugging uses. Feel free to extend via pull requests or something.
use cocoa::base::{id, nil};
use cocoa::foundation::NSString;
use objc_id::Id; 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, NSString};
/// Represents an `NSAlert`. Has no information other than the retained pointer to the Objective C /// Represents an `NSAlert`. Has no information other than the retained pointer to the Objective C
/// side, so... don't bother inspecting this. /// side, so... don't bother inspecting this.
pub struct Alert { pub struct Alert(Id<Object>);
pub inner: Id<Object>
}
impl Alert { impl Alert {
/// Creates a basic `NSAlert`, storing a pointer to it in the Objective C runtime. /// Creates a basic `NSAlert`, storing a pointer to it in the Objective C runtime.
/// You can show this alert by calling `show()`. /// You can show this alert by calling `show()`.
pub fn new(title: &str, message: &str) -> Self { pub fn new(title: &str, message: &str) -> Self {
Alert { let title = NSString::new(title);
inner: unsafe { let message = NSString::new(message);
let cls = class!(NSAlert); let x = NSString::new("OK");
let alert: id = msg_send![cls, new];
let title = NSString::alloc(nil).init_str(title); Alert(unsafe {
let alert: id = msg_send![class!(NSAlert), new];
let _: () = msg_send![alert, setMessageText:title]; let _: () = msg_send![alert, setMessageText:title];
let message = NSString::alloc(nil).init_str(message);
let _: () = msg_send![alert, setInformativeText:message]; let _: () = msg_send![alert, setInformativeText:message];
let x = NSString::alloc(nil).init_str("OK");
let _: () = msg_send![alert, addButtonWithTitle:x]; let _: () = msg_send![alert, addButtonWithTitle:x];
Id::from_ptr(alert) Id::from_ptr(alert)
} })
}
} }
/// Shows this alert as a modal. /// Shows this alert as a modal.
pub fn show(&self) { pub fn show(&self) {
unsafe { unsafe {
let _: () = msg_send![&*self.inner, runModal]; let _: () = msg_send![&*self.0, runModal];
} }
} }
} }

View file

@ -8,21 +8,18 @@ use std::unreachable;
use block::Block; use block::Block;
use cocoa::base::{id, nil, BOOL, YES, NO};
use cocoa::foundation::{NSUInteger};
use objc::{class, msg_send, sel, sel_impl}; use objc::{class, msg_send, sel, sel_impl};
use objc::declare::ClassDecl; use objc::declare::ClassDecl;
use objc::runtime::{Class, Object, Sel}; use objc::runtime::{Class, Object, Sel};
use url::Url; use url::Url;
use crate::foundation::{id, nil, BOOL, YES, NO, NSUInteger, NSArray, NSString};
use crate::app::traits::AppController; use crate::app::traits::AppController;
use crate::constants::APP_PTR; use crate::constants::APP_PTR;
use crate::error::AppKitError; use crate::error::AppKitError;
use crate::printing::PrintSettings; use crate::printing::PrintSettings;
use crate::user_activity::UserActivity; use crate::user_activity::UserActivity;
use crate::utils::{map_nsarray, str_from};
#[cfg(feature = "cloudkit")] #[cfg(feature = "cloudkit")]
use crate::cloudkit::share::CKShareMetaData; use crate::cloudkit::share::CKShareMetaData;
@ -122,7 +119,6 @@ extern fn should_handle_reopen<T: AppController>(this: &Object, _: Sel, _: id, h
/// Fires when the application delegate receives a `applicationDockMenu:` request. /// Fires when the application delegate receives a `applicationDockMenu:` request.
extern fn dock_menu<T: AppController>(this: &Object, _: Sel, _: id) -> id { extern fn dock_menu<T: AppController>(this: &Object, _: Sel, _: id) -> id {
// @TODO: Confirm this is safe to do and not leaky.
match app::<T>(this).dock_menu() { match app::<T>(this).dock_menu() {
Some(mut menu) => &mut *menu.inner, Some(mut menu) => &mut *menu.inner,
None => nil None => nil
@ -143,9 +139,9 @@ extern fn did_change_screen_parameters<T: AppController>(this: &Object, _: Sel,
/// Fires when the application receives a `application:willContinueUserActivityWithType:` /// Fires when the application receives a `application:willContinueUserActivityWithType:`
/// notification. /// notification.
extern fn will_continue_user_activity_with_type<T: AppController>(this: &Object, _: Sel, _: id, activity_type: id) -> BOOL { extern fn will_continue_user_activity_with_type<T: AppController>(this: &Object, _: Sel, _: id, activity_type: id) -> BOOL {
let activity = str_from(activity_type); let activity = NSString::wrap(activity_type);
match app::<T>(this).will_continue_user_activity(activity) { match app::<T>(this).will_continue_user_activity(activity.to_str()) {
true => YES, true => YES,
false => NO false => NO
} }
@ -171,7 +167,7 @@ extern fn continue_user_activity<T: AppController>(this: &Object, _: Sel, _: id,
/// `application:didFailToContinueUserActivityWithType:error:` message. /// `application:didFailToContinueUserActivityWithType:error:` message.
extern fn failed_to_continue_user_activity<T: AppController>(this: &Object, _: Sel, _: id, activity_type: id, error: id) { extern fn failed_to_continue_user_activity<T: AppController>(this: &Object, _: Sel, _: id, activity_type: id, error: id) {
app::<T>(this).failed_to_continue_user_activity( app::<T>(this).failed_to_continue_user_activity(
str_from(activity_type), NSString::wrap(activity_type).to_str(),
AppKitError::new(error) AppKitError::new(error)
); );
} }
@ -188,8 +184,8 @@ extern fn registered_for_remote_notifications<T: AppController>(_this: &Object,
} }
/// Fires when the application receives a `application:didFailToRegisterForRemoteNotificationsWithError:` message. /// Fires when the application receives a `application:didFailToRegisterForRemoteNotificationsWithError:` message.
extern fn failed_to_register_for_remote_notifications<T: AppController>(_this: &Object, _: Sel, _: id, _: id) { extern fn failed_to_register_for_remote_notifications<T: AppController>(this: &Object, _: Sel, _: id, error: id) {
app::<T>(this).failed_to_register_for_remote_notifications(AppKitError::new(error));
} }
/// Fires when the application receives a `application:didReceiveRemoteNotification:` message. /// Fires when the application receives a `application:didReceiveRemoteNotification:` message.
@ -207,10 +203,12 @@ extern fn accepted_cloudkit_share<T: AppController>(_this: &Object, _: Sel, _: i
/// Fires when the application receives an `application:openURLs` message. /// Fires when the application receives an `application:openURLs` message.
extern fn open_urls<T: AppController>(this: &Object, _: Sel, _: id, file_urls: id) { extern fn open_urls<T: AppController>(this: &Object, _: Sel, _: id, file_urls: id) {
let urls = map_nsarray(file_urls, |url| unsafe { let urls = NSArray::wrap(file_urls).map(|url| {
let absolute_string = msg_send![url, absoluteString]; let uri = NSString::wrap(unsafe {
let uri = str_from(absolute_string); msg_send![url, absoluteString]
Url::parse(uri) });
Url::parse(uri.to_str())
}).into_iter().filter_map(|url| url.ok()).collect(); }).into_iter().filter_map(|url| url.ok()).collect();
app::<T>(this).open_urls(urls); app::<T>(this).open_urls(urls);
@ -218,9 +216,9 @@ extern fn open_urls<T: AppController>(this: &Object, _: Sel, _: id, file_urls: i
/// Fires when the application receives an `application:openFileWithoutUI:` message. /// Fires when the application receives an `application:openFileWithoutUI:` message.
extern fn open_file_without_ui<T: AppController>(this: &Object, _: Sel, _: id, file: id) -> BOOL { extern fn open_file_without_ui<T: AppController>(this: &Object, _: Sel, _: id, file: id) -> BOOL {
let filename = str_from(file); let filename = NSString::wrap(file);
match app::<T>(this).open_file_without_ui(filename) { match app::<T>(this).open_file_without_ui(filename.to_str()) {
true => YES, true => YES,
false => NO false => NO
} }
@ -244,9 +242,9 @@ extern fn open_untitled_file<T: AppController>(this: &Object, _: Sel, _: id) ->
/// Fired when the application receives an `application:openTempFile:` message. /// Fired when the application receives an `application:openTempFile:` message.
extern fn open_temp_file<T: AppController>(this: &Object, _: Sel, _: id, filename: id) -> BOOL { extern fn open_temp_file<T: AppController>(this: &Object, _: Sel, _: id, filename: id) -> BOOL {
let filename = str_from(filename); let filename = NSString::wrap(filename);
match app::<T>(this).open_temp_file(filename) { match app::<T>(this).open_temp_file(filename.to_str()) {
true => YES, true => YES,
false => NO false => NO
} }
@ -254,9 +252,9 @@ extern fn open_temp_file<T: AppController>(this: &Object, _: Sel, _: id, filenam
/// Fired when the application receives an `application:printFile:` message. /// Fired when the application receives an `application:printFile:` message.
extern fn print_file<T: AppController>(this: &Object, _: Sel, _: id, file: id) -> BOOL { extern fn print_file<T: AppController>(this: &Object, _: Sel, _: id, file: id) -> BOOL {
let filename = str_from(file); let filename = NSString::wrap(file);
match app::<T>(this).print_file(filename) { match app::<T>(this).print_file(filename.to_str()) {
true => YES, true => YES,
false => NO false => NO
} }
@ -265,8 +263,8 @@ extern fn print_file<T: AppController>(this: &Object, _: Sel, _: id, file: id) -
/// Fired when the application receives an `application:printFiles:withSettings:showPrintPanels:` /// Fired when the application receives an `application:printFiles:withSettings:showPrintPanels:`
/// message. /// message.
extern fn print_files<T: AppController>(this: &Object, _: Sel, _: id, files: id, settings: id, show_print_panels: BOOL) -> NSUInteger { extern fn print_files<T: AppController>(this: &Object, _: Sel, _: id, files: id, settings: id, show_print_panels: BOOL) -> NSUInteger {
let files = map_nsarray(files, |file| { let files = NSArray::wrap(files).map(|file| {
str_from(file).to_string() NSString::wrap(file).to_str().to_string()
}); });
let settings = PrintSettings::with_inner(settings); let settings = PrintSettings::with_inner(settings);
@ -287,9 +285,9 @@ extern fn did_change_occlusion_state<T: AppController>(this: &Object, _: Sel, _:
/// Note: this may not fire in sandboxed applications. Apple's documentation is unclear on the /// Note: this may not fire in sandboxed applications. Apple's documentation is unclear on the
/// matter. /// matter.
extern fn delegate_handles_key<T: AppController>(this: &Object, _: Sel, _: id, key: id) -> BOOL { extern fn delegate_handles_key<T: AppController>(this: &Object, _: Sel, _: id, key: id) -> BOOL {
let key = str_from(key); let key = NSString::wrap(key);
match app::<T>(this).delegate_handles_key(key) { match app::<T>(this).delegate_handles_key(key.to_str()) {
true => YES, true => YES,
false => NO false => NO
} }

View file

@ -1,6 +1,6 @@
//! Various types used at the AppController level. //! Various types used at the AppController level.
use cocoa::foundation::NSUInteger; use crate::foundation::NSUInteger;
/// Used for determining how an application should handle quitting/terminating. /// Used for determining how an application should handle quitting/terminating.
/// You return this in your `AppController` `should_terminate` method. /// You return this in your `AppController` `should_terminate` method.
@ -29,32 +29,3 @@ impl From<TerminateResponse> for NSUInteger {
} }
} }
} }
/// Used for handling printing files. You return this in relevant `AppController` methods.
#[derive(Copy, Clone, Debug)]
pub enum PrintResponse {
/// Printing was cancelled.
Cancelled,
/// Printing was a success.
Success,
/// Printing failed.
Failure,
/// For when the result of printing cannot be returned immediately (e.g, if printing causes a sheet to appear).
/// If your method returns PrintResponse::ReplyLater it must always invoke `App::reply_to_open_or_print()` when the
/// entire print operation has been completed, successfully or not.
ReplyLater
}
impl From<PrintResponse> for NSUInteger {
fn from(response: PrintResponse) -> NSUInteger {
match response {
PrintResponse::Cancelled => 0,
PrintResponse::Success => 1,
PrintResponse::Failure => 3,
PrintResponse::ReplyLater => 2
}
}
}

View file

@ -1,9 +1,6 @@
//! A wrapper for `NSApplicationDelegate` on macOS. Handles looping back events and providing a very janky //! A wrapper for `NSApplicationDelegate` on macOS. Handles looping back events and providing a very janky
//! messaging architecture. //! messaging architecture.
use cocoa::base::{id, nil};
use cocoa::appkit::{NSRunningApplication};
use objc_id::Id; 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};
@ -12,6 +9,7 @@ mod class;
pub mod traits; pub mod traits;
pub mod enums; pub mod enums;
use crate::foundation::{id, AutoReleasePool};
use crate::constants::APP_PTR; use crate::constants::APP_PTR;
use crate::menu::Menu; use crate::menu::Menu;
use class::{register_app_class, register_app_controller_class}; use class::{register_app_class, register_app_controller_class};
@ -24,6 +22,7 @@ pub struct App<T = (), M = ()> {
pub inner: Id<Object>, pub inner: Id<Object>,
pub objc_delegate: Id<Object>, pub objc_delegate: Id<Object>,
pub delegate: Box<T>, pub delegate: Box<T>,
pub pool: AutoReleasePool,
_t: std::marker::PhantomData<M> _t: std::marker::PhantomData<M>
} }
@ -51,35 +50,17 @@ impl App {
} }
impl<T, M> App<T, M> where M: Send + Sync + 'static, T: AppController + Dispatcher<Message = M> { impl<T, M> App<T, M> where M: Send + Sync + 'static, T: AppController + Dispatcher<Message = M> {
/// 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.
pub fn dispatch(message: M) {
let queue = dispatch::Queue::main();
queue.exec_async(move || unsafe {
let app: id = msg_send![register_app_class(), sharedApplication];
let app_delegate: id = msg_send![app, delegate];
let delegate_ptr: usize = *(*app_delegate).get_ivar(APP_PTR);
let delegate = delegate_ptr as *const T;
(&*delegate).on_message(message);
});
}
/// 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
/// Objective-C side of things. /// Objective-C side of things.
pub fn new(_bundle_id: &str, delegate: T) -> Self { pub fn new(_bundle_id: &str, delegate: T) -> Self {
// set_bundle_id(bundle_id); // set_bundle_id(bundle_id);
let _pool = unsafe { let pool = AutoReleasePool::new();
//msg_send![class!(
cocoa::foundation::NSAutoreleasePool::new(nil)
};
let inner = unsafe { let inner = unsafe {
let app: id = msg_send![register_app_class(), sharedApplication]; let app: id = msg_send![register_app_class(), sharedApplication];
let _: () = msg_send![app, setActivationPolicy:0]; let _: () = msg_send![app, setActivationPolicy:0];
//app.setActivationPolicy_(cocoa::appkit::NSApplicationActivationPolicyRegular);
Id::from_ptr(app) Id::from_ptr(app)
}; };
@ -98,17 +79,32 @@ impl<T, M> App<T, M> where M: Send + Sync + 'static, T: AppController + Dispatch
objc_delegate: objc_delegate, objc_delegate: objc_delegate,
inner: inner, inner: inner,
delegate: app_delegate, delegate: app_delegate,
pool: pool,
_t: std::marker::PhantomData _t: std::marker::PhantomData
} }
} }
/// 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.
pub fn dispatch(message: M) {
let queue = dispatch::Queue::main();
queue.exec_async(move || unsafe {
let app: id = msg_send![register_app_class(), sharedApplication];
let app_delegate: id = msg_send![app, delegate];
let delegate_ptr: usize = *(*app_delegate).get_ivar(APP_PTR);
let delegate = delegate_ptr as *const T;
(&*delegate).on_message(message);
});
}
/// Kicks off the NSRunLoop for the NSApplication instance. This blocks when called. /// Kicks off the NSRunLoop for the NSApplication instance. This blocks when called.
/// If you're wondering where to go from here... you need an `AppController` that implements /// If you're wondering where to go from here... you need an `AppController` that implements
/// `did_finish_launching`. :) /// `did_finish_launching`. :)
pub fn run(&self) { pub fn run(&self) {
unsafe { unsafe {
let current_app = cocoa::appkit::NSRunningApplication::currentApplication(nil); let current_app: id = msg_send![class!(NSRunningApplication), currentApplication];
current_app.activateWithOptions_(cocoa::appkit::NSApplicationActivateIgnoringOtherApps); let _: () = msg_send![current_app, activateWithOptions:0];
let shared_app: id = msg_send![class!(RSTApplication), sharedApplication]; let shared_app: id = msg_send![class!(RSTApplication), sharedApplication];
let _: () = msg_send![shared_app, run]; let _: () = msg_send![shared_app, run];
} }

View file

@ -3,13 +3,14 @@
use url::Url; use url::Url;
use crate::app::enums::{TerminateResponse, PrintResponse}; use crate::app::enums::TerminateResponse;
use crate::error::AppKitError; use crate::error::AppKitError;
use crate::menu::Menu; use crate::menu::Menu;
use crate::printing::enums::PrintResponse;
use crate::printing::settings::PrintSettings; use crate::printing::settings::PrintSettings;
use crate::user_activity::UserActivity; use crate::user_activity::UserActivity;
#[cfg(feature = "user-notifications")] #[cfg(feature = "cloudkit")]
use crate::cloudkit::share::CKShareMetaData; use crate::cloudkit::share::CKShareMetaData;
/// Controllers interested in processing messages can implement this to respond to messages as /// Controllers interested in processing messages can implement this to respond to messages as
@ -121,6 +122,18 @@ pub trait AppController {
/// Fired after the user activity object has been updated. /// Fired after the user activity object has been updated.
fn updated_user_activity(&self, _activity: UserActivity) {} fn updated_user_activity(&self, _activity: UserActivity) {}
/// Fired when you've successfully registered for remote notifications with APNS.
fn registered_for_remote_notifications(&self, _token: &str) {
}
/// Fired after you've received a push notification from APNS.
//fn did_receive_remote_notification(&self, notification: PushNotification) {}
/// Fired if there was a failure to register for remote notifications with APNS - e.g,
/// connection issues or something.
fn failed_to_register_for_remote_notifications(&self, _error: AppKitError) {}
/// Fires after the user accepted a CloudKit sharing invitation associated with your /// Fires after the user accepted a CloudKit sharing invitation associated with your
/// application. /// application.
#[cfg(feature = "cloudkit")] #[cfg(feature = "cloudkit")]

View file

@ -8,8 +8,6 @@
use std::ffi::CString; use std::ffi::CString;
use std::mem; use std::mem;
use cocoa::foundation::{NSString};
use cocoa::base::{id, nil, BOOL, YES};//, NO};
use objc::{class, msg_send, sel, sel_impl, Encode, Encoding, EncodeArguments, Message}; use objc::{class, msg_send, sel, sel_impl, Encode, Encoding, EncodeArguments, Message};
use objc::runtime::{Class, Sel, Method, Object, Imp}; use objc::runtime::{Class, Sel, Method, Object, Imp};
use objc::runtime::{ use objc::runtime::{
@ -19,6 +17,8 @@ use objc::runtime::{
method_exchangeImplementations method_exchangeImplementations
}; };
use crate::foundation::{id, nil, BOOL, YES, NSString};
/// Types that can be used as the implementation of an Objective-C method. /// Types that can be used as the implementation of an Objective-C method.
pub trait MethodImplementation { pub trait MethodImplementation {
/// The callee type of the method. /// The callee type of the method.

View file

@ -3,13 +3,12 @@
use std::sync::Once; use std::sync::Once;
use cocoa::base::{id, nil};
use cocoa::foundation::{NSString};
use objc_id::Id; use objc_id::Id;
use objc::declare::ClassDecl; use objc::declare::ClassDecl;
use objc::runtime::{Class, Object}; use objc::runtime::{Class, Object};
use objc::{msg_send, sel, sel_impl}; use objc::{class, msg_send, sel, sel_impl};
use crate::foundation::{nil, NSString};
/// A wrapper for `NSButton`. Holds (retains) pointers for the Objective-C runtime /// A wrapper for `NSButton`. Holds (retains) pointers for the Objective-C runtime
/// where our `NSButton` lives. /// where our `NSButton` lives.
@ -21,10 +20,9 @@ impl Button {
/// Creates a new `NSButton` instance, configures it appropriately, /// Creates a new `NSButton` instance, configures it appropriately,
/// and retains the necessary Objective-C runtime pointer. /// and retains the necessary Objective-C runtime pointer.
pub fn new(text: &str) -> Self { pub fn new(text: &str) -> Self {
let title = NSString::new(text);
let inner = unsafe { let inner = unsafe {
let title = NSString::alloc(nil).init_str(text); Id::from_ptr(msg_send![register_class(), buttonWithTitle:title target:nil action:nil])
let button: id = msg_send![register_class(), buttonWithTitle:title target:nil action:nil];
Id::from_ptr(button)
}; };
Button { Button {
@ -47,7 +45,7 @@ fn register_class() -> *const Class {
static INIT: Once = Once::new(); static INIT: Once = Once::new();
INIT.call_once(|| unsafe { INIT.call_once(|| unsafe {
let superclass = Class::get("NSButton").unwrap(); let superclass = class!(NSButton);
let decl = ClassDecl::new("RSTButton", superclass).unwrap(); let decl = ClassDecl::new("RSTButton", superclass).unwrap();
VIEW_CLASS = decl.register(); VIEW_CLASS = decl.register();
}); });

View file

@ -1,10 +1,10 @@
//! Implements `Color`. Heavily based on the `Color` module in Servo's CSS parser, but tweaked //! Implements `Color`. Heavily based on the `Color` module in Servo's CSS parser, but tweaked
//! for (what I believe) is a friendlier API. //! for (what I believe) is a friendlier API.
use cocoa::base::id;
use core_graphics::base::CGFloat;
use objc::{class, msg_send, sel, sel_impl}; use objc::{class, msg_send, sel, sel_impl};
use crate::foundation::{id, CGFloat};
/// A color with red, green, blue, and alpha components, in a byte each. /// A color with red, green, blue, and alpha components, in a byte each.
#[derive(Clone, Copy, PartialEq, Debug)] #[derive(Clone, Copy, PartialEq, Debug)]
pub struct Color { pub struct Color {

View file

@ -2,12 +2,11 @@
//! across the codebase, hence why they're here - they're not currently exhaustive, so feel free to //! across the codebase, hence why they're here - they're not currently exhaustive, so feel free to
//! tinker and pull request. //! tinker and pull request.
use cocoa::foundation::NSUInteger;
use objc::runtime::Object; use objc::runtime::Object;
use objc::{msg_send, sel, sel_impl}; use objc::{msg_send, sel, sel_impl};
use objc_id::Id; use objc_id::Id;
use crate::foundation::NSUInteger;
use crate::pasteboard::Pasteboard; use crate::pasteboard::Pasteboard;
/// Represents operations that can happen for a given drag/drop scenario. /// Represents operations that can happen for a given drag/drop scenario.

View file

@ -6,11 +6,9 @@
use std::error; use std::error;
use std::fmt; use std::fmt;
use cocoa::base::{id, nil};
use cocoa::foundation::{NSInteger, NSString};
use objc::{class, msg_send, sel, sel_impl}; use objc::{class, msg_send, sel, sel_impl};
use crate::utils::str_from; use crate::foundation::{id, nil, NSInteger, NSString};
/// A wrapper around pieces of data extracted from `NSError`. This could be improved: right now, it /// A wrapper around pieces of data extracted from `NSError`. This could be improved: right now, it
/// allocates `String` instances when theoretically it could be avoided, and we might be erasing /// allocates `String` instances when theoretically it could be avoided, and we might be erasing
@ -29,16 +27,16 @@ impl AppKitError {
pub fn new(error: id) -> Self { pub fn new(error: id) -> Self {
let (code, domain, description) = unsafe { let (code, domain, description) = unsafe {
let code: usize = msg_send![error, code]; let code: usize = msg_send![error, code];
let domain: id = msg_send![error, domain]; let domain = NSString::wrap(msg_send![error, domain]);
let description: id = msg_send![error, localizedDescription]; let description = NSString::wrap(msg_send![error, localizedDescription]);
(code, domain, description) (code, domain, description)
}; };
AppKitError { AppKitError {
code: code, code: code,
domain: str_from(domain).to_string(), domain: domain.to_str().to_string(),
description: str_from(description).to_string() description: description.to_str().to_string()
} }
} }
@ -51,7 +49,7 @@ impl AppKitError {
/// thread safe. /// thread safe.
pub fn into_nserror(self) -> id { pub fn into_nserror(self) -> id {
unsafe { unsafe {
let domain = NSString::alloc(nil).init_str(&self.domain); let domain = NSString::new(&self.domain);
let code = self.code as NSInteger; let code = self.code as NSInteger;
msg_send![class!(NSError), errorWithDomain:domain code:code userInfo:nil] msg_send![class!(NSError), errorWithDomain:domain code:code userInfo:nil]
} }

View file

@ -1,7 +1,7 @@
//! Hoists some type definitions in a way that I personally find cleaner than what's in the Servo //! Hoists some type definitions in a way that I personally find cleaner than what's in the Servo
//! code. //! code.
use cocoa::foundation::NSUInteger; use crate::foundation::NSUInteger;
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub enum EventModifierFlag { pub enum EventModifierFlag {

View file

@ -0,0 +1,82 @@
//! A wrapper type for `NSArray`. This is abstracted out as we need to use `NSArray` in a ton of
//! instances in this framework, and down the road I'd like to investigate using `CFArray` instead
//! of `NSArray` (i.e, if the ObjC runtime is ever pulled or something - perhaps those types would
//! stick around).
//!
//! Essentially, consider this some sanity/cleanliness/future-proofing. End users should never need
//! to touch this.
use objc::{class, msg_send, sel, sel_impl};
use objc::runtime::Object;
use objc_id::Id;
use crate::foundation::id;
/// A wrapper for `NSArray` that makes common operations in our framework a bit easier to handle
/// and reason about.
#[derive(Debug)]
pub struct NSArray(pub Id<Object>);
impl From<Vec<&Object>> for NSArray {
/// Given a set of `Object`s, creates an `NSArray` that holds them.
fn from(objects: Vec<&Object>) -> Self {
NSArray(unsafe {
Id::from_ptr(msg_send![class!(NSArray), arrayWithObjects:objects.as_ptr() count:objects.len()])
})
}
}
impl From<Vec<id>> for NSArray {
/// Given a set of `*mut Object`s, creates an `NSArray` that holds them.
fn from(objects: Vec<id>) -> Self {
NSArray(unsafe {
Id::from_ptr(msg_send![class!(NSArray), arrayWithObjects:objects.as_ptr() count:objects.len()])
})
}
}
impl NSArray {
/// Given a set of `Object`s, creates an `NSArray` that holds them.
pub fn new(objects: &[id]) -> Self {
NSArray(unsafe {
Id::from_ptr(msg_send![class!(NSArray), arrayWithObjects:objects.as_ptr() count:objects.len()])
})
}
/// In some cases, we're vended an `NSArray` by the system, and it's ideal to not retain that.
/// This handles that edge case.
pub fn wrap(array: id) -> Self {
NSArray(unsafe {
Id::from_retained_ptr(array)
})
}
/// Consumes and returns the underlying Objective-C value.
pub fn into_inner(mut self) -> id {
&mut *self.0
}
/// Returns the `count` (`len()` equivalent) for the backing `NSArray`.
pub fn count(&self) -> usize {
unsafe { msg_send![self.0, count] }
}
/// A helper method for mapping over the backing `NSArray` items.
/// Often times we need to map in this framework to convert between Rust types, so isolating
/// this out makes life much easier.
pub fn map<T, F: Fn(id) -> T>(&self, transform: F) -> Vec<T> {
let count = self.count();
let mut ret: Vec<T> = Vec::with_capacity(count);
let mut index = 0;
loop {
let item: id = unsafe { msg_send![&*self.0, objectAtIndex:index] };
ret.push(transform(item));
index += 1;
if index == count { break }
}
ret
}
}

View file

@ -0,0 +1,19 @@
//! A lightweight wrapper around `NSAutoreleasePool`.
use objc::{class, msg_send, sel, sel_impl};
use objc::runtime::Object;
use objc_id::Id;
pub struct AutoReleasePool(pub Id<Object>);
impl AutoReleasePool {
pub fn new() -> Self {
AutoReleasePool(unsafe {
Id::from_retained_ptr(msg_send![class!(NSAutoreleasePool), new])
})
}
pub fn drain(self) {
let _: () = unsafe { msg_send![&*self.0, drain] };
}
}

View file

@ -0,0 +1,10 @@
//! A wrapper for `NSDictionary`, which aims to make dealing with the class throughout this
//! framework a tad bit simpler.
use objc::runtime::Object;
use objc_id::Id;
#[derive(Debug)]
pub struct NSDictionary(Id<Object>);
impl NSDictionary {}

View file

@ -0,0 +1,81 @@
//! Implements Core Graphics Geometry types. Most of this is lifted from `servo/core-foundation-rs`
//! - as such, we include a copy of the license below.
//!
//! Copyright (c) 2012-2013 Mozilla Foundation
//!
//! Permission is hereby granted, free of charge, to any
//! person obtaining a copy of this software and associated
//! documentation files (the "Software"), to deal in the
//! Software without restriction, including without
//! limitation the rights to use, copy, modify, merge,
//! publish, distribute, sublicense, and/or sell copies of
//! the Software, and to permit persons to whom the Software
//! is furnished to do so, subject to the following
//! conditions:
//!
//! The above copyright notice and this permission notice
//! shall be included in all copies or substantial portions
//! of the Software.
//!
//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
//! ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
//! TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
//! PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
//! SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
//! CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
//! OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
//! IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
//! DEALINGS IN THE SOFTWARE.
use crate::foundation::CGFloat;
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct CGSize {
pub width: CGFloat,
pub height: CGFloat,
}
impl CGSize {
#[inline]
pub fn new(width: CGFloat, height: CGFloat) -> CGSize {
CGSize {
width: width,
height: height,
}
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct CGPoint {
pub x: CGFloat,
pub y: CGFloat,
}
impl CGPoint {
#[inline]
pub fn new(x: CGFloat, y: CGFloat) -> CGPoint {
CGPoint {
x: x,
y: y,
}
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct CGRect {
pub origin: CGPoint,
pub size: CGSize
}
impl CGRect {
#[inline]
pub fn new(origin: &CGPoint, size: &CGSize) -> CGRect {
CGRect {
origin: *origin,
size: *size,
}
}
}

View file

@ -0,0 +1,51 @@
//! This module contains some lightweight wrappers over certain data types that we use throughout
//! the framework. Some of it is pulled/inspired from Servo's cocoa-rs (e.g, the "id" type). While
//! this isn't a clone of their module (we don't need everything from there, but remaining
//! compatible in case an end-user wants to drop that low is deal), it's worth linking their
//! license and repository - they've done really incredible work and it's 100% worth acknowledging.
//!
//! - [core-foundation-rs Repository](https://github.com/servo/core-foundation-rs)
//! - [core-foundation-rs MIT License](https://github.com/servo/core-foundation-rs/blob/master/LICENSE-MIT)
//! - [core-foundation-rs Apache License](https://github.com/servo/core-foundation-rs/blob/master/LICENSE-APACHE)
#![allow(non_camel_case_types)]
#![allow(non_upper_case_globals)]
use objc::runtime;
pub use objc::runtime::{BOOL, NO, YES};
pub mod autoreleasepool;
pub use autoreleasepool::AutoReleasePool;
pub mod array;
pub use array::NSArray;
pub mod string;
pub use string::NSString;
pub mod dictionary;
pub use dictionary::NSDictionary;
pub mod geometry;
pub use geometry::{CGSize, CGPoint, CGRect};
#[allow(non_camel_case_types)]
pub type id = *mut runtime::Object;
#[allow(non_upper_case_globals)]
pub const nil: id = 0 as id;
#[cfg(target_pointer_width = "32")]
pub type NSInteger = libc::c_int;
#[cfg(target_pointer_width = "32")]
pub type NSUInteger = libc::c_uint;
#[cfg(target_pointer_width = "64")]
pub type NSInteger = libc::c_long;
#[cfg(target_pointer_width = "64")]
pub type NSUInteger = libc::c_ulong;
#[cfg(target_pointer_width = "64")]
pub type CGFloat = libc::c_double;
#[cfg(not(target_pointer_width = "64"))]
pub type CGFloat = libc::c_float;

View file

@ -0,0 +1,56 @@
//! A wrapper library for `NSString`, which we use throughout the framework. This is abstracted out
//! for a few reasons, but namely:
//!
//! - It's used often, so we want a decent enough API.
//! - Playing around with performance for this type is ideal, as it's a lot of heap allocation.
//!
//! End users should never need to interact with this.
use std::{slice, str};
use std::os::raw::c_char;
use objc::{class, msg_send, sel, sel_impl};
use objc::runtime::Object;
use objc_id::Id;
use crate::foundation::id;
const UTF8_ENCODING: usize = 4;
/// Wraps an underlying `NSString`.
#[derive(Debug)]
pub struct NSString(pub Id<Object>);
impl NSString {
pub fn new(s: &str) -> Self {
NSString(unsafe {
let nsstring: *mut Object = msg_send![class!(NSString), alloc];
//msg_send![nsstring, initWithBytesNoCopy:s.as_ptr() length:s.len() encoding:4 freeWhenDone:NO]
Id::from_ptr(msg_send![nsstring, initWithBytes:s.as_ptr() length:s.len() encoding:UTF8_ENCODING])
})
}
pub fn wrap(object: id) -> Self {
NSString(unsafe {
Id::from_retained_ptr(object)
})
}
pub fn into_inner(mut self) -> id {
&mut *self.0
}
/// A utility method for taking an `NSString` and bridging it to a Rust `&str`.
pub fn to_str(self) -> &'static str {
unsafe {
let bytes = {
let bytes: *const c_char = msg_send![&*self.0, UTF8String];
bytes as *const u8
};
let len = msg_send![&*self.0, lengthOfBytesUsingEncoding:UTF8_ENCODING];
let bytes = slice::from_raw_parts(bytes, len);
str::from_utf8(bytes).unwrap()
}
}
}

View file

@ -1,6 +1,6 @@
//! Wrapper methods for various geometry types (rects, sizes, ec). //! Wrapper methods for various geometry types (rects, sizes, ec).
use cocoa::foundation::{NSRect, NSPoint, NSSize}; use crate::foundation::{CGRect, CGPoint, CGSize};
/// A struct that represents a box - top, left, width and height. /// A struct that represents a box - top, left, width and height.
pub struct Rect { pub struct Rect {
@ -29,11 +29,11 @@ impl Rect {
} }
} }
impl From<Rect> for NSRect { impl From<Rect> for CGRect {
fn from(rect: Rect) -> NSRect { fn from(rect: Rect) -> CGRect {
NSRect::new( CGRect::new(
NSPoint::new(rect.top, rect.left), &CGPoint::new(rect.top, rect.left),
NSSize::new(rect.width, rect.height) &CGSize::new(rect.width, rect.height)
) )
} }
} }

View file

@ -2,13 +2,12 @@
//! escape hatch, if you need it (we use it for things like width and height, which aren't handled //! escape hatch, if you need it (we use it for things like width and height, which aren't handled
//! by an axis). //! by an axis).
use cocoa::base::id;
use core_graphics::base::CGFloat;
use objc::{class, msg_send, sel, sel_impl}; use objc::{class, msg_send, sel, sel_impl};
use objc::runtime::Object; use objc::runtime::Object;
use objc_id::ShareId; use objc_id::ShareId;
use crate::foundation::{id, CGFloat};
/// A wrapper for `NSLayoutConstraint`. This both acts as a central path through which to activate /// A wrapper for `NSLayoutConstraint`. This both acts as a central path through which to activate
/// constraints, as well as a wrapper for layout constraints that are not axis bound (e.g, width or /// constraints, as well as a wrapper for layout constraints that are not axis bound (e.g, width or
/// height). /// height).

View file

@ -1,13 +1,11 @@
//! A wrapper for `NSLayoutAnchorDimension`, which is typically used to handle `width` and `height` //! A wrapper for `NSLayoutAnchorDimension`, which is typically used to handle `width` and `height`
//! values for how a given view should layout. //! values for how a given view should layout.
use cocoa::base::id;
use core_graphics::base::CGFloat;
use objc::{msg_send, sel, sel_impl}; use objc::{msg_send, sel, sel_impl};
use objc::runtime::Object; use objc::runtime::Object;
use objc_id::ShareId; use objc_id::ShareId;
use crate::foundation::{id, CGFloat};
use crate::layout::constraint::LayoutConstraint; use crate::layout::constraint::LayoutConstraint;
/// A wrapper for `NSLayoutAnchor`. You should never be creating this yourself - it's more of a /// A wrapper for `NSLayoutAnchor`. You should never be creating this yourself - it's more of a

View file

@ -2,12 +2,11 @@
//! given view should layout along the x-axis. Of note: the only thing that can't be protected //! given view should layout along the x-axis. Of note: the only thing that can't be protected
//! against is mixing/matching incorrect left/leading and right/trailing anchors. Be careful! //! against is mixing/matching incorrect left/leading and right/trailing anchors. Be careful!
use cocoa::base::id;
use objc::{msg_send, sel, sel_impl}; use objc::{msg_send, sel, sel_impl};
use objc::runtime::Object; use objc::runtime::Object;
use objc_id::ShareId; use objc_id::ShareId;
use crate::foundation::id;
use crate::layout::constraint::LayoutConstraint; use crate::layout::constraint::LayoutConstraint;
/// A wrapper for `NSLayoutAnchor`. You should never be creating this yourself - it's more of a /// A wrapper for `NSLayoutAnchor`. You should never be creating this yourself - it's more of a

View file

@ -2,12 +2,11 @@
//! given view should layout along the x-axis. Of note: the only thing that can't be protected //! given view should layout along the x-axis. Of note: the only thing that can't be protected
//! against is mixing/matching incorrect left/leading and right/trailing anchors. Be careful! //! against is mixing/matching incorrect left/leading and right/trailing anchors. Be careful!
use cocoa::base::id;
use objc::{msg_send, sel, sel_impl}; use objc::{msg_send, sel, sel_impl};
use objc::runtime::Object; use objc::runtime::Object;
use objc_id::ShareId; use objc_id::ShareId;
use crate::foundation::id;
use crate::layout::constraint::LayoutConstraint; use crate::layout::constraint::LayoutConstraint;
/// A wrapper for `NSLayoutAnchor`. You should never be creating this yourself - it's more of a /// A wrapper for `NSLayoutAnchor`. You should never be creating this yourself - it's more of a

View file

@ -16,15 +16,11 @@
//! your own risk. With that said, provided you follow the rules (regarding memory/ownership) it's //! your own risk. With that said, provided you follow the rules (regarding memory/ownership) it's
//! already fine for some apps. Check the README for more info! //! already fine for some apps. Check the README for more info!
pub use objc_id::ShareId;
pub use objc::runtime::Object;
pub use cocoa::base::id;
pub mod alert; pub mod alert;
pub mod app; pub mod app;
pub mod button; pub mod button;
#[cfg(feature = "user-notifications")] #[cfg(feature = "cloudkit")]
pub mod cloudkit; pub mod cloudkit;
pub mod color; pub mod color;
@ -33,7 +29,8 @@ pub mod constants;
pub mod dragdrop; pub mod dragdrop;
pub mod error; pub mod error;
pub mod events; pub mod events;
pub mod filesystem; //pub mod filesystem;
pub mod foundation;
pub mod geometry; pub mod geometry;
pub mod layout; pub mod layout;
pub mod menu; pub mod menu;
@ -47,7 +44,7 @@ pub mod printing;
pub mod toolbar; pub mod toolbar;
pub mod user_activity; pub mod user_activity;
pub mod utils; pub mod utils;
pub mod view; /*pub mod view;
pub mod webview; pub mod webview;
pub mod window; pub mod window;
@ -81,4 +78,4 @@ pub mod prelude {
pub use appkit_derive::{ pub use appkit_derive::{
WindowWrapper, ViewWrapper WindowWrapper, ViewWrapper
}; };
} }*/

View file

@ -2,13 +2,11 @@
//! one level deep; this could change in the future but is fine for //! one level deep; this could change in the future but is fine for
//! now. //! now.
use cocoa::base::{id, nil};
use cocoa::foundation::{NSString, NSUInteger};
use objc::{class, msg_send, sel, sel_impl}; use objc::{class, msg_send, sel, sel_impl};
use objc::runtime::{Object, Sel}; use objc::runtime::{Object, Sel};
use objc_id::ShareId; use objc_id::ShareId;
use crate::foundation::{id, nil, NSString, NSUInteger};
use crate::events::EventModifierFlag; use crate::events::EventModifierFlag;
/// Internal method (shorthand) for generating `NSMenuItem` holders. /// Internal method (shorthand) for generating `NSMenuItem` holders.
@ -21,10 +19,10 @@ fn make_menu_item(
unsafe { unsafe {
let cls = class!(NSMenuItem); let cls = class!(NSMenuItem);
let alloc: id = msg_send![cls, alloc]; let alloc: id = msg_send![cls, alloc];
let title = NSString::alloc(nil).init_str(title); let title = NSString::new(title);
// Note that AppKit requires a blank string if nil, not nil. // Note that AppKit requires a blank string if nil, not nil.
let key = NSString::alloc(nil).init_str(match key { let key = NSString::new(match key {
Some(s) => s, Some(s) => s,
None => "" None => ""
}); });
@ -74,7 +72,7 @@ impl MenuItem {
MenuItem::Action(item) => { MenuItem::Action(item) => {
unsafe { unsafe {
let key = NSString::alloc(nil).init_str(key); let key = NSString::new(key);
let _: () = msg_send![&*item, setKeyEquivalent:key]; let _: () = msg_send![&*item, setKeyEquivalent:key];
} }

View file

@ -1,12 +1,10 @@
//! Wraps NSMenu and handles instrumenting necessary delegate pieces. //! Wraps NSMenu and handles instrumenting necessary delegate pieces.
use cocoa::base::{id, nil};
use cocoa::foundation::NSString;
use objc_id::Id; 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, NSString};
use crate::menu::item::MenuItem; use crate::menu::item::MenuItem;
/// A struct that represents an `NSMenu`. It takes ownership of items, and handles instrumenting /// A struct that represents an `NSMenu`. It takes ownership of items, and handles instrumenting
@ -23,7 +21,7 @@ impl Menu {
let inner = unsafe { let inner = unsafe {
let cls = class!(NSMenu); let cls = class!(NSMenu);
let alloc: id = msg_send![cls, alloc]; let alloc: id = msg_send![cls, alloc];
let title = NSString::alloc(nil).init_str(title); let title = NSString::new(title);
let inner: id = msg_send![alloc, initWithTitle:title]; let inner: id = msg_send![alloc, initWithTitle:title];
Id::from_ptr(inner) Id::from_ptr(inner)
}; };

View file

@ -1,13 +1,11 @@
//! A lightweight wrapper over some networking components, like `NSURLRequest` and co. //! A lightweight wrapper over some networking components, like `NSURLRequest` and co.
//! This is currently not meant to be exhaustive. //! This is currently not meant to be exhaustive.
use cocoa::base::id;
use objc_id::Id;
use objc::{msg_send, sel, sel_impl}; use objc::{msg_send, sel, sel_impl};
use objc::runtime::Object; use objc::runtime::Object;
use objc_id::Id;
use crate::utils::str_from; use crate::foundation::{id, NSString};
pub struct URLRequest { pub struct URLRequest {
pub inner: Id<Object> pub inner: Id<Object>
@ -21,10 +19,9 @@ impl URLRequest {
} }
pub fn url(&self) -> &'static str { pub fn url(&self) -> &'static str {
unsafe { NSString::wrap(unsafe {
let url: id = msg_send![&*self.inner, URL]; let url: id = msg_send![&*self.inner, URL];
let path: id = msg_send![url, absoluteString]; msg_send![url, absoluteString]
str_from(path) }).to_str()
}
} }
} }

View file

@ -1,6 +1,96 @@
//! A wrapper for NSPasteBoard, which is the interface for copy/paste and general transferring
//! (think: drag and drop between applications). It exposes a Rust interface that tries to be
//! complete, but might not cover everything 100% right now - feel free to pull request.
use std::error::Error;
use objc::runtime::Object;
use objc::{class, msg_send, sel, sel_impl};
use objc_id::Id;
use url::Url;
use crate::foundation::{id, nil, NSString, NSArray};
use crate::error::AppKitError;
pub mod types; pub mod types;
pub use types::*; pub use types::{PasteboardName, PasteboardType};
pub mod pasteboard; /// Represents an `NSPasteboard`, enabling you to handle copy/paste/drag and drop.
pub use pasteboard::*; pub struct Pasteboard(pub Id<Object>);
impl Default for Pasteboard {
fn default() -> Self {
Pasteboard(unsafe {
Id::from_retained_ptr(msg_send![class!(NSPasteboard), generalPasteboard])
})
}
}
impl Pasteboard {
/// Used internally for wrapping a Pasteboard returned from operations (say, drag and drop).
pub(crate) fn with(existing: id) -> Self {
Pasteboard(unsafe {
Id::from_retained_ptr(existing)
})
}
/// Retrieves the system Pasteboard for the given name/type.
pub fn named(name: PasteboardName) -> Self {
Pasteboard(unsafe {
let name: NSString = name.into();
Id::from_retained_ptr(msg_send![class!(NSPasteboard), pasteboardWithName:&*name.0])
})
}
/// Creates and returns a new pasteboard with a name that is guaranteed to be unique with
/// respect to other pasteboards in the system.
pub fn unique() -> Self {
Pasteboard(unsafe {
Id::from_ptr(msg_send![class!(NSPasteboard), pasteboardWithUniqueName])
})
}
/// Releases the receivers resources in the pasteboard server. It's rare-ish to need to use
/// this, but considering this stuff happens on the Objective-C side you may need it.
pub fn release_globally(&self) {
unsafe {
let _: () = msg_send![&*self.0, releaseGlobally];
}
}
/// Clears the existing contents of the pasteboard.
pub fn clear_contents(&self) {
unsafe {
let _: () = msg_send![&*self.0, clearContents];
}
}
/// Looks inside the pasteboard contents and extracts what FileURLs are there, if any.
pub fn get_file_urls(&self) -> Result<Vec<Url>, Box<dyn Error>> {
unsafe {
let class: id = msg_send![class!(NSURL), class];
let classes = NSArray::new(&[class]);
let contents: id = msg_send![&*self.0, readObjectsForClasses:classes options:nil];
// This can happen if the Pasteboard server has an error in returning items.
// In our case, we'll bubble up an error by checking the pasteboard.
if contents == nil {
// This error is not necessarily "correct", but in the event of an error in
// Pasteboard server retrieval I'm not sure where to check... and this stuff is
// kinda ancient and has conflicting docs in places. ;P
return Err(Box::new(AppKitError {
code: 666,
domain: "com.appkit-rs.pasteboard".to_string(),
description: "Pasteboard server returned no data.".to_string()
}));
}
let urls = NSArray::wrap(contents).map(|url| {
let path = NSString::wrap(msg_send![url, path]);
Url::parse(&format!("file://{}", path.to_str()))
}).into_iter().filter_map(|r| r.ok()).collect();
Ok(urls)
}
}
}

View file

@ -1,128 +0,0 @@
//! A wrapper for NSPasteBoard, which is the interface for copy/paste and general transferring
//! (think: drag and drop between applications). It exposes a Rust interface that tries to be
//! complete, but might not cover everything 100% right now - feel free to pull request.
use std::error::Error;
use cocoa::base::{id, nil};
use cocoa::foundation::{NSArray};
use objc::runtime::Object;
use objc::{class, msg_send, sel, sel_impl};
use objc_id::Id;
use url::Url;
use crate::error::AppKitError;
use crate::pasteboard::types::{PasteboardName};
use crate::utils::str_from;
/// Represents an `NSPasteboard`, enabling you to handle copy/paste/drag and drop.
pub struct Pasteboard {
/// The internal pointer to the Objective-C side.
pub inner: Id<Object>
}
impl Default for Pasteboard {
fn default() -> Self {
Pasteboard {
inner: unsafe { Id::from_ptr(msg_send![class!(NSPasteboard), generalPasteboard]) }
}
}
}
impl Pasteboard {
/// Used internally for wrapping a Pasteboard returned from operations (say, drag and drop).
pub(crate) fn with(existing: id) -> Self {
Pasteboard {
inner: unsafe { Id::from_ptr(existing) }
}
}
/// Retrieves the system Pasteboard for the given name/type.
pub fn named(name: PasteboardName) -> Self {
Pasteboard {
inner: unsafe {
let name = name.to_nsstring();
Id::from_ptr(msg_send![class!(NSPasteboard), pasteboardWithName:name])
}
}
}
/// Creates and returns a new pasteboard with a name that is guaranteed to be unique with
/// respect to other pasteboards in the system.
pub fn unique() -> Self {
Pasteboard {
inner: unsafe { Id::from_ptr(msg_send![class!(NSPasteboard), pasteboardWithUniqueName]) }
}
}
/// Releases the receivers resources in the pasteboard server. It's rare-ish to need to use
/// this, but considering this stuff happens on the Objective-C side you may need it.
pub fn release_globally(&self) {
unsafe {
let _: () = msg_send![&*self.inner, releaseGlobally];
}
}
/// Clears the existing contents of the pasteboard.
pub fn clear_contents(&self) {
unsafe {
let _: () = msg_send![&*self.inner, clearContents];
}
}
/// Looks inside the pasteboard contents and extracts what FileURLs are there, if any.
pub fn get_file_urls(&self) -> Result<Vec<Url>, Box<dyn Error>> {
unsafe {
let mut i = 0;
let class: id = msg_send![class!(NSURL), class];
let classes: id = NSArray::arrayWithObjects(nil, &[class]);
let contents: id = msg_send![&*self.inner, readObjectsForClasses:classes options:nil];
// This can happen if the Pasteboard server has an error in returning items.
// In our case, we'll bubble up an error by checking the pasteboard.
if contents == nil {
// This error is not necessarily "correct", but in the event of an error in
// Pasteboard server retrieval I'm not sure where to check... and this stuff is
// kinda ancient and has conflicting docs in places. ;P
return Err(Box::new(AppKitError {
code: 666,
domain: "com.appkit-rs.pasteboard".to_string(),
description: "Pasteboard server returned no data.".to_string()
}));
}
let count: usize = msg_send![contents, count];
let mut urls: Vec<Url> = Vec::with_capacity(count);
loop {
let nsurl: id = msg_send![contents, objectAtIndex:i];
let path: id = msg_send![nsurl, path];
let s = str_from(path);
urls.push(Url::parse(&format!("file://{}", s))?);
i += 1;
if i == count { break; }
}
Ok(urls)
}
}
/*
/// Retrieves the pasteboard contents as a string. This can be `None` (`nil` on the Objective-C
/// side) if the pasteboard data doesn't match the requested type, so check accordingly.
///
/// Note: In macOS 10.6 and later, if the receiver contains multiple items that can provide string,
/// RTF, or RTFD data, the text data from each item is returned as a combined result separated by newlines.
/// This Rust wrapper is a quick pass, and could be improved. ;P
pub fn contents_for(&self, pasteboard_type: PasteboardType) -> Option<String> {
unsafe {
let contents: id = msg_send![&*self.inner, stringForType:pasteboard_type.to_nsstring()];
if contents != nil {
return Some(str_from(contents).to_string());
}
}
None
}*/
}

View file

@ -1,8 +1,7 @@
//! This module provides some basic wrappers for Pasteboard functionality. It's currently not an //! This module provides some basic wrappers for Pasteboard functionality. It's currently not an
//! exhaustive clone, but feel free to pull request accordingly! //! exhaustive clone, but feel free to pull request accordingly!
use cocoa::base::{id, nil}; use crate::foundation::NSString;
use cocoa::foundation::NSString;
/// Constants for the standard system pasteboard names. /// Constants for the standard system pasteboard names.
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
@ -23,11 +22,9 @@ pub enum PasteboardName {
Ruler Ruler
} }
impl PasteboardName { impl From<PasteboardName> for NSString {
/// Creates an `NSString` out of the underlying type. fn from(name: PasteboardName) -> Self {
pub fn to_nsstring(&self) -> id { NSString::new(match name {
unsafe {
NSString::alloc(nil).init_str(match self {
PasteboardName::Drag => "Apple CFPasteboard drag", PasteboardName::Drag => "Apple CFPasteboard drag",
PasteboardName::Find => "Apple CFPasteboard find", PasteboardName::Find => "Apple CFPasteboard find",
PasteboardName::Font => "Apple CFPasteboard font", PasteboardName::Font => "Apple CFPasteboard font",
@ -35,7 +32,6 @@ impl PasteboardName {
PasteboardName::Ruler => "Apple CFPasteboard ruler" PasteboardName::Ruler => "Apple CFPasteboard ruler"
}) })
} }
}
} }
/// Represents different Pasteboard types that can be referred to. /// Represents different Pasteboard types that can be referred to.
@ -87,11 +83,9 @@ pub enum PasteboardType {
TIFF TIFF
} }
impl PasteboardType { impl From<PasteboardType> for NSString {
/// Creates an `NSString` out of the underlying type. fn from(pboard_type: PasteboardType) -> Self {
pub fn to_nsstring(&self) -> id { NSString::new(match pboard_type {
unsafe {
NSString::alloc(nil).init_str(match self {
PasteboardType::URL => "public.url", PasteboardType::URL => "public.url",
PasteboardType::Color => "com.apple.cocoa.pasteboard.color", PasteboardType::Color => "com.apple.cocoa.pasteboard.color",
PasteboardType::FileURL => "public.file-url", PasteboardType::FileURL => "public.file-url",
@ -109,5 +103,4 @@ impl PasteboardType {
PasteboardType::TIFF => "public.tiff", PasteboardType::TIFF => "public.tiff",
}) })
} }
}
} }

View file

@ -0,0 +1,32 @@
//! Enums used through the general printing flow.
use crate::foundation::NSUInteger;
/// Used for handling printing files. You return this in relevant `AppController` methods.
#[derive(Copy, Clone, Debug)]
pub enum PrintResponse {
/// Printing was cancelled.
Cancelled,
/// Printing was a success.
Success,
/// Printing failed.
Failure,
/// For when the result of printing cannot be returned immediately (e.g, if printing causes a sheet to appear).
/// If your method returns PrintResponse::ReplyLater it must always invoke `App::reply_to_open_or_print()` when the
/// entire print operation has been completed, successfully or not.
ReplyLater
}
impl From<PrintResponse> for NSUInteger {
fn from(response: PrintResponse) -> NSUInteger {
match response {
PrintResponse::Cancelled => 0,
PrintResponse::Success => 1,
PrintResponse::Failure => 3,
PrintResponse::ReplyLater => 2
}
}
}

View file

@ -1,5 +1,8 @@
//! Implements types used for printing (both configuring print jobs, as well as the act of printing //! Implements types used for printing (both configuring print jobs, as well as the act of printing
//! itself). //! itself).
pub mod enums;
pub use enums::PrintResponse;
pub mod settings; pub mod settings;
pub use settings::PrintSettings; pub use settings::PrintSettings;

View file

@ -1,10 +1,11 @@
//! Represents settings for printing items. Backed by an `NSDictionary` in Objective-C, this struct //! Represents settings for printing items. Backed by an `NSDictionary` in Objective-C, this struct
//! aims to make it easier to query/process printing operations. //! aims to make it easier to query/process printing operations.
use cocoa::base::id;
use objc::runtime::Object; use objc::runtime::Object;
use objc_id::ShareId; use objc_id::ShareId;
use crate::foundation::id;
/// `PrintSettings` represents options used in printing, typically passed to you by the /// `PrintSettings` represents options used in printing, typically passed to you by the
/// application/user. /// application/user.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]

View file

@ -3,58 +3,51 @@
use std::rc::Rc; use std::rc::Rc;
use std::sync::Once; use std::sync::Once;
use cocoa::base::{id, nil};
use cocoa::foundation::{NSArray, NSString};
use objc::declare::ClassDecl; use objc::declare::ClassDecl;
use objc::runtime::{Class, Object, Sel}; use objc::runtime::{Class, Object, Sel};
use objc::{class, sel, sel_impl}; use objc::{class, sel, sel_impl};
use crate::foundation::{id, NSArray, NSString};
use crate::constants::TOOLBAR_PTR; use crate::constants::TOOLBAR_PTR;
use crate::toolbar::traits::ToolbarController; use crate::toolbar::traits::ToolbarController;
use crate::utils::{load, str_from}; use crate::utils::load;
/// Retrieves and passes the allowed item identifiers for this toolbar. /// Retrieves and passes the allowed item identifiers for this toolbar.
extern fn allowed_item_identifiers<T: ToolbarController>(this: &Object, _: Sel, _: id) -> id { extern fn allowed_item_identifiers<T: ToolbarController>(this: &Object, _: Sel, _: id) -> id {
let toolbar = load::<T>(this, TOOLBAR_PTR); let toolbar = load::<T>(this, TOOLBAR_PTR);
unsafe { let identifiers: NSArray = {
let identifiers = {
let t = toolbar.borrow(); let t = toolbar.borrow();
(*t).allowed_item_identifiers().iter().map(|identifier| { (*t).allowed_item_identifiers().iter().map(|identifier| {
NSString::alloc(nil).init_str(identifier) NSString::new(identifier).into_inner()
}).collect::<Vec<id>>() }).collect::<Vec<id>>().into()
}; };
Rc::into_raw(toolbar); Rc::into_raw(toolbar);
NSArray::arrayWithObjects(nil, &identifiers) identifiers.into_inner()
}
} }
/// Retrieves and passes the default item identifiers for this toolbar. /// Retrieves and passes the default item identifiers for this toolbar.
extern fn default_item_identifiers<T: ToolbarController>(this: &Object, _: Sel, _: id) -> id { extern fn default_item_identifiers<T: ToolbarController>(this: &Object, _: Sel, _: id) -> id {
let toolbar = load::<T>(this, TOOLBAR_PTR); let toolbar = load::<T>(this, TOOLBAR_PTR);
unsafe { let identifiers: NSArray = {
let identifiers = {
let t = toolbar.borrow(); let t = toolbar.borrow();
(*t).default_item_identifiers().iter().map(|identifier| { (*t).default_item_identifiers().iter().map(|identifier| {
NSString::alloc(nil).init_str(identifier) NSString::new(identifier).into_inner()
}).collect::<Vec<id>>() }).collect::<Vec<id>>().into()
}; };
Rc::into_raw(toolbar); Rc::into_raw(toolbar);
NSArray::arrayWithObjects(nil, &identifiers) identifiers.into_inner()
}
} }
/// Loads the controller, grabs whatever item is for this identifier, and returns what the /// Loads the controller, grabs whatever item is for this identifier, and returns what the
/// Objective-C runtime needs. /// Objective-C runtime needs.
extern fn item_for_identifier<T: ToolbarController>(this: &Object, _: Sel, _: id, identifier: id, _: id) -> id { extern fn item_for_identifier<T: ToolbarController>(this: &Object, _: Sel, _: id, identifier: id, _: id) -> id {
let toolbar = load::<T>(this, TOOLBAR_PTR); let toolbar = load::<T>(this, TOOLBAR_PTR);
let identifier = str_from(identifier); let identifier = NSString::wrap(identifier).to_str();
let mut item = { let mut item = {
let t = toolbar.borrow(); let t = toolbar.borrow();

View file

@ -1,13 +1,11 @@
//! A wrapper for the underlying `NSToolbar`, which is safe to clone and pass around. We do this to //! A wrapper for the underlying `NSToolbar`, which is safe to clone and pass around. We do this to
//! provide a uniform and expectable API. //! provide a uniform and expectable API.
use cocoa::base::{YES, NO};
use cocoa::foundation::{NSUInteger};
use objc::{msg_send, sel, sel_impl}; use objc::{msg_send, sel, sel_impl};
use objc::runtime::Object; use objc::runtime::Object;
use objc_id::ShareId; use objc_id::ShareId;
use crate::foundation::{YES, NO, NSUInteger};
use crate::toolbar::types::{ToolbarDisplayMode, ToolbarSizeMode}; use crate::toolbar::types::{ToolbarDisplayMode, ToolbarSizeMode};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]

View file

@ -3,13 +3,11 @@
//! //!
//! UNFORTUNATELY, this is a very old and janky API. So... yeah. //! UNFORTUNATELY, this is a very old and janky API. So... yeah.
use cocoa::base::{id, nil};
use cocoa::foundation::{NSSize, NSString};
use objc_id::Id; 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, CGSize, NSString};
use crate::button::Button; use crate::button::Button;
/// A wrapper for `NSWindow`. Holds (retains) pointers for the Objective-C runtime /// A wrapper for `NSWindow`. Holds (retains) pointers for the Objective-C runtime
@ -28,7 +26,7 @@ impl ToolbarItem {
let identifier = identifier.into(); let identifier = identifier.into();
let inner = unsafe { let inner = unsafe {
let identifr = NSString::alloc(nil).init_str(&identifier); let identifr = NSString::new(&identifier);
let alloc: id = msg_send![class!(NSToolbarItem), alloc]; let alloc: id = msg_send![class!(NSToolbarItem), alloc];
let item: id = msg_send![alloc, initWithItemIdentifier:identifr]; let item: id = msg_send![alloc, initWithItemIdentifier:identifr];
Id::from_ptr(item) Id::from_ptr(item)
@ -44,7 +42,7 @@ impl ToolbarItem {
/// Sets the title for this item. /// Sets the title for this item.
pub fn set_title(&mut self, title: &str) { pub fn set_title(&mut self, title: &str) {
unsafe { unsafe {
let title = NSString::alloc(nil).init_str(title); let title = NSString::new(title);
let _: () = msg_send![&*self.inner, setTitle:title]; let _: () = msg_send![&*self.inner, setTitle:title];
} }
} }
@ -63,7 +61,7 @@ impl ToolbarItem {
/// Sets the minimum size for this button. /// Sets the minimum size for this button.
pub fn set_min_size(&mut self, width: f64, height: f64) { pub fn set_min_size(&mut self, width: f64, height: f64) {
unsafe { unsafe {
let size = NSSize::new(width.into(), height.into()); let size = CGSize::new(width.into(), height.into());
let _: () = msg_send![&*self.inner, setMinSize:size]; let _: () = msg_send![&*self.inner, setMinSize:size];
} }
} }
@ -71,7 +69,7 @@ impl ToolbarItem {
/// Sets the maximum size for this button. /// Sets the maximum size for this button.
pub fn set_max_size(&mut self, width: f64, height: f64) { pub fn set_max_size(&mut self, width: f64, height: f64) {
unsafe { unsafe {
let size = NSSize::new(width.into(), height.into()); let size = CGSize::new(width.into(), height.into());
let _: () = msg_send![&*self.inner, setMaxSize:size]; let _: () = msg_send![&*self.inner, setMaxSize:size];
} }
} }

View file

@ -6,12 +6,10 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use cocoa::base::{id, nil};
use cocoa::foundation::NSString;
use objc_id::ShareId; use objc_id::ShareId;
use objc::{msg_send, sel, sel_impl}; use objc::{msg_send, sel, sel_impl};
use crate::foundation::{id, NSString};
use crate::constants::TOOLBAR_PTR; use crate::constants::TOOLBAR_PTR;
use crate::toolbar::class::register_toolbar_class; use crate::toolbar::class::register_toolbar_class;
use crate::toolbar::handle::ToolbarHandle; use crate::toolbar::handle::ToolbarHandle;
@ -49,7 +47,7 @@ impl<T> Toolbar<T> where T: ToolbarController + 'static {
let objc_controller = unsafe { let objc_controller = unsafe {
let delegate_class = register_toolbar_class::<T>(); let delegate_class = register_toolbar_class::<T>();
let identifier = NSString::alloc(nil).init_str(&identifier); let identifier = NSString::new(&identifier);
let alloc: id = msg_send![delegate_class, alloc]; let alloc: id = msg_send![delegate_class, alloc];
let toolbar: id = msg_send![alloc, initWithIdentifier:identifier]; let toolbar: id = msg_send![alloc, initWithIdentifier:identifier];

View file

@ -1,6 +1,6 @@
//! Various types used for Toolbar configuration. //! Various types used for Toolbar configuration.
use cocoa::foundation::NSUInteger; use crate::foundation::NSUInteger;
/// Represents the display mode(s) a Toolbar can render in. /// Represents the display mode(s) a Toolbar can render in.
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]

View file

@ -1,9 +1,10 @@
//! A module wrapping `NSUserActivity`. //! A module wrapping `NSUserActivity`.
use cocoa::base::id;
use objc::runtime::Object; use objc::runtime::Object;
use objc_id::ShareId; use objc_id::ShareId;
use crate::foundation::id;
/// Represents an `NSUserActivity`, which acts as a lightweight method to capture the state of your /// Represents an `NSUserActivity`, which acts as a lightweight method to capture the state of your
/// app. /// app.
pub struct UserActivity { pub struct UserActivity {

View file

@ -4,51 +4,9 @@
use std::rc::Rc; use std::rc::Rc;
use std::cell::RefCell; use std::cell::RefCell;
use std::{slice, str};
use std::os::raw::c_char;
use cocoa::base::id;
use cocoa::foundation::NSString;
use objc::{msg_send, sel, sel_impl};
use objc::runtime::Object; use objc::runtime::Object;
/// A utility method for taking an `NSString` and bridging it to a Rust `&str`.
pub fn str_from(nsstring: id) -> &'static str {
unsafe {
let bytes = {
let bytes: *const c_char = msg_send![nsstring, UTF8String];
bytes as *const u8
};
let len = nsstring.len();
let bytes = slice::from_raw_parts(bytes, len);
str::from_utf8(bytes).unwrap()
}
}
/// A utility method for mapping over NSArray instances. There's a number of places where we want
/// or need this functionality to provide Rust interfaces - this tries to do it in a way where the
/// `Vec` doesn't need to resize after being allocated.
pub fn map_nsarray<T, F>(array: id, transform: F) -> Vec<T>
where F: Fn(id) -> T {
let count: usize = unsafe { msg_send![array, count] };
let mut ret: Vec<T> = Vec::with_capacity(count);
let mut index = 0;
loop {
let file: id = unsafe { msg_send![array, objectAtIndex:index] };
ret.push(transform(file));
index += 1;
if index == count { break }
}
ret
}
/// Used for moving a pointer back into an Rc, so we can work with the object held behind it. Note /// Used for moving a pointer back into an Rc, so we can work with the object held behind it. Note
/// that it's very important to make sure you reverse this when you're done (using /// that it's very important to make sure you reverse this when you're done (using
/// `Rc::into_raw()`) otherwise you'll cause problems due to the `Drop` logic. /// `Rc::into_raw()`) otherwise you'll cause problems due to the `Drop` logic.

View file

@ -9,13 +9,13 @@ use std::ffi::c_void;
use block::Block; use block::Block;
use cocoa::base::{id, nil, YES, NO};
use cocoa::foundation::{NSRect, NSPoint, NSSize, NSString, NSArray, NSInteger}; use cocoa::foundation::{NSRect, NSPoint, NSSize, NSString, NSArray, NSInteger};
use objc::declare::ClassDecl; use objc::declare::ClassDecl;
use objc::runtime::{Class, Object, Sel, BOOL}; use objc::runtime::{Class, Object, Sel, BOOL};
use objc::{class, msg_send, sel, sel_impl}; use objc::{class, msg_send, sel, sel_impl};
use crate::foundation::{id, nil, YES, NO};
use crate::webview::traits::WebViewController; use crate::webview::traits::WebViewController;
extern fn download_delegate(this: &Object, _: Sel) -> id { extern fn download_delegate(this: &Object, _: Sel) -> id {

View file

@ -11,13 +11,11 @@
use std::rc::Rc; use std::rc::Rc;
use std::cell::RefCell; use std::cell::RefCell;
use cocoa::base::{id, nil, YES, NO};
use cocoa::foundation::NSString;
use objc_id::ShareId; use objc_id::ShareId;
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, nil, YES, NO, NSString};
use crate::constants::{WEBVIEW_VAR, WEBVIEW_CONFIG_VAR, WEBVIEW_CONTROLLER_PTR}; use crate::constants::{WEBVIEW_VAR, WEBVIEW_CONFIG_VAR, WEBVIEW_CONTROLLER_PTR};
use crate::view::ViewController; use crate::view::ViewController;
use crate::webview::config::{WebViewConfig, InjectAt}; use crate::webview::config::{WebViewConfig, InjectAt};