222 lines
8.1 KiB
Markdown
222 lines
8.1 KiB
Markdown
|
|
# Gemini先生との EguiBox API設計相談セッション
|
|||
|
|
**日時**: 2025年8月9日
|
|||
|
|
**テーマ**: 膨大なegui APIをシンプルにする革命的アーキテクチャ提案
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🤔 **相談内容**
|
|||
|
|
|
|||
|
|
**質問**: Nyashプログラミング言語でEguiBoxを実装したいのですが、eguiのAPIが膨大すぎて全部Box化するのは現実的ではありません。Everything is Box哲学を維持しながら、API数を大幅に削減する賢い方法はありませんか?
|
|||
|
|
|
|||
|
|
**現在の課題**:
|
|||
|
|
- egui has 数百のUI要素・メソッド
|
|||
|
|
- すべてをBox化すると実装・保守が困難
|
|||
|
|
- でもEverything is Box哲学は維持したい
|
|||
|
|
- 創作プログラミング(ゲーム・アート)に特化したい
|
|||
|
|
- WebAssemblyでも動作する必要がある
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔥 **Gemini先生の革命的提案: データ駆動型UI**
|
|||
|
|
|
|||
|
|
### **核心アイデア**
|
|||
|
|
UIの構造と状態をNyashのデータ構造(リストやマップ)で定義し、それを解釈して`egui`の描画命令に変換する**単一の`EguiBox`メソッド**を用意する。
|
|||
|
|
|
|||
|
|
### **🎯 EguiBox API設計**
|
|||
|
|
`EguiBox`がNyashに公開するメソッドは**たった2つ**:
|
|||
|
|
|
|||
|
|
1. `Egui.new()`: `EguiBox`のインスタンスを作成
|
|||
|
|
2. `Egui.draw(ui_definition, state_map)`: UIを描画し、インタラクションの結果を返す
|
|||
|
|
|
|||
|
|
### **✨ Nyash での UI定義例**
|
|||
|
|
|
|||
|
|
```nyash
|
|||
|
|
# UIの状態を保持するマップ
|
|||
|
|
let ui_state = {
|
|||
|
|
"name": "Nyash",
|
|||
|
|
"age": 10,
|
|||
|
|
"is_cool": true
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
# UIの構造をデータとして定義
|
|||
|
|
let ui_definition = [
|
|||
|
|
["label", "Hello, world!"],
|
|||
|
|
["separator"],
|
|||
|
|
["text_input", "name"], # ID "name" が ui_state のキーと対応
|
|||
|
|
["slider", "age", { "min": 0, "max": 100 }], # ID "age" が ui_state のキーと対応
|
|||
|
|
["checkbox", "is_cool", "Is Nyash cool?"], # ID "is_cool" が ui_state のキーと対応
|
|||
|
|
["button", "reset_button", "Reset Age"]
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
# EguiBoxのインスタンスを作成
|
|||
|
|
let Egui = Egui.new();
|
|||
|
|
|
|||
|
|
# メインループ (ゲームループや毎フレームの描画)
|
|||
|
|
loop {
|
|||
|
|
# 1. UIを描画し、更新された状態とイベントを受け取る
|
|||
|
|
let results = Egui.draw(ui_definition, ui_state);
|
|||
|
|
|
|||
|
|
# 2. Nyash側の状態を更新する
|
|||
|
|
ui_state = results.state;
|
|||
|
|
|
|||
|
|
# 3. イベントを処理する
|
|||
|
|
if (results.events.contains("reset_button")) {
|
|||
|
|
ui_state.age = 10;
|
|||
|
|
print("Age has been reset!");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# ... (次のフレームを待つ処理)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### **🚀 創作プログラミング応用例**
|
|||
|
|
|
|||
|
|
```nyash
|
|||
|
|
# 🎨 動的にUIを生成 - アート作品のパラメータ調整
|
|||
|
|
static box ArtApp {
|
|||
|
|
init { egui, artParams, ui }
|
|||
|
|
|
|||
|
|
main() {
|
|||
|
|
me.egui = new EguiBox()
|
|||
|
|
me.artParams = new MapBox()
|
|||
|
|
me.artParams.set("color_red", 128)
|
|||
|
|
me.artParams.set("color_green", 64)
|
|||
|
|
me.artParams.set("brush_size", 10)
|
|||
|
|
me.artParams.set("auto_animate", true)
|
|||
|
|
|
|||
|
|
# UIをコードで構築!
|
|||
|
|
me.ui = new ArrayBox()
|
|||
|
|
me.ui.push(new ArrayBox(["label", "🎨 Art Generator Controls"]))
|
|||
|
|
me.ui.push(new ArrayBox(["slider", "color_red", new MapBox("min", 0, "max", 255)]))
|
|||
|
|
me.ui.push(new ArrayBox(["slider", "color_green", new MapBox("min", 0, "max", 255)]))
|
|||
|
|
me.ui.push(new ArrayBox(["slider", "brush_size", new MapBox("min", 1, "max", 50)]))
|
|||
|
|
me.ui.push(new ArrayBox(["checkbox", "auto_animate", "Auto Animation"]))
|
|||
|
|
me.ui.push(new ArrayBox(["button", "generate", "🚀 Generate Art!"]))
|
|||
|
|
|
|||
|
|
return me.runArtLoop()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
runArtLoop() {
|
|||
|
|
loop(true) {
|
|||
|
|
# 1回の関数呼び出しでUI更新+イベント取得
|
|||
|
|
results = me.egui.draw(me.ui, me.artParams)
|
|||
|
|
|
|||
|
|
me.artParams = results.get("state")
|
|||
|
|
events = results.get("events")
|
|||
|
|
|
|||
|
|
# イベント処理
|
|||
|
|
if events.contains("generate") {
|
|||
|
|
me.generateArt()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# パラメータが変更されたら自動更新
|
|||
|
|
if me.artParams.get("auto_animate") {
|
|||
|
|
me.updateArtInRealTime()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🎯 **このアーキテクチャの革命的利点**
|
|||
|
|
|
|||
|
|
### 1. **APIの最小化**
|
|||
|
|
- `EguiBox`が公開するAPIは`draw`のみ
|
|||
|
|
- `egui`に100個のウィジェットが追加されても、Nyash側の`EguiBox`のAPIは変更不要
|
|||
|
|
|
|||
|
|
### 2. **Everything is Box哲学の維持**
|
|||
|
|
- UIの定義そのものがNyashのデータ構造(Boxで構成されるリストやマップ)
|
|||
|
|
- 状態もBox化されたマップ
|
|||
|
|
- Nyashの世界観と完全に一致
|
|||
|
|
|
|||
|
|
### 3. **実装と保守の容易さ**
|
|||
|
|
- 新しいウィジェット(例:`color_picker`)に対応するには、Rust側の`match`文に分岐を一つ追加するだけ
|
|||
|
|
- Nyashのインタプリタのコア部分に触る必要なし
|
|||
|
|
|
|||
|
|
### 4. **高い拡張性**
|
|||
|
|
- レイアウト(`horizontal`, `vertical`)も、ネストしたリストで表現可能
|
|||
|
|
- `["horizontal", [ ["button", "A"], ["button", "B"] ] ]`
|
|||
|
|
|
|||
|
|
### 5. **WASM フレンドリー**
|
|||
|
|
- NyashとRust(WASM)の間でやり取りするデータが、シリアライズしやすい巨大なデータ構造一つにまとまる
|
|||
|
|
- 細々とした関数呼び出しを多数行うよりも効率的
|
|||
|
|
|
|||
|
|
### 6. **創作プログラミングとの親和性**
|
|||
|
|
- ゲームのパラメータ調整やアート作品のインタラクションパネルを、Nyashのコード内で動的に生成・変更するのが非常に簡単
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 💡 **Rust側の実装概念**
|
|||
|
|
|
|||
|
|
```rust
|
|||
|
|
// In EguiBox's implementation
|
|||
|
|
pub fn draw(&mut self, ui_definition: Vec<Box>, state_map: MapBox) -> MapBox {
|
|||
|
|
let mut new_state = state_map.clone(); // 更新用の状態マップ
|
|||
|
|
let mut events = Vec::new(); // クリックなどのイベントリスト
|
|||
|
|
|
|||
|
|
// eframe/eguiのUIコールバック内
|
|||
|
|
self.egui_context.run(move |ctx| {
|
|||
|
|
egui::CentralPanel::default().show(ctx, |ui| {
|
|||
|
|
// 1. ui_definitionリストをイテレート
|
|||
|
|
for widget_def_box in ui_definition {
|
|||
|
|
let widget_def = widget_def_box.as_vec().unwrap(); // `["type", "id", ...]`
|
|||
|
|
|
|||
|
|
let widget_type = widget_def[0].as_string().unwrap();
|
|||
|
|
let widget_id = widget_def[1].as_string().unwrap();
|
|||
|
|
|
|||
|
|
// 2. ウィジェット種別に応じてeguiの関数を呼び出す
|
|||
|
|
match widget_type.as_str() {
|
|||
|
|
"label" => {
|
|||
|
|
ui.label(widget_id); // この場合idがラベル文字列
|
|||
|
|
}
|
|||
|
|
"slider" => {
|
|||
|
|
// state_mapから現在の値を取得
|
|||
|
|
let mut value = new_state.get(&widget_id).unwrap().as_f64().unwrap();
|
|||
|
|
// eguiのスライダーを作成
|
|||
|
|
if ui.add(egui::Slider::new(&mut value, 0.0..=100.0)).changed() {
|
|||
|
|
// 値が変更されたらnew_stateを更新
|
|||
|
|
new_state.insert(widget_id, Box::new(value));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
"button" => {
|
|||
|
|
let label = widget_def[2].as_string().unwrap();
|
|||
|
|
if ui.button(label).clicked() {
|
|||
|
|
// クリックされたらeventsリストに追加
|
|||
|
|
events.push(widget_id);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// ... 他のウィジェットも同様に実装
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 3. 結果をMapBoxとしてNyashに返す
|
|||
|
|
let mut results = MapBox::new();
|
|||
|
|
results.insert("state", Box::new(new_state));
|
|||
|
|
results.insert("events", Box::new(events));
|
|||
|
|
results
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🎊 **結論**
|
|||
|
|
|
|||
|
|
**Gemini先生の提案は天才的!**
|
|||
|
|
|
|||
|
|
- **数百のAPI → たった1つのdraw()メソッド**
|
|||
|
|
- **Everything is Box哲学完全維持**
|
|||
|
|
- **創作プログラミングに最適**
|
|||
|
|
- **WebAssembly親和性抜群**
|
|||
|
|
- **実装・保守が超簡単**
|
|||
|
|
|
|||
|
|
この**データ駆動型UI**アーキテクチャにより、Nyashは他に類を見ない革新的なGUI言語となる!
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**📝 記録者**: Claude Code
|
|||
|
|
**🤖 AI協業**: Gemini × Claude
|
|||
|
|
**🌟 革命度**: ★★★★★ (最高評価)
|