UI で Property Editor コマンドを使用する

このページでは、独自プラグインのユーザ インタフェース内で Stingray エディタの Property Editor ウィジェットを再使用する 2 つの方法を示します。

プラグインの定義

プラグインに新しいパネルを追加するには、まず .stingray_plugin ファイル内でパネルを定義する必要があります。ここでは、views および menus 拡張機能を使用します。

 views = [
    {
        type = "panel"
        name = "property-editor-usage"
        path = "property-editor-usage"
        title = "Property Editor usage"
        width = 500
        height = 600
    }
]
menus = [
    {
        path = "Window/My Property Editor Panel"
        view = "plugin-manager"
        shortcut = "Alt+Shift+P"
        order = 379
    }
]

名前の付いたパネルまたはダイアログ ボックスを作成する」および「新規メニュー項目を作成する」も参照してください。

パネルの初期化

パネルの JavaScript ファイルは、インクルードする他の JavaScript モジュールを指定する define ブロックで開始する必要があります。この作業を行う理由と、その方法の詳細については、「プラグインの開発に関するヒント」の最後のセクションを参照してください。

// This is the list of all modules we depend on:
define([
    'properties/mithril-property-ext',
    'properties/property-editor-utils',
    'properties/property-editor-component',
    'properties/property-document',
    'components/dom-tools',
    'services/data-type-service',
    'services/marshalling-service',
    'services/host-service',
    'services/project-service',
    'services/element-service',
    'services/asset-service',
    'services/file-system-service'
],
    /** This is the initialization function of your module. Each required dependency gets injected
        in the same order it is included. Module starting with capital letter a generally Component
        or class (that can be used with new). Module starting with a lowerCase are generally namespace
        containing functions and data.
    */
    function (m, props, PropertyEditor, PropertyDocument, domTools, dataTypeService,
             marshallingService, hostService, projectService, elementService, assetService, fileSystemService) {
    'use strict';
    document.title = "Simple tool";

    // Ensure all CSS are loaded for the property editor:
    domTools.loadCss("core/css/widgets/json-component.css");
    domTools.loadCss("core/css/widgets/property-editor.css");

上記の define 呼び出しに含まれるコア Stingray サービスの詳細については、「組み込みのエディタ サービスを使用する」も参照してください。

方法 1: type ファイルを使用する

最初に、Property Editor ウィジェットに表示するデータのタイプ記述子を指定します。そのためには、エディタの fileSystemService で提供される readJSON() メソッドを使用するなどして、ディスクから .type ファイルをロードします。ただし、ここでは JavaScript 内で直接(純粋な JSON を使用して)定義します。

// Create the type definition "inline". Alternatively, it could have been loaded from a .type file:
var typeDefinition = {
    type: ':struct',
    fields: {
        Number: {
            type: ":number",
            default: 1,
            min: 0,
            max: 1,
            editor: {
                control: "Number",
                step: 0.3,
                priority: 4
            }
        },
        Boolean: {
            type: ":bool",
            default: true
        },
        Enum: {
            type: ":enum",
            value: ":string",
            default: "InLoading",
            cases: [
                "Loaded",
                "Unloaded",
                "InLoading",
                "ErrorLoading"
            ],
            editor: {
                control: "Choice",
                label: "Load state",
                case_labels: {
                    "Loaded": "Loaded",
                    "Unloaded": "Unloaded",
                    "In Loading...": "InLoading",
                    "Error while Loading": "ErrorLoading"
                }
            }
        },
        Unit: {
            type: ":resource",
            extension: "unit",
            default: 'core/units/light'
        },
        FilePath: {
            type: ":string",
            default: 'C:/Program Files/Git/bin/git.exe',
            editor: {
                control: "PathProperty",
                browseType: "File",
                browseTitle: "Select a exe",
                browseFilter: "*.exe"
            }
        },
        DirPath: {
            type: ":string",
            default: 'C:/Program Files/Git',
            editor: {
                control: "PathProperty",
                browseType: "Folder",
                browseTitle: "Select a folder"
            }
        }
    }
};

上の手順で定義したタイプ記述子には 6 つの異なるプロパティがあります。各フィールドの内容は、次のとおりです。

プロパティ ドキュメントの設定

タイプ記述子から Property Editor を初期化するには、PropertyDocument クラスを使用します。このクラスを使用すると、ユーザが Property Editor のコントロールを使用して JSON 値を変更するときに発生する valueChanged イベントを分類して、リッスンすることができます。

// Use the dataTypeService to create an initial value that conforms to the type definition.
var initialValue = dataTypeService.createDefaultValue(typeDefinition);

// This instance will holds all the categories and properties created from the type definition.
var documentFromType = new PropertyDocument();

// you can register a global listener to know when a property changes:
documentFromType.on('valueChanged', function (path, value) {
    console.log('valueChanged', path, value);
    m.utils.redraw();
});

// Now we add a category block mapped to a typeDefinition and a value:
documentFromType.addCategory("My Data", {}, initialValue, typeDefinition);

documentFromTypePropertyEditor コンポーネントに渡されて、HTML パネルにレンダリングされます。ただし、UI コードを仕上げる前に、タイプ記述子を使用しないで、JavaScript から純粋に Property Editor を作成する方法を見てみましょう。

方法 2: コンパクト表記を使用する

Stingray エディタに用意されているすべてのプロパティ コントロールにアクセスするには、property-editor-utils モジュールを使用します。上記の[Initialization]セクション内で設定した define 呼び出しでは、このモジュールが必要であり、props 変数に割り当てられていました。

純粋な JavaScript コードを使用すると、これらの異なるプロパティのパラメータをコントロールし、これらのプロパティ モデルの動作を微調整することができます。

すべてのプロパティは「モデル」という特殊な getter/setter 関数を使用して、これらのデータへのアクセスや変更を行います。この関数を使用することにより、プロパティの入力方法を定義するときの柔軟性が高まります。プロパティ モデルは、1 つまたは 2 つの引数を指定して呼び出すことができる関数です。

パネルに、プロパティ モデル generator 関数が作成されています。

function genModel (value) {
    // This returns a property model function that uses a closure to stores its value.
    return function (newValue) {
        if (arguments.length) {
            // arguments is a JavaScript built in variable available in a function. It allows access to
            // all the parameters passed to the function. Here we check if we have more than
            // 1 parameters so see if the model should be used as a setter.
            value = newValue;
        }
        return value;
    };
}

次のコードはエディタのコンパクト表記を使用して、上記の .type の例で設定したのとまったく同じプロパティ セットを表現します。

// The compact notation allows easy configuration of a PropertyEditor using pure JavaScript:
var compactNotationDocument = new PropertyDocument([
    props.category("My Data also!", {}, [
        props.number("Number", genModel(1), {
            default: 1, min: 0, max: 1, increment: 0.3, priority: 4
        }),
        props.bool('Boolean', genModel(true)),
        props.choice('Load State', genModel('InLoading'), {
                "Loaded": "Loaded",
                "Unloaded": "Unloaded",
                "In Loading...": "InLoading",
                "Error while Loading": "ErrorLoading"
            }),
        props.resource('Unit', genModel('core/units/light'), 'unit'),
        props.file('FilePath', genModel('C:/Program Files/Git/bin/git.exe')),
        props.directory('DirPath', genModel('C:/Program Files/Git')),
    ])
]);

Stingray コンパクト表記でサポートされるさまざまなプロパティ コントロールの詳細については、「組み込みメタデータのプロパティ」を参照してください。

Mithril ビューを作成してマウントする

パネルを完成させるために残っている作業は、2 つの Property Editor ウィジェットを保持するビューを作成することのみです。オートデスクでは、Mithril という軽量な JavaScript ライブラリを使用して、データ モデルとの同期を維持する動的な HTML の作成を簡易化する予定です。デバッグを容易に実行できる純粋な JavaScript で、(テンプレート作成を含む)あらゆる処理を実行できます。

オートデスクでは Mithril に関する 2 つの主要概念を使用しています。

ビュー コードは次のようになります。

// Our app component
var PropertyEditorUsageApp = {
    view: function () {
        return m('div', {class :"panel-fill panel-flex-horizontal fullscreen", style:"overflow: auto;"}, [
            m('div', {class: "panel-fill"}, [
                // Use the PropertyEditor Mithril component to attach a
                // Property editor in our view and pass it a configuration:
                PropertyEditor.component({document: documentFromType })
            ]),
            m('div', {class: "panel-fill"}, [
                PropertyEditor.component({document: compactNotationDocument})
            ])
        ]);
    }
};

// Mount the component on DOM
m.mount($('#mithril-root')[0], m.component(PropertyEditorUsageApp, {}));

全体の仕組み

次に、エディタ内で最終的なパネルがどのように表示されるのかを示します。

完全なソース コードを以下に示します。

define([
    'properties/mithril-property-ext',
    'properties/property-editor-utils',
    'properties/property-editor-component',
    'properties/property-document',
    'components/dom-tools',
    'services/data-type-service',
    'services/marshalling-service',
    'services/host-service',
    'services/project-service',
    'services/element-service',
    'services/asset-service',
    'services/file-system-service'
], function (m, props, PropertyEditor, PropertyDocument, domTools, dataTypeService,
             marshallingService, hostService, projectService, elementService, assetService, fileSystemService) {
    'use strict';
    document.title = "Simple tool";

    // Ensure all CSS are loaded for the property editor:
    domTools.loadCss("core/css/widgets/json-component.css");
    domTools.loadCss("core/css/widgets/property-editor.css");

    //////////////////////////////////////////////////////////////////
    // Initialization of a property editor using a Type file to populate itself.

    // Create type definition "inline". could have been loaded from a .type file.
    var typeDefinition = {
        type: ':struct',
        fields: {
            Number: {
                type: ":number",
                default: 1,
                min: 0,
                max: 1,
                editor: {
                    control: "Number",
                    step: 0.3,
                    priority: 4
                }
            },
            Boolean: {
                type: ":bool",
                default: true
            },
            Enum: {
                type: ":enum",
                value: ":string",
                default: "InLoading",
                cases: [
                    "Loaded",
                    "Unloaded",
                    "InLoading",
                    "ErrorLoading"
                ],
                editor: {
                    control: "Choice",
                    label: "Load state",
                    case_labels: {
                        "Loaded": "Loaded",
                        "Unloaded": "Unloaded",
                        "In Loading...": "InLoading",
                        "Error while Loading": "ErrorLoading"
                    }
                }
            },
            Unit: {
                type: ":resource",
                extension: "unit",
                default: 'core/units/light'
            },
            FilePath: {
                type: ":string",
                default: 'C:/Program Files/Git/bin/git.exe',
                editor: {
                    control: "PathProperty",
                    browseType: "File",
                    browseTitle: "Select a exe",
                    browseFilter: "*.exe"
                }
            },
            DirPath: {
                type: ":string",
                default: 'C:/Program Files/Git',
                editor: {
                    control: "PathProperty",
                    browseType: "Folder",
                    browseTitle: "Select a folder"
                }
            }
        }
    };

    // Use the dataTypeService to create an initial value conforming to the type definition.
    var initialValue = dataTypeService.createDefaultValue(typeDefinition);

    // This instance will holds all the categories and properties created from the type definition.
    var documentFromType = new PropertyDocument();

    // you can register global listener to know when a property changes:
    documentFromType.on('valueChanged', function (path, value, property, category, doc) {
        console.log('valueChanged', path, value, property, category, doc);
        m.utils.redraw();
    });

    // Now we add a category block mapped to a typeDescriptor and a value:
    documentFromType.addCategory("My Data", {}, initialValue, typeDefinition);

    //////////////////////////////////////////////////////////////////
    // Initialization of a property editor using a Type file to populate itself.

    // This will be our model generator:
    function genModel (value) {
        return function (property, newValue) {
            if (arguments.length > 1) {
                value = newValue;
            }
            return value;
        };
    }

    // The compact notation allows easy configuration of a PropertyEditor using pure JavaScript:
    var compactNotationDocument = new PropertyDocument([
        props.category("My Data also!", {}, [
            props.number("Number", genModel(1), {
                default: 1, min: 0, max: 1, increment: 0.3, priority: 4
            }),

            props.bool('Boolean', genModel(true)),

            props.choice('Load State', genModel('InLoading'), {
                    "Loaded": "Loaded",
                    "Unloaded": "Unloaded",
                    "In Loading...": "InLoading",
                    "Error while Loading": "ErrorLoading"
                }),

            props.resource('Unit', genModel('core/units/light'), 'unit'),

            props.file('FilePath', genModel('C:/Program Files/Git/bin/git.exe')),

            props.directory('DirPath', genModel('C:/Program Files/Git')),
        ])
    ]);

    var PropertyEditorUsageApp = {
        view: function () {
            return m('div', {class :"panel-fill panel-flex-horizontal fullscreen", style:"overflow: auto;"}, [
                m('div', {class: "panel-fill"}, [
                    PropertyEditor.component(documentFromType)
                ]),
                m('div', {class: "panel-fill"}, [
                    PropertyEditor.component(compactNotationDocument)
                ])
            ]);
        }
    };

    // Initialize the application
    m.mount($('#mithril-root')[0], m.component(PropertyEditorUsageApp, {}));

    return {
        noAngular: true
    };
});