Files
hakorune/src/boxes/egui_box.rs

194 lines
5.7 KiB
Rust
Raw Normal View History

/*! 🖼️ EguiBox - デスクトップGUIアプリBox
* Everything is Box哲学によるGUIフレームワーク統合
* Boxにできる
*
* ## 📝
* Rustの人気GUI框架eframeを使ったネイティブデスクトップアプリ作成
* Nyashコードから直接GUI操作が可能
*
* ## 🛠
* - `setTitle(title)` -
* - `setSize(width, height)` -
* - `run()` - GUIアプリ実行開始
* - `addText(text)` -
* - `addButton(label)` -
* - `close()` -
*
* ## 💡 使
* ```nyash
* // 基本的なGUIアプリ
* local app
* app = new EguiBox()
* app.setTitle("Nyash GUI Demo")
* app.setSize(800, 600)
* app.addText("Welcome to Nyash!")
* app.addButton("Click Me")
* app.run() // GUIアプリ開始
* ```
*
* ##
* - WASM環境では無効
* - `run()`
*/
🎨 feat: EguiBox GUI開発基盤完成 + パーサー無限ループバグ修正 ## 🚀 主要機能追加 ### EguiBox - GUI開発基盤 - Windows版GUIメモ帳アプリ (simple_notepad.rs, nyash_notepad_jp.rs) - 日本語フォント対応 (NotoSansJP-VariableFont_wght.ttf) - BMPアイコン表示システム (c_drive_icon.bmp) - Windowsエクスプローラー風アプリ (nyash_explorer.rs) - アイコン抽出システム (test_icon_extraction.rs) ### ビジュアルプログラミング準備 - NyashFlow プロジェクト設計完成 (NYASHFLOW_PROJECT_HANDOVER.md) - ビジュアルノードプロトタイプ基盤 - WebAssembly対応準備 ## 🔧 重大バグ修正 ### パーサー無限ループ問題 (3引数メソッド呼び出し) - 原因: メソッドパラメータ解析ループの予約語処理不備 - 修正: src/parser/mod.rs - 非IDENTIFIERトークンのエラーハンドリング追加 - 効果: "from"等の予約語で適切なエラー報告、ハング→瞬時エラー ### MapBoxハング問題調査 - MapBox+3引数メソッド呼び出し組み合わせ問題特定 - バグレポート作成 (MAPBOX_HANG_BUG_REPORT.md) - 事前評価vs必要時評価の設計問題明確化 ## 🧹 コード品質向上 - box_methods.rs を8モジュールに機能分離 - 一時デバッグコード全削除 (eprintln\!, unsafe等) - 構文チェック通過確認済み ## 📝 ドキュメント整備 - CLAUDE.md にGUI開発セクション追加 - Gemini/ChatGPT先生相談ログ保存 (sessions/) - 段階的デバッグ手法確立 ## 🎯 次の目標 - must_advance\!マクロ実装 (無限ループ早期検出) - コマンド引数でデバッグ制御 (--debug-fuel) - MapBox問題の根本修正 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-10 07:54:03 +09:00
use crate::box_trait::{NyashBox, StringBox, BoolBox};
use crate::interpreter::RuntimeError;
use std::any::Any;
use std::sync::{Arc, Mutex};
use eframe::{self, epaint::Vec2};
/// EguiBox - GUI アプリケーションを包むBox
///
/// # 使用例
/// ```nyash
/// app = new EguiBox()
/// app.setTitle("My Nyash App")
/// app.setSize(800, 600)
/// app.run()
/// ```
pub struct EguiBox {
title: String,
size: Vec2,
app_state: Arc<Mutex<Box<dyn Any + Send>>>,
update_fn: Option<Arc<dyn Fn(&mut Box<dyn Any + Send>, &egui::Context) + Send + Sync>>,
}
impl std::fmt::Debug for EguiBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EguiBox")
.field("title", &self.title)
.field("size", &self.size)
.finish()
}
}
impl EguiBox {
pub fn new() -> Self {
Self {
title: "Nyash GUI Application".to_string(),
size: Vec2::new(800.0, 600.0),
app_state: Arc::new(Mutex::new(Box::new(()) as Box<dyn Any + Send>)),
update_fn: None,
}
}
/// アプリケーション状態を設定
pub fn set_app_state<T: Any + Send + 'static>(&mut self, state: T) {
self.app_state = Arc::new(Mutex::new(Box::new(state)));
}
/// 更新関数を設定
pub fn set_update_fn<F>(&mut self, f: F)
where
F: Fn(&mut Box<dyn Any + Send>, &egui::Context) + Send + Sync + 'static
{
self.update_fn = Some(Arc::new(f));
}
}
// NyashApp - eframe::Appを実装する内部構造体
struct NyashApp {
app_state: Arc<Mutex<Box<dyn Any + Send>>>,
update_fn: Arc<dyn Fn(&mut Box<dyn Any + Send>, &egui::Context) + Send + Sync>,
}
impl eframe::App for NyashApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
if let Ok(mut state) = self.app_state.lock() {
(self.update_fn)(&mut *state, ctx);
}
}
}
impl NyashBox for EguiBox {
fn to_string_box(&self) -> StringBox {
StringBox::new(
format!("EguiBox('{}', {}x{})", self.title, self.size.x, self.size.y)
)
}
fn clone_box(&self) -> Box<dyn NyashBox> {
// GUI Boxはクローン不可単一インスタンス
Box::new(Self {
title: self.title.clone(),
size: self.size,
app_state: Arc::new(Mutex::new(Box::new(()) as Box<dyn Any + Send>)),
update_fn: None,
})
}
fn as_any(&self) -> &dyn Any {
self
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_egui) = other.as_any().downcast_ref::<EguiBox>() {
BoolBox::new(self.title == other_egui.title && self.size == other_egui.size)
} else {
BoolBox::new(false)
}
}
fn type_name(&self) -> &'static str {
"EguiBox"
}
fn box_id(&self) -> u64 {
// 簡易的なIDとしてポインタアドレスを使用
self as *const _ as u64
}
}
// EguiBoxのメソッド実装実際にはインタープリターから呼ばれない
impl EguiBox {
pub fn run_gui(&self) -> Result<(), RuntimeError> {
if let Some(update_fn) = &self.update_fn {
let app_state = Arc::clone(&self.app_state);
let update_fn = Arc::clone(update_fn);
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default()
.with_inner_size(self.size)
.with_title(&self.title),
..Default::default()
};
let app = NyashApp {
app_state,
update_fn,
};
// 注意: これはブロッキング呼び出し
let _ = eframe::run_native(
&self.title,
options,
Box::new(|_cc| Ok(Box::new(app))),
);
Ok(())
} else {
Err(RuntimeError::InvalidOperation {
message: "No update function set for EguiBox".to_string(),
})
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_egui_box_creation() {
let gui = EguiBox::new();
assert_eq!(gui.title, "Nyash GUI Application");
assert_eq!(gui.size, Vec2::new(800.0, 600.0));
}
#[test]
fn test_egui_box_to_string() {
let gui = EguiBox::new();
let s = gui.to_string_box();
assert_eq!(s.value, "EguiBox('Nyash GUI Application', 800x600)");
}
}