SixtyFPSをさわる その2

## はじめに

https://qiita.com/hhatto/items/120295f2d4d1b20d15cc
導入編でビルドして動かせることまで確認できたので、今日はもう少しだけ凝ったUIを実装してみます。

## 環境等
Rust 1.56.1 stable
MacOSX 11.6 (Intel Mac)
SixtyFPS 0.1.5

## 準備

ドキュメントみると、SixtyFPSプロジェクト用のテンプレートがあるみたいなので、それを元にしてみます。

https://github.com/sixtyfpsui/sixtyfps-rust-template

```shell-session
## macosx以外の場合は --features vendored-openssl は不要かも
$ cargo install cargo-generate --features vendored-openssl
$ cargo generate --git sixtyfpsui/sixtyfps-rust-template --name sixtyfps_second
⚠️   Unable to load config file: ~/.cargo/cargo-generate.toml
⚠️   Renaming project called `sixtyfps_second` to `sixtyfps-second`...
🔧   Generating template ...
[ 1/10]   Done: .gitignore
[ 2/10]   Done: .vscode/extensions.json
[ 3/10]   Done: .vscode
[ 4/10]   Done: Cargo.toml
[ 5/10]   Done: README.md
[ 6/10]   Done: build.rs
[ 7/10]   Done: src/main.rs
[ 8/10]   Done: src
[ 9/10]   Done: ui/appwindow.60
[10/10]   Done: ui
🔧   Moving generated files into: `/path/to/sixtyfps-second`...
✨   Done! New project created /path/to/sixtyfps-second
$ cd sixtyfps-second
```

Rustプロジェクト名の関係で`_` は `-`に置き換えられるらしい。（あとプロジェクト名は数字で始めることはできないみたい `1203-sixtyfps-second`みたいなのはアウト❌らしい）

`cargo run`で実行するとアプリが起動する。

![start-template.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1775/04ac4179-3a4f-1afc-5df4-30a4e0068980.png)

`Increase value` をクリックするとCounterの値が増える。

## ファイル構成

SixtyFPSの流儀のディレクトリ構成を少しみておく。

```shell-session
$ tree -L 2
.
├── Cargo.lock
├── Cargo.toml
├── README.md
├── build.rs
├── src
│   └── main.rs
├── target
│   ├── CACHEDIR.TAG
│   └── debug
└── ui
    └── appwindow.60
```

`ui/appwindow.60` がキモになるファイルで、GUIのレイアウトを定義するファイルになる。

```rust:ui/appwindow.60
$ cat ui/appwindow.60
import { Button, VerticalBox } from "sixtyfps_widgets.60";

export AppWindow := Window {
    property<int> counter: 42;
    callback request-increase-value();
    VerticalBox {
        Text {
            text: "Counter: \{counter}";
        }
        Button {
            text: "Increase value";
            clicked => {
                request-increase-value();
            }
        }
     }
}
```

`sixtyfps_widgets.60` に基本的なウィジェットがすでに準備されているようなので、それを使って少し遊んでみる。

ちなみにビルドは `build.rs` 経由で `ui/appwindow.60` を呼び出すような形になっている。

```rust:build.rs
fn main() {
    sixtyfps_build::compile("ui/appwindow.60").unwrap();
}
```

で、 `src/main.rs` で `ui/appwindow.60` で定義したコンポーネント?(ウィジェット?)を呼び出して実行する。

```rust:src/main.rs
sixtyfps::include_modules!();

fn main() {
    let ui = AppWindow::new();

    let ui_handle = ui.as_weak();
    ui.on_request_increase_value(move || {
        let ui = ui_handle.unwrap();
        ui.set_counter(ui.get_counter() + 1);
    });

    ui.run();
}
```

## 遊んでみる

https://github.com/sixtyfpsui/sixtyfps/tree/master/examples

SixtyFPSリポジトリのexamplesディレクトリ見れば色々やりたいこと見つけれそうなので、そのあたりを見つつ色々いじってみます。

### ボタン追加

手始めに、インクリメントボタンがあるのでデクリメントボタンをつけてみる。

