rust_macios/appkit/
ns_application_delegate.rs

1#![allow(trivial_casts)]
2
3use std::sync::Once;
4
5use objc::{
6    class,
7    declare::ClassDecl,
8    runtime::{Class, Object, Sel},
9    sel, sel_impl,
10};
11
12use crate::objective_c_runtime::id;
13
14use super::{NSApplicationTerminateReply, NSMenu, NSAPPLICATION_PTR};
15
16/// A set of methods that manage your app’s life cycle and its interaction
17/// with common system services.
18
19pub trait PNSApplicationDelegate {
20    /// Called right before the application will finish launching. You really, probably, want to do
21    /// your setup in `did_finish_launching` unless you're sure of what you're doing.
22    fn will_finish_launching(&mut self) {}
23
24    /// Fired when the application has finished launching.
25    fn did_finish_launching(&mut self) {}
26
27    /// Fired when the application is about to become active.
28    fn did_become_active(&mut self) {}
29
30    /// Fired when the application is about to resign active state.
31    fn will_resign_active(&mut self) {}
32
33    /// Fired when the user is going to continue an activity.
34    fn will_continue_user_activity(&mut self, _activity_type: &str) -> bool {
35        false
36    }
37
38    /// Fired before the application terminates. You can use this to do any required cleanup.
39    fn will_terminate(&mut self) {}
40
41    /// Fired immediately before the application is about to become active.
42    fn will_become_active(&mut self) {}
43
44    /// Fired when the application has resigned active state.
45    fn did_resign_active(&mut self) {}
46
47    /// Fired when the application is about to hide.
48    fn will_hide(&mut self) {}
49
50    /// Fired after the application has hidden.
51    fn did_hide(&mut self) {}
52
53    /// Fired when the application is about to unhide itself.
54    fn will_unhide(&mut self) {}
55
56    /// Fired after the application has unhidden itself.
57    fn did_unhide(&mut self) {}
58
59    /// Fired immediately before the application object updates its windows.
60    fn will_update(&mut self) {}
61
62    /// Fired immediately after the application object updates its windows.
63    fn did_update(&mut self) {}
64
65    /// This is fired after the `Quit` menu item has been selected, or after you've called `App::terminate()`.
66    ///
67    /// In most cases you just want `TerminateResponse::Now` here, which enables business as usual. If you need,
68    /// though, you can cancel the termination via `TerminateResponse::Cancel` to continue something essential. If
69    /// you do this, you'll need to be sure to call `App::reply_to_termination_request()` to circle
70    /// back.
71    fn should_terminate(&mut self) -> NSApplicationTerminateReply {
72        NSApplicationTerminateReply::Now
73    }
74
75    /// Called after closing the last open window. Return `true` here if you want
76    /// the application to terminate.
77    fn should_terminate_after_last_window_closed(&mut self) -> bool {
78        false
79    }
80
81    /// Sent by the application to the delegate prior to default behavior to reopen AppleEvents.
82    ///
83    /// `has_visible_windows` indicates whether the Application object found any visible windows in your application.
84    /// You can use this value as an indication of whether the application would do anything if you return `true`.
85    ///
86    /// Return `true` if you want the application to perform its normal tasks, or `false` if you want the
87    /// application to do nothing. The default implementation of this method returns `true`.
88    ///
89    /// Some finer points of discussion, from Apple documentation:
90    ///
91    /// These events are sent whenever the Finder reactivates an already running application because someone
92    /// double-clicked it again or used the dock to activate it.
93    ///
94    /// For most document-based applications, an untitled document will be created.
95    ///
96    /// [Read more
97    /// here](https://developer.apple.com/documentation/appkit/nsapplicationdelegate/1428638-applicationshouldhandlereopen?language=objc)
98    fn should_handle_reopen(&mut self, _has_visible_windows: bool) -> bool {
99        true
100    }
101
102    /// Supply a dock menu for the application dynamically. The default implementation for this
103    /// method returns `None`, for no menu.
104    fn dock_menu(&mut self) -> Option<NSMenu> {
105        None
106    }
107}
108
109/// A handy method for grabbing our `NSApplicationDelegate` from the pointer. This is different from our
110/// standard `utils` version as this doesn't require `RefCell` backing.
111fn app<T>(this: &mut Object) -> &mut T {
112    unsafe {
113        let app_ptr: usize = *this.get_ivar(NSAPPLICATION_PTR);
114        let app = app_ptr as *mut T;
115        &mut *app
116    }
117}
118
119/// Fires when the Application Delegate receives a `applicationWillFinishLaunching` notification.
120extern "C" fn will_finish_launching<T: PNSApplicationDelegate>(this: &mut Object, _: Sel, _: id) {
121    app::<T>(this).will_finish_launching();
122}
123
124/// Fires when the Application Delegate receives a `applicationDidFinishLaunching` notification.
125extern "C" fn did_finish_launching<T: PNSApplicationDelegate>(this: &mut Object, _: Sel, _: id) {
126    app::<T>(this).did_finish_launching();
127}
128
129/// Fires when the Application Delegate receives a `applicationWillBecomeActive` notification.
130extern "C" fn will_become_active<T: PNSApplicationDelegate>(this: &mut Object, _: Sel, _: id) {
131    app::<T>(this).will_become_active();
132}
133
134/// Fires when the Application Delegate receives a `applicationDidBecomeActive` notification.
135extern "C" fn did_become_active<T: PNSApplicationDelegate>(this: &mut Object, _: Sel, _: id) {
136    app::<T>(this).did_become_active();
137}
138
139/// Fires when the Application Delegate receives a `applicationWillResignActive` notification.
140extern "C" fn will_resign_active<T: PNSApplicationDelegate>(this: &mut Object, _: Sel, _: id) {
141    app::<T>(this).will_resign_active();
142}
143
144/// Fires when the Application Delegate receives a `applicationDidResignActive` notification.
145extern "C" fn did_resign_active<T: PNSApplicationDelegate>(this: &mut Object, _: Sel, _: id) {
146    app::<T>(this).did_resign_active();
147}
148
149/// Fires when the Application Delegate receives a 'applicationShouldTerminate:` notification.
150extern "C" fn should_terminate<T: PNSApplicationDelegate>(
151    this: &mut Object,
152    _: Sel,
153    _: id,
154) -> NSApplicationTerminateReply {
155    app::<T>(this).should_terminate()
156}
157
158/// Fires when the Application Delegate receives a `applicationWillTerminate:` notification.
159extern "C" fn will_terminate<T: PNSApplicationDelegate>(this: &mut Object, _: Sel, _: id) {
160    app::<T>(this).will_terminate();
161}
162
163/// Fires when the Application Delegate receives a `applicationWillHide:` notification.
164extern "C" fn will_hide<T: PNSApplicationDelegate>(this: &mut Object, _: Sel, _: id) {
165    app::<T>(this).will_hide();
166}
167
168/// Fires when the Application Delegate receives a `applicationDidHide:` notification.
169extern "C" fn did_hide<T: PNSApplicationDelegate>(this: &mut Object, _: Sel, _: id) {
170    app::<T>(this).did_hide();
171}
172
173/// Fires when the Application Delegate receives a `applicationWillUnhide:` notification.
174extern "C" fn will_unhide<T: PNSApplicationDelegate>(this: &mut Object, _: Sel, _: id) {
175    app::<T>(this).will_unhide();
176}
177
178/// Fires when the Application Delegate receives a `applicationDidUnhide:` notification.
179extern "C" fn did_unhide<T: PNSApplicationDelegate>(this: &mut Object, _: Sel, _: id) {
180    app::<T>(this).did_unhide();
181}
182
183/// Fires when the Application Delegate receives a `applicationWillUpdate:` notification.
184extern "C" fn will_update<T: PNSApplicationDelegate>(this: &mut Object, _: Sel, _: id) {
185    app::<T>(this).will_update();
186}
187
188/// Fires when the Application Delegate receives a `applicationDidUpdate:` notification.
189extern "C" fn did_update<T: PNSApplicationDelegate>(this: &mut Object, _: Sel, _: id) {
190    app::<T>(this).did_update();
191}
192
193extern "C" fn should_terminate_after_last_window_closed<T>(this: &mut Object, _: Sel, _: id) -> bool
194where
195    T: PNSApplicationDelegate,
196{
197    app::<T>(this).should_terminate_after_last_window_closed()
198}
199
200/// Fires when the Application Delegate receives a
201/// `applicationShouldHandleReopen:hasVisibleWindows:` notification.
202extern "C" fn should_handle_reopen<T: PNSApplicationDelegate>(
203    this: &mut Object,
204    _: Sel,
205    _: id,
206    has_visible_windows: bool,
207) -> bool {
208    app::<T>(this).should_handle_reopen(has_visible_windows)
209}
210
211/// Fires when the application delegate receives a `applicationDockMenu:` request.
212#[allow(improper_ctypes_definitions)]
213extern "C" fn dock_menu<T: PNSApplicationDelegate>(this: &mut Object, _: Sel, _: id) -> NSMenu {
214    app::<T>(this).dock_menu().unwrap_or_default()
215}
216
217/// Registers an `NSObject` application delegate, and configures it for the various callbacks and
218/// pointers we need to have.
219pub fn register_app_delegate_class<T: PNSApplicationDelegate + PNSApplicationDelegate>(
220) -> *const Class {
221    static mut DELEGATE_CLASS: *const Class = 0 as *const Class;
222    static INIT: Once = Once::new();
223
224    INIT.call_once(|| unsafe {
225        let superclass = class!(NSObject);
226        let mut decl = ClassDecl::new("RSTNSApplicationDelegate", superclass).unwrap();
227
228        decl.add_ivar::<usize>(NSAPPLICATION_PTR);
229
230        // Launching Applications
231        decl.add_method(
232            sel!(applicationWillFinishLaunching:),
233            will_finish_launching::<T> as extern "C" fn(&mut Object, _, _),
234        );
235        decl.add_method(
236            sel!(applicationDidFinishLaunching:),
237            did_finish_launching::<T> as extern "C" fn(&mut Object, _, _),
238        );
239
240        // Managing Active Status
241        decl.add_method(
242            sel!(applicationWillBecomeActive:),
243            will_become_active::<T> as extern "C" fn(&mut Object, _, _),
244        );
245        decl.add_method(
246            sel!(applicationDidBecomeActive:),
247            did_become_active::<T> as extern "C" fn(&mut Object, _, _),
248        );
249        decl.add_method(
250            sel!(applicationWillResignActive:),
251            will_resign_active::<T> as extern "C" fn(&mut Object, _, _),
252        );
253        decl.add_method(
254            sel!(applicationDidResignActive:),
255            did_resign_active::<T> as extern "C" fn(&mut Object, _, _),
256        );
257
258        // Terminating Applications
259        decl.add_method(
260            sel!(applicationShouldTerminate:),
261            should_terminate::<T>
262                as extern "C" fn(&mut Object, _, _) -> NSApplicationTerminateReply,
263        );
264        decl.add_method(
265            sel!(applicationWillTerminate:),
266            will_terminate::<T> as extern "C" fn(&mut Object, _, _),
267        );
268
269        // Hiding Applications
270        decl.add_method(
271            sel!(applicationWillHide:),
272            will_hide::<T> as extern "C" fn(&mut Object, _, _),
273        );
274        decl.add_method(
275            sel!(applicationDidHide:),
276            did_hide::<T> as extern "C" fn(&mut Object, _, _),
277        );
278        decl.add_method(
279            sel!(applicationWillUnhide:),
280            will_unhide::<T> as extern "C" fn(&mut Object, _, _),
281        );
282        decl.add_method(
283            sel!(applicationDidUnhide:),
284            did_unhide::<T> as extern "C" fn(&mut Object, _, _),
285        );
286
287        // Managing Windows
288        decl.add_method(
289            sel!(applicationWillUpdate:),
290            will_update::<T> as extern "C" fn(&mut Object, _, _),
291        );
292        decl.add_method(
293            sel!(applicationDidUpdate:),
294            did_update::<T> as extern "C" fn(&mut Object, _, _),
295        );
296        decl.add_method(
297            sel!(applicationShouldHandleReopen:hasVisibleWindows:),
298            should_handle_reopen::<T> as extern "C" fn(&mut Object, _, _, bool) -> bool,
299        );
300
301        // Dock Menu
302        decl.add_method(
303            sel!(applicationDockMenu:),
304            dock_menu::<T> as extern "C" fn(&mut Object, _, _) -> NSMenu,
305        );
306
307        decl.add_method(
308            sel!(applicationShouldTerminateAfterLastWindowClosed:),
309            should_terminate_after_last_window_closed::<T>
310                as extern "C" fn(&mut Object, _, _) -> bool,
311        );
312
313        DELEGATE_CLASS = decl.register();
314    });
315
316    unsafe { DELEGATE_CLASS }
317}