use std::sync::{Arc, RwLock};
use core_graphics::base::CGFloat;
use core_graphics::color::CGColor;
use objc::runtime::Object;
use objc::{class, msg_send, sel, sel_impl};
use objc_id::Id;
use crate::foundation::id;
use crate::utils::os;
#[cfg(feature = "appkit")]
mod appkit_dynamic_color;
#[cfg(feature = "appkit")]
use appkit_dynamic_color::{
AQUA_DARK_COLOR_HIGH_CONTRAST, AQUA_DARK_COLOR_NORMAL_CONTRAST, AQUA_LIGHT_COLOR_HIGH_CONTRAST,
AQUA_LIGHT_COLOR_NORMAL_CONTRAST
};
#[derive(Copy, Clone, Debug)]
pub enum Theme {
Light,
Dark
}
#[derive(Copy, Clone, Debug)]
pub enum Contrast {
Normal,
High
}
#[derive(Copy, Clone, Debug)]
pub struct Style {
pub theme: Theme,
pub contrast: Contrast
}
#[derive(Clone, Debug)]
pub enum Color {
Custom(Arc<RwLock<Id<Object>>>),
SystemBlack,
SystemWhite,
SystemBrown,
SystemBlue,
SystemGreen,
SystemIndigo,
SystemOrange,
SystemPink,
SystemPurple,
SystemRed,
SystemTeal,
SystemYellow,
SystemGray,
SystemGray2,
SystemGray3,
SystemGray4,
SystemGray5,
SystemGray6,
Clear,
Label,
LabelSecondary,
LabelTertiary,
LabelQuaternary,
SystemFill,
SystemFillSecondary,
SystemFillTertiary,
SystemFillQuaternary,
PlaceholderText,
SystemBackground,
SystemBackgroundSecondary,
SystemBackgroundTertiary,
Separator,
#[cfg(feature = "uikit")]
OpaqueSeparator,
Link,
DarkText,
LightText,
#[cfg(feature = "appkit")]
MacOSWindowBackgroundColor,
#[cfg(feature = "appkit")]
MacOSUnderPageBackgroundColor
}
impl Color {
pub fn rgba(red: u8, green: u8, blue: u8, alpha: u8) -> Self {
let r = red as CGFloat / 255.0;
let g = green as CGFloat / 255.0;
let b = blue as CGFloat / 255.0;
let a = alpha as CGFloat / 255.0;
#[cfg(feature = "appkit")]
let ptr = unsafe { Id::from_ptr(msg_send![class!(NSColor), colorWithCalibratedRed:r green:g blue:b alpha:a]) };
#[cfg(all(feature = "uikit", not(feature = "appkit")))]
let ptr = unsafe { Id::from_ptr(msg_send![class!(UIColor), colorWithRed:r green:g blue:b alpha:a]) };
Color::Custom(Arc::new(RwLock::new(ptr)))
}
pub fn rgb(red: u8, green: u8, blue: u8) -> Self {
Color::rgba(red, green, blue, 255)
}
pub fn hsba(hue: u8, saturation: u8, brightness: u8, alpha: u8) -> Self {
let h = hue as CGFloat / 255.0;
let s = saturation as CGFloat / 255.0;
let b = brightness as CGFloat / 255.0;
let a = alpha as CGFloat / 255.0;
Color::Custom(Arc::new(RwLock::new(unsafe {
#[cfg(feature = "appkit")]
{
Id::from_ptr(msg_send![class!(NSColor), colorWithCalibratedHue:h saturation:s brightness:b alpha:a])
}
#[cfg(all(feature = "uikit", not(feature = "appkit")))]
{
Id::from_ptr(msg_send![class!(UIColor), colorWithHue:h saturation:s brightness:b alpha:a])
}
})))
}
pub fn hsb(hue: u8, saturation: u8, brightness: u8) -> Self {
Color::hsba(hue, saturation, brightness, 255)
}
pub fn white_alpha(level: CGFloat, alpha: CGFloat) -> Self {
Color::Custom(Arc::new(RwLock::new(unsafe {
#[cfg(feature = "appkit")]
{
Id::from_ptr(msg_send![class!(NSColor), colorWithCalibratedWhite:level alpha:alpha])
}
#[cfg(all(feature = "uikit", not(feature = "appkit")))]
{
Id::from_ptr(msg_send![class!(UIColor), colorWithWhite:level alpha:alpha])
}
})))
}
pub fn white(level: CGFloat) -> Self {
Color::white_alpha(level, 1.0)
}
pub fn hexa(_hex: &str, _alpha: u8) -> Self {
Color::SystemRed
}
pub fn hex(hex: &str) -> Self {
Color::hexa(hex, 255)
}
#[cfg(feature = "appkit")]
pub fn dynamic<F>(handler: F) -> Self
where
F: Fn(Style) -> Color + 'static
{
Color::Custom(Arc::new(RwLock::new(unsafe {
let color: id = msg_send![appkit_dynamic_color::register_class(), new];
(&mut *color).set_ivar(AQUA_LIGHT_COLOR_NORMAL_CONTRAST, {
let color: id = handler(Style {
theme: Theme::Light,
contrast: Contrast::Normal
})
.into();
color
});
(&mut *color).set_ivar(AQUA_LIGHT_COLOR_HIGH_CONTRAST, {
let color: id = handler(Style {
theme: Theme::Light,
contrast: Contrast::High
})
.into();
color
});
(&mut *color).set_ivar(AQUA_DARK_COLOR_NORMAL_CONTRAST, {
let color: id = handler(Style {
theme: Theme::Dark,
contrast: Contrast::Normal
})
.into();
color
});
(&mut *color).set_ivar(AQUA_DARK_COLOR_HIGH_CONTRAST, {
let color: id = handler(Style {
theme: Theme::Light,
contrast: Contrast::Normal
})
.into();
color
});
Id::from_ptr(color)
})))
}
pub fn cg_color(&self) -> CGColor {
unsafe {
let objc: id = self.into();
msg_send![objc, CGColor]
}
}
}
impl AsRef<Color> for Color {
#[inline]
fn as_ref(&self) -> &Color {
self
}
}
impl From<Color> for id {
fn from(color: Color) -> Self {
unsafe { to_objc(&color) }
}
}
impl From<&Color> for id {
fn from(color: &Color) -> Self {
unsafe { to_objc(color) }
}
}
macro_rules! system_color_with_fallback {
($class:ident, $color:ident, $fallback:ident) => {{
#[cfg(feature = "appkit")]
{
#[cfg(feature = "color-fallbacks")]
if os::minimum_semversion(10, 10, 0) {
msg_send![$class, $color]
} else {
msg_send![$class, $fallback]
}
#[cfg(not(feature = "color-fallbacks"))]
msg_send![$class, $color]
}
#[cfg(feature = "uikit")]
{
msg_send![$class, $color]
}
}};
}
unsafe fn to_objc(obj: &Color) -> id {
#[cfg(feature = "appkit")]
let color = class!(NSColor);
#[cfg(feature = "uikit")]
let color = class!(UIColor);
match obj {
Color::Custom(color) => {
let mut ptr = color.write().unwrap();
&mut **ptr
},
Color::SystemBlack => msg_send![color, blackColor],
Color::SystemWhite => msg_send![color, whiteColor],
Color::SystemBrown => msg_send![color, brownColor],
Color::SystemBlue => system_color_with_fallback!(color, systemBlueColor, blueColor),
Color::SystemGreen => system_color_with_fallback!(color, systemGreenColor, greenColor),
Color::SystemIndigo => system_color_with_fallback!(color, systemIndigoColor, magentaColor),
Color::SystemOrange => system_color_with_fallback!(color, systemOrangeColor, orangeColor),
Color::SystemPink => system_color_with_fallback!(color, systemPinkColor, pinkColor),
Color::SystemPurple => system_color_with_fallback!(color, systemPurpleColor, purpleColor),
Color::SystemRed => system_color_with_fallback!(color, systemRedColor, redColor),
Color::SystemTeal => system_color_with_fallback!(color, systemTealColor, blueColor),
Color::SystemYellow => system_color_with_fallback!(color, systemYellowColor, yellowColor),
Color::SystemGray => system_color_with_fallback!(color, systemGrayColor, darkGrayColor),
Color::SystemGray2 => system_color_with_fallback!(color, systemGray2Color, grayColor),
Color::SystemGray3 => system_color_with_fallback!(color, systemGray3Color, lightGrayColor),
Color::SystemGray4 => system_color_with_fallback!(color, systemGray4Color, lightGrayColor),
Color::SystemGray5 => system_color_with_fallback!(color, systemGray5Color, lightGrayColor),
Color::SystemGray6 => system_color_with_fallback!(color, systemGray6Color, lightGrayColor),
Color::Clear => msg_send![color, clearColor],
Color::Label => system_color_with_fallback!(color, labelColor, blackColor),
Color::LabelSecondary => system_color_with_fallback!(color, secondaryLabelColor, blackColor),
Color::LabelTertiary => system_color_with_fallback!(color, tertiaryLabelColor, blackColor),
Color::LabelQuaternary => system_color_with_fallback!(color, quaternaryLabelColor, blackColor),
Color::SystemFill => system_color_with_fallback!(color, systemFillColor, clearColor),
Color::SystemFillSecondary => system_color_with_fallback!(color, secondarySystemFillColor, clearColor),
Color::SystemFillTertiary => system_color_with_fallback!(color, tertiarySystemFillColor, clearColor),
Color::SystemFillQuaternary => system_color_with_fallback!(color, quaternarySystemFillColor, clearColor),
Color::PlaceholderText => system_color_with_fallback!(color, placeholderTextColor, darkGrayColor),
Color::SystemBackground => system_color_with_fallback!(color, systemBackgroundColor, clearColor),
Color::SystemBackgroundSecondary => system_color_with_fallback!(color, secondarySystemBackgroundColor, clearColor),
Color::SystemBackgroundTertiary => system_color_with_fallback!(color, tertiarySystemBackgroundColor, clearColor),
Color::Separator => system_color_with_fallback!(color, separatorColor, lightGrayColor),
#[cfg(feature = "uikit")]
Color::OpaqueSeparator => system_color_with_fallback!(color, opaqueSeparatorColor, darkGrayColor),
Color::Link => system_color_with_fallback!(color, linkColor, blueColor),
Color::DarkText => system_color_with_fallback!(color, darkTextColor, blackColor),
Color::LightText => system_color_with_fallback!(color, lightTextColor, whiteColor),
#[cfg(feature = "appkit")]
Color::MacOSWindowBackgroundColor => system_color_with_fallback!(color, windowBackgroundColor, clearColor),
#[cfg(feature = "appkit")]
Color::MacOSUnderPageBackgroundColor => system_color_with_fallback!(color, underPageBackgroundColor, clearColor)
}
}