#![deny(unsafe_op_in_unsafe_fn)] use std::cell::OnceCell; use objc2::rc::Retained; use objc2::runtime::ProtocolObject; use objc2::{define_class, msg_send, DefinedClass, MainThreadOnly}; use objc2_app_kit::{ NSApplication, NSApplicationActivationPolicy, NSApplicationDelegate, NSAutoresizingMaskOptions, NSBackingStoreType, NSColor, NSFont, NSTextAlignment, NSTextField, NSWindow, NSWindowDelegate, NSWindowStyleMask, }; use objc2_foundation::{ ns_string, MainThreadMarker, NSNotification, NSObject, NSObjectProtocol, NSPoint, NSRect, NSSize, }; #[derive(Debug, Default)] struct AppDelegateIvars { window: OnceCell>, } define_class!( // SAFETY: // - The superclass NSObject does not have any subclassing requirements. // - `Delegate` does not implement `Drop`. #[unsafe(super = NSObject)] #[thread_kind = MainThreadOnly] #[ivars = AppDelegateIvars] struct Delegate; // SAFETY: `NSObjectProtocol` has no safety requirements. unsafe impl NSObjectProtocol for Delegate {} // SAFETY: `NSApplicationDelegate` has no safety requirements. unsafe impl NSApplicationDelegate for Delegate { // SAFETY: The signature is correct. #[unsafe(method(applicationDidFinishLaunching:))] fn did_finish_launching(&self, notification: &NSNotification) { let mtm = self.mtm(); let app = unsafe { notification.object() } .unwrap() .downcast::() .unwrap(); let text_field = unsafe { let text_field = NSTextField::labelWithString(ns_string!("Hello, World!"), mtm); text_field.setFrame(NSRect::new( NSPoint::new(5.0, 100.0), NSSize::new(290.0, 100.0), )); text_field.setTextColor(Some(&NSColor::colorWithSRGBRed_green_blue_alpha( 0.0, 0.5, 0.0, 1.0, ))); text_field.setAlignment(NSTextAlignment::Center); text_field.setFont(Some(&NSFont::systemFontOfSize(45.0))); text_field.setAutoresizingMask( NSAutoresizingMaskOptions::ViewWidthSizable | NSAutoresizingMaskOptions::ViewHeightSizable, ); text_field }; // SAFETY: We disable releasing when closed below. let window = unsafe { NSWindow::initWithContentRect_styleMask_backing_defer( NSWindow::alloc(mtm), NSRect::new(NSPoint::new(0.0, 0.0), NSSize::new(300.0, 300.0)), NSWindowStyleMask::Titled | NSWindowStyleMask::Closable | NSWindowStyleMask::Miniaturizable | NSWindowStyleMask::Resizable, NSBackingStoreType::Buffered, false, ) }; // SAFETY: Disable auto-release when closing windows. // This is required when creating `NSWindow` outside a window // controller. unsafe { window.setReleasedWhenClosed(false) }; // Set various window properties. window.setTitle(ns_string!("A window")); let view = window.contentView().expect("window must have content view"); unsafe { view.addSubview(&text_field) }; window.center(); unsafe { window.setContentMinSize(NSSize::new(300.0, 300.0)) }; window.setDelegate(Some(ProtocolObject::from_ref(self))); // Show the window. window.makeKeyAndOrderFront(None); // Store the window in the delegate. self.ivars().window.set(window).unwrap(); app.setActivationPolicy(NSApplicationActivationPolicy::Regular); // Activate the application. // Required when launching unbundled (as is done with Cargo). #[allow(deprecated)] app.activateIgnoringOtherApps(true); } } // SAFETY: `NSWindowDelegate` has no safety requirements. unsafe impl NSWindowDelegate for Delegate { #[unsafe(method(windowWillClose:))] fn window_will_close(&self, _notification: &NSNotification) { // Quit the application when the window is closed. unsafe { NSApplication::sharedApplication(self.mtm()).terminate(None) }; } } ); impl Delegate { fn new(mtm: MainThreadMarker) -> Retained { let this = Self::alloc(mtm).set_ivars(AppDelegateIvars::default()); // SAFETY: The signature of `NSObject`'s `init` method is correct. unsafe { msg_send![super(this), init] } } } fn main() { let mtm = MainThreadMarker::new().unwrap(); let app = NSApplication::sharedApplication(mtm); let delegate = Delegate::new(mtm); app.setDelegate(Some(ProtocolObject::from_ref(&*delegate))); app.run(); }