...ing logging 4.0

はてなブログに移行しました。D言語の話とかいろいろ。

DFLの新しいイベントシステムの仕組み


DFLはD言語用のメジャーなGUIライブラリだ.
現在はまだWindows専用だが,私の記憶が確かならば,将来的にはGTK+ベースに書き換えられてマルチプラットフォームのライブラリになる予定(は未定)だ.
DFLにはまだまともなマニュアルがないが,サンプルコードを読めばだいたい仕様を把握することができる.
そういう素直な設計をしているし,随所が.NET Frameworkに似ているので,そっちを使ったことがあれば入門は容易なはずだ.


現在,日本にどれくらいのDFLユーザがいるのか全くわからないが,それほど多くはないだろう.
そのせいもあってか,今のところ日本語での説明はほとんどない.
そこで,日本語でDFLの説明をしてみようと思うが,ButtonやListBox,ListViewなどの基本的なクラスの使い方は公式のサンプルコードを見てもらえばよいだろう(前述のように簡単だから).
ここでは少し趣向を変えて,DFLの新しいイベントシステムの仕組みの理解に役立つであろうサンプルコードを書いてみた.


具体的には,

  • UserControlクラスを用いたユーザ定義コントロールの作成方法
  • ユーザ定義コントロールのウィンドウプロシージャの書き方
  • EventArgsクラスとEventテンプレートを用いたユーザ定義イベントハンドラの作成/使用方法

について学べるはずだ.

環境

  • dmd 2.014
  • DFL 0.9.7.01+snapshot (Beta)

サンプルコード

//
// DFLの新しいイベントシステムの仕組み
//
 
//デバッグモードでのコンパイル方法の例:
// dfl main -gui -w -debug -version=DFL_UNICODE
 
//リリースモードでのコンパイル方法の例:
// dfl main -gui -O -release -version=DFL_UNICODE
// DFLを使うときに必ずimportするモジュール
import dfl.all;
// 自作コントロールクラスを作るためにウィンドウメッセージの定義を使うので
import dfl.internal.winapi : WM_LBUTTONDOWN;
// 文字列整形に使用
import std.stream : MemoryStream;

// EventArgsを継承してイベント情報連絡用のクラスを作って
class MyControlFireArgs : EventArgs
{
    // 好きなメンバを定義する
    string myName;
    Point usoPos;
}

// UserControlを継承したクラスを作って
class MyControl : UserControl
{
    // このクラスとイベント情報連絡用のクラスでEvent!をインスタンス化してイベントハンドラを作る
    Event!(MyControl, MyControlFireArgs) fire;
    
    // ウィンドウプロシージャを定義する(protected推奨)
    protected override void wndProc(ref Message m)
    {
        switch(m.msg)
        {
            // ハンドルしたいイベントを検出したら
            case WM_LBUTTONDOWN:
                //(本当は m から必要な情報を読み取り)データにセットして
                auto mca = new MyControlFireArgs;
                mca.myName = "test";
                mca.usoPos = Point(123,456);
                // 自分自身とデータをイベントハンドラに与えて呼び出す
                fire(this, mca);
                break;
            
            default:
                ;
        }
        super.wndProc(m);//自身のwndProc()が呼ばれたら必ず親クラスのものも呼ぶ
    }
}

// Formを継承したクラスを作って
class MainForm : Form
{
    // 子コントロールをメンバとして定義する
    // これはカプセル化のためと,親コントロールよりも長い寿命を維持するため
    private ListView lv_;
    private MyControl mc_;
    
    this()
    {
        // フォームを設定
        this.text = "How to UserControl of DFL";
        this.size = Size(200, 200);
        
        // 自作コントロールを設定
        mc_ = new MyControl;
        mc_.parent = this; // フォームを覚えさせないとメッセージを受け取れないので必須
        mc_.location = Point(70, 70);
        mc_.size = Size(50,50);//設定しないと横 0 ×縦 0 で見えなかったので
        mc_.fire ~= (MyControl /+mc+/, MyControlFireArgs mca)// 自作のイベントハンドラに処理を登録
        {
            scope ms = new MemoryStream;
            ms.writef("name=%s" \n "uso mouse position=(%d,%d)", mca.myName, mca.usoPos.x, mca.usoPos.y);
            msgBox(ms.toString(), "MyControl");
        };
        
        // リストビューを設定
        lv_ = new ListView;
        lv_.parent = this;// 必須
        lv_.location = Point(30,30);
        lv_.items.add("foo");
        lv_.selectedIndexChanged ~= (ListView list, EventArgs /+ea+/) // DFLが用意しているイベントハンドラに処理を登録
        {
            msgBox(list.selectedItems[0].toString(), "ListView");
        };
    }
}

int main()
{
    // 自分でエラーをハンドリングしたいので try-catch に入れてみた
    try
    {
        // フォームを渡すとメッセージループ開始
        Application.run(new MainForm);
    }
    catch(Exception e)
    {
        msgBox(e.toString());
    }
    return 0;
}