use objc::runtime::{Class, Object};
use objc::{msg_send, sel, sel_impl};
use objc_id::ShareId;
use crate::color::Color;
use crate::foundation::{id, nil, NSArray, NSInteger, NSString, NSUInteger, NO, YES};
use crate::layout::Layout;
use crate::objc_access::ObjcAccess;
use crate::text::{AttributedString, Font, LineBreakMode, TextAlign};
use crate::utils::properties::ObjcProperty;
#[cfg(feature = "autolayout")]
use crate::layout::{LayoutAnchorDimension, LayoutAnchorX, LayoutAnchorY};
#[cfg(feature = "appkit")]
mod appkit;
#[cfg(feature = "appkit")]
use appkit::{register_view_class, register_view_class_with_delegate};
mod traits;
pub use traits::LabelDelegate;
pub(crate) static LABEL_DELEGATE_PTR: &str = "rstLabelDelegatePtr";
fn allocate_view(registration_fn: fn() -> *const Class) -> id {
unsafe {
#[cfg(feature = "appkit")]
let view: id = {
let blank = NSString::no_copy("");
let label: id = msg_send![registration_fn(), wrappingLabelWithString:&*blank];
let _: () = msg_send![label, setSelectable: NO];
label
};
#[cfg(feature = "uikit")]
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 Label<T = ()> {
pub objc: ObjcProperty,
pub delegate: Option<Box<T>>,
#[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 Label {
fn default() -> Self {
Label::new()
}
}
impl Label {
pub fn new() -> Self {
let view = allocate_view(register_view_class);
Label {
delegate: None,
#[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),
objc: ObjcProperty::retain(view)
}
}
}
impl<T> Label<T>
where
T: LabelDelegate + 'static
{
pub fn with(delegate: T) -> Label<T> {
let delegate = Box::new(delegate);
let label = allocate_view(register_view_class_with_delegate::<T>);
unsafe {
let ptr: *const T = &*delegate;
(&mut *label).set_ivar(LABEL_DELEGATE_PTR, ptr as usize);
};
let mut label = Label {
delegate: None,
#[cfg(feature = "autolayout")]
top: LayoutAnchorY::top(label),
#[cfg(feature = "autolayout")]
left: LayoutAnchorX::left(label),
#[cfg(feature = "autolayout")]
leading: LayoutAnchorX::leading(label),
#[cfg(feature = "autolayout")]
right: LayoutAnchorX::right(label),
#[cfg(feature = "autolayout")]
trailing: LayoutAnchorX::trailing(label),
#[cfg(feature = "autolayout")]
bottom: LayoutAnchorY::bottom(label),
#[cfg(feature = "autolayout")]
width: LayoutAnchorDimension::width(label),
#[cfg(feature = "autolayout")]
height: LayoutAnchorDimension::height(label),
#[cfg(feature = "autolayout")]
center_x: LayoutAnchorX::center(label),
#[cfg(feature = "autolayout")]
center_y: LayoutAnchorY::center(label),
objc: ObjcProperty::retain(label)
};
label.delegate = Some(delegate);
label
}
}
impl<T> Label<T> {
pub(crate) fn clone_as_handle(&self) -> Label {
Label {
delegate: None,
#[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(),
objc: self.objc.clone()
}
}
pub fn set_background_color<C: AsRef<Color>>(&self, color: C) {
self.objc.with_mut(|obj| unsafe {
let color = color.as_ref().cg_color();
let layer: id = msg_send![obj, layer];
let _: () = msg_send![layer, setBackgroundColor: color];
});
}
pub fn set_text_color<C: AsRef<Color>>(&self, color: C) {
let color: id = color.as_ref().into();
self.objc.with_mut(|obj| unsafe {
let _: () = msg_send![obj, setTextColor: color];
});
}
pub fn set_text<S: AsRef<str>>(&self, text: S) {
let text = text.as_ref();
let s = NSString::new(text);
self.objc.with_mut(|obj| unsafe {
let _: () = msg_send![obj, setStringValue:&*s];
});
}
pub fn set_attributed_text(&self, text: AttributedString) {
self.objc.with_mut(|obj| unsafe {
let _: () = msg_send![obj, setAttributedStringValue:&*text];
});
}
pub fn get_text(&self) -> String {
self.objc
.get(|obj| unsafe { NSString::retain(msg_send![obj, stringValue]).to_string() })
}
pub fn set_text_alignment(&self, alignment: TextAlign) {
self.objc.with_mut(|obj| unsafe {
let alignment: NSInteger = alignment.into();
let _: () = msg_send![obj, setAlignment: alignment];
});
}
pub fn set_font<F: AsRef<Font>>(&self, font: F) {
let font = font.as_ref().clone();
self.objc.with_mut(|obj| unsafe {
let _: () = msg_send![obj, setFont:&*font];
});
}
pub fn set_hidden(&self, hidden: bool) {
self.objc.with_mut(|obj| unsafe {
let _: () = msg_send![obj, setHidden:match hidden {
true => YES,
false => NO
}];
});
}
pub fn set_max_number_of_lines(&self, num: NSInteger) {
self.objc.with_mut(|obj| unsafe {
let _: () = msg_send![obj, setMaximumNumberOfLines: num];
});
}
pub fn set_line_break_mode(&self, mode: LineBreakMode) {
#[cfg(feature = "appkit")]
self.objc.with_mut(|obj| unsafe {
let cell: id = msg_send![obj, cell];
let mode = mode as NSUInteger;
let _: () = msg_send![cell, setTruncatesLastVisibleLine: YES];
let _: () = msg_send![cell, setLineBreakMode: mode];
});
}
}
impl<T> ObjcAccess for Label<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 Label<T> {}
impl<T> Drop for Label<T> {
fn drop(&mut self) {
}
}