makepad-2.0-splash について
Splashは、Rustに`script_mod!`で組み込むMakepad 2.0のランタイムUIスクリプト言語です。ホットリロードとインクリメンタル評価に対応し、ストリーミングコード生成を含むMakepad UI開発に使えます。
主な機能
- 01RustでのランタイムUIスクリプト
- 02スクリプトモジュールのホットリロード
- 03インクリメンタル評価とストリーミング評価
- 04リアクティブな状態管理とイベント処理
- 05HTTPリクエストとHTML解析
- 06UI構築用ウィジェットライブラリ
活用シーン
- 01ランタイムスクリプトによるMakepad UI構築
- 02ホットリロードを使ったUIスクリプト更新
- 03ストリーミングコード生成中のUI描画
- 04UIイベントとリアクティブ状態の処理
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ zhanghandong/ makepad-skills --skill makepad-2.0-splashインストール後、Claude Code で/makepad-2.0-splashを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/canvas-splash-patterns.mdsplash-language-reference.mdsplash-scripting-patterns.md詳細
Makepad 2.0 スプラッシュスクリプト言語
Splash は Makepad 2.0 の中核となるランタイム UI スクリプト言語で、2026 年 2 月 12 日にリリースされました。従来のコンパイル時 live_design! マクロシステムに代わり、ホットリロード、ストリーミング評価、AI ファーストのコード生成を可能にするランタイム script_mod! マクロを使用します。
中核となる概念
スクリプトの構造
すべての Splash スクリプトは use インポートから始まり、script_mod!{} マクロを使って Rust に埋め込まれます。
use makepad_widgets::*;
app_main!(App);
script_mod! {
use mod.prelude.widgets.*
// let bindings, functions, state, and UI definitions go here
startup() do #(App::script_component(vm)){
ui: Root{
main_window := Window{
window.inner_size: vec2(800, 600)
body +: {
// UI content
}
}
}
}
}
構文ルール
- プロパティ間にカンマは不要です。空白で区切ります
- セミコロンは不要です。LLM による生成に最適化された、より簡潔な構文です
- プロパティの代入:
key: value - ドットパスの省略記法:
draw_bg.color: #f00(draw_bg +: { color: #f00 }と同等です) - マージ演算子:
key +: { ... }は置き換えずに親を拡張します - 名前付き子要素:
name := Widget{...}(参照可能で、インスタンスごとに上書きできます) - let バインディング:
let MyTemplate = Widget{...}(ローカルスコープで使用され、使用前に定義する必要があります) - Rust バインディング:
#(Struct::register_widget(vm))によって Splash と Rust の構造体を接続します - デバッグログ:
~expressionは評価中に値をログに出力します
状態管理
状態は mod.state オブジェクトとリアクティブな on_render コールバックを使って管理します。
// Define state
let state = { counter: 0 }
mod.state = state
// Reactive rendering -- re-runs when .render() is called
main_view := View{
on_render: ||{
Label{ text: "Count: " + state.counter }
}
}
イベント処理
イベントは Splash 内と Rust の両方から処理できます。
// Inline event handlers in Splash
add_button := Button{
text: "Add"
on_click: ||{
add_todo(ui.todo_input.text(), "")
ui.todo_input.set_text("")
}
}
// TextInput return key
todo_input := TextInput{
on_return: || ui.add_button.on_click()
}
// Startup event
on_startup: ||{
ui.main_view.render()
}
Rust からは script_eval! を使って Splash コードを実行します。
impl MatchEvent for App {
fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions) {
if self.ui.button(cx, ids!(increment_button)).clicked(actions) {
script_eval!(cx, {
mod.state.counter += 1
ui.main_view.render()
});
}
}
}
関数
fn tag_color(tag) {
if tag == "dev" theme.color_highlight
else if tag == "design" theme.color_selection_focus
else theme.color_highlight
}
fn add_todo(text, tag) {
todos.push({text: text, tag: tag, done: false})
ui.todo_list.render()
}
制御フロー
// If/else
if todos.len() == 0
EmptyState{}
else for i, todo in todos {
TodoItem{ label.text: todo.text }
}
// For loops
for i, item in array {
Label{ text: item.name }
}
// While
while condition { ... }
HTTP リクエスト
let req = net.HttpRequest{
url: "https://api.example.com/data"
method: net.HttpMethod.GET
headers: {"User-Agent": "MakepadApp/1.0"}
}
net.http_request(req) do net.HttpEvents{
on_response: |res| {
let text = res.body.to_string()
let json = res.body.parse_json()
}
on_error: |e| { /* handle error */ }
}
ストリーミングレスポンスでは、is_streaming: true と on_stream および on_complete コールバックを使用します。
HTML の解析
let doc = html_string.parse_html()
doc.query("p") // all <p> elements
doc.query("#main") // by id
doc.query("p.bold") // by class
doc.query("div > p") // direct children
doc.query("p[0]").text // text content
doc.query("a@href") // attribute value
ストリーミング評価
Splash のパーサーは、AI/LLM のストリーミングコード生成向けに設計された、チェックポイントベースのインクリメンタル解析に対応しています。
// Rust API for streaming evaluation
vm.eval_with_append_source(script_mod, &code, NIL.into())
これにより、評価前にスクリプト全体が完成するのを待たず、コードがトークン単位で生成されるのに合わせて UI をリアルタイムに更新できます。
ホットリロードとスクリプト Mod の追跡
Splash スクリプトは --hot フラグを使ったホットリロードに対応しています。VM は各 script_mod! ブロックを一意の ScriptModKey(ファイル、行、列)で追跡します。
// Internal: ScriptModKey uniquely identifies a script_mod! block
ScriptModKey { file: "src/app.rs", line: 5, col: 1 }
// Runtime substitution via overrides
ScriptCode::script_mod_overrides // HashMap of ScriptModKey -> updated source
ホットリロードの仕組み:
- ファイルウォッチャー(
makepad_live_reload_core)がソースファイルの変更を検出します - Rust ソースから
script_mod!ブロックを抽出します(生文字列、コメント、文字リテラルを処理します) - Rust プレースホルダーの数(
#(...))を追跡します。プレースホルダーを追加または削除する場合は、完全な再ビルドが必要です - 検証済みのスクリプト Mod を
script_mod_overridesを通じて適用します - IP と位置のマッピングによりエラー報告用のソースマップを提供します(合成オペコードの場合は最も近いトークンにフォールバックします)
ScriptSource のバリアント:
ScriptSource::Mod— 標準のモジュール評価(起動時)ScriptSource::Streaming— インクリメンタルなストリーミング評価(AI/LLM)
重要なレイアウトルール
- コンテナには必ず
height: Fitを設定してください。デフォルトのheight: Fillでは UI が表示されなくなります(高さが 0px になります) - ルートコンテナには
width: Fillを使用してください。最上位で固定ピクセル幅を指定しないでください - テキストの子要素を含み、
show_bg: trueが設定された View には、new_batch: trueを設定してください - テンプレートでは、名前付き子要素に
:=を使用してください。使用しない場合、テキストの上書きはエラーなしで失敗します draw_bg.border_radiusには Inset ではなく浮動小数点数を指定します。draw_bg.border_radius: 16.0- 生の
View{show_bg: true}ではなく、スタイル付き View(RoundedView、SolidView)を使用してください
ウィジェットリファレンス
コアコンテナー:View、SolidView、RoundedView、RectView、RoundedShadowView、CircleView、GradientXView、GradientYView、ScrollXYView、ScrollXView、ScrollYView
テキスト:Label、H1-H4、P、TextBox、TextInput、LinkLabel、Markdown、Html
コントロール:Button、ButtonFlat、ButtonFlatter、CheckBox、Toggle、RadioButton、Slider、DropDown
レイアウト:Splitter、FoldHeader、Hr、Vr、Filler
リスト:PortalList、FlatList
ナビゲーション:Modal、Tooltip、PopupNotification、SlidePanel、ExpandablePanel、PageFlip、StackNavigation
ドック:Dock、DockSplitter、DockTabs、DockTab
メディア:Image、Icon、LoadingSpinner、Vector、MathView、MapView
Canvas:Claude Code から Splash をレンダリングする
Makepad Canvas(tools/canvas/)は、HTTP/WS 経由で受信した Splash コードをレンダリングするスタンドアロンアプリです。Claude Code がビジュアル出力に使用します。
HTTP API(Splash の送信に推奨)
PORT=$(cat /tmp/makepad-canvas.port)
# Full render
curl -s -X POST "http://127.0.0.1:$PORT/splash" -d 'View{width:Fill height:Fit Label{text:"Hello"}}'
# Streaming render
curl -s -X POST "http://127.0.0.1:$PORT/splash/stream" # begin
curl -s -X POST "http://127.0.0.1:$PORT/splash/stream" -d 'View{...' # append
curl -s -X POST "http://127.0.0.1:$PORT/splash/end" # end
# Clear
curl -s -X POST "http://127.0.0.1:$PORT/clear"
WS イベントリスニング(クリックイベントの受信)
# Long-lived WS connection receives button click events as JSON
mkfifo /tmp/ws_fifo; (sleep 99999 > /tmp/ws_fifo &)
websocat ws://127.0.0.1:$PORT < /tmp/ws_fifo > /tmp/canvas_events &
# Events arrive as: {"event":"click","widget":"btn_name"}
インタラクティブボタン
name := Button{...} を使って、クリック可能なボタンを作成します。クリックイベントでは name が送信されます:
View{width:Fill height:Fit flow:Right spacing:12
btn_save := Button{text:"Save"}
btn_cancel := Button{text:"Cancel"}
}
クリック時:{"event":"click","widget":"btn_save"}
Splash のベクターアニメーション
// Pulsing dot (loop_:true = indefinite, NOT "indefinite"!)
Vector{width:16 height:16
Circle{cx:8 cy:8 r:6 fill:#x44ddaa opacity:Tween{from:0.3 to:1.0 dur:1.5 loop_:true}}
}
// Moving dot with color change
Vector{width:Fill height:30
Path{d:"M 20 15 L 400 15" stroke:#x222244 stroke_width:1.}
Circle{cx:Tween{from:20 to:400 dur:3.0 loop_:true} cy:15 r:4 fill:Tween{from:#x44ddaa to:#xffaa44 dur:3.0 loop_:true}}
}
Canvas Splash の構文(重要 -- script_mod! と異なります)
Canvas の HTTP レンダリング用 Splash(POST /splash)を生成する場合は、以下のパターンを正確に使用してください。Canvas Splash の構文は、いくつかの重要な点で script_mod! マクロのコンテキストと異なります:
1. プロパティはネストしたブロックではなく、インラインのドットパスを使用します:
// WRONG -- nested block syntax does not render backgrounds
RoundedView{height: Fit draw_bg: { color: #x1a1a2e border_radius: 8.0 } }
// CORRECT -- dot-path inline
RoundedView{width: Fill height: Fit draw_bg.color: #x1a1a2e draw_bg.radius: 8.}
2. ボーダー半径には draw_bg.border_radius ではなく draw_bg.radius を使用します:
// WRONG
draw_bg.border_radius: 8.0
// CORRECT
draw_bg.radius: 8.
3. パディングには、末尾にドットを付けた浮動小数点数を使う明示的な Inset{} 型を使用します:
// WRONG -- bare number or nested block
padding: 20
padding: { top: 20 bottom: 20 }
// CORRECT
padding: Inset{left: 20. right: 20. top: 16. bottom: 16.}
4. Align には明示的な Align{} 型を使用します:
// WRONG
align: { y: 0.5 }
// CORRECT
align: Align{y: 0.5}
align: Center
5. 浮動小数点数には末尾にドットを付けます:
// WRONG // CORRECT
8.0 8.
16.0 16.
0.5 0.5
6. SolidView と RoundedView では、背景が標準でレンダリングされるため、show_bg: true や new_batch: true は不要です。
7. curl で複数行の Splash を送信する場合は --data-binary を使用します -- 通常の -d は改行を削除します。
実証済み Canvas ダッシュボードテンプレート
ソース:tools/canvas/examples/token-dashboard.splash
SolidView{width: Fill height: Fit draw_bg.color: #x0c0c18 flow: Down padding: Inset{left: 32. right: 32. top: 24. bottom: 24.} spacing: 20
// Title
Label{text: "Dashboard Title" draw_text.color: #xeeeeff draw_text.text_style.font_size: 20}
// Card row
View{width: Fill height: Fit flow: Right spacing: 16
RoundedView{width: Fill height: Fit draw_bg.color: #x161628 draw_bg.radius: 8. padding: Inset{left: 20. right: 20. top: 16. bottom: 16.} flow: Down spacing: 6
Label{text: "Metric Name" draw_text.color: #x888899 draw_text.text_style.font_size: 10}
Label{text: "Value" draw_text.color: #xcc66ff draw_text.text_style.font_size: 28}
}
}
// Horizontal bar chart row
View{width: Fill height: Fit flow: Right spacing: 8 align: Align{y: 0.5}
Label{text: "Label" width: 100 draw_text.color: #xbbbbbb draw_text.text_style.font_size: 10}
RoundedView{width: 200 height: 12 draw_bg.color: #xf44336 draw_bg.radius: 2.}
Label{text: "200" draw_text.color: #x777777 draw_text.text_style.font_size: 10}
}
// Vertical bar chart (bars bottom-aligned)
View{width: Fill height: 130 flow: Right spacing: 2 align: Align{y: 1.0}
View{width: Fill height: Fit flow: Down align: Center spacing: 4
RoundedView{width: 14 height: 80 draw_bg.color: #x7733cc draw_bg.radius: 2.}
Label{text: "Mon" draw_text.color: #x444455 draw_text.text_style.font_size: 7}
}
}
}
Canvas のヒント
- Splash には HTTP、イベントには WS を使用 — 最も信頼性の高いパターンです
- ベクターシェイプのプロパティ:
fill、stroke、stroke_width—draw_bg.*ではありません - CJK/中国語テキスト:本文テキストとコードブロックの両方でサポートされています。CodeEditor は CJK 文字に全角幅のカラムを使用します(2026-03-23 に修正)。テーマの
font_codeには、中国語のフォールバックとして LXGWWenKai が含まれています。 - 大きな POST 本文:Canvas は最大 512KB の HTTP 本文をサポートします
- Pub/sub ブロードキャスト:接続しているすべての WS クライアントがクリックイベントを受信します
- ヘルスチェック:
GET /healthではなくGET /pingを使用します - モニタートグル:
/tmp/canvas-monitor-activeフラグファイルがステータスラインの自動更新を制御します。Canvas のサイドバーにはトグルボタンがあります。
コンパイルと Eval:ウィジェット作成における根本的に異なる 2 つの経路
Splash の Eval の問題をデバッグする際は、この違いを理解することが非常に重要です:
Compiled path (script_mod!, examples, studio):
parse → compile → execute → create_widget
→ script_apply(FULL type default object with all vec + map entries)
→ on_after_apply(value = full object) ✓ ← ScriptHook runs
→ Templates registered, #[live] fields populated from type default
Eval path (POST /splash, Splash.set_text):
parse → eval → create_widget_from_prototype
→ script_apply(EVAL VALUE ONLY, e.g. {body: "..."})
→ on_after_apply: SKIPPED (is_eval() guard in TextFlow/Markdown)
→ Templates NOT registered, #[live] fields use defaults (not type default)
| 項目 | コンパイル(script_mod!) | Eval(set_text) |
|---|---|---|
| ウィジェット作成 | Rust と ScriptVm の初期化を完全に実行 | ScriptVm で文字列のみを評価 |
on_after_apply | 型のデフォルト値全体で呼び出される | 呼び出されない |
| 型のデフォルト vec(名前付き子要素) | 完全に継承される | proto コピーでエントリーが失われる場合がある |
| 型のデフォルト map(プロパティ) | #[live] フィールドに適用される | Eval の値のみ適用される |
| テンプレートの登録 | apply 中に ScriptHook 経由で行う | draw_walk で遅延登録する必要がある |
ScrollYView | 動作する | 何もレンダリングされない — View を使用 |
Eval 経路の問題に対する修正パターン:draw_walk で遅延初期化を実装し、状態がないことを検出して、cx.with_vm(|vm| vm.bx.heap.type_default_for_object(...)) 経由で型のデフォルト値を検索します。これは、Markdown の code_block テンプレート継承の修正に使用した方法です。
Splash 評価時の落とし穴(重大 — 2026-03-23 に判明)
これらの問題は、Splash ランタイム評価(POST /splash、Splash.set_text())で作成されたウィジェットにのみ影響し、コンパイル済みの script_mod! ウィジェットには影響しません。
1. Splash 評価では ScrollYView は動作せず、何も描画されません
// WRONG -- Splash eval renders nothing
ScrollYView{ width: Fill height: Fill flow: Down
Label{ text: "invisible" }
}
// CORRECT -- use View, Canvas wraps it in its own ScrollYView
View{ width: Fill height: Fit flow: Down
Label{ text: "visible" }
}
2. 評価で作成されたウィジェットでは on_after_apply / ScriptHook が呼び出されません
Splash 評価でウィジェット(例:Markdown{body: "..."})を作成しても、ScriptHook::on_after_apply コールバックは呼び出されません。on_after_apply に依存する初期化には、フォールバック経路(例:draw_walk での遅延初期化)が必要です。
3. 評価経路では型のデフォルトプロパティが完全には継承されません
set_type_default() でウィジェット型に追加のプロパティ(use_code_block_widget: true など)や名前付きの子(code_block := View{...} など)を設定しても、Splash 評価で作成されたインスタンスには継承されないことがあります。#[live] フィールドに設定されるのは、評価時の適用値(例:{body: "..."})のみであり、型のデフォルト全体ではありません。
回避策:draw_walk で状態の欠落を確認し、vm.bx.heap.type_default_for_object() を使って型のデフォルトを参照します。
4. 型のデフォルトの vec エントリー(名前付きの子)がインスタンスにコピーされないことがあります
copy_type_default_vec が存在していても、set_type_default() の vec エントリーは、登録からインスタンス作成までの間に失われることがあります。new_with_proto_impl の自動 proto vec コピーは、型のデフォルトよりエントリー数が少ないことがある、直接の proto からコピーします。
5. Splash 内の入れ子の Markdown{} は動作しますが、型のデフォルトテンプレートが必要です
Canvas が mod.widgets.Markdown を code_block := View{CodeView{...}} でオーバーライドし、Splash 評価で Markdown{body: "..."} を作成した場合、code_block テンプレートは自動的には利用できません。修正(widgets/src/markdown.rs 内)では、描画時に型のデフォルトを遅延参照し、欠落しているテンプレートを登録します。
Splash のデバッグ
コマンドラインフラグ
| フラグ | 説明 |
|---|---|
--hot | ホットリロードを有効にします。script_mod! のソースファイルを監視し、保存時に UI を自動更新します。Splash DSL のみを再読み込みします。Rust の変更には再コンパイルが必要です。 |
--stdin-loop | Studio モード:stdin/websocket 経由で Makepad Studio と通信します。Studio 内部で使用されます。 |
print によるデバッグ
std.println() / std.print() は主要なデバッグツールです。出力はターミナルと Studio の Log View の両方に表示されます。
std.println("debug: state.counter = " + state.counter)
~expression のデバッグログ構文でも、評価中に値を出力できます。
~state.counter // prints the value of state.counter during eval
Makepad Studio との統合
Studio は Run List パネルから Splash スクリプトを直接実行できます(プロジェクトルートの makepad.splash を探します)。スクリプトのエラーは、ファイルパスとエラーの詳細とともに Log View に表示されます。
Studio で実行すると、Splash スクリプトでは hub モジュールを使用できます。
| API | 説明 |
|---|---|
hub.run(env, cmd, args) | Splash からサブプロセスを起動します |
hub.set_run_items(items) | Studio の Run List に実行可能な項目を登録します |
hub.studio_ip | Studio の WebSocket アドレス |
現在の制限事項
- ブレークポイントによるデバッグには非対応 — Splash VM はブレークポイントやステップ実行をサポートしていません
- AST ダンプフラグはありません — 解析結果を確認するには、Rust ソース(
script/)にログを追加する必要があります - print ベースのデバッグ —
std.println()と~exprが主要なデバッグツールです
ファイル参照
- 言語マニュアル全文:
splash.md(2559 行) - 移行ガイド:
AGENTS.md(815 行) - Counter の例:
examples/counter/src/app.rs - Todo の例:
examples/todo/src/app.rs - 詳細リファレンス:
skills/makepad-2.0-splash/references/splash-language-reference.md - パターンガイド:
skills/makepad-2.0-splash/references/splash-scripting-patterns.md
Splash の実践的な知見(2026-03-31 に判明、Vox 音声入力アプリの構築から)
レッスン 1:+: ブロック内に instance フィールドを追加できない
Splash の最も重大な制限です。draw_bg +: { } ブロック内に instance my_var: 0.5 を追加すると、実行時エラー 「cannot push to frozen vec」 が発生します。
// WRONG — runtime crash
draw_bg +: {
instance hover: 0.0 // CRASH: cannot push to frozen vec
pixel: fn() { ... }
}
// CORRECT — override pixel function only, use built-in variables
draw_bg +: {
pixel: fn() {
let t = self.draw_pass.time // built-in, always available
return Pal.premul(vec4(t, 0.0, 0.0, 1.0))
}
}
回避策:
- 時間ベースのアニメーションには
self.draw_pass.timeを使用します self.pos、self.rect_sizeを使用します(常に利用可能です)- カスタムのインスタンス変数には、Rust の
DrawQuadサブタイプを作成します(makepad-2.0-shaders スキルを参照) - シンプルなアニメーションインジケーターには
LoadingSpinnerウィジェットを使用します
レッスン 2:透明なフローティングウィンドウの設定方法
真に透明なオーバーレイウィンドウを作成するには、次の 3 つが必要です。
my_window := Window{
show_caption_bar: false
window.transparent: true // 1. Window-level transparency
pass.clear_color: #x00000000 // 2. Render pass clear to fully transparent
body +: {
// 3. Do NOT set draw_bg on body — it overrides transparency
View{
width: Fill height: Fill
// Only visible elements show; rest is see-through
}
}
}
よくある間違い:
window.transparent: trueだけを設定する — ウィンドウは不透明のままです(背景がグレーになります)- body に
draw_bg.color: #x00000000を設定する — 透明にはならず、黒くなるだけです - Rust 側でもフローティングパネルとして設定する必要があります:
MacosWindowConfig::floating_panel()+Borderless
参考実装: Makepad の tools/canvas/src/app.rs で、まったく同じパターンが使われています。
レッスン 3:存在しないプロパティ名
Splash は未知のプロパティを黙って無視します。次の項目では、デバッグに実際に時間を取られました。
| 誤り | 正しい設定 | 備考 |
|---|---|---|
password: true | is_password: true | TextInput のパスワードモード |
LoadingSpinner の color: #fff | (サポートされていません) | LoadingSpinner に color プロパティはありません |
window.backdrop: Vibrancy | 動作しますが、transparent: true も必要です | Backdrop だけではウィンドウは透明になりません |
レッスン 4:絵文字の表示
Makepad のテキストレンダラーは、一部の絵文字には対応していますが、すべてには対応していません。
| 表示できる | 表示できない |
|---|---|
| 🎙 🔍 🔄 | ✨ ⏳ |
絵文字が四角形や文字化けとして表示される場合は、別の絵文字を試してください。BMP(基本多言語面)の基本的な絵文字を使用してください。
レッスン 5:width: Fit + 大きな border_radius = 先端が尖った形状
height: 56 のカプセル形状の RoundedView に border_radius: 28.0 を指定すると、半円ではなくひし形の端になります。基盤の sdf.box() の式は、radius >= min(w,h)/2 の場合に破綻します。
修正方法: RoundedView の代わりにカスタム SDF カプセルシェーダーを使用します。
View{
show_bg: true
draw_bg +: {
pixel: fn() {
let r = self.rect_size.y * 0.5
let px = self.pos.x * self.rect_size.x
let py = self.pos.y * self.rect_size.y
let cx = clamp(px, r, max(r, self.rect_size.x - r))
let d = length(vec2(px - cx, py - self.rect_size.y * 0.5)) - r
let alpha = 1.0 - smoothstep(-1.0, 1.0, d)
return Pal.premul(vec4(0.1, 0.1, 0.18, alpha * 0.82))
}
}
}
レッスン 6:マルチウィンドウアプリ — ウィンドウの表示制御
script_mod! で宣言されたウィンドウは、起動時に自動表示されます。Makepad の WindowRef に close()/open() メソッドはありません。
動作するパターン: ウィンドウを通常のサイズで宣言します。必要に応じて configure_window() を使って前面に表示します。
// Show: configure_window triggers makeKeyAndOrderFront on macOS
let settings = self.ui.window(cx, ids!(settings_window));
settings.configure_window(cx, dvec2(480.0, 560.0), dvec2(500.0, 200.0), false, "Settings".into());
// Hide: resize to 1x1 (no close/minimize on WindowRef)
let capsule = self.ui.window(cx, ids!(capsule_window));
capsule.resize(cx, dvec2(1.0, 1.0));
注: reposition(cx, dvec2(-9999, -9999)) では、macOS のフローティングパネルを確実に非表示にできません。
レッスン 7:new_batch: true とウィジェットの Z オーダー
カスタムシェーダーの View 内に LoadingSpinner(または独自の描画シェーダーを持つ子ウィジェット)を追加すると、端からはみ出して描画されることがあります。子ウィジェットが親の SDF マスクの外側にも描画されるためです。
修正方法: 次のいずれかを行います。
- 親自身のピクセルシェーダーでアニメーションを描画します(子ウィジェットの Z オーダーに関する問題はありません)
- 親に
clip_x: true+clip_y: trueを設定します(完全には解決しない場合があります)
レッスン 8:時間ベースのシェーダーアニメーションにおける継続的な再描画
シェーダー内の self.draw_pass.time は、ウィジェットが再描画されたときにのみ進みます。明示的に再描画しないと、時間が止まります。
fn handle_next_frame(&mut self, cx: &mut Cx, _e: &NextFrameEvent) {
if self.inner.state != STATE_IDLE {
// Redraw the WINDOW (not just the view) to update draw_pass.time
self.ui.widget(cx, ids!(my_window)).redraw(cx);
self.inner.next_frame = cx.new_next_frame();
}
}
重要: 内側の View だけでなく、Window ウィジェットを再描画してください。描画パスの時間はウィンドウごとに管理されます。
レッスン 9:スクリプトから派生した構造体の複雑な型を持つ #[rust] フィールド
#[derive(Script, ScriptHook)] 構造体の #[rust] フィールドには、型が Default を実装しているものだけを指定できます。チャンネルやハンドルなど、Default を実装していない複雑な型は、Inner 構造体でラップします。
#[derive(Default)]
struct Inner {
timer: Timer,
rx: Option<crossbeam_channel::Receiver<u64>>,
handle: Option<SomeNonDefaultHandle>,
}
#[derive(Script, ScriptHook)]
pub struct App {
#[live] ui: WidgetRef,
#[rust] inner: Inner, // Single Default-able wrapper
}