2020-02-27 18:34:34 -08:00
|
|
|
//! Implements an NSToolbar, which is one of those macOS niceties
|
|
|
|
//! that makes it feel... "proper".
|
|
|
|
//!
|
|
|
|
//! UNFORTUNATELY, this is a very old and janky API. So... yeah.
|
|
|
|
|
2020-03-11 19:45:35 -07:00
|
|
|
use std::cell::RefCell;
|
|
|
|
use std::rc::Rc;
|
2020-02-27 18:34:34 -08:00
|
|
|
use std::sync::Once;
|
|
|
|
|
|
|
|
use cocoa::base::{id, nil};
|
|
|
|
use cocoa::foundation::{NSArray, NSString};
|
|
|
|
|
2020-03-11 19:45:35 -07:00
|
|
|
use objc_id::ShareId;
|
2020-02-27 18:34:34 -08:00
|
|
|
use objc::declare::ClassDecl;
|
|
|
|
use objc::runtime::{Class, Object, Sel};
|
2020-03-06 19:35:18 -08:00
|
|
|
use objc::{class, msg_send, sel, sel_impl};
|
2020-02-27 18:34:34 -08:00
|
|
|
|
2020-03-06 19:35:18 -08:00
|
|
|
use crate::constants::TOOLBAR_PTR;
|
2020-03-11 19:45:35 -07:00
|
|
|
use crate::toolbar::traits::ToolbarController;
|
|
|
|
use crate::utils::{load, str_from};
|
2020-02-27 18:34:34 -08:00
|
|
|
|
2020-03-06 19:35:18 -08:00
|
|
|
/// A wrapper for `NSToolbar`. Holds (retains) pointers for the Objective-C runtime
|
|
|
|
/// where our `NSToolbar` and associated delegate live.
|
2020-03-11 19:45:35 -07:00
|
|
|
pub struct Toolbar<T> {
|
|
|
|
internal_callback_ptr: *const RefCell<T>,
|
|
|
|
pub identifier: String,
|
|
|
|
pub objc_controller: ShareId<Object>,
|
|
|
|
pub controller: Rc<RefCell<T>>
|
2020-02-27 18:34:34 -08:00
|
|
|
}
|
|
|
|
|
2020-03-11 19:45:35 -07:00
|
|
|
impl<T> Toolbar<T> where T: ToolbarController + 'static {
|
2020-02-27 18:34:34 -08:00
|
|
|
/// Creates a new `NSToolbar` instance, configures it appropriately, injects an `NSObject`
|
|
|
|
/// delegate wrapper, and retains the necessary Objective-C runtime pointers.
|
2020-03-11 19:45:35 -07:00
|
|
|
pub fn new<S: Into<String>>(identifier: S, controller: T) -> Self {
|
|
|
|
let identifier = identifier.into();
|
|
|
|
let controller = Rc::new(RefCell::new(controller));
|
|
|
|
|
|
|
|
let internal_callback_ptr = {
|
|
|
|
let cloned = Rc::clone(&controller);
|
|
|
|
Rc::into_raw(cloned)
|
2020-02-27 18:34:34 -08:00
|
|
|
};
|
|
|
|
|
2020-03-11 19:45:35 -07:00
|
|
|
let objc_controller = unsafe {
|
|
|
|
let delegate_class = register_delegate_class::<T>();
|
|
|
|
let identifier = NSString::alloc(nil).init_str(&identifier);
|
|
|
|
let alloc: id = msg_send![delegate_class, alloc];
|
|
|
|
let toolbar: id = msg_send![alloc, initWithIdentifier:identifier];
|
|
|
|
|
|
|
|
(&mut *toolbar).set_ivar(TOOLBAR_PTR, internal_callback_ptr as usize);
|
|
|
|
let _: () = msg_send![toolbar, setDelegate:toolbar];
|
2020-02-27 18:34:34 -08:00
|
|
|
|
2020-03-11 19:45:35 -07:00
|
|
|
ShareId::from_ptr(toolbar)
|
2020-02-27 18:34:34 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
Toolbar {
|
2020-03-11 19:45:35 -07:00
|
|
|
internal_callback_ptr: internal_callback_ptr,
|
|
|
|
identifier: identifier,
|
|
|
|
objc_controller: objc_controller,
|
|
|
|
controller: controller
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> Drop for Toolbar<T> {
|
|
|
|
/// A bit of extra cleanup for delegate callback pointers.
|
|
|
|
fn drop(&mut self) {
|
|
|
|
unsafe {
|
|
|
|
let _ = Rc::from_raw(self.internal_callback_ptr);
|
2020-02-27 18:34:34 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Loops back to the delegate.
|
2020-03-11 19:45:35 -07:00
|
|
|
extern fn allowed_item_identifiers<T: ToolbarController>(this: &Object, _: Sel, _: id) -> id {
|
|
|
|
let toolbar = load::<T>(this, TOOLBAR_PTR);
|
|
|
|
|
2020-02-27 18:34:34 -08:00
|
|
|
unsafe {
|
2020-03-11 19:45:35 -07:00
|
|
|
let identifiers = {
|
|
|
|
let t = toolbar.borrow();
|
|
|
|
|
|
|
|
(*t).allowed_item_identifiers().iter().map(|identifier| {
|
|
|
|
NSString::alloc(nil).init_str(identifier)
|
|
|
|
}).collect::<Vec<id>>()
|
|
|
|
};
|
2020-02-27 18:34:34 -08:00
|
|
|
|
2020-03-11 19:45:35 -07:00
|
|
|
Rc::into_raw(toolbar);
|
2020-02-27 18:34:34 -08:00
|
|
|
NSArray::arrayWithObjects(nil, &identifiers)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Loops back to the delegate.
|
2020-03-11 19:45:35 -07:00
|
|
|
extern fn default_item_identifiers<T: ToolbarController>(this: &Object, _: Sel, _: id) -> id {
|
|
|
|
let toolbar = load::<T>(this, TOOLBAR_PTR);
|
|
|
|
|
2020-02-27 18:34:34 -08:00
|
|
|
unsafe {
|
2020-03-11 19:45:35 -07:00
|
|
|
let identifiers = {
|
|
|
|
let t = toolbar.borrow();
|
|
|
|
|
|
|
|
(*t).default_item_identifiers().iter().map(|identifier| {
|
|
|
|
NSString::alloc(nil).init_str(identifier)
|
|
|
|
}).collect::<Vec<id>>()
|
|
|
|
};
|
2020-02-27 18:34:34 -08:00
|
|
|
|
2020-03-11 19:45:35 -07:00
|
|
|
Rc::into_raw(toolbar);
|
2020-02-27 18:34:34 -08:00
|
|
|
NSArray::arrayWithObjects(nil, &identifiers)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Loops back to the delegate.
|
2020-03-11 19:45:35 -07:00
|
|
|
extern fn item_for_identifier<T: ToolbarController>(this: &Object, _: Sel, _: id, identifier: id, _: id) -> id {
|
|
|
|
let toolbar = load::<T>(this, TOOLBAR_PTR);
|
|
|
|
let identifier = str_from(identifier);
|
|
|
|
|
|
|
|
let mut item = {
|
|
|
|
let t = toolbar.borrow();
|
|
|
|
let item = (*t).item_for(identifier);
|
|
|
|
item
|
|
|
|
};
|
|
|
|
|
|
|
|
Rc::into_raw(toolbar);
|
|
|
|
&mut *item.inner
|
2020-02-27 18:34:34 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Registers an `NSObject` subclass, and configures it to hold some ivars for various things we need
|
|
|
|
/// to store.
|
2020-03-11 19:45:35 -07:00
|
|
|
fn register_delegate_class<T: ToolbarController>() -> *const Class {
|
|
|
|
static mut TOOLBAR_CLASS: *const Class = 0 as *const Class;
|
2020-02-27 18:34:34 -08:00
|
|
|
static INIT: Once = Once::new();
|
|
|
|
|
|
|
|
INIT.call_once(|| unsafe {
|
2020-03-11 19:45:35 -07:00
|
|
|
let superclass = class!(NSToolbar);
|
|
|
|
let mut decl = ClassDecl::new("RSTToolbar", superclass).unwrap();
|
2020-02-27 18:34:34 -08:00
|
|
|
|
|
|
|
// For callbacks
|
|
|
|
decl.add_ivar::<usize>(TOOLBAR_PTR);
|
|
|
|
|
|
|
|
// Add callback methods
|
2020-03-11 19:45:35 -07:00
|
|
|
decl.add_method(sel!(toolbarAllowedItemIdentifiers:), allowed_item_identifiers::<T> as extern fn(&Object, _, _) -> id);
|
|
|
|
decl.add_method(sel!(toolbarDefaultItemIdentifiers:), default_item_identifiers::<T> as extern fn(&Object, _, _) -> id);
|
|
|
|
decl.add_method(sel!(toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar:), item_for_identifier::<T> as extern fn(&Object, _, _, _, _) -> id);
|
2020-02-27 18:34:34 -08:00
|
|
|
|
2020-03-11 19:45:35 -07:00
|
|
|
TOOLBAR_CLASS = decl.register();
|
2020-02-27 18:34:34 -08:00
|
|
|
});
|
|
|
|
|
2020-03-11 19:45:35 -07:00
|
|
|
unsafe { TOOLBAR_CLASS }
|
2020-02-27 18:34:34 -08:00
|
|
|
}
|