DelphiからCocoaを使う その2。
ShowMessage?しらんがな(´・ω・`)
前回は何だったんだという話になりかねないですが、正直言うとダイアログの形とかどうでもいいですよね。それよりコントロールを置きたいんです。コントロールを置いてあんなところやこんなところを弄くりまわしたい!prpr!
よろしい、ならばボタンだ
というわけで今回はNSButtonとそのイベントハンドリングについて扱いますう予定でしたが、思いの外解説するのが面倒だったのと、Cocoaコントロールのラッパーが作りかけながらできてきたので、力尽きるまでNSButtonとは特に関係ない基本的な話をした後、その使い方でお茶を濁したいと思います。
まずはソースコードをどうぞ
NSObjectさんとTObjectくん
CocoaはObjective-Cで書かれているわけですが、その基底クラスはNSObjectというクラスになっていて、原則として全てのクラスはここから派生しています。TObjectを基底とするDelphiととてもよく似たしくみです。
当然のことながら、NSObjectはObjective-Cで、TObjectはDelphiで書かれていて、両者を混在させることはできません。ですがそれではDelphiからCocoaを使うことはできないという話になってしまうので、この両者を繋ぐしくみが必要になります。
ドラ「いんた〜ふぇ〜す〜♪」
DelphiのインターフェースはWindowsのCOM用として作られた経緯がありますが、面白いことにMacとの橋渡しにもこれが採用されたようです。前回も少し書きましたが、Cocoaのクラスを使うために内部ではこのインターフェースを使ってかなり高度なことが行われています。
インターフェースというのは、物凄く簡単に言えば単なる関数テーブルです。DelphiではIInterfaceのような特別な型を用いていますが、この中身は関数ポインタを並べたリストのようなものでしかありません。つまり、このリストは自由に書き換えが可能ですし、テーブルサイズのメモリを確保し、その中に好きなように関数ポインタを代入していけば、実行時にインターフェースを生成することもできます。
Cocoaのクラスとの橋渡しにも、このような仕組みが使われています。正確に言えばObjective-Cではメソッド呼び出しの代わりにメッセージの送信が用いられているので、Delphi側でインターフェースのメソッドをコールすると、内部ではメッセージの送信に変換され*1、その返答を戻り値として取得するようになっています。
力尽きた\(^o^)/
まだObjective-Cのクラスを継承する方法とか、メソッドをオーバーライドする方法などがあるんですが、自分でも何書いてるのかよく分からなくなってきたのでCocoa.Controlsの説明に移ります。このソースの中で継承もオーバーライドも扱ってるし、プログラマなら説明読むよりコード見た方が早いよね!ということで。
七つめのラッパーと永遠のCocoa
と言ってもそんなに説明することはないです。何より作りかけですし。
簡単な使い方はソースコードの一番上にも書いてありますが、こんな感じです。
var button: TCocoaButton; begin button := TCocoaButton.Create; button.Left := 8; { Cocoaの座標系は左下が原点なので、Topの扱いが逆なのに注意 } button.Top := CliengHeight - button.Height - 8 - 1; button.Caption := 'ボタン'; button.OnAction := ButtonAction; { Parentを設定することで画面に表示される。Handleはフォームのハンドル } button.Parent := GetView(Handle); end;
一応VCLをコードで生成する時の書き方に似せてあります。VCLと同じようにCreateにOwnerを持たせるかどうか悩んだんですが、基本的にCocoaではOwnerとParentは一緒なので、今のところはこうなってます(変わる可能性もあります)。
設計時にはダミーを表示することでポトペタも可能にできたらいいなとは思ってるんですが、Objective-Cのクラスを継承する場合、どうしてもDelphi側の基底クラスがTOCLocalというクラスになってしまうこともあり、色々回りくどい実装をしないと実現できなそうだったので諦めました。
*1:もっと正確に言えばobjc_msgSend関数の呼び出しになる