GUI program on Mac OS in D?
Adam D. Ruppe
destructionator at gmail.com
Thu Dec 21 15:45:32 UTC 2017
On Thursday, 14 December 2017 at 19:10:26 UTC, mrphobby wrote:
> This looks pretty awesome and very much like something I was
> looking for. Would really appreciate if you could share your
> work. Otherwise I'll have to roll up my sleeves and try hacking
> it on my own :)
oh sorry i forgot to post this sooner here's my code so far.
when i'm reasonably happy with it, it will be part of my
simpledisplay.d. I might leave it undocumented, but if you wanted
to dive into my source the final version will be in there
somewhere.
CODE MODULE
---------
import helper_module;
import core.stdc.math;
import core.stdc.stdio;
void main() {
auto delegate_ = AppDelegate.alloc.init;
assert(delegate_, "AppDelegate null");
NSApp.delegate_ = delegate_;
NSApp.setActivationPolicy(NSApplicationActivationPolicy.regular);
NSApp.run();
}
@ObjCParentOverride("NSObject")
extern (Objective-C) interface AppDelegate :
NSApplicationDelegate {
mixin IVar!(NSWindow, "window");
mixin IVar!(ViewController, "controller");
extern (C)
@ObjCMethodOverride("applicationShouldTerminateAfterLastWindowClosed:")
static bool
applicationShouldTerminateAfterLastWindowClosed(AppDelegate self,
SEL sel, NSNotification notification) {
return true;
}
extern (C)
@ObjCMethodOverride("applicationDidFinishLaunching:")
static void applicationDidFinishLaunching(AppDelegate self, SEL
sel, NSNotification notification) {
NSApp.menu = mainMenu();
immutable style = NSWindowStyleMask.resizable |
NSWindowStyleMask.closable |
NSWindowStyleMask.miniaturizable |
NSWindowStyleMask.titled;
auto window = NSWindow.alloc.initWithContentRect(
NSMakeRect(10, 10, 300, 300),
style,
NSBackingStoreType.buffered,
false
);
window.title = "D Rox";
auto controller = ViewController.alloc.init;
window.contentView = controller.view;
window.center();
self.window = window;
self.controller = controller;
window.makeKeyAndOrderFront(null);
NSApp.activateIgnoringOtherApps(true);
}
// copy these two lines on any class
typeof(this) init() @selector("init");
mixin RegisterObjCClass;
}
extern (Objective-C) interface ViewController : NSViewController {
extern (C)
@ObjCMethodOverride("loadView")
static void loadView(ViewController self, SEL sel) {
printf("loadView\n");
}
extern (C)
@ObjCMethodOverride("viewDidLoad")
static void viewDidLoad(ViewController self, SEL sel) {
printf("viewDidLoad\n");
}
ViewController init() @selector("init");
mixin RegisterObjCClass;
}
NSMenu mainMenu() {
auto mainMenu = NSMenu.alloc.init("MainMenu".toNSString);
auto title = "Apple";
auto menu = NSMenu.alloc.init(title.toNSString);
auto item = mainMenu.addItem(title.toNSString, null,
"".toNSString);
mainMenu.setSubmenu(menu, item);
menu.addItem(NSMenuItem.alloc.init("Quit", "stop:", "q"));
return mainMenu;
}
---------
HELPER MODULE:
-----
version(OSX) {
/* **************************** */
// Obj-C / Cocoa bindings and helpers
/* **************************** */
// Special thanks to Jacob Carlborg
// see:
http://forum.dlang.org/thread/qzitebxwvavcfamsluji@forum.dlang.org
/// Add an instance var to an Obj-C subclass
mixin template IVar(T, string name) {
extern(D) final {
mixin("@IVarAttr T " ~ name ~ "() {
return this.ivar!(name, T);
}");
mixin("void " ~ name ~ "(T v) {
this.ivar!(name, T) = v;
}");
}
}
/// Add to your extern(Objective-C) interfaces if you need the
parent class to be different
/// from what in inherits from
struct ObjCParentOverride { string parentClassName; }
/// Attach to a method like `extern(C) static R
name(typeof(this) self, SEL sel, Args...)`
/// to give it a selector to override.
struct ObjCMethodOverride { string selector; }
/// And mix this in to your subclasses of the
extern(Objective-C) interfaces
mixin template RegisterObjCClass() {
mixin ClassTrait;
private static objc_method method(alias imp, string selector,
const char* type = null)() {
return objc_method(sel_registerName(selector.ptr), type,
cast(IMP) &imp);
}
shared static this() {
string name = typeof(this).stringof;
string parent = "NSObject";
static if(is(typeof(this) P == super)) {
parent = P[0].stringof;
}
foreach(attr; __traits(getAttributes, typeof(this))) {
static if(is(typeof(attr) == ObjCParentOverride))
parent = attr.parentClassName;
}
objc_method[] methods;
objc_ivar[] ivars;
foreach(member; __traits(derivedMembers, typeof(this))) {
foreach(attr; __traits(getAttributes, __traits(getMember,
typeof(this), member))) {
static if(is(attr == IVarAttr)) {
static if(is(typeof(__traits(getMember, typeof(this),
member)) R == return)) {
ivars ~= objc_ivar(member, "^v", cast(int) log2(R.sizeof),
R.sizeof);
}
} else static if(is(typeof(attr) == ObjCMethodOverride)) {
enum sel = attr.selector; // weird hack here, passing
attr.selector directly below caused compile errors :S
methods ~= method!(__traits(getMember, typeof(this),
member), sel);
}
}
}
registerClassInternal(name.ptr, parent.ptr, methods, ivars);
}
}
/// leaks memory
NSString toNSString(string str) {
return NSString.alloc.initWithBytes(
cast(immutable(ubyte)*) str.ptr,
str.length,
NSStringEncoding.NSUTF8StringEncoding
);
}
/* Rest is private and/or bindings */
enum IVarAttr;
void registerClassInternal(const char* name, const char*
superClassName, objc_method[] methods, objc_ivar[] ivars = []) {
auto superClass = objc_lookUpClass(superClassName);
assert(superClass, "Failed to lookup superclass");
auto cls = objc_allocateClassPair(superClass, name, 0);
assert(cls, "Failed to allocate class pair");
foreach (method ; methods) {
auto result = cls.class_addMethod(
method.method_name,
method.method_imp,
method.method_types
);
assert(result, "Failed to add method");
}
foreach (objc_ivar ivar ; ivars) {
auto result = cls.class_addIvar(
ivar.ivar_name,
size_t(ivar.space),
cast(byte) ivar.ivar_offset,
ivar.ivar_type
);
assert(result, "Failed to add instance variable");
}
objc_registerClassPair(cls);
}
struct IvarInternal(const char* name, T) {
id self;
alias value this;
T value() {
T value;
object_getInstanceVariable(self, name, cast(void**) &value);
return value;
}
void opAssign(T value) {
object_setInstanceVariable(self, name, cast(void*) value);
}
}
auto ivar(const char* name, T, Self)(Self self) {
return IvarInternal!(name, T)(cast(id) self);
}
extern (Objective-C)
enum NSApplicationActivationPolicy : ptrdiff_t {
/* The application is an ordinary app that appears in the Dock
and may have a user interface. This is the default for bundled
apps, unless overridden in the Info.plist. */
regular,
/* The application does not appear in the Dock and does not
have a menu bar, but it may be activated programmatically or by
clicking on one of its windows. This corresponds to
LSUIElement=1 in the Info.plist. */
accessory,
/* The application does not appear in the Dock and may not
create windows or be activated. This corresponds to
LSBackgroundOnly=1 in the Info.plist. This is also the default
for unbundled executables that do not have Info.plists. */
prohibited
};
extern (Objective-C)
interface NSApplication : NSResponder {
interface Class {
mixin MetaclassTrait;
NSApplication shared_() @selector("sharedApplication");
}
extern (D) static NSApplication shared_() {
return Class.classof.shared_();
}
NSApplicationDelegate delegate_() @selector("delegate");
void delegate_(NSApplicationDelegate) @selector("setDelegate:");
bool setActivationPolicy(NSApplicationActivationPolicy
activationPolicy) @selector("setActivationPolicy:");
// use `int` as workaround for
https://github.com/ldc-developers/ldc/issues/2387
void activateIgnoringOtherApps(int flag)
@selector("activateIgnoringOtherApps:");
void run() @selector("run");
}
extern (Objective-C)
__gshared NSApplication NSApp_;
NSApplication NSApp() {
if(NSApp_ is null)
NSApp_ = NSApplication.shared_;
return NSApp_;
}
extern (Objective-C)
interface NSApplicationDelegate {
void applicationDidFinishLaunching(NSNotification notification)
@selector("applicationDidFinishLaunching:");
}
extern (Objective-C)
interface NSColor {
private alias This = typeof(this);
interface Class {
mixin MetaclassTrait;
This alloc() @selector("alloc");
NSColor redColor() @selector("redColor");
}
extern (D) {
private static Class classof() {
return Class.classof;
}
static This alloc() {
return classof.alloc();
}
static NSColor redColor() {
return classof.redColor;
}
}
CGColorRef CGColor() @selector("CGColor");
}
enum NSBackingStoreType : size_t {
retained = 0,
nonretained = 1,
buffered = 2
}
extern (Objective-C)
interface NSMenu : NSObject {
mixin ClassTrait;
NSMenu init() @selector("init");
NSMenu init(NSString title) @selector("initWithTitle:");
void setSubmenu(NSMenu menu, NSMenuItem item)
@selector("setSubmenu:forItem:");
void addItem(NSMenuItem newItem) @selector("addItem:");
NSMenuItem addItem(
NSString title,
SEL selector,
NSString charCode
) @selector("addItemWithTitle:action:keyEquivalent:");
}
extern (Objective-C)
interface NSMenuItem : NSObject {
mixin ClassTrait;
NSMenuItem init() @selector("init");
NSMenuItem init(
NSString title,
SEL selector,
NSString charCode
) @selector("initWithTitle:action:keyEquivalent:");
extern (D) final
{
NSMenuItem init(string title, const(char)* selector, string
charCode) {
return init(title.toNSString, sel_registerName(selector),
charCode.toNSString);
}
}
}
extern (Objective-C)
interface NSResponder : NSObject {
mixin ClassTrait;
NSMenu menu() @selector("menu");
void menu(NSMenu menu) @selector("setMenu:");
}
extern (Objective-C)
interface NSView {
mixin ClassTrait;
NSView init() @selector("init");
NSView initWithFrame(NSRect frameRect)
@selector("initWithFrame:");
void addSubview(NSView view) @selector("addSubview:");
bool wantsLayer() @selector("wantsLayer");
// use `int` as workaround for
https://github.com/ldc-developers/ldc/issues/2387
void wantsLayer(int value) @selector("setWantsLayer:");
CALayer layer() @selector("layer");
void uiDelegate(NSObject) @selector("setUIDelegate:");
}
extern (Objective-C)
interface NSViewController : NSObject {
NSView view() @selector("view");
void view(NSView view) @selector("setView:");
}
extern (Objective-C)
enum NSWindowStyleMask : size_t {
borderless = 0,
titled = 1 << 0,
closable = 1 << 1,
miniaturizable = 1 << 2,
resizable = 1 << 3,
/* Specifies a window with textured background. Textured
windows generally don't draw a top border line under the
titlebar/toolbar. To get that line, use the
NSUnifiedTitleAndToolbarWindowMask mask.
*/
texturedBackground = 1 << 8,
/* Specifies a window whose titlebar and toolbar have a unified
look - that is, a continuous background. Under the titlebar and
toolbar a horizontal separator line will appear.
*/
unifiedTitleAndToolbar = 1 << 12,
/* When set, the window will appear full screen. This mask is
automatically toggled when toggleFullScreen: is called.
*/
fullScreen = 1 << 14,
/* If set, the contentView will consume the full size of the
window; it can be combined with other window style masks, but is
only respected for windows with a titlebar.
Utilizing this mask opts-in to layer-backing. Utilize the
contentLayoutRect or auto-layout contentLayoutGuide to layout
views underneath the titlebar/toolbar area.
*/
fullSizeContentView = 1 << 15,
/* The following are only applicable for NSPanel (or a subclass
thereof)
*/
utilityWindow = 1 << 4,
docModalWindow = 1 << 6,
nonactivatingPanel = 1 << 7, // Specifies that a panel that
does not activate the owning application
hUDWindow = 1 << 13 // Specifies a heads up display panel
}
extern (Objective-C)
interface NSWindow {
mixin ClassTrait;
NSWindow init() @selector("init");
NSWindow initWithContentRect(
NSRect contentRect,
NSWindowStyleMask style,
NSBackingStoreType bufferingType,
bool flag
) @selector("initWithContentRect:styleMask:backing:defer:");
void makeKeyAndOrderFront(id sender)
@selector("makeKeyAndOrderFront:");
NSView contentView() @selector("contentView");
void orderFrontRegardless() @selector("orderFrontRegardless");
void center() @selector("center");
void contentView(NSView view) @selector("setContentView:");
NSString title() @selector("title");
void title(NSString value) @selector("setTitle:");
extern (D) final
{
void title(string value) {
this.title = value.toNSString;
}
}
}
struct CGColor;
alias CGColorRef = CGColor*;
alias CGFloat = double;
struct NSPoint {
CGFloat x;
CGFloat y;
}
struct NSSize {
CGFloat width;
CGFloat height;
}
struct NSRect {
NSPoint origin;
NSSize size;
}
pragma(inline, true) NSPoint NSMakePoint(CGFloat x, CGFloat y) {
NSPoint p;
p.x = x;
p.y = y;
return p;
}
pragma(inline, true) NSSize NSMakeSize(CGFloat w, CGFloat h) {
NSSize s;
s.width = w;
s.height = h;
return s;
}
pragma(inline, true) NSRect NSMakeRect(CGFloat x, CGFloat y,
CGFloat w, CGFloat h) {
NSRect r;
r.origin.x = x;
r.origin.y = y;
r.size.width = w;
r.size.height = h;
return r;
}
extern (Objective-C)
interface NSNotification {
}
extern (Objective-C)
interface NSObject {
mixin ClassTrait;
NSObject init() @selector("init");
}
extern (Objective-C)
interface NSString {
mixin ClassTrait;
NSString init() @selector("init");
NSString initWithBytes(
const(ubyte)* bytes,
NSUInteger length,
NSStringEncoding encoding
) @selector("initWithBytes:length:encoding:");
}
extern (Objective-C)
enum NSStringEncoding : NSUInteger {
NSASCIIStringEncoding = 1, /* 0..127 only */
NSNEXTSTEPStringEncoding = 2,
NSJapaneseEUCStringEncoding = 3,
NSUTF8StringEncoding = 4,
NSISOLatin1StringEncoding = 5,
NSSymbolStringEncoding = 6,
NSNonLossyASCIIStringEncoding = 7,
NSShiftJISStringEncoding = 8, /*
kCFStringEncodingDOSJapanese */
NSISOLatin2StringEncoding = 9,
NSUnicodeStringEncoding = 10,
NSWindowsCP1251StringEncoding = 11, /* Cyrillic; same as
AdobeStandardCyrillic */
NSWindowsCP1252StringEncoding = 12, /* WinLatin1 */
NSWindowsCP1253StringEncoding = 13, /* Greek */
NSWindowsCP1254StringEncoding = 14, /* Turkish */
NSWindowsCP1250StringEncoding = 15, /* WinLatin2 */
NSISO2022JPStringEncoding = 21, /* ISO 2022 Japanese encoding
for e-mail */
NSMacOSRomanStringEncoding = 30,
NSUTF16StringEncoding = NSUnicodeStringEncoding, /* An alias
for NSUnicodeStringEncoding */
NSUTF16BigEndianStringEncoding = 0x90000100, /*
NSUTF16StringEncoding encoding with explicit endianness specified
*/
NSUTF16LittleEndianStringEncoding = 0x94000100, /*
NSUTF16StringEncoding encoding with explicit endianness specified
*/
NSUTF32StringEncoding = 0x8c000100,
NSUTF32BigEndianStringEncoding = 0x98000100, /*
NSUTF32StringEncoding encoding with explicit endianness specified
*/
NSUTF32LittleEndianStringEncoding = 0x9c000100 /*
NSUTF32StringEncoding encoding with explicit endianness specified
*/
}
alias NSUInteger = size_t;
alias NSInteger = ptrdiff_t;
mixin template MetaclassTrait() {
import std.meta : Alias;
alias Parent = Alias!(__traits(parent, typeof(this)));
// extern (C) pragma(mangle, "objc_lookUpClass")
// static typeof(this) objc_lookUpClass(const(char)* name);
extern (D) private static Class classof() {
enum name = __traits(identifier, Parent);
auto cls = cast(typeof(this)) objc_lookUpClass(name);
assert(cls, "Failed to lookup class: " ~ name);
return cls;
}
}
mixin template ClassTrait() {
extern(Objective-C) interface Class {
mixin MetaclassTrait;
Parent alloc() @selector("alloc");
}
extern (D) private static Class classof() {
return Class.classof;
}
extern (D) static typeof(this) alloc() {
return classof.alloc();
}
}
alias objc_ivar* Ivar;
alias objc_method* Method;
alias objc_object Protocol;
alias char* SEL;
alias objc_class* Class;
alias objc_object* id;
alias extern (C) id function(id, SEL, ...) IMP;
version (X86)
const int STRUCT_SIZE_LIMIT = 8;
else version (PPC)
const int STRUCT_SIZE_LIMIT = 4;
else version (X86_64)
const int STRUCT_SIZE_LIMIT = 16;
else version (PPC64)
const int STRUCT_SIZE_LIMIT = 16;
struct objc_object {
Class isa;
}
struct objc_super {
id receiver;
Class clazz;
// for dwt compatibility
alias clazz cls;
alias clazz super_class;
}
struct objc_class {
Class isa;
Class super_class;
const char* name;
int versionn;
int info;
int instance_size;
objc_ivar_list* ivars;
objc_method_list** methodLists;
objc_cache* cache;
objc_protocol_list* protocols;
}
struct objc_ivar {
const(char)* ivar_name;
const(char)* ivar_type;
int ivar_offset;
version (X86_64)
int space;
}
struct objc_ivar_list {
int ivar_count;
version (X86_64)
int space;
/* variable length structure */
objc_ivar[1] ivar_list;
}
struct objc_method {
SEL method_name;
const(char)* method_types;
IMP method_imp;
}
struct objc_method_list {
objc_method_list* obsolete;
int method_count;
version (X86_64)
int space;
/* variable length structure */
objc_method[1] method_list;
}
struct objc_cache {
uint mask /* total = mask + 1 */;
uint occupied;
Method[1] buckets;
}
struct objc_protocol_list {
objc_protocol_list* next;
long count;
Protocol*[1] list;
}
extern (Objective-C)
interface CALayer {
CGFloat borderWidth() @selector("borderWidth");
void borderWidth(CGFloat value) @selector("setBorderWidth:");
CGColorRef borderColor() @selector("borderColor");
void borderColor(CGColorRef) @selector("setBorderColor:");
}
extern (C) {
bool class_addIvar (Class cls, const(char)* name, size_t size,
byte alignment, const(char)* types);
bool class_addMethod (Class cls, SEL name, IMP imp,
const(char)* types);
bool class_addProtocol(Class cls, Protocol* protocol);
IMP class_getMethodImplementation(Class cls, SEL name);
const(char)* class_getName(Class cls);
Class objc_allocateClassPair (Class superclass, const(char)*
name, size_t extraBytes);
Class objc_getClass (const(char)* name);
Protocol* objc_getProtocol(const(char)* name);
Class objc_lookUpClass (const(char)* name);
void objc_registerClassPair (Class cls);
Class object_getClass (id object);
const(char)* object_getClassName (id obj);
Class object_setClass (id object, Class cls);
Ivar object_getInstanceVariable (id obj, const(char)* name,
void** outValue);
Ivar object_setInstanceVariable (id obj, const(char)* name,
void* value);
SEL sel_registerName (const(char)* str);
// id objc_msgSend (id theReceiver, SEL theSelector, ...);
void objc_msgSend_stret(void* stretAddr, id theReceiver, SEL
theSelector, ...);
id objc_msgSendSuper (objc_super* superr, SEL op, ...);
Method class_getClassMethod (Class aClass, SEL aSelector);
Method class_getInstanceMethod (Class aClass, SEL aSelector);
Class class_getSuperclass (Class cls);
IMP method_setImplementation (Method method, IMP imp);
id class_createInstance (Class cls, size_t extraBytes);
id objc_getMetaClass (char* name);
void objc_msgSendSuper_stret(void* stretAddr, objc_super*
superContext, SEL theSelector, ...);
void instrumentObjcMessageSends(bool val);
version (X86)
double objc_msgSend_fpret(id self, SEL op, ...);
}
}
-----
More information about the Digitalmars-d-learn
mailing list