use day_pieces::colorpicker::*;
use day_spec::Color;
use day_spec::Event;
use std::cell::RefCell;
use std::collections::HashMap;
use crate::AppKit;
use day_spec::{NodeId, Proposal, Size};
use objc2::rc::Retained;
use objc2::runtime::{AnyObject, NSObjectProtocol};
use objc2::{DefinedClass, MainThreadMarker, MainThreadOnly, define_class, msg_send, sel};
use objc2_app_kit::{NSColor, NSColorPanel, NSColorSpace, NSColorWell, NSColorWellStyle, NSView};
use objc2_foundation::NSObject;
struct TargetIvars {
node: NodeId,
}
define_class!(
#[unsafe(super(NSObject))]
#[thread_kind = MainThreadOnly]
#[name = "DayColorWellTarget"]
#[ivars = TargetIvars]
struct WellTarget;
unsafe impl NSObjectProtocol for WellTarget {}
impl WellTarget {
#[unsafe(method(fire:))]
fn fire(&self, sender: &AnyObject) {
let Some(well) = sender.downcast_ref::<NSColorWell>() else {
return;
};
if let Some(c) = to_day_color(&well.color()) {
crate::emit(self.ivars().node, Event::custom(PICK_TAG, c.to_string()));
}
}
}
);
impl WellTarget {
fn new(mtm: MainThreadMarker, node: NodeId) -> Retained<Self> {
let this = Self::alloc(mtm).set_ivars(TargetIvars { node });
unsafe { msg_send![super(this), init] }
}
}
day_core::tls_group! {
static TARGETS: RefCell<HashMap<usize, Retained<WellTarget>>> = RefCell::new(HashMap::new());
}
fn to_day_color(c: &NSColor) -> Option<Color> {
let srgb_space = NSColorSpace::sRGBColorSpace();
let srgb = c.colorUsingColorSpace(&srgb_space)?;
Some(Color::rgba(
srgb.redComponent(),
srgb.greenComponent(),
srgb.blueComponent(),
srgb.alphaComponent(),
))
}
fn to_ns_color(c: Color) -> Retained<NSColor> {
NSColor::colorWithSRGBRed_green_blue_alpha(c.r, c.g, c.b, c.a)
}
fn make(backend: &mut AppKit, p: &ColorProps, id: NodeId) -> Retained<NSView> {
let mtm = backend.mtm();
let well = NSColorWell::colorWellWithStyle(NSColorWellStyle::Default, mtm);
well.setColor(&to_ns_color(p.color));
if p.alpha {
if well.respondsToSelector(sel!(setSupportsAlpha:)) {
well.setSupportsAlpha(true);
}
NSColorPanel::sharedColorPanel(mtm).setShowsAlpha(true);
}
if !p.title.is_empty() {
NSColorPanel::sharedColorPanel(mtm)
.setTitle(&objc2_foundation::NSString::from_str(&p.title));
}
let target = WellTarget::new(mtm, id);
unsafe {
well.setTarget(Some(&target));
well.setAction(Some(sel!(fire:)));
}
let view: Retained<NSView> = Retained::from(<NSColorWell as AsRef<NSView>>::as_ref(&well));
TARGETS.with(|m| {
m.borrow_mut()
.insert((view.as_ref() as *const NSView) as usize, target)
});
view
}
fn update(_backend: &mut AppKit, h: &Retained<NSView>, patch: &ColorPatch) {
let ColorPatch::SetColor(c) = patch;
let Some(well) = h.downcast_ref::<NSColorWell>() else {
return;
};
if to_day_color(&well.color()) != Some(*c) {
well.setColor(&to_ns_color(*c));
}
}
fn measure(_backend: &mut AppKit, h: &Retained<NSView>, _p: Proposal) -> Size {
let s = h.fittingSize();
Size::new(s.width.ceil().max(44.0), s.height.ceil().max(24.0))
}
fn release(_backend: &mut AppKit, h: &Retained<NSView>) {
TARGETS.with(|m| {
m.borrow_mut()
.remove(&((h.as_ref() as *const NSView) as usize));
});
}
day_pieces::renderer!(crate::RENDERERS, AppKit,
kind: KIND, props: ColorProps, patch: ColorPatch,
make: make, update: update, measure: measure, release: release);