```diff
diff --git a/1203-sixtyfps-second/src/main.rs b/1203-sixtyfps-second/src/main.rs
index 1d38226..a600386 100644
--- a/1203-sixtyfps-second/src/main.rs
+++ b/1203-sixtyfps-second/src/main.rs
@@ -8,6 +8,11 @@ fn main() {
         let ui = ui_handle.unwrap();
         ui.set_counter(ui.get_counter() + 1);
     });
+    let ui_handle = ui.as_weak();
+    ui.on_request_decrease_value(move || {
+        let ui = ui_handle.unwrap();
+        ui.set_counter(ui.get_counter() - 1);
+    });

     ui.run();
 }
diff --git a/1203-sixtyfps-second/ui/appwindow.60 b/1203-sixtyfps-second/ui/appwindow.60
index 011b1f3..fa55110 100644
--- a/1203-sixtyfps-second/ui/appwindow.60
+++ b/1203-sixtyfps-second/ui/appwindow.60
@@ -3,6 +3,7 @@ import { Button, VerticalBox } from "sixtyfps_widgets.60";
 export AppWindow := Window {
     property<int> counter: 42;
     callback request-increase-value();
+    callback request-decrease-value();
     VerticalBox {
         Text {
             text: "Counter: \{counter}";
@@ -13,5 +14,11 @@ export AppWindow := Window {
                 request-increase-value();
             }
         }
+        Button {
+            text: "Decrease value";
+            clicked => {
+                request-decrease-value();
+            }
+        }
      }
 }
```

こんな変更をいれると、以下のようになる。

![add-decrease.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1775/f6e423c9-268c-d885-ff26-3f0aed7c5917.png)

callbackには引数渡すこともできるので、以下のような実装でもOK

```diff
diff --git a/1203-sixtyfps-second/src/main.rs b/1203-sixtyfps-second/src/main.rs
index a600386..b36d4b2 100644
--- a/1203-sixtyfps-second/src/main.rs
+++ b/1203-sixtyfps-second/src/main.rs
@@ -4,9 +4,9 @@ fn main() {
     let ui = AppWindow::new();

     let ui_handle = ui.as_weak();
-    ui.on_request_increase_value(move || {
+    ui.on_request_increase_value(move |v| {
         let ui = ui_handle.unwrap();
-        ui.set_counter(ui.get_counter() + 1);
+        ui.set_counter(ui.get_counter() + v);
     });
     let ui_handle = ui.as_weak();
     ui.on_request_decrease_value(move || {
diff --git a/1203-sixtyfps-second/ui/appwindow.60 b/1203-sixtyfps-second/ui/appwindow.60
index fa55110..c5f40f1 100644
--- a/1203-sixtyfps-second/ui/appwindow.60
+++ b/1203-sixtyfps-second/ui/appwindow.60
@@ -2,7 +2,7 @@ import { Button, VerticalBox } from "sixtyfps_widgets.60";

 export AppWindow := Window {
     property<int> counter: 42;
-    callback request-increase-value();
+    callback request-increase-value(int);
     callback request-decrease-value();
     VerticalBox {
         Text {
@@ -11,13 +11,14 @@ export AppWindow := Window {
         Button {
             text: "Increase value";
             clicked => {
-                request-increase-value();
+                request-increase-value(1);
             }
         }
         Button {
             text: "Decrease value";
             clicked => {
-                request-decrease-value();
+                // request-decrease-value();
+                request-increase-value(-1);
             }
         }
      }
```

### ウィンドウサイズ

`preferred-height` と `preferred-width` で起動直後の初期ウィンドウサイズを指定できます。

```diff
diff --git a/1203-sixtyfps-second/ui/appwindow.60 b/1203-sixtyfps-second/ui/appwindow.60
index 5d67a24..3ed3cf2 100644
--- a/1203-sixtyfps-second/ui/appwindow.60
+++ b/1203-sixtyfps-second/ui/appwindow.60
@@ -1,6 +1,8 @@
 import { Button, VerticalBox } from "sixtyfps_widgets.60";

 export AppWindow := Window {
+    preferred-height: 300px;
+    preferred-width: 300px;
     property<int> counter: 42;
     callback request-increase-value(int);
     VerticalBox {
```

![window-size.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1775/443afe00-6e9b-c1b2-4a20-32da3c4b8bee.png)


`.60` ファイルのイディオムとウィジェットあれば結構色々なことができそうです。
`.60` ファイル向けのVimシンタックスファイル欲しい...

今日はここまで。