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/qzitebxwvavcfamsl...@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, ...);
        }
}

-----

Reply via email to