mcpField
M
makepad-2.0-dsl

makepad-2.0-dsl

github.com/zhanghandong開発75253 回インストール29 週間前に更新
リポジトリ

makepad-2.0-dsl について

Makepad 2.0を使う開発者向けのDSL構文スキルです。Splashと`script_mod!`のプロパティ、登録、モジュール構成を解説し、よくある誤りと対処法もまとめています。

主な機能

  1. 01プロパティ、インスタンス、マージ構文
  2. 02コンポーネントとウィジェットの登録パターン
  3. 03モジュール構成とモジュール間共有
  4. 04ランタイムのプロパティ更新
  5. 05よくある構文上の問題

活用シーン

  1. 01`live_design!`から2.0への移行
  2. 02カスタムウィジェットの定義と登録
  3. 03レイアウトやプロパティ上書きの問題調査

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/zhanghandong/makepad-skills --skill makepad-2.0-dsl

インストール後、Claude Code で/makepad-2.0-dslを入力して読み込まれたか確認します。

同梱ファイルreferences/dsl-syntax-reference.mdreferences/property-system.md

詳細

Makepad 2.0 DSL 構文スキル

概要

Makepad 2.0 では、コンパイル時の live_design! マクロが、Splash スクリプト言語を基盤とするランタイムの script_mod! マクロに置き換えられました。このスキルでは、DSL の構文、プロパティシステム、登録パターン、よくある問題を網羅します。

主な構文ルール

プロパティの代入:等号ではなくコロン
key: value          // CORRECT - colon syntax
key = value         // WRONG - old 1.x syntax, no longer works

プロパティは空白または改行で区切ります。同階層の要素の間にカンマは不要です。

View{
    width: Fill
    height: Fit
    flow: Down
    spacing: 10
    padding: 15
}
名前付きインスタンス::= 演算子

:= を使って、参照可能な名前付きウィジェットインスタンスを作成します。

my_button := Button{ text: "Click me" }
title := Label{ text: "Hello" }

名前付きインスタンスは次のように扱えます。

  • Rust コードから id!(my_button) または ids!(my_button) で参照できます
  • ドットパス構文でオーバーライドできます:MyTemplate{ title.text: "New text" }
  • スクリプトオブジェクトの vec に格納されます(map ではありません)

通常のプロパティは : を使い、map に格納されます。

width: Fill       // regular property -> map
label := Label{}  // named child -> vec
マージ演算子:+:

+: 演算子は、置き換えではなく親を拡張/マージします。

draw_bg +: {
    color: #f00    // Only overrides color, keeps all other draw_bg properties
}

+: がない場合は、プロパティ全体が置き換えられます。

draw_bg: { color: #f00 }    // REPLACES all of draw_bg - loses hover, border, etc.
draw_bg +: { color: #f00 }  // MERGES - only changes color, keeps everything else
ドットパスの省略記法

ドットパスはマージの糖衣構文です。

draw_bg.color: #f00
// is equivalent to:
draw_bg +: { color: #f00 }

draw_text.text_style.font_size: 14
// is equivalent to:
draw_text +: { text_style +: { font_size: 14 } }
let バインディング:ローカルテンプレート

let を使うと、script_mod! ブロック内で再利用可能なローカルテンプレートを作成できます。

let MyCard = RoundedView{
    width: Fill height: Fit
    padding: 16 flow: Down spacing: 8
    draw_bg.color: #2a2a3d
    draw_bg.border_radius: 8.0
    title := Label{ text: "Default Title" draw_text.color: #fff }
    body := Label{ text: "" draw_text.color: #aaa }
}

// Instantiate and override:
MyCard{ title.text: "Card 1" body.text: "Content here" }
MyCard{ title.text: "Card 2" body.text: "More content" }

重要:let バインディングは script_mod! ブロック内でのみ有効です。他の script_mod! ブロックからアクセスすることはできません。モジュール間で共有するには、mod.widgets.* に格納してください。

スプレッド演算子:..

別の定義からすべてのプロパティを継承します。

set_type_default() do #(DrawMyShader::script_shader(vm)){
    ..mod.draw.DrawQuad   // Inherit from DrawQuad
}

スクリプトモジュールの構成

基本的なアプリ構成
use makepad_widgets::*;

app_main!(App);

script_mod!{
    use mod.prelude.widgets.*

    load_all_resources() do #(App::script_component(vm)){
        ui: Root{
            main_window := Window{
                window.inner_size: vec2(800, 600)
                body +: {
                    // UI content here
                    my_button := Button{ text: "Click" }
                }
            }
        }
    }
}

impl App {
    fn run(vm: &mut ScriptVm) -> Self {
        crate::makepad_widgets::script_mod(vm);  // 1. Register base widgets
        App::from_script_mod(vm, self::script_mod)
    }
}

#[derive(Script, ScriptHook)]
pub struct App {
    #[source] source: ScriptObjectRef,   // REQUIRED for Script-derived structs
    #[live] ui: WidgetRef,
}

impl MatchEvent for App {
    fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions) {
        if self.ui.button(ids!(my_button)).clicked(actions) {
            log!("Button clicked!");
        }
    }
}

impl AppMain for App {
    fn handle_event(&mut self, cx: &mut Cx, event: &Event) {
        self.match_event(cx, event);
        self.ui.handle_event(cx, event, &mut Scope::empty());
    }
}
ウィジェット定義モジュール
script_mod!{
    use mod.prelude.widgets_internal.*   // For widget library internals
    use mod.widgets.*                     // Access other registered widgets

    // Step 1: Register the Rust struct as a widget base
    mod.widgets.MyWidgetBase = #(MyWidget::register_widget(vm))

    // Step 2: Create a styled variant with default properties
    mod.widgets.MyWidget = set_type_default() do mod.widgets.MyWidgetBase{
        width: Fill
        height: Fit
        padding: theme.space_2

        draw_bg +: {
            color: theme.color_bg_app
        }
    }
}

登録パターン

ウィジェットの登録

Widget トレイトを実装する構造体の場合:

mod.widgets.MyWidgetBase = #(MyWidget::register_widget(vm))

Rust 側:

#[derive(Script, ScriptHook, Widget)]
pub struct MyWidget {
    #[source] source: ScriptObjectRef,  // REQUIRED
    #[walk] walk: Walk,
    #[layout] layout: Layout,
    #[redraw] #[live] draw_bg: DrawQuad,
    #[live] draw_text: DrawText,
    #[rust] my_state: i32,  // Runtime-only, not exposed to script
}
コンポーネントの登録

スクリプトとの連携が必要な、ウィジェットではない構造体の場合:

mod.widgets.MyComponentBase = #(MyComponent::script_component(vm))
描画シェーダーの登録

シェーダーフィールドを持つカスタム描画型の場合:

set_type_default() do #(DrawMyShader::script_shader(vm)){
    ..mod.draw.DrawQuad   // Inherit from DrawQuad
}

Rust 側:

#[derive(Script, ScriptHook)]
#[repr(C)]
pub struct DrawMyShader {
    #[deref] draw_super: DrawQuad,
    #[live] my_param: f32,
}
型のデフォルト値の設定
mod.widgets.MyWidget = set_type_default() do mod.widgets.MyWidgetBase{
    width: Fill height: Fit
    draw_bg +: { color: theme.color_bg_app }
}

登録順序(重要)

ウィジェットモジュールは、それらを使用する UI モジュールより先に登録する必要があります。

impl App {
    fn run(vm: &mut ScriptVm) -> Self {
        crate::makepad_widgets::script_mod(vm);   // 1. Base widgets FIRST
        crate::my_widgets::script_mod(vm);         // 2. Custom widgets SECOND
        crate::app_ui::script_mod(vm);             // 3. UI using widgets THIRD
        App::from_script_mod(vm, self::script_mod) // 4. App component LAST
    }
}
複数モジュールの集約(lib.rs パターン)
pub fn script_mod(vm: &mut ScriptVm) {
    crate::module_a::script_mod(vm);
    crate::module_b::script_mod(vm);
    // ... all widget modules
}

Prelude システム

利用可能な Prelude
Prelude用途
mod.prelude.widgets.*アプリ開発 - 標準ウィジェットをすべて含みます
mod.prelude.widgets_internal.*ウィジェットライブラリの内部開発
Prelude のエイリアス構文
mod.prelude.widgets = {
    ..mod.std,            // Spread all of mod.std into scope
    theme:mod.theme,      // Create 'theme' as alias for mod.theme
    draw:mod.draw,        // Create 'draw' as alias for mod.draw
}

エイリアスを指定しない場合(theme: を付けずに mod.theme, と記述した場合)、モジュールは含まれますが、アクセス可能な名前はありません。

モジュール間の共有

共有方法は mod オブジェクトのみ
// In widget_module.rs - export to mod.widgets namespace
script_mod!{
    use mod.prelude.widgets_internal.*
    mod.widgets.MyWidget = set_type_default() do mod.widgets.MyWidgetBase{ ... }
}

// In app_ui.rs - import via mod.widgets
script_mod!{
    use mod.prelude.widgets.*
    use mod.widgets.*   // Now MyWidget is in scope
    // ...
    MyWidget{}
}

use crate.module.* は機能しません。crate. プレフィックスは script_mod では使用できません。

ランタイムでのプロパティ更新

以前の apply_over と live! の代わりに script_apply_eval! を使用します。

// Old system
item.apply_over(cx, live!{ height: (height) });

// New system - use #(expr) for Rust expression interpolation
script_apply_eval!(cx, item, {
    height: #(height)
    draw_bg: { is_even: #(if is_even { 1.0 } else { 0.0 }) }
});

デバッグログ

スクリプトの評価中に値を記録するには、~expression を使用します。

script_mod!{
    ~mod.theme           // Logs the theme object
    ~mod.prelude.widgets // Logs what's in the prelude
    ~some_variable       // Logs a variable's value
}

よくある問題

1. #[source] source: ScriptObjectRef の不足

Script を派生するすべての構造体には、このフィールドが必要です。

#[derive(Script, ScriptHook)]
pub struct MyStruct {
    #[source] source: ScriptObjectRef,  // REQUIRED - will fail without it
    // ...
}
2. コンテナーでの height: Fit の指定漏れ

デフォルトの高さは Fill です。Fit の親の中で Fill を使うと循環依存が発生し、高さが 0 になって表示されません。

// WRONG - invisible!
View{ flow: Down
    Label{ text: "You can't see me" }
}

// CORRECT
View{ height: Fit flow: Down
    Label{ text: "Visible!" }
}
3. : と := の混同
  • key: value -- プロパティを設定します(map に格納されます)
  • name := Widget{} -- 名前付きで参照可能な子要素を作成します(vec に格納されます)
  • label := Label{ text: "x" } -- 名前付きで、Template{ label.text: "y" } によるオーバーライドが可能です
  • label: Label{ text: "x" } -- 名前がなく、参照できません。オーバーライドはエラーなしで失敗します
4. +: マージ演算子の指定漏れ
// WRONG - replaces ALL of draw_bg (loses hover, border, animations)
draw_bg: { color: #f00 }

// CORRECT - merges, only changes color
draw_bg +: { color: #f00 }
5. テーマへの誤ったアクセス
// WRONG
color: THEME_COLOR_BG     // old 1.x constant syntax
color: (THEME_COLOR_BG)   // old 1.x parenthesized reference

// CORRECT
color: theme.color_bg_app
padding: theme.space_2
font_size: theme.font_size_p
6. e を含む 16 進カラーには #x プレフィックスが必要

Rust のトークナイザーは、16 進リテラル内の e/E を科学表記の指数として解釈します。

// WRONG - Rust parse error: "expected at least one digit in exponent"
color: #2ecc71
color: #1e1e2e

// CORRECT - use #x prefix
color: #x2ecc71
color: #x1e1e2e

// Colors without 'e' work fine with plain #
color: #ff4444    // OK
color: #44cc44    // OK
7. script_mod では pub キーワードは無効
// WRONG
pub mod.widgets.MyWidget = ...

// CORRECT - visibility is controlled by Rust module system
mod.widgets.MyWidget = ...
8. マージン/パディングの Inset{...} コンストラクター構文
// WRONG
margin: { left: 10 }
align: { x: 0.5 y: 0.5 }

// CORRECT - use constructor syntax
margin: Inset{ left: 10 }
align: Align{ x: 0.5 y: 0.5 }
padding: Inset{ top: 5 bottom: 5 left: 10 right: 10 }

// Bare number for uniform values is OK
padding: 15
margin: 0.
9. #[repr(C)]でシェーダー構造体のフィールド順序を指定する

インスタンスデータではないフィールド(#[rust]、インスタンスではない#[live]フィールド)は、必ず#[deref]より前に置いてください。インスタンスフィールド(シェーダー入力)のみ、その後に置きます:

// CORRECT
#[derive(Script, ScriptHook)]
#[repr(C)]
pub struct MyDrawShader {
    #[live] pub svg: Option<ScriptHandleRef>,  // non-instance, BEFORE deref
    #[rust] my_state: bool,                     // non-instance, BEFORE deref
    #[deref] pub draw_super: DrawQuad,          // contains DrawVars + base instances
    #[live] pub tint: Vec4f,                    // instance field, AFTER deref - OK
}

// WRONG - #[rust] after instance fields corrupts GPU buffer
#[derive(Script, ScriptHook)]
#[repr(C)]
pub struct MyDrawShader {
    #[deref] pub draw_super: DrawQuad,
    #[live] pub tint: Vec4f,
    #[rust] my_state: bool,      // BAD: between instance fields
}
10. script_mod!の最初のコードより前にコメントを置かない

Rustのproc macroはトークンストリームからコメントを削除するため、エラー位置がずれます:

// WRONG
script_mod!{
    // This comment shifts error line info
    use mod.prelude.widgets.*
}

// CORRECT - start with real code immediately
script_mod!{
    use mod.prelude.widgets.*
    // Comments after first code are fine
}
その他の注意点
  • カーソル値:cursor: Handやcursor: @Handではなく、cursor: MouseCursor.Handを使用します
  • リソースパス:dep("crate://self/path")ではなく、crate_resource("self://path")を使用します
  • テクスチャ宣言:tex: texture2dではなく、tex: texture_2d(float)を使用します
  • シェーダーのmodとmodf:浮動小数点数の剰余にはmod(a, b)ではなく、modf(a, b)を使用します
  • 列挙型のデフォルト値:列挙型のデフォルト値には、@プレフィックスを付けたdefault: @offを使用します
  • DefaultNone derive:DefaultNone deriveは使用せず、#[default]属性と#[derive(Default)]を使用します
  • シェーダーでのメソッドチェーン:::method()ではなく、.method()を使用します(例:Sdf2d.viewport(...))
  • 色の混合:ネストしたmix()呼び出しより、color1.mix(color2, hover)によるチェーンを優先します
  • ウィジェット登録の漏れ:App::run()で独自のモジュールより前に、crate::makepad_widgets::script_mod(vm)を呼び出します

構文クイックリファレンス

旧形式(live_design!)新形式(script_mod!)
<BaseWidget>mod.widgets.BaseWidget{}またはBaseWidget{}(インポート済みの場合)
{{StructName}}#(Struct::register_widget(vm))
(THEME_COLOR_X)theme.color_x
<THEME_FONT>theme.font_regular
instance hover: 0.0hover: instance(0.0)
uniform color: #fffcolor: uniform(#fff)
draw_bg: {}(置換)draw_bg +: {}(マージ)
default: offdefault: @off
fn pixel(self)pixel: fn()
item.apply_over(cx, live!{...})script_apply_eval!(cx, item, {...})

参考ファイル

コメント