rust_macios/appkit/
ns_application.rs

1use std::{fmt, marker::PhantomData, sync::Once};
2
3use objc::{
4    class,
5    declare::ClassDecl,
6    msg_send,
7    runtime::{Class, Object},
8    sel, sel_impl,
9};
10use objc_id::Id;
11
12use crate::{
13    objective_c_runtime::{
14        id,
15        traits::{FromId, PNSObject, ToId},
16    },
17    utils::to_bool,
18};
19
20use super::{
21    ns_application_delegate::PNSApplicationDelegate, register_app_delegate_class, INSResponder,
22    NSApplicationActivationPolicy, NSApplicationDelegateReply, NSMenu,
23};
24///
25pub static NSAPPLICATION_PTR: &str = "rstNSApplicationPtr";
26
27fn register_app_class() -> *const Class {
28    static mut APP_CLASS: *const Class = 0 as *const Class;
29    static INIT: Once = Once::new();
30
31    INIT.call_once(|| unsafe {
32        let superclass = class!(NSApplication);
33        let decl = ClassDecl::new("RSTNSApplication", superclass).unwrap();
34        APP_CLASS = decl.register();
35    });
36
37    unsafe { APP_CLASS }
38}
39
40/// An object that manages an app’s main event loop and resources used by all of that app’s objects.
41pub struct NSApplication<'app, M = ()> {
42    /// The underlying Objective-C object.
43    pub ptr: Id<Object>,
44    _message: PhantomData<&'app M>,
45}
46
47/// An object that manages an app’s main event loop and resources used by all
48/// of that app’s objects.
49pub trait INSApplication: INSResponder {
50    /// Returns the application instance, creating it if it doesn’t exist yet.
51    fn p_shared_application() -> Self
52    where
53        Self: Sized + FromId,
54    {
55        unsafe { Self::from_id(msg_send![Self::m_class(), sharedApplication]) }
56    }
57
58    /// The app delegate object.
59    fn p_delegate(&self) -> id {
60        unsafe { msg_send![self.m_self(), delegate] }
61    }
62
63    /// Sets the app delegate object.
64    fn p_set_delegate<'app, T>(&'app mut self, app_delegate: T)
65    where
66        T: PNSApplicationDelegate + 'app,
67    {
68        unsafe {
69            let delegate_class = register_app_delegate_class::<T>();
70            let delegate: id = msg_send![delegate_class, new];
71            let delegate_ptr: *const T = &app_delegate;
72            (*delegate).set_ivar(NSAPPLICATION_PTR, delegate_ptr as usize);
73            msg_send![self.m_self(), setDelegate: delegate]
74        }
75    }
76
77    /* Managing the Event Loop
78     */
79
80    /// A Boolean value indicating whether the main event loop is running.
81    fn p_running(&self) -> bool {
82        unsafe { msg_send![self.m_self(), isRunning] }
83    }
84
85    /// Starts the main event loop.
86    fn m_run(&self) {
87        unsafe { msg_send![self.m_self(), run] }
88    }
89
90    /// Activates the app, opens any files specified by the NSOpen user default, and unhighlights the app’s icon.
91    fn m_finish_launching(&self) {
92        unsafe { msg_send![self.m_self(), finishLaunching] }
93    }
94
95    /// Stops the main event loop.
96    fn m_stop(&self, sender: id) {
97        unsafe { msg_send![self.m_self(), stop: sender] }
98    }
99
100    /* Terminating the App
101     */
102
103    /// Terminates the receiver.
104    fn m_terminate(&self, sender: id) {
105        unsafe { msg_send![self.m_self(), terminate: sender] }
106    }
107
108    /// Responds to NSTerminateLater once the app knows whether it can terminate.
109    fn m_reply_to_application_should_terminate(&self, should_terminate: bool) {
110        unsafe {
111            msg_send![
112                self.m_self(),
113                replyToApplicationShouldTerminate: should_terminate
114            ]
115        }
116    }
117
118    /* Activating and Deactivating the App
119     */
120
121    /// A Boolean value indicating whether this is the active app.
122    fn p_active(&self) -> bool {
123        unsafe { to_bool(msg_send![self.m_self(), isActive]) }
124    }
125
126    /// Makes the receiver the active app.
127    fn m_activate_ignoring_other_apps(&mut self, flag: bool) {
128        unsafe { msg_send![self.m_self(), activateIgnoringOtherApps: flag] }
129    }
130
131    /// Deactivates the receiver.
132    fn m_deactivate(&mut self) {
133        unsafe { msg_send![self.m_self(), deactivate] }
134    }
135
136    /* Managing Relaunch on Login
137     */
138
139    /// Disables relaunching the app on login.
140    fn m_disable_relaunch_on_login(&mut self) {
141        unsafe { msg_send![self.m_self(), disableRelaunchOnLogin] }
142    }
143
144    /// Enables relaunching the app on login.
145    fn m_enable_relaunch_on_login(&mut self) {
146        unsafe { msg_send![self.m_self(), enableRelaunchOnLogin] }
147    }
148
149    /* Managing Remote Notifications
150     */
151
152    /// Register for notifications sent by Apple Push Notification service (APNs).
153    fn m_register_for_remote_notifications(&mut self) {
154        unsafe { msg_send![self.m_self(), registerForRemoteNotifications] }
155    }
156
157    /// Unregister for notifications received from Apple Push Notification service.
158    fn m_unregister_for_remote_notifications(&mut self) {
159        unsafe { msg_send![self.m_self(), unregisterForRemoteNotifications] }
160    }
161
162    /* Managing User Attention Requests
163     */
164
165    /// Handles errors that might occur when the user attempts to open or print files.
166    fn m_reply_to_open_or_print(&self, response: NSApplicationDelegateReply) {
167        unsafe { msg_send![self.m_self(), replyToOpenOrPrint: response] }
168    }
169
170    /* Configuring the Activation Policy
171     */
172
173    /// Returns the app’s activation policy.
174    fn m_activation_policy(&self) -> NSApplicationActivationPolicy {
175        unsafe { msg_send![self.m_self(), activationPolicy] }
176    }
177
178    /// Sets the app’s activation policy.
179    ///
180    /// # Arguments
181    ///
182    /// * `policy` - The activation policy to set.
183    fn m_set_activation_policy(&mut self, policy: NSApplicationActivationPolicy) {
184        unsafe { msg_send![self.m_self(), setActivationPolicy: policy] }
185    }
186
187    /* Menu */
188
189    /// The app’s main menu bar.
190    fn p_main_menu(&self) -> NSMenu {
191        unsafe { msg_send![self.m_self(), mainMenu] }
192    }
193
194    /// Sets the app’s main menu bar.
195    fn p_set_main_menu(&mut self, menu: NSMenu) {
196        unsafe { msg_send![self.m_self(), setMainMenu: menu] }
197    }
198}
199
200impl<'app> NSApplication<'app> {
201    /// Returns the application instance, creating it if it doesn’t exist yet.
202    pub fn shared_application() -> NSApplication<'app> {
203        NSApplication::p_shared_application()
204    }
205
206    /// The app delegate object.
207    pub fn delegate(&self) -> id {
208        self.p_delegate()
209    }
210}
211
212impl NSApplication<'_> {
213    /// A Boolean value indicating whether the main event loop is running.
214    pub fn running(&self) -> bool {
215        self.p_running()
216    }
217
218    /// Starts the main event loop.
219    pub fn run(&mut self) {
220        self.m_run();
221    }
222
223    /// Activates the app, opens any files specified by the NSOpen user default, and unhighlights the app’s icon.
224    pub fn finish_launching(&mut self) {
225        self.m_finish_launching()
226    }
227
228    /// Stops the main event loop.
229    pub fn stop(&mut self, sender: id) {
230        self.m_stop(sender)
231    }
232
233    /// Terminates the receiver.
234    pub fn terminate(&mut self, sender: id) {
235        self.m_terminate(sender)
236    }
237
238    /// Responds to NSTerminateLater once the app knows whether it can terminate.
239    pub fn reply_to_application_should_terminate(&self, should_terminate: bool) {
240        self.m_reply_to_application_should_terminate(should_terminate)
241    }
242
243    /// Disables relaunching the app on login.
244    pub fn disable_relaunch_on_login(&mut self) {
245        self.m_disable_relaunch_on_login()
246    }
247
248    /// Enables relaunching the app on login.
249    pub fn enable_relaunch_on_login(&mut self) {
250        self.m_enable_relaunch_on_login()
251    }
252
253    /// Register for notifications sent by Apple Push Notification service (APNs).
254    pub fn register_for_remote_notifications(&mut self) {
255        self.m_register_for_remote_notifications()
256    }
257
258    /// Unregister for notifications received from Apple Push Notification service.
259    pub fn unregister_for_remote_notifications(&mut self) {
260        self.m_unregister_for_remote_notifications()
261    }
262
263    /// Handles errors that might occur when the user attempts to open or print files.
264    pub fn reply_to_open_or_print(&self, response: NSApplicationDelegateReply) {
265        self.m_reply_to_open_or_print(response)
266    }
267
268    /// Returns the app’s activation policy.
269    pub fn activation_policy(&self) -> NSApplicationActivationPolicy {
270        self.m_activation_policy()
271    }
272
273    /// Sets the app’s activation policy.
274    ///
275    /// # Arguments
276    ///
277    /// * `policy` - The activation policy to set.
278    pub fn set_activation_policy(&mut self, policy: NSApplicationActivationPolicy) {
279        self.m_set_activation_policy(policy)
280    }
281
282    /// The app’s main menu bar.
283    pub fn main_menu(&self) -> NSMenu {
284        self.p_main_menu()
285    }
286
287    /// Sets the app’s main menu bar.
288    pub fn set_main_menu(&mut self, menu: NSMenu) {
289        self.p_set_main_menu(menu)
290    }
291}
292
293impl NSApplication<'_> {
294    /// Creates a new [`NSApplication`],
295    pub fn new() -> Self {
296        let ptr = unsafe {
297            let app: id = msg_send![register_app_class(), sharedApplication];
298            Id::from_ptr(app)
299        };
300
301        Self {
302            ptr,
303            _message: PhantomData,
304        }
305    }
306}
307
308impl Default for NSApplication<'_> {
309    fn default() -> Self {
310        Self::new()
311    }
312}
313
314impl PNSObject for NSApplication<'_> {
315    fn m_class<'a>() -> &'a Class {
316        unsafe { &*register_app_class() }
317    }
318
319    fn m_self(&self) -> id {
320        unsafe { msg_send![&*self.ptr, self] }
321    }
322}
323
324impl INSResponder for NSApplication<'_> {}
325
326impl INSApplication for NSApplication<'_> {}
327
328impl fmt::Debug for NSApplication<'_> {
329    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
330        write!(f, "{}", self.p_debug_description())
331    }
332}
333
334impl ToId for NSApplication<'_> {
335    fn to_id(mut self) -> id {
336        &mut *self.ptr
337    }
338}
339
340impl FromId for NSApplication<'_> {
341    unsafe fn from_id(ptr: id) -> Self {
342        Self {
343            ptr: Id::from_ptr(ptr),
344            _message: PhantomData,
345        }
346    }
347}