use std::cell::RefCell;
use std::rc::Rc;
use objc::runtime::{Class, Object};
use objc::{class, msg_send, sel, sel_impl};
use objc_id::{Id, ShareId};
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;
use crate::view::{ViewAnimatorProxy, ViewDelegate};
#[cfg(feature = "autolayout")]
use crate::layout::{LayoutAnchorDimension, LayoutAnchorX, LayoutAnchorY, SafeAreaLayoutGuide};
#[cfg(feature = "appkit")]
mod appkit;
#[cfg(feature = "appkit")]
use appkit::{register_listview_row_class, register_listview_row_class_with_delegate};
pub(crate) static BACKGROUND_COLOR: &str = "cacaoBackgroundColor";
pub(crate) static LISTVIEW_ROW_DELEGATE_PTR: &str = "cacaoListViewRowDelegatePtr";
fn allocate_view(registration_fn: fn() -> *const Class) -> id {
unsafe {
let view: id = msg_send![registration_fn(), new];
#[cfg(feature = "autolayout")]
let _: () = msg_send![view, setTranslatesAutoresizingMaskIntoConstraints: NO];
#[cfg(feature = "appkit")]
let _: () = msg_send![view, setWantsLayer: YES];
view
}
}
#[derive(Debug)]
pub struct ListViewRow<T = ()> {
pub animator: ViewAnimatorProxy,
pub objc: ObjcProperty,
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 ListViewRow {
fn default() -> Self {
ListViewRow::new()
}
}
impl ListViewRow {
pub fn new() -> Self {
let view = allocate_view(register_listview_row_class);
ListViewRow {
delegate: None,
objc: ObjcProperty::retain(view),
animator: ViewAnimatorProxy::new(view),
#[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)
}
}
}
impl<T> ListViewRow<T>
where
T: ViewDelegate + 'static
{
pub(crate) fn from_cached(view: id) -> ListViewRow<T> {
let delegate = unsafe {
let ptr: usize = *(&*view).get_ivar(LISTVIEW_ROW_DELEGATE_PTR);
let obj = ptr as *mut T;
Box::from_raw(obj)
};
let view = ListViewRow {
delegate: Some(delegate),
objc: ObjcProperty::retain(view),
animator: ViewAnimatorProxy::new(view),
#[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)
};
view
}
pub fn with(delegate: T) -> ListViewRow<T> {
let delegate = Box::new(delegate);
Self::with_boxed(delegate)
}
pub fn with_boxed(mut delegate: Box<T>) -> ListViewRow<T> {
let view = allocate_view(register_listview_row_class_with_delegate::<T>);
unsafe {
let ptr: *const T = &*delegate;
(&mut *view).set_ivar(LISTVIEW_ROW_DELEGATE_PTR, ptr as usize);
};
let mut view = ListViewRow {
delegate: None,
objc: ObjcProperty::retain(view),
animator: ViewAnimatorProxy::new(view),
#[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)
};
(&mut delegate).did_load(view.clone_as_handle());
view.delegate = Some(delegate);
view
}
pub fn into_row(mut self) -> ListViewRow {
let delegate = self.delegate.take();
if let Some(d) = delegate {
let _ = Box::into_raw(d);
}
ListViewRow {
delegate: None,
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()
}
}
}
impl<T> ListViewRow<T> {
pub(crate) fn clone_as_handle(&self) -> crate::view::View {
crate::view::View {
delegate: None,
is_handle: true,
layer: Layer::new(), 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_identifier(&self, identifier: &'static str) {
let identifier = NSString::new(identifier);
self.objc.with_mut(|obj| unsafe {
let _: () = msg_send![obj, setIdentifier:&*identifier];
});
}
pub fn set_background_color<C: AsRef<Color>>(&self, color: C) {
let color: id = color.as_ref().into();
self.objc.with_mut(|obj| unsafe {
(&mut *obj).set_ivar(BACKGROUND_COLOR, color);
});
}
}
impl<T> ObjcAccess for ListViewRow<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 ListViewRow<T> {}
impl<T> Drop for ListViewRow<T> {
fn drop(&mut self) {}
}