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

Mouse Event

マウスで Bezier曲線の変形を表示してみました。

mouse event の sample code

サンプルコード

Powered by Prism

curve/dub.json

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

/**
 *  curve/source/app.d     Version 0.0.5        mouse event Test
 */
module curve.main;
import core.attribute : optional, selector;
import std.stdio;
import std.format;
import std.conv : to;
import cocoa.window;
import cocoa.control;
import cocoa.menus;

string appName;
string versionNo = "0.0.5";
string helpMessage = "CocoaAPIを使用して マウスで曲線の変形を、\nD言語で表示してみました。";

NSWindow win;
ViewController vCtrler;
CurveView cvView;
NSBezierPath curvePath, linePath;
NSBezierPath[4] po;
NSRect[4] poRect;
NSPoint[4] p;
NSRect cvFrame;
double transparency = 0.8;
Label transValue;

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

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") {
        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("0.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 = miniMenu(appName);
    NSRect w_rect = nRect(0, 0, 300, 300);
    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(300, 328);
    win.moveTop = nP(100, 950);
    cvView = CurveView.alloc.init;
    win.contentView = cvView;
    // cvView.bgColor = NSColor.clear;
    win.bgColor = NSColor.rgba(1, 1, 1, transparency);
    vCtrler = ViewController.alloc.init;
    vCtrler.view.subview = cvView;
    win.makeKeyAndOrderFront = null;
    NSApp.activateIgnoringOtherApps = true;
}

void buildContents() {
    Slider slider = Slider.alloc.init(nRect(180, 5, 110, 25));
    slider.setValues(0.8, 0, 1);
    transValue = Label.alloc.init(nRect(145, 7, 50, 20));
    transValue.text = format("%1.2f", transparency);
}

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.action_ = "sliderAction"; //
        // this.action = sel_registerName("sliderAction".toCs); //
        this.continuous = true;
        this.maxValue = max;
        this.minValue = min;
        this.value = start;
        this.sliderType = NSSliderType.Linear;
        this.vertical = false;
        cvView.subview = this;
    }
    void sliderAction() @selector("sliderAction") {
        transparency = this.value;
        transValue.text = format("%1.2f", transparency);
        win.bgColor = NSColor.rgba(1, 1, 1, transparency);
        cvView.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;
        cvView.subview = this;
    }
}

extern (Objective-C) 
class ViewController : NSViewController {
    override static ViewController alloc() @selector("alloc");
    override ViewController init() @selector("init");
    override void loadView() @selector("loadView") { 
        // printf("loadView\n");
        p[0] = nP(50, 100);
        p[1] = nP(100, 230);
        p[2] = nP(200, 200);
        p[3] = nP(250, 250);
    }
    override void viewDidLoad() @selector("viewDidLoad") { 
        printf("viewDidLoad\n"); 
    }
}

extern (Objective-C)
class CurveView : NSView {
    override static CurveView alloc() @selector("alloc");
    override CurveView init() @selector("init");
    override CurveView init(NSRect) @selector("initWithFrame:");
    override void drawRect(NSRect) @selector("drawRect:") {
        this.bezierCurve();
        pointPrint();
    }
    override void mouseDown(NSEvent event) @selector("mouseDown:") {
        matchLocation(event.locationInWindow);
        this.needsDisplay = true;
    }
    override void mouseDragged(NSEvent event) @selector("mouseDragged:") {
        matchLocation(event.locationInWindow);
        this.needsDisplay = true;
    }
    void bezierCurve() @selector("bezierCurve") {
        curvePath.lineWidth = 3;
        curvePath = NSBezierPath.bezierPath;
        NSColor.red.stroke;
        curvePath.moveToPoint(p[0]);
        curvePath.curveToPoint(p[3], p[1], p[2]);
        curvePath.stroke;

        linePath.lineWidth = 0.2;
        linePath = NSBezierPath.bezierPath;
        NSColor.black.stroke;
        foreach (i; 0..3) {
            linePath.strokeLine(p[i], p[i + 1]);
        }
        linePath.stroke;

        NSColor.black.fill;
        foreach (i; 0..4) {
            poRect[i] = nRect(p[i].x - 5, p[i].y - 5, 10, 10);
            po[i] = NSBezierPath.oval(poRect[i]);
            po[i].stroke;
            po[i].fill;
        }
    }
}

void matchLocation(NSPoint mouseP) {
    double mx = mouseP.x;
    double my = mouseP.y;
    foreach (i; 0..4) {
        if (p[i].x - 8 < mx && mx < p[i].x + 8 && 
            p[i].y - 8 < my && my < p[i].y + 8) {
                p[i] = mouseP;
                return;
        }
    }
    double w = cvView.frame.size.width;
    double h = cvView.frame.size.height;
    foreach (i; 0..4) {
        if ( (p[i].x > 0 && p[i].x < w && mx > 0 && mx < w) &&   // 上下
            ((p[i].y < 0 && my < 8) || (p[i].y > h && my > h - 8)) ) {
                p[i] = mouseP;
                return;
        }
        if ( (p[i].y > 0 && p[i].y < h && my > 0 && my < h) &&   // 左右
            ((p[i].x < 0 && mx < 8) || (p[i].x > w && mx > w - 8)) ) {
                p[i] = mouseP;
                return;
        }
        if ((p[i].x < 0 && p[i].y < 0 && mx < 20 && my < 20) ||  // 四隅
            (p[i].x < 0 && p[i].y > h && mx < 20 && my > h - 20) ||
            (p[i].x > w && p[i].y < 0 && mx > w - 20 && my < 20) ||
            (p[i].x > w && p[i].y > h && mx > w - 20 && my > h - 20)) {
                p[i] = mouseP;
                return;
        }
    }
}

void pointPrint() {
    string str, strx, stry;
    foreach (i; 0..4) {
        strx = format("%4.1f", p[i].x);
        stry = format("%4.1f", p[i].y);
        str = "p" ~ i.to!string ~ " .x:" 
               ~ strx.to!string ~ " .y:" ~ stry.to!string;
        str.toNs.drawAtPoint(nP(10, 70 - i *20), null);
    }
}

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