cgs 0.1.0

a ui library for macos
use objc2::AnyThread;
use objc2::rc::Retained;
use objc2::runtime::AnyObject;
use objc2_app_kit::*;
use objc2_foundation::{NSAttributedString, NSDictionary, NSString, NSURL};

use crate::{NativeControl, NativeView, Ui};

pub struct Font;
impl Font {
    pub fn body() -> Retained<NSFont> { NSFont::systemFontOfSize(NSFont::systemFontSize()) }

    pub fn body_emphasized() -> Retained<NSFont> {
        NSFont::boldSystemFontOfSize(NSFont::systemFontSize())
    }

    pub fn caption() -> Retained<NSFont> { NSFont::systemFontOfSize(NSFont::smallSystemFontSize()) }

    pub fn title() -> Retained<NSFont> {
        NSFont::systemFontOfSize_weight(20.0, unsafe { NSFontWeightSemibold })
    }

    pub fn section_title() -> Retained<NSFont> {
        NSFont::systemFontOfSize_weight(NSFont::systemFontSize(), unsafe { NSFontWeightSemibold })
    }

    pub fn subsection_title() -> Retained<NSFont> {
        NSFont::systemFontOfSize_weight(NSFont::smallSystemFontSize(), unsafe {
            NSFontWeightSemibold
        })
    }

    pub fn monospaced() -> Retained<NSFont> {
        NSFont::monospacedSystemFontOfSize_weight(NSFont::systemFontSize(), unsafe {
            NSFontWeightRegular
        })
    }
}
pub struct Color;
impl Color {
    pub fn label() -> Retained<NSColor> { NSColor::labelColor() }

    pub fn secondary_label() -> Retained<NSColor> { NSColor::secondaryLabelColor() }

    pub fn tertiary_label() -> Retained<NSColor> { NSColor::tertiaryLabelColor() }

    pub fn separator() -> Retained<NSColor> { NSColor::separatorColor() }

    pub fn control_accent() -> Retained<NSColor> { NSColor::controlAccentColor() }

    pub fn window_background() -> Retained<NSColor> { NSColor::windowBackgroundColor() }

    pub fn under_page_background() -> Retained<NSColor> { NSColor::underPageBackgroundColor() }

    pub fn rgba(r: f64, g: f64, b: f64, a: f64) -> Retained<NSColor> {
        NSColor::colorWithSRGBRed_green_blue_alpha(r, g, b, a)
    }
}

pub struct Label(Retained<NSTextField>);
impl Label {
    pub fn new(ui: &Ui, text: &str) -> Self {
        let native = NSTextField::labelWithString(&NSString::from_str(text), ui.mtm());
        native.setFont(Some(&Font::body()));
        Self(native)
    }

    pub fn ns_text_field(&self) -> &NSTextField { &self.0 }

    pub fn set_text(&self, value: &str) {
        if self.0.stringValue().to_string() != value {
            self.0.setStringValue(&NSString::from_str(value));
        }
    }

    pub fn font(self, value: &NSFont) -> Self {
        self.0.setFont(Some(value));
        self
    }

    pub fn color(self, value: &NSColor) -> Self {
        self.0.setTextColor(Some(value));
        self
    }

    pub fn wrapping(self) -> Self {
        if let Some(cell) = self.0.cell() {
            cell.setWraps(true);
        }
        self.0.setMaximumNumberOfLines(0);
        self
    }
}
impl NativeView for Label {
    fn ns_view(&self) -> &NSView { &self.0 }
}
impl NativeControl for Label {
    fn ns_control(&self) -> &NSControl { &self.0 }
}
macro_rules! text_style {
    ($name:ident, $font:ident, $color:ident) => {
        pub struct $name(Label);
        impl $name {
            pub fn new(ui: &Ui, text: &str) -> Self {
                Self(Label::new(ui, text).font(&Font::$font()).color(&Color::$color()).wrapping())
            }

            pub fn set_text(&self, text: &str) { self.0.set_text(text); }

            pub fn ns_text_field(&self) -> &NSTextField { self.0.ns_text_field() }
        }
        impl NativeView for $name {
            fn ns_view(&self) -> &NSView { self.0.ns_view() }
        }
    };
}
text_style!(SecondaryLabel, body, secondary_label);
text_style!(Title, title, label);
text_style!(SectionTitle, section_title, label);
text_style!(SubsectionTitle, subsection_title, secondary_label);
text_style!(Caption, caption, secondary_label);
text_style!(MonospaceLabel, monospaced, label);
text_style!(WrappingLabel, body, label);

pub struct Link(Label);
impl Link {
    pub fn new(ui: &Ui, text: &str, url: &str) -> Self {
        let label = Label::new(ui, text);
        if let Some(url) = NSURL::URLWithString(&NSString::from_str(url)) {
            let attrs =
                NSDictionary::from_slices(
                    &[unsafe { NSLinkAttributeName }],
                    &[&*url as &AnyObject],
                );
            let text = unsafe {
                NSAttributedString::initWithString_attributes(
                    NSAttributedString::alloc(),
                    &NSString::from_str(text),
                    Some(&attrs),
                )
            };
            label.ns_text_field().setAttributedStringValue(&text);
            label.ns_text_field().setAllowsEditingTextAttributes(true);
            label.ns_text_field().setSelectable(true);
        }
        Self(label)
    }

    pub fn ns_text_field(&self) -> &NSTextField { self.0.ns_text_field() }
}
impl NativeView for Link {
    fn ns_view(&self) -> &NSView { self.0.ns_view() }
}

pub struct Symbol;
impl Symbol {
    pub fn named(name: &str) -> Option<Retained<NSImage>> {
        NSImage::imageWithSystemSymbolName_accessibilityDescription(&NSString::from_str(name), None)
    }
}
pub struct ImageView(Retained<NSImageView>);
impl ImageView {
    pub fn new(ui: &Ui, image: &NSImage) -> Self {
        let native = NSImageView::new(ui.mtm());
        native.setImage(Some(image));
        Self(native)
    }

    pub fn symbol(ui: &Ui, name: &str) -> Option<Self> {
        Symbol::named(name).map(|image| Self::new(ui, &image))
    }

    pub fn ns_image_view(&self) -> &NSImageView { &self.0 }
}
impl NativeView for ImageView {
    fn ns_view(&self) -> &NSView { &self.0 }
}