2020-03-05 13:33:11 +11:00
|
|
|
//! A wrapper for `NSViewController`. Uses interior mutability to
|
|
|
|
|
|
|
|
use std::cell::RefCell;
|
2020-03-12 11:56:17 +11:00
|
|
|
use std::rc::Rc;
|
2020-03-05 13:33:11 +11:00
|
|
|
|
2020-03-11 14:09:24 +11:00
|
|
|
use cocoa::base::{id, nil, YES};
|
2020-03-07 14:35:18 +11:00
|
|
|
use cocoa::foundation::NSArray;
|
2020-03-05 13:33:11 +11:00
|
|
|
|
|
|
|
use objc_id::ShareId;
|
|
|
|
use objc::runtime::Object;
|
2020-03-07 14:35:18 +11:00
|
|
|
use objc::{msg_send, sel, sel_impl};
|
2020-03-05 13:33:11 +11:00
|
|
|
|
2020-03-07 14:35:18 +11:00
|
|
|
use crate::color::Color;
|
|
|
|
use crate::constants::{BACKGROUND_COLOR, VIEW_CONTROLLER_PTR};
|
2020-03-13 12:18:32 +11:00
|
|
|
use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension};
|
2020-03-07 14:35:18 +11:00
|
|
|
use crate::pasteboard::PasteboardType;
|
2020-03-05 13:33:11 +11:00
|
|
|
use crate::view::controller::register_controller_class;
|
2020-03-13 12:18:32 +11:00
|
|
|
use crate::view::traits::ViewController;
|
2020-03-05 13:33:11 +11:00
|
|
|
|
2020-03-12 11:56:17 +11:00
|
|
|
/// A clone-able handler to a `ViewController` reference in the Objective C runtime. We use this
|
|
|
|
/// instead of a stock `View` for easier recordkeeping, since it'll need to hold the `View` on that
|
|
|
|
/// side anyway.
|
|
|
|
#[derive(Debug, Default, Clone)]
|
2020-03-13 12:18:32 +11:00
|
|
|
pub struct ViewHandle {
|
|
|
|
/// A pointer to the Objective-C runtime view controller.
|
|
|
|
pub objc: Option<ShareId<Object>>,
|
|
|
|
|
|
|
|
/// A pointer to the Objective-C runtime top layout constraint.
|
|
|
|
pub top: LayoutAnchorY,
|
|
|
|
|
|
|
|
/// A pointer to the Objective-C runtime leading layout constraint.
|
|
|
|
pub leading: LayoutAnchorX,
|
|
|
|
|
|
|
|
/// A pointer to the Objective-C runtime trailing layout constraint.
|
|
|
|
pub trailing: LayoutAnchorX,
|
|
|
|
|
|
|
|
/// A pointer to the Objective-C runtime bottom layout constraint.
|
|
|
|
pub bottom: LayoutAnchorY,
|
|
|
|
|
|
|
|
/// A pointer to the Objective-C runtime width layout constraint.
|
|
|
|
pub width: LayoutAnchorDimension,
|
|
|
|
|
|
|
|
/// A pointer to the Objective-C runtime height layout constraint.
|
|
|
|
pub height: LayoutAnchorDimension,
|
|
|
|
|
|
|
|
/// A pointer to the Objective-C runtime center X layout constraint.
|
|
|
|
pub center_x: LayoutAnchorX,
|
|
|
|
|
|
|
|
/// A pointer to the Objective-C runtime center Y layout constraint.
|
|
|
|
pub center_y: LayoutAnchorY
|
|
|
|
}
|
2020-03-05 13:33:11 +11:00
|
|
|
|
2020-03-12 11:56:17 +11:00
|
|
|
impl ViewHandle {
|
2020-03-13 12:18:32 +11:00
|
|
|
pub(crate) fn new(object: ShareId<Object>) -> Self {
|
|
|
|
let view: id = unsafe {
|
|
|
|
msg_send![&*object, view]
|
|
|
|
};
|
|
|
|
|
|
|
|
ViewHandle {
|
|
|
|
objc: Some(object),
|
|
|
|
top: LayoutAnchorY::new(unsafe { msg_send![view, topAnchor] }),
|
|
|
|
leading: LayoutAnchorX::new(unsafe { msg_send![view, leadingAnchor] }),
|
|
|
|
trailing: LayoutAnchorX::new(unsafe { msg_send![view, trailingAnchor] }),
|
|
|
|
bottom: LayoutAnchorY::new(unsafe { msg_send![view, bottomAnchor] }),
|
|
|
|
width: LayoutAnchorDimension::new(unsafe { msg_send![view, widthAnchor] }),
|
|
|
|
height: LayoutAnchorDimension::new(unsafe { msg_send![view, heightAnchor] }),
|
|
|
|
center_x: LayoutAnchorX::new(unsafe { msg_send![view, centerXAnchor] }),
|
|
|
|
center_y: LayoutAnchorY::new(unsafe { msg_send![view, centerYAnchor] }),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-12 11:56:17 +11:00
|
|
|
/// Call this to set the background color for the backing layer.
|
|
|
|
pub fn set_background_color(&self, color: Color) {
|
2020-03-13 12:18:32 +11:00
|
|
|
if let Some(objc) = &self.objc {
|
2020-03-12 11:56:17 +11:00
|
|
|
unsafe {
|
2020-03-13 12:18:32 +11:00
|
|
|
let view: id = msg_send![*objc, view];
|
2020-03-12 11:56:17 +11:00
|
|
|
(*view).set_ivar(BACKGROUND_COLOR, color.into_platform_specific_color());
|
|
|
|
let _: () = msg_send![view, setNeedsDisplay:YES];
|
|
|
|
}
|
|
|
|
}
|
2020-03-05 13:33:11 +11:00
|
|
|
}
|
|
|
|
|
2020-03-12 11:56:17 +11:00
|
|
|
/// Register this view for drag and drop operations.
|
2020-03-07 14:35:18 +11:00
|
|
|
pub fn register_for_dragged_types(&self, types: &[PasteboardType]) {
|
2020-03-13 12:18:32 +11:00
|
|
|
if let Some(objc) = &self.objc {
|
2020-03-05 13:33:11 +11:00
|
|
|
unsafe {
|
|
|
|
let types = NSArray::arrayWithObjects(nil, &types.iter().map(|t| {
|
|
|
|
t.to_nsstring()
|
|
|
|
}).collect::<Vec<id>>());
|
|
|
|
|
2020-03-13 12:18:32 +11:00
|
|
|
let view: id = msg_send![*objc, view];
|
2020-03-05 13:33:11 +11:00
|
|
|
let _: () = msg_send![view, registerForDraggedTypes:types];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-03-13 12:18:32 +11:00
|
|
|
|
|
|
|
pub fn add_subview<T: Layout>(&self, subview: &T) {
|
|
|
|
if let Some(this) = &self.objc {
|
|
|
|
if let Some(subview_controller) = subview.get_backing_node() {
|
|
|
|
unsafe {
|
|
|
|
let _: () = msg_send![*this, addChildViewController:&*subview_controller];
|
|
|
|
|
|
|
|
let subview: id = msg_send![&*subview_controller, view];
|
|
|
|
let view: id = msg_send![*this, view];
|
|
|
|
let _: () = msg_send![view, addSubview:subview];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-03-12 11:56:17 +11:00
|
|
|
}
|
2020-03-07 14:35:18 +11:00
|
|
|
|
2020-03-12 11:56:17 +11:00
|
|
|
/// A `View` wraps two different controllers - one on the Objective-C/Cocoa side, which forwards
|
|
|
|
/// calls into your supplied `ViewController` trait object. This involves heap allocation, but all
|
|
|
|
/// of Cocoa is essentially Heap'd, so... well, enjoy.
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct View<T> {
|
|
|
|
internal_callback_ptr: *const RefCell<T>,
|
|
|
|
pub objc_controller: ViewHandle,
|
|
|
|
pub controller: Rc<RefCell<T>>
|
2020-03-05 13:33:11 +11:00
|
|
|
}
|
|
|
|
|
2020-03-12 11:56:17 +11:00
|
|
|
impl<T> View<T> where T: ViewController + 'static {
|
|
|
|
/// Allocates and configures a `ViewController` in the Objective-C/Cocoa runtime that maps over
|
|
|
|
/// to your supplied view controller.
|
|
|
|
pub fn new(controller: T) -> Self {
|
|
|
|
let controller = Rc::new(RefCell::new(controller));
|
|
|
|
|
|
|
|
let internal_callback_ptr = {
|
|
|
|
let cloned = Rc::clone(&controller);
|
|
|
|
Rc::into_raw(cloned)
|
|
|
|
};
|
|
|
|
|
|
|
|
let inner = unsafe {
|
|
|
|
let view_controller: id = msg_send![register_controller_class::<T>(), new];
|
|
|
|
(&mut *view_controller).set_ivar(VIEW_CONTROLLER_PTR, internal_callback_ptr as usize);
|
|
|
|
|
|
|
|
let view: id = msg_send![view_controller, view];
|
|
|
|
(&mut *view).set_ivar(VIEW_CONTROLLER_PTR, internal_callback_ptr as usize);
|
|
|
|
|
|
|
|
ShareId::from_ptr(view_controller)
|
|
|
|
};
|
2020-03-05 13:33:11 +11:00
|
|
|
|
2020-03-13 12:18:32 +11:00
|
|
|
let handle = ViewHandle::new(inner);
|
|
|
|
|
2020-03-05 13:33:11 +11:00
|
|
|
{
|
2020-03-12 11:56:17 +11:00
|
|
|
let mut vc = controller.borrow_mut();
|
2020-03-13 12:18:32 +11:00
|
|
|
(*vc).did_load(handle.clone());
|
2020-03-05 13:33:11 +11:00
|
|
|
}
|
|
|
|
|
2020-03-12 11:56:17 +11:00
|
|
|
View {
|
|
|
|
internal_callback_ptr: internal_callback_ptr,
|
2020-03-13 12:18:32 +11:00
|
|
|
objc_controller: handle,
|
2020-03-12 11:56:17 +11:00
|
|
|
controller: controller
|
|
|
|
}
|
2020-03-05 13:33:11 +11:00
|
|
|
}
|
|
|
|
|
2020-03-12 11:56:17 +11:00
|
|
|
pub fn set_background_color(&self, color: Color) {
|
|
|
|
self.objc_controller.set_background_color(color);
|
2020-03-05 13:33:11 +11:00
|
|
|
}
|
|
|
|
|
2020-03-07 14:35:18 +11:00
|
|
|
pub fn register_for_dragged_types(&self, types: &[PasteboardType]) {
|
2020-03-12 11:56:17 +11:00
|
|
|
self.objc_controller.register_for_dragged_types(types);
|
2020-03-05 13:33:11 +11:00
|
|
|
}
|
2020-03-13 12:18:32 +11:00
|
|
|
|
2020-03-13 13:18:16 +11:00
|
|
|
pub fn top(&self) -> &LayoutAnchorY {
|
|
|
|
&self.objc_controller.top
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn leading(&self) -> &LayoutAnchorX {
|
|
|
|
&self.objc_controller.leading
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn trailing(&self) -> &LayoutAnchorX {
|
|
|
|
&self.objc_controller.trailing
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn bottom(&self) -> &LayoutAnchorY {
|
|
|
|
&self.objc_controller.bottom
|
|
|
|
}
|
|
|
|
|
2020-03-13 12:18:32 +11:00
|
|
|
pub fn width(&self) -> &LayoutAnchorDimension {
|
|
|
|
&self.objc_controller.width
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn height(&self) -> &LayoutAnchorDimension {
|
|
|
|
&self.objc_controller.height
|
|
|
|
}
|
2020-03-12 11:56:17 +11:00
|
|
|
}
|
2020-03-07 14:35:18 +11:00
|
|
|
|
2020-03-13 12:18:32 +11:00
|
|
|
impl<T> Layout for View<T> {
|
2020-03-12 11:56:17 +11:00
|
|
|
/// Returns the Objective-C object used for handling the view heirarchy.
|
|
|
|
fn get_backing_node(&self) -> Option<ShareId<Object>> {
|
2020-03-13 12:18:32 +11:00
|
|
|
self.objc_controller.objc.clone()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn add_subview<V: Layout>(&self, subview: &V) {
|
|
|
|
self.objc_controller.add_subview(subview);
|
2020-03-07 14:35:18 +11:00
|
|
|
}
|
2020-03-05 13:33:11 +11:00
|
|
|
}
|
2020-03-11 14:09:24 +11:00
|
|
|
|
2020-03-12 11:56:17 +11:00
|
|
|
impl<T> std::fmt::Debug for View<T> {
|
2020-03-11 14:09:24 +11:00
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
write!(f, "View ({:p})", self)
|
|
|
|
}
|
|
|
|
}
|
2020-03-12 11:56:17 +11:00
|
|
|
|
|
|
|
impl<T> Drop for View<T> {
|
|
|
|
/// A bit of extra cleanup for delegate callback pointers.
|
|
|
|
fn drop(&mut self) {
|
|
|
|
unsafe {
|
|
|
|
let _ = Rc::from_raw(self.internal_callback_ptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|