DlangUI入門

Windows,Linux,OSXに対応しており、D言語ネイティブ。そんな便利そうなGUIライブラリの入門記事です。
[公式のGetting Started](https://github.com/buggins/dlangui/wiki/Getting-Started)と内容はほぼ同じですが...
#どんなものが作れるか
![dman_optimized.gif](https://qiita-image-store.s3.amazonaws.com/0/77773/868f85a0-0325-f2a1-c90b-cccdd21b8b60.gif)
こんなものが割と簡単に作れます。
この記事ではこれを何も見ずに作れるように記事を書いていきます
#DUBでプロジェクトを作る
今回はDlangManという名前でプロジェクトを作ります。
```dub init DlangMan```
として、dub.sdlを

```yaml:dub.sdl
name "dlangman"
description "A minimal D application."
copyright "Copyright © 2015, namachan"
authors "namachan"
dependency "dlangui" version="~master"
```
としてもいいですが、
```dub init DlangMan dlangui```とすると自動的にDlangUIの最新版が使えるので楽です。
#動かしてみる
では次に、プロジェクトのディレクトリの中のsourceディレクトリにあるapp.dを次のように書き換えてください

```d:app.d
import dlangui;

mixin APP_ENTRY_POINT;

extern(C) int UIAppMain(string[] args)
{
	Platform.instance.uiLanguage="en";
	Platform.instance.uiTheme="theme_default";
				    
	auto window = Platform.instance.createWindow("DlangMan",null);
	auto button = new Button;
	button.text = "DlangMan";
	window.mainWidget = button;
	window.show;
	return Platform.instance.enterMessageLoop();
}
```
これを実行すると![Screenshot_from_2015-12-19 22:32:58.png](https://qiita-image-store.s3.amazonaws.com/0/77773/6a07d17d-8e6d-6f9d-4b4e-d0fdb0bbd76e.png)
このような画面が表示されます。
Platform.instance.uiLanguageは言語の設定をする関数です。一見変数に直接代入しているように見えますが、実際は```@property```を付けてあたかも変数のように扱えるようにした関数です。
使用できる言語は、英語、フランス語、ロシア語を指定でき、それぞれen,fr,ruと表記します。
Platform.instance.uiThemeはUIのテーマを指定でき、theme_darkとtheme_defaultがもともとあります。また、Android APIのテーマと同じ形式で記述されたテーマファイルを使うことでテーマをカスタマイズすることが可能です。
```Platform.instance.createWindow```の第一引数にはウィンドウのタイトルバーの文字列、第二引数には親ウィンドウを渡します。今回は親ウィンドウが無いのでnullを渡します。
第三引数にはWindowFlagで定義されているフラグを渡すことでフルスクリーン、モーダルウィンドウ、大きさ変更可能なウィンドウに出来ます。それぞれ、```WindowFlag.Fullscreen```,```WindowFlag.Modal```,```WindowFlag.Resizable```です。
今回は```window.mainWidget```に直接Buttonをセットしていますが、実際にはLayoutにウィジェットやLayoutを追加していき、それをセットすることが多いと思います。
```Platform.instance.enterMessageLoop();```でメッセージループに入り、ボタンなどが使用できるようになります。
#Buttonを使ってみる
まずは押すと標準出力に```push```と出力するボタンを作ってみましょう。最初のプログラムに少し付け加えるだけです。
ボタンは、

```d
auto button = new Button
button.text = "push!";
```
これで、ボタンをヒープに生成して表示する文字をセットします。
引数を取るコンストラクタもあるのですが、まだよく分かりません...。

```d
button.addOnClickListener(delegate(Widget src)
    {
          "pushed".writeln;
    });
```
でクリックされた際に呼び出されるデリゲートをセットします。
そしてこの```button```を```window```の```mainWidget```に設定すればボタンを押すと標準出力にpushedと表示されるはずです。
![Screenshot_from_2015-12-21 01:12:52.png](https://qiita-image-store.s3.amazonaws.com/0/77773/b531f68c-62a9-bb7f-2532-ee82f7071d82.png)

```d
import dlangui;
import std.stdio;

mixin APP_ENTRY_POINT;

extern(C) int UIAppMain(string[] args)
{
	Platform.instance.uiLanguage = "en";
	Platform.instance.uiTheme = "theme_default";
	auto window = Platform.instance.createWindow("push",null);
	auto button = new Button;
	button.text = "push!";
	button.addOnClickListener(delegate(Widget src)
			{
				"pushed".writeln;
				return true;
			});
	window.mainWidget = button;
	window.show;
	return Platform.instance.enterMessageLoop();
}
```
#画像の読み込みと描画
画像の読み込みは簡単です。
```dlangui.graphics.image.loadImage```を使うことで、画像を```DrawBuf```として読み込めます。DrawBufはJavaで言うBuffredImageみたいなもので、DrawBufに線やフォントやDrawBufを描画したり出来ます。
では早速

```d
import std.stdio;
import dlangui;

mixin APP_ENTRY_POINT;

extern(C) int UIAppMain(string[] args)
{
	Platform.instance.uiLanguage = "en";
	Platform.instance.uiTheme = "theme_default";
	auto window = Platform.instance.createWindow("dman",null);
	auto dman = new Dman;
	window.mainWidget = dman;
	window.show;
	return Platform.instance.enterMessageLoop();
}

class Dman : Widget
{
private:
	DrawBuf image;			
public:
	this()
	{
		image = loadImage("dman.png");
	}
	override void onDraw(DrawBuf buf)
	{
		buf.drawImage(0,0,image);
	}
}
```
ここでは、```Button```や```Layout```などのスーパークラスである```Widget```を継承し、その```onDraw```関数をオーバーライドして描画します。このonDraw関数は、画面の再描画時に呼び出される関数で、渡される```DrawBuf```に描画することで```Widget```の見た目が変化します。
再描画を要求する関数は```invalidate()```です。
#フォントの変更
僕の環境では日本語を描画すると豆腐になってしまうのでフォントの変更は必須です。

```d
import std.stdio;
import dlangui;

mixin APP_ENTRY_POINT;

extern(C) int UIAppMain(string[] args)
{
	Platform.instance.uiLanguage = "en";
	Platform.instance.uiTheme = "theme_default";
	auto window = Platform.instance.createWindow("dman",null);
	auto button = new JaButton;
	window.mainWidget = button;
	button.text = "D言語くん可愛い";
	window.show;
	return Platform.instance.enterMessageLoop();
}

class JaButton : Button
{
private:
	FontRef jaFont;
public:
	this()
	{
		jaFont = FontManager.instance.getFont(20,FontWeight.Normal,false,FontFamily.MonoSpace,"JKゴシックM");
	}
	override FontRef font() @property const
	{
		return cast(FontRef)jaFont;
	}
}
```
フォント名のところは自分の環境にある日本語フォントに読み替えてください。
![Screenshot_from_2015-12-21 00:05:00.png](https://qiita-image-store.s3.amazonaws.com/0/77773/3d602870-8fc0-6181-1769-7c82baa18c57.png)
ここでは、```Button```で日本語を表示できるようにしました。
```Button```は```@property```の付いた```font```という関数を使ってフォントを取得しています。そのため、この```font```関数をオーバーライドすることで比較的簡単に表示されるフォントを変更することが出来ます。
フォントには、通常の```Font```と、FreeTypeを利用した```FreeTypeFont```があり、それぞれ```FontManager```と```FreeTypeFontManager```で読み込みます。
DrawBufに文字列を描画する関数は、```DrawBuf```にではなく、```Font```に用意されてあるので注意してください。
#Layoutについて
僕自身、まだ全然わからないのですが、これが使えないとGUI組めないので解説します。
デフォルトで用意されているLayoutは主に5つあり、

* VerticalLayout
* HorizontalLayout
* TableLayout
* LinerLayout
* FrameLayout

です。
```Widget```や```Layout```を追加するには```addChild()```関数を利用します。
###VerticalLayout
名前の通り垂直に並べます
![Screenshot_from_2015-12-21 00:25:39.png](https://qiita-image-store.s3.amazonaws.com/0/77773/2d7939b5-1b9f-be9e-a026-00ce4915653d.png)
###HorizontalLayout
同じく平行に並べます![Screenshot_from_2015-12-21 00:27:46.png](https://qiita-image-store.s3.amazonaws.com/0/77773/ea72b09a-5702-ce45-db80-decb8b5e726b.png)
###FrameLayout![Screenshot_from_2015-12-21 00:30:10.png](https://qiita-image-store.s3.amazonaws.com/0/77773/bf10ce5a-6d9e-0df8-37d2-2d9e33f2aaf2.png)
こうなってしまいます。
これはButtonの大きさとかマージンとか考えずに上に重ねてしまうからです。
###TableLayout
Excelみたいに並べるやつです
![Screenshot_from_2015-12-21 00:33:40.png](https://qiita-image-store.s3.amazonaws.com/0/77773/f32e2a11-8e3b-9193-f2a9-eb6eb9822fe0.png)

また、これらのレイアウト以外にも自作のレイアウトを使うことも出来ます。
###DMLの利用
DML(DlangUI Markup Language)という言語を使ってレイアウトを作ることが出来ます。

```d
import std.stdio;
import dlangui;

mixin APP_ENTRY_POINT;

extern(C) int UIAppMain(string[] args)
{
	Platform.instance.uiLanguage = "en";
	Platform.instance.uiTheme = "theme_default";
	auto window = Platform.instance.createWindow("dman",null);
	auto layout = parseML(q{
		TableLayout {
			colCount: 2
			margins: 30;padding: 20
			Button {text: "button1"}
			Button {text:"button2"}
			Button {text:"button3"}
			HorizontalLayout {
				Button {text:"button4"}
				Button {text:"button5"}
			}
		}
	});
	window.mainWidget = layout;
	window.show;
	return Platform.instance.enterMessageLoop();
}
```
DMLを中括弧を宣言の次の行に書くFreeBSDスタイルで書くとエラーが出ます(経験済み)
![Screenshot_from_2015-12-21 00:44:01.png](https://qiita-image-store.s3.amazonaws.com/0/77773/6cf2f92b-3602-0b56-edf9-6492918e4969.png)
#終わりに
大変遅れてしまいました...
当初はD言語くんAdventCalenderのプログラムに関してなにか書こうと思っていたのですが、書くことが無さ過ぎてDlangUIの入門記事に変更しました。