From 13e9fdca73e4be36dfdd2d4b943750cb1096ca3a Mon Sep 17 00:00:00 2001 From: shirakaba <14055146+shirakaba@users.noreply.github.com> Date: Wed, 4 Jan 2023 16:29:08 +0900 Subject: [PATCH 1/2] chore: proof-of-concept of running an AppKit app --- examples/appkit.js | 65 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 examples/appkit.js diff --git a/examples/appkit.js b/examples/appkit.js new file mode 100644 index 0000000..4dabeae --- /dev/null +++ b/examples/appkit.js @@ -0,0 +1,65 @@ +'use strict'; + +const objc = require('../src/index'); + +objc.import('AppKit'); + +const { + NSApplication, +} = objc; + +// For app lifecycle, refer to: +// https://github.com/TooTallNate/NodObjC/blob/master/examples/NodeCocoaHelloWorld.app/Contents/MacOS/app.js + +const AppDelegate = objc.createClass('AppDelegate', 'NSObject', { + 'applicationWillBecomeActive:': (self, cmd, notification) => { + console.log('-[UIApplicationDelegate applicationWillBecomeActive:]'); + }, + 'applicationDidFinishLaunching:': (self, cmd, notification) => { + console.log('-[UIApplicationDelegate application:didFinishLaunchingWithOptions:]'); + return 1; + }, + 'application:willFinishLaunchingWithOptions:': (self, cmd, application, options) => { + console.log('-[UIApplicationDelegate application:willFinishLaunchingWithOptions:]'); + }, + 'application:didFinishLaunchingWithOptions:': (self, cmd, application, options) => { + console.log('-[UIApplicationDelegate application:didFinishLaunchingWithOptions:]'); + return 1; + }, + + _encodings: { + 'applicationWillBecomeActive:': ['v', ['@', ':', '@']], + 'applicationDidFinishLaunching:': ['v', ['@', ':', '@']], + 'application:willFinishLaunchingWithOptions:': ['v', ['@', ':', '@', '@']], + 'application:didFinishLaunchingWithOptions:': ['c', ['@', ':', '@', '@']], + } +}); +const delegate = AppDelegate.new(); + +const app = NSApplication.sharedApplication(); + +// I can't get the symbol 'NSApplicationActivationPolicyRegular', somehow... +// app.setActivationPolicy(objc.NSApplicationActivationPolicyRegular); + +// Until get/set is implemented, we can achieve the same using setDelegate(). +app.setDelegate_(delegate); + +// TODO: convert this NSArray to char*[] and pass it into +// NSApplicationMain. We might need ref-array-napi for this. +// https://github.com/Janealter/ref-array-napi +// const argv = objc.NSProcessInfo.new().arguments(); + +// Not sure why I can't get NSApplicationMain via objc.NSApplicationMain... +// const dynamicLib = new require('ffi-napi').Library(null, { +// NSApplicationMain: ['int', ['int', 'string']], +// }); + +// Run the app. For now, we effectively pass no args. We have to read +// NSApplicationMain from our dynamicLib because it's a method, not a class, so +// won't be found by objc_getClass (which the objc library calls internally). +// dynamicLib.NSApplicationMain(0, ''); + +// NSApplicationMain doesn't seem to result in the application launch methods +// getting called, so we'll use this run method instead (sadly ignoring the CLI +// args passed in). We can refer to the CLI args by closing over them, anyway. +app.run(); From 94d685ee1768c047f216c016eae7d426be0e68c8 Mon Sep 17 00:00:00 2001 From: shirakaba <14055146+shirakaba@users.noreply.github.com> Date: Wed, 4 Jan 2023 17:05:03 +0900 Subject: [PATCH 2/2] chore: AppKit UI example --- examples/appkit.js | 60 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/examples/appkit.js b/examples/appkit.js index 4dabeae..37396cb 100644 --- a/examples/appkit.js +++ b/examples/appkit.js @@ -6,6 +6,9 @@ objc.import('AppKit'); const { NSApplication, + NSColor, + NSTextView, + NSWindow, } = objc; // For app lifecycle, refer to: @@ -40,6 +43,7 @@ const app = NSApplication.sharedApplication(); // I can't get the symbol 'NSApplicationActivationPolicyRegular', somehow... // app.setActivationPolicy(objc.NSApplicationActivationPolicyRegular); +app.setActivationPolicy(0); // Until get/set is implemented, we can achieve the same using setDelegate(). app.setDelegate_(delegate); @@ -50,16 +54,68 @@ app.setDelegate_(delegate); // const argv = objc.NSProcessInfo.new().arguments(); // Not sure why I can't get NSApplicationMain via objc.NSApplicationMain... -// const dynamicLib = new require('ffi-napi').Library(null, { +// const lib = new ffi.Library(null, { // NSApplicationMain: ['int', ['int', 'string']], // }); // Run the app. For now, we effectively pass no args. We have to read // NSApplicationMain from our dynamicLib because it's a method, not a class, so // won't be found by objc_getClass (which the objc library calls internally). -// dynamicLib.NSApplicationMain(0, ''); +// lib.NSApplicationMain(0, ''); + +// FIXME: as various symbol lookups are failing, maybe best to refer back to how +// NodObjC does it: +// https://github.com/TooTallNate/NodObjC/blob/e4710fb8b73d3a2860de1e959e335a6de3e2191c/lib/import.js#L147 + +const CGFloat = objc.types.double; +const CGPoint = objc.defineStruct('CGPoint', { + x: CGFloat, + y: CGFloat +}); + +const CGSize = objc.defineStruct('CGSize', { + width: CGFloat, + height: CGFloat +}); + +const CGRect = objc.defineStruct('CGRect', { + origin: CGPoint, + size: CGSize +}); + +const win = NSWindow.alloc().initWithContentRect_styleMask_backing_defer_( + CGRect.new( + CGPoint.new(0, 0), + CGSize.new(200, 200), + ), + // Failing to look up these symbols. + // objc.NSTitledWindowMask | objc.NSResizableWindowMask | objc.NSClosableWindowMask + 1 << 0 | 1 << 3 | 1 << 1, + // objc.NSBackingStoreBuffered + 2, + false, +); +win.setAlphaValue_(0); +win.cascadeTopLeftFromPoint_(CGPoint.new(20, 20)); +win.setTitle_('Hello, World'); +win.makeKeyAndOrderFront_(win); +win.center(); +win.setBackgroundColor_(NSColor.greenColor()); +win.animator().setAlphaValue_(1); + +const textV = NSTextView.alloc().initWithFrame_( + CGRect.new( + CGPoint.new(0, 100), + CGSize.new(100, 100), + ) +); + +// textV.setAutoresizingMask(objc.NSViewWidthSizable | objc.NSViewMinYMargin); +textV.setAutoresizingMask_(2 | 8); +win.contentView().addSubview(textV); // NSApplicationMain doesn't seem to result in the application launch methods // getting called, so we'll use this run method instead (sadly ignoring the CLI // args passed in). We can refer to the CLI args by closing over them, anyway. +app.activateIgnoringOtherApps_(true); app.run();