use std::fmt;
use std::sync::{Arc, Mutex, Once};
use block::{Block, ConcreteBlock, RcBlock};
use objc::declare::ClassDecl;
use objc::runtime::{Class, Object, Sel};
use objc::{class, msg_send, sel, sel_impl};
use objc_id::ShareId;
use crate::foundation::{id, nil, NSString};
use crate::utils::load;
pub static ACTION_CALLBACK_PTR: &str = "rstTargetActionPtr";
pub struct Action(Box<dyn Fn() + Send + Sync + 'static>);
impl fmt::Debug for Action {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Action").finish()
}
}
#[derive(Debug)]
pub struct TargetActionHandler {
action: Box<Action>,
invoker: ShareId<Object>
}
impl TargetActionHandler {
pub fn new<F: Fn() + Send + Sync + 'static>(control: &Object, action: F) -> Self {
let block = Box::new(Action(Box::new(action)));
let ptr = Box::into_raw(block);
let invoker = unsafe {
ShareId::from_ptr({
let invoker: id = msg_send![register_invoker_class::<F>(), alloc];
let invoker: id = msg_send![invoker, init];
(&mut *invoker).set_ivar(ACTION_CALLBACK_PTR, ptr as usize);
let _: () = msg_send![control, setAction: sel!(perform:)];
let _: () = msg_send![control, setTarget: invoker];
invoker
})
};
TargetActionHandler {
invoker: invoker,
action: unsafe { Box::from_raw(ptr) }
}
}
}
extern "C" fn perform<F: Fn() + 'static>(this: &mut Object, _: Sel, _sender: id) {
let action = load::<Action>(this, ACTION_CALLBACK_PTR);
(action.0)();
}
pub(crate) fn register_invoker_class<F: Fn() + 'static>() -> *const Class {
static mut VIEW_CLASS: *const Class = 0 as *const Class;
static INIT: Once = Once::new();
INIT.call_once(|| unsafe {
let superclass = class!(NSObject);
let mut decl = ClassDecl::new("RSTTargetActionHandler", superclass).unwrap();
decl.add_ivar::<usize>(ACTION_CALLBACK_PTR);
decl.add_method(sel!(perform:), perform::<F> as extern "C" fn(&mut Object, _, id));
VIEW_CLASS = decl.register();
});
unsafe { VIEW_CLASS }
}