use objc::runtime::{Class, Object};
use objc::{msg_send, sel, sel_impl};
use crate::color::Color;
use crate::foundation::{id, nil, NSArray, NSString, NO, YES};
use crate::layer::Layer;
use crate::layout::Layout;
use crate::objc_access::ObjcAccess;
use crate::utils::properties::ObjcProperty;
#[cfg(feature = "autolayout")]
use crate::layout::{LayoutAnchorDimension, LayoutAnchorX, LayoutAnchorY, SafeAreaLayoutGuide};
#[cfg(feature = "appkit")]
use crate::pasteboard::PasteboardType;
mod animator;
pub use animator::ViewAnimatorProxy;
#[cfg_attr(feature = "appkit", path = "appkit.rs")]
#[cfg_attr(feature = "uikit", path = "uikit.rs")]
mod native_interface;
mod controller;
pub use controller::ViewController;
#[cfg(feature = "appkit")]
mod splitviewcontroller;
#[cfg(feature = "appkit")]
pub use splitviewcontroller::SplitViewController;
mod traits;
pub use traits::ViewDelegate;
pub(crate) static BACKGROUND_COLOR: &str = "cacaoBackgroundColor";
pub(crate) static VIEW_DELEGATE_PTR: &str = "rstViewDelegatePtr";
#[derive(Debug)]
pub struct View<T = ()> {
pub is_handle: bool,
pub objc: ObjcProperty,
pub animator: ViewAnimatorProxy,
pub layer: Layer,
pub delegate: Option<Box<T>>,
#[cfg(feature = "autolayout")]
pub safe_layout_guide: SafeAreaLayoutGuide,
#[cfg(feature = "autolayout")]
pub top: LayoutAnchorY,
#[cfg(feature = "autolayout")]
pub leading: LayoutAnchorX,
#[cfg(feature = "autolayout")]
pub left: LayoutAnchorX,
#[cfg(feature = "autolayout")]
pub trailing: LayoutAnchorX,
#[cfg(feature = "autolayout")]
pub right: LayoutAnchorX,
#[cfg(feature = "autolayout")]
pub bottom: LayoutAnchorY,
#[cfg(feature = "autolayout")]
pub width: LayoutAnchorDimension,
#[cfg(feature = "autolayout")]
pub height: LayoutAnchorDimension,
#[cfg(feature = "autolayout")]
pub center_x: LayoutAnchorX,
#[cfg(feature = "autolayout")]
pub center_y: LayoutAnchorY
}
impl Default for View {
fn default() -> Self {
View::new()
}
}
impl View {
pub(crate) fn init<T>(view: id) -> View<T> {
unsafe {
#[cfg(feature = "autolayout")]
let _: () = msg_send![view, setTranslatesAutoresizingMaskIntoConstraints: NO];
#[cfg(feature = "appkit")]
let _: () = msg_send![view, setWantsLayer: YES];
}
View {
is_handle: false,
delegate: None,
#[cfg(feature = "autolayout")]
safe_layout_guide: SafeAreaLayoutGuide::new(view),
#[cfg(feature = "autolayout")]
top: LayoutAnchorY::top(view),
#[cfg(feature = "autolayout")]
left: LayoutAnchorX::left(view),
#[cfg(feature = "autolayout")]
leading: LayoutAnchorX::leading(view),
#[cfg(feature = "autolayout")]
right: LayoutAnchorX::right(view),
#[cfg(feature = "autolayout")]
trailing: LayoutAnchorX::trailing(view),
#[cfg(feature = "autolayout")]
bottom: LayoutAnchorY::bottom(view),
#[cfg(feature = "autolayout")]
width: LayoutAnchorDimension::width(view),
#[cfg(feature = "autolayout")]
height: LayoutAnchorDimension::height(view),
#[cfg(feature = "autolayout")]
center_x: LayoutAnchorX::center(view),
#[cfg(feature = "autolayout")]
center_y: LayoutAnchorY::center(view),
layer: Layer::wrap(unsafe { msg_send![view, layer] }),
animator: ViewAnimatorProxy::new(view),
objc: ObjcProperty::retain(view)
}
}
pub fn new() -> Self {
View::init(unsafe { msg_send![native_interface::register_view_class(), new] })
}
}
impl<T> View<T>
where
T: ViewDelegate + 'static
{
pub fn with(delegate: T) -> View<T> {
let class = native_interface::register_view_class_with_delegate(&delegate);
let mut delegate = Box::new(delegate);
let view = unsafe {
let view: id = msg_send![class, new];
let ptr = Box::into_raw(delegate);
(&mut *view).set_ivar(VIEW_DELEGATE_PTR, ptr as usize);
delegate = Box::from_raw(ptr);
view
};
let mut view = View::init(view);
(&mut delegate).did_load(view.clone_as_handle());
view.delegate = Some(delegate);
view
}
}
impl<T> View<T> {
pub fn clone_as_handle(&self) -> View {
View {
delegate: None,
is_handle: true,
layer: self.layer.clone(),
objc: self.objc.clone(),
animator: self.animator.clone(),
#[cfg(feature = "autolayout")]
safe_layout_guide: self.safe_layout_guide.clone(),
#[cfg(feature = "autolayout")]
top: self.top.clone(),
#[cfg(feature = "autolayout")]
leading: self.leading.clone(),
#[cfg(feature = "autolayout")]
left: self.left.clone(),
#[cfg(feature = "autolayout")]
trailing: self.trailing.clone(),
#[cfg(feature = "autolayout")]
right: self.right.clone(),
#[cfg(feature = "autolayout")]
bottom: self.bottom.clone(),
#[cfg(feature = "autolayout")]
width: self.width.clone(),
#[cfg(feature = "autolayout")]
height: self.height.clone(),
#[cfg(feature = "autolayout")]
center_x: self.center_x.clone(),
#[cfg(feature = "autolayout")]
center_y: self.center_y.clone()
}
}
pub fn set_background_color<C: AsRef<Color>>(&self, color: C) {
let color: id = color.as_ref().into();
#[cfg(feature = "appkit")]
self.objc.with_mut(|obj| unsafe {
(&mut *obj).set_ivar(BACKGROUND_COLOR, color);
});
#[cfg(feature = "uikit")]
self.objc.with_mut(|obj| unsafe {
let _: () = msg_send![&*obj, setBackgroundColor: color];
});
}
}
impl<T> ObjcAccess for View<T> {
fn with_backing_obj_mut<F: Fn(id)>(&self, handler: F) {
self.objc.with_mut(handler);
}
fn get_from_backing_obj<F: Fn(&Object) -> R, R>(&self, handler: F) -> R {
self.objc.get(handler)
}
}
impl<T> Layout for View<T> {}
impl<T> Drop for View<T> {
fn drop(&mut self) {
if !self.is_handle {
self.remove_from_superview();
}
}
}