rust-macios 0.4.2

Apple Frameworks for Rust
Documentation
use std::sync::{Arc, Mutex};

use rust_macios::{
    appkit::{
        ns_application_main, INSApplication, INSButton, INSLayoutAnchor, INSResponder, INSView,
        INSViewController, NSApplication, NSApplicationActivationPolicy, NSLayoutConstraint,
        NSPopover, NSPopoverBehavior, NSStatusBar, NSStatusItem, NSTextField, NSView,
        PNSApplicationDelegate,
    },
    foundation::{NSPoint, NSRect, NSRectEdge, NSSize, NSString},
    nsarray,
    objective_c_runtime::{
        class_init, id, msg_send, nil, objc_sel, register_class, runtime::Object, sel, sel_impl,
        traits::PNSObject, Id, ShareId,
    },
};

pub struct ViewController {
    pub ptr: ShareId<Object>,
}

#[register_class(NSViewController)]
impl ViewController {
    #[class_init]
    fn init() -> Self {
        Self {
            ptr: unsafe { Id::from_ptr(msg_send![Self::m_class(), new]) },
        }
    }

    #[objc_sel("viewDidLoad")]
    pub fn view_did_load(&self) {
        // 1: Create a view
        self.p_set_view(NSView::init_with_frame(NSRect {
            origin: NSPoint { x: 0.0, y: 0.0 },
            size: NSSize {
                width: 300.0,
                height: 300.0,
            },
        }));

        // 2: Create a label
        let label =
            NSTextField::label_with_string(NSString::from("NSViewController without Storyboard"));

        label.p_set_translates_autoresizing_mask_to_constraints(false);
        let view = self.p_view();

        view.p_add_subview(label.clone());
        self.p_set_view(view);

        NSLayoutConstraint::activate_constraints(nsarray![
            label
                .p_center_x_anchor()
                .m_constraint_equal_to_anchor(self.p_view().p_center_x_anchor()),
            label
                .p_center_y_anchor()
                .m_constraint_equal_to_anchor(self.p_view().p_center_y_anchor())
        ])
    }
}

impl INSResponder for ViewController {}

impl INSViewController for ViewController {}

#[derive(Default, Clone)]
struct AppDelegate {
    status_bar_item: NSStatusItem,
    popover: NSPopover,
}

impl AppDelegate {
    pub fn toggle_popover(&self, sender: id) {
        if let Some(button) = self.status_bar_item.button() {
            if self.popover.shown() {
                self.popover.perform_close(sender)
            } else {
                self.popover.show_relative_to_rect_of_view_preferred_edge(
                    button.p_bounds(),
                    button,
                    NSRectEdge::MinY,
                )
            }
        }
    }
}

unsafe impl Sync for AppDelegate {}

unsafe impl Send for AppDelegate {}

impl PNSApplicationDelegate for AppDelegate {
    fn did_finish_launching(&mut self) {
        // Create a popover
        self.popover.set_behavior(NSPopoverBehavior::Transient);

        let view_controller = ViewController::init();

        view_controller.view_did_load();
        self.popover.set_content_view_controller(view_controller);

        self.status_bar_item = NSStatusBar::system_status_bar()
            .status_item_with_length(NSStatusItem::NSVARIABLE_STATUS_ITEM_LENGTH);

        if let Some(mut button) = self.status_bar_item.button() {
            button.p_set_title("🦀".into());

            let mutex = Arc::new(Mutex::new(self.clone()));

            let _mutex_cloned = mutex.clone();

            let tmp_callback = Box::new(move || {
                let mutex = mutex.lock().unwrap();
                mutex.toggle_popover(nil);
            });

            button.set_action(tmp_callback);
        }
    }
}

#[ns_application_main]
fn main() {
    let mut app = NSApplication::shared_application();

    app.p_set_delegate(AppDelegate::default());
    app.set_activation_policy(NSApplicationActivationPolicy::Regular);
}