このページでは、独自プラグインのユーザ インタフェース内で Stingray エディタの Property Editor ウィジェットを再使用する 2 つの方法を示します。
1 番目の方法では、.type ファイルを使用してデータ リソースの構造、およびこれらのデータ値を編集するためにエディタで公開する必要があるコントロールを定義します。これらの .type ファイルの背景については、「Stingray タイプ システム」も参照してください。
2 番目の方法では、純粋な JavaScript を使用して、Property Editor ウィジェットと、そこで管理する必要があるデータを提供します。
プラグインに新しいパネルを追加するには、まず .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 サービスの詳細については、「組み込みのエディタ サービスを使用する」も参照してください。
最初に、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 つの異なるプロパティがあります。各フィールドの内容は、次のとおりです。
type。このフィールドに保持されるデータのタイプ(数値、文字列、リソース名など)を指定します。エディタに組み込まれている基本タイプのリストについては、「組み込みタイプのリファレンス」を参照してください。
editor ブロック。このフィールドを変更するためにエディタが Property Editorfor 内に表示する必要があるプロパティ コントロールを指定します。スピナー コントロール内の隣接する数値のステップ サイズなど、コントロールに関する他のメタデータも設定します。エディタでサポートされているプロパティ コントロールの全セットの詳細については、「組み込みメタデータのプロパティ」を参照してください。
タイプ記述子から 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);
documentFromType が PropertyEditor コンポーネントに渡されて、HTML パネルにレンダリングされます。ただし、UI コードを仕上げる前に、タイプ記述子を使用しないで、JavaScript から純粋に Property Editor を作成する方法を見てみましょう。
Stingray エディタに用意されているすべてのプロパティ コントロールにアクセスするには、property-editor-utils モジュールを使用します。上記の[Initialization]セクション内で設定した define 呼び出しでは、このモジュールが必要であり、props 変数に割り当てられていました。
純粋な JavaScript コードを使用すると、これらの異なるプロパティのパラメータをコントロールし、これらのプロパティ モデルの動作を微調整することができます。
すべてのプロパティは「モデル」という特殊な getter/setter 関数を使用して、これらのデータへのアクセスや変更を行います。この関数を使用することにより、プロパティの入力方法を定義するときの柔軟性が高まります。プロパティ モデルは、1 つまたは 2 つの引数を指定して呼び出すことができる関数です。
最初の引数は、モデルの変更をトリガするプロパティです。
2 番目の引数が指定されていない場合、関数は getter になり、プロパティの値を返す必要があります。
2 番目の引数が指定されている場合、関数は setter になり、プロパティの値を変更する必要があります。
パネルに、プロパティ モデル 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 コンパクト表記でサポートされるさまざまなプロパティ コントロールの詳細については、「組み込みメタデータのプロパティ」を参照してください。
パネルを完成させるために残っている作業は、2 つの Property Editor ウィジェットを保持するビューを作成することのみです。オートデスクでは、Mithril という軽量な JavaScript ライブラリを使用して、データ モデルとの同期を維持する動的な HTML の作成を簡易化する予定です。デバッグを容易に実行できる純粋な JavaScript で、(テンプレート作成を含む)あらゆる処理を実行できます。
オートデスクでは Mithril に関する 2 つの主要概念を使用しています。
コンポーネント: これは、view 関数を含める必要があるプレーンな JavaScript オブジェクトです。view 関数は m というハイパースクリプト ヘルパーを使用して、ビュー内でユーザに表示される実際の HTML DOM に接続される仮想 DOM を作成します。Mithril テンプレート作成言語(実際は JavaScript 内で HTML で表現される)の使用方法の詳細については、こちらを参照してください。
マウント: オートデスクで Mithril コンポーネントを使用する場合に、適切にレンダリングするには、実際の DOM ノードにこのコンポーネントをマウントする必要があります。
ビュー コードは次のようになります。
// 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 }; });