use objc::runtime::{Object, Sel, Class};
use objc::declare::ClassDecl;
use cocoa::base::{id, class};
use std;
type Void = std::os::raw::c_void;
pub trait Actionable {
fn act(&mut self);
}
impl<F> Actionable for F
where F: FnMut()
{
fn act(&mut self) {
self()
}
}
struct Action(pub Box<Actionable>);
impl Action {
fn act(&mut self) {
self.0.act();
}
}
pub fn register() {
let superclass = Class::get("NSObject").expect("NSObject");
let mut decl = match ClassDecl::new("Action", superclass) {
Some(decl) => decl,
_ => return, };
decl.add_ivar::<*mut Void>("_action");
extern "C" fn action_initialize(this: &mut Object, _: Sel, action: *mut Void) {
unsafe { this.set_ivar("_action", action) };
}
extern "C" fn action_act(this: &mut Object, _cmd: Sel) {
unsafe {
let action: &mut Box<Action> =
std::mem::transmute(this.get_mut_ivar::<*mut Void>("_action"));
action.act();
}
}
extern "C" fn action_dealloc(this: &Object, _cmd: Sel) {
if let Some(superclass) = this.class().superclass() {
unsafe { msg_send![super(this, superclass), dealloc] };
}
}
unsafe {
decl.add_method(sel!(initialize:),
action_initialize as extern "C" fn(&mut Object, Sel, *mut Void));
decl.add_method(sel!(act), action_act as extern "C" fn(&mut Object, Sel));
decl.add_method(sel!(dealloc), action_dealloc as extern "C" fn(&Object, Sel));
}
decl.register();
}
pub fn create<F: FnMut() + 'static>(action: F) -> id {
register();
unsafe {
let act: id = msg_send![class("Action"), alloc];
let target: id = msg_send![act, init];
msg_send![target, autorelease];
let action = Box::new(Action(Box::new(action)));
let action: *mut Void = std::mem::transmute(Box::into_raw(action));
msg_send![target, initialize: action];
act
}
}
pub fn spawn<F: FnMut() + Send + 'static>(action: F) {
let action = create(action);
unsafe {
use cocoa::base::nil;
msg_send![action, performSelectorInBackground:sel!(act)
withObject:nil]
};
}