|
| 1 | +import { cocoa } from './cocoa-runtime'; |
| 2 | +import { defineObjcClass } from './cocoa-runtime-class'; |
| 3 | +import type { Handle } from './objc'; |
| 4 | + |
| 5 | +/** |
| 6 | + * Bridges `NSApplicationDelegate` callbacks to JS (D026). |
| 7 | + * |
| 8 | + * AppKit reports app-level activation to the application's delegate. We define |
| 9 | + * the delegate class once at runtime, instantiate one, and route its callbacks |
| 10 | + * to the registered JS handlers — the same mechanism proven for the window and |
| 11 | + * navigation delegates. |
| 12 | + * |
| 13 | + * `applicationShouldHandleReopen:hasVisibleWindows:` is AppKit's Dock-reopen |
| 14 | + * hook and the source of Electron's `activate` event. The delegate object is |
| 15 | + * created with `alloc`/`init` (retain count +1) and never released, so it |
| 16 | + * outlives `NSApp` (which holds its delegate weakly). |
| 17 | + */ |
| 18 | + |
| 19 | +/** JS handlers an `NSApplicationDelegate` instance routes callbacks to. */ |
| 20 | +export type AppDelegateHandlers = { |
| 21 | + /** The app was re-activated; `hasVisibleWindows` is AppKit's flag. */ |
| 22 | + readonly activate: (hasVisibleWindows: boolean) => void; |
| 23 | +}; |
| 24 | + |
| 25 | +let delegateClass: Handle | undefined; |
| 26 | +let current: AppDelegateHandlers | undefined; |
| 27 | + |
| 28 | +const ensureDelegateClass = (): Handle => { |
| 29 | + if (delegateClass !== undefined) { |
| 30 | + return delegateClass; |
| 31 | + } |
| 32 | + delegateClass = defineObjcClass('SambarAppDelegate', 'NSObject', [ |
| 33 | + { |
| 34 | + // BOOL applicationShouldHandleReopen:(id)sender hasVisibleWindows:(BOOL)flag |
| 35 | + selector: 'applicationShouldHandleReopen:hasVisibleWindows:', |
| 36 | + typeEncoding: 'c@:@c', |
| 37 | + args: ['object', 'object'], |
| 38 | + returns: 'bool', |
| 39 | + impl: (_self, _cmd, _sender, hasVisibleWindows) => { |
| 40 | + current?.activate(hasVisibleWindows === 1n); |
| 41 | + // Return YES so AppKit performs its default reopen behavior. |
| 42 | + return 1; |
| 43 | + }, |
| 44 | + }, |
| 45 | + ]); |
| 46 | + return delegateClass; |
| 47 | +}; |
| 48 | + |
| 49 | +/** The Objective-C delegate instance to pass to `[NSApp setDelegate:]`. */ |
| 50 | +export type AppDelegate = { |
| 51 | + readonly handle: Handle; |
| 52 | +}; |
| 53 | + |
| 54 | +/** |
| 55 | + * Create an `NSApplicationDelegate` instance routing callbacks to `handlers`. |
| 56 | + * There is one application delegate per process; the most recent handlers win. |
| 57 | + */ |
| 58 | +export const createAppDelegate = (handlers: AppDelegateHandlers): AppDelegate => { |
| 59 | + const rt = cocoa(); |
| 60 | + const cls = ensureDelegateClass(); |
| 61 | + current = handlers; |
| 62 | + const handle = rt.msgSend(rt.msgSend(cls, rt.selectors.get('alloc')), rt.selectors.get('init')); |
| 63 | + return { handle }; |
| 64 | +}; |
0 commit comments