D言語からObjective-CとCocoaAPIを使いたい

BezierPath 図形を表示します

「MacOS X プログラミング入門 Cocoa + Java (2001年)」の FirstBezier を参考にしました。

アイコンとCredits.rtfを作って、aboutPanelを設定してみました。

flower のような図形 flower の aboutPanel 例

サンプルコード

Powered by Prism

flower/dub.json

{
    "authors": ["hidhok"],
    "description": "A D/Objective-C Cocoa application.",
    "name": "flower",
    "dflags": ["-L-framework", "-LCocoa"],
    "sourceFiles": ["../cocoa/window",
                    "../cocoa/control",
                    "../cocoa/menus"]
}               
flower/source/app.d

/**
 *  flower/source/app.d     Version 0.0.3        Bezier View Sample Program
 */
module flower.main;
import core.attribute : optional, selector;
import std.stdio;
import std.conv;
import std.format;
import std.math;
import cocoa.window;
import cocoa.control;
import cocoa.menus;

string appName;
string versionNo = "0.0.3";
string helpMessage =  "CocoaAPIを使用して、\nBezier曲線図形を\nD言語で表示しています。"
                    ~ "\naboutPanel に Options を加えてみました。";
NSWindow win;
BezierView bzView;
NSScrollView scrView;
Label[3] sldVal;
double scaleFactor = 1;
int numbers = 15;
double distance = 0;
double transparency = 0.3;

void main() {
    auto delegate_ = AppDelegate.alloc.init;
    NSApp.delegate_ = cast(void*) delegate_;
    NSApp.activationPolicy = NSAppActivationPolicy.regular;
    NSApp.run;
}

extern (Objective-C) 
class ViewController : NSViewController, NSWindowDelegate {
    override static ViewController alloc() @selector("alloc");
    override ViewController init() @selector("init");
    override void loadView() @selector("loadView") { 
        printf("loadView\n"); 
    }
    override void viewDidLoad() @selector("viewDidLoad") { 
        printf("viewDidLoad\n"); 
    }
    void winDidResize(NSNotification) @selector("windowDidResize:") {
        bzView.needsDisplay = true;
        bzView.setZoomToCenter; // error回避のため順序を入れ替えました
    }
}

extern (Objective-C)
class AppDelegate : NSObject {
    override static AppDelegate alloc() @selector("alloc");
    override typeof(this) init() @selector("init");
    bool applicationShouldTerminateAfterLastWindowClosed(
            NSNotification notification) @selector(
            "applicationShouldTerminateAfterLastWindowClosed:") {
        return true;
    }
    void applicationDidFinishLaunching(NSNotification notification) 
            @selector("applicationDidFinishLaunching:") {
        buildWindow;
        buildContents;
    }
    void aboutPanel() @selector("aboutPanel") {
        // dialog(appName, "Version " ~ versionNo);
        aboutPanelOpt;
    }
    void aboutPanelOpt() @selector("aboutPanelOpt") {
        NSMutableDictionary options = NSMutableDictionary.dictionary;
        // NSImage img = NSImage.name("AppIcon.icns".toNs);
        // options.setObjKey(img, "ApplicationIcon".toNs);
        options.setObjKey(versionNo.toNs, "Version".toNs);
        options.setObjKey("horkew.la.coocan.jp".toNs, "Copyright".toNs);
        // options.setObjKey("1".toNs, "ApplicationVersion".toNs);
        NSApplication.shared_.aboutPanelOptions(options.copy);
    }
    void showHelp() @selector("showHelp") {
        dialog("Help", helpMessage);
    }
}

void buildWindow() {
    appName = NSProcessInfo.processInfo.processName.toDs;
    NSApp.menu = smallMenu(appName);
    NSRect w_rect = nRect(150, 500, 300, 400);
    immutable w_style = NSWindowStyleMask.titled | 
                        NSWindowStyleMask.closable | 
                        NSWindowStyleMask.resizable | 
                        NSWindowStyleMask.miniaturizable;
    win = NSWindow.alloc.init(w_rect, w_style, backing.buffered, false);
    win.title = (appName ~ "  (D言語)").toNs;
    win.minSize = NSSize(200, 328);
    win.bgColor = NSColor.white;
    ViewController vCtrler = ViewController.alloc.init;
    vCtrler.view.subview = win.contentView;
    win.delegate_ = vCtrler;
    win.makeKeyAndOrderFront = null;
    NSApp.activateIgnoringOtherApps = true;
}

void buildContents() {
   /* ScrollView ClipView BezierView */
    scrView = NSScrollView.alloc.init(nRect(0, 100, 300, 300));
    scrView.resizeMask = ResizeMask.Width | ResizeMask.Height;
    win.contentView.subview = scrView;
    NSClipView cpView = NSClipView.alloc.init;
    scrView.contentView = cpView;
    bzView = BezierView.alloc.init(nRect(0, 0, 300, 300));
    cpView.subview = bzView;
   /* slider  label */
    Slider[3] slider;
    Label[3] sldTitle;
    string[3] titleStr = ["Numbers", "Distance", "Transparency"];
    double[3][3] startVals = [[15, 1.0, 100], [0, -30, 60.0], [0.3, 0.01, 1]];
    double[3][3] tp = [[18, 60, 82], [23, 35, 77], [10, 10, 90]];
    foreach(i; 0..3) {
        sldTitle[i] = Label.alloc.init(nRect(tp[i][0], tp[i][1], tp[i][2], 25));
        slider[i] = Slider.alloc.init(nRect(140, 60 - i * 25, 150, 25));
        slider[i].setValues(startVals[i][0], startVals[i][1], startVals[i][2]);
        slider[i].action_ = "sliderAction" ~ i.to!string; //
        sldTitle[i].text = format("%13s", titleStr[i]); 
        sldVal[i] = Label.alloc.init(nRect(100, 60 - i * 25, 40, 25));
        sldVal[i].text = format("%5.2f", startVals[i][0]);
    }
    sldVal[0].text = format("%5.0f", startVals[0][0]);
}

extern (Objective-C)
class Slider : NSSlider {
    override static Slider alloc() @selector("alloc");
    override Slider init(NSRect) @selector("initWithFrame:");
    void setValues(double start, double min, double max) 
            @selector("setValues:min:max:") {
        this.resizeMask = ResizeMask.Width;
        this.target = this;
        this.continuous = true;
        this.maxValue = max;
        this.minValue = min;
        this.value = start;
        this.sliderType = NSSliderType.Linear;
        this.vertical = false;
        win.contentView.subview = this;
    }
    void sliderAction0() @selector("sliderAction0") {
        numbers = this.value.to!int;
        sldVal[0].text = format("%5d", numbers);
        bzView.needsDisplay = true;
    }
    void sliderAction1() @selector("sliderAction1") {
        distance = this.value;
        sldVal[1].text = format("%5.1f", distance);
        bzView.needsDisplay = true;
    }
    void sliderAction2() @selector("sliderAction2") {
        transparency = this.value;
        sldVal[2].text = format("%5.2f", transparency);
        bzView.needsDisplay = true;
    }
} 

extern (Objective-C)
class Label : NSTextField {
    override static Label alloc() @selector("alloc");
    override Label init(NSRect) @selector("initWithFrame:");
    void text(string labelStr) @selector("setValuesText:") {
        this.bgColor = NSColor.clear;
        this.bordered = false;
        this.editable = false;
        this.selectable = false;
        this.str = labelStr;
        win.contentView.subview = this;
    }
}

extern (Objective-C)
class BezierView : NSView {
    override static BezierView alloc() @selector("alloc");
    override BezierView init() @selector("init");
    override BezierView init(NSRect) @selector("initWithFrame:");
    override void drawRect(NSRect rect) @selector("drawRect:") {
        NSBezierPath bzPath;
        double radian, cx = 150, cy = 150;

        foreach(int i; 0 .. numbers) {
            bzPath = NSBezierPath.bezierPath;
            radian = i * 2 * PI / numbers;
            bzPath.moveToPoint = iPoint(nP(10, 150 - distance), radian);
            NSPoint p1 = iPoint(nP(290, 150 + distance), radian);
            NSPoint p2 = nP(10, 290 - distance * 2);
            NSPoint p3 = nP(290 - distance * 2, 10);
            bzPath.curveToPoint(p1, p2, p3);
            NSColor.colorHSB(1.0 * i / numbers, 1.0, 1.0, transparency).set;
            bzPath.fill;
        }
    }
    void setZoomToCenter() @selector("setZoomToCenter") {
        double scrW, scrH, bzW = 300, bzH = 300;
        scrW = scrView.frame.size.width;
        scrH = scrView.frame.size.height;
        double sf = scrW < scrH ? (scrW / bzW) : (scrH / bzH);
        NSRect frame = this.frame;
        NSRect bounds = this.bounds;
        frame.size.width = bzW * sf;
        frame.size.height = bzH * sf;
        this.frame = frame;
        this.bounds = bounds;
        double ox, oy;
        if (scrW < scrH) {
            ox = 0;
            oy = (scrH - scrW) / 2;
        } else {
            ox = (scrW - scrH) / 2;
            oy = 0;
        }
        this.frameOrigin = nP(ox, oy);
    }
}

NSPoint iPoint(NSPoint pt, double radian) {
    double X = pt.x - 150;
    double Y = pt.y - 150;
    double M = sqrt(X * X + Y * Y);
    double R = atan(Y / X);
    X = M * cos(R + radian);
    Y = M * sin(R + radian);
    return NSPoint(X + 150, Y + 150);
}

NSPoint nP(double x, double y) {
    return NSMakePoint(x, y);
}               
cocoa/window.d
cocoa/control.d
cocoa/menus.d