ビジュアル レイアウト エディタの機能拡張

概要

VLE 機能拡張エディタ の章は、 ユーザー自身によって VLE パレット選択の拡張・強化する目的の VLE パレット拡張ファイルの作成ツールである VLE 機能拡張エディタの説明があります。この章では、VLE 機能拡張ファイルの内容と構成のさらに詳しい説明があります。 この章では最も簡単なケースから始めて、いくつかの高度なトピックに進んで解説します。 以下のセクションでは簡単な概要を説明しています。これらを読んだ後、他のセクションでさらに深く掘り下げて学ぶことができます。
この章には、VLE パレットに組み込む実行時パッケージの作成法を解説するセクション、 「VLE で使われる実行時パッケージの定義 」が含まれます。
実行時クラスを定義してその VLE 機能拡張を作成する場合は、以下のセクションにも目を通す必要があります。
Curl IDE ドキュメントには、このセクションに関する豊富なサンプルが含まれています。これらのサンプルをブラウザで実行し、IDE エディタと VLE でソース コードを見ることができます。「内容の充実したサンプル ファイル」を参照してください。

上級者の方へ

この章では、追加の リーフ (取り外し可能)オブジェクトを扱うために VLE 機能を拡張するための十分な情報が含まれています。 リーフ オブジェクトは、VLE で編集される他のオブジェクトのコンテナとしての機能を持ちません。 ここで説明されている方法よりさらに洗練された方法で VLE を拡張する場合は、VLE のアーキテクチャに関するより詳細な知識が必要です。これを習得するには、次のディレクトリにある VLE ソース ファイルを直接調べてみてください。
d:\automated-build-temp\build\win32-atom\ide\vle\editor
以下のセクションのいくつかには、このディレクトリにある特定ファイルへの参照が含まれていて、そこで特定の VLE 機能についてさらに学習することができます。「コンテナ オブジェクトのエディタ」のセクションでは、コンテナ オブジェクトを編集するための VLE 機能拡張の作成について解説しています。

互換性に関する注意

ここで説明する VLE の機能拡張テクノロジーは、Curl IDE のバージョン 3.0.4 とそれ以降のバージョンに関連しています。このテクノロジーは、バージョン 2.0 と 3.0.0 から大幅に変化しています。その結果、これらの以前のバージョンで作成された VLE 機能拡張はバージョン 3.0.4 とそれ以降のバージョンで必ず動作するとは限らず、さらに、この章で説明されている手順に従って作成された VLE 機能拡張が以前のバージョンで動作するという保証はありません。以前のバージョンの VLE を使って作成された VLE ソース ファイルをバージョン 3.0.4 とそれ以降のバージョンの VLE にインポートすることは可能ですが、3.0.4 以前のバージョンで作成された VLE 機能拡張ファイルのほとんどは、手動で更新する必要があります。
先の話になりますが、この章に掲載されている情報に従って作成された VLE 機能拡張は、今後の VLE バージョンでも動作することになっています。だたし、VLE 機能拡張ファイルが複数の VLE ソース ファイルを読み取って集められた情報に依存する場合は、今後の VLE バージョンで動作するために修正が必要になることがあります。

Scrollbar の VLE オブジェクト エディタ

VLE では、オブジェクト エディタをパレット上のアイコンとして表します。オブジェクト エディタでは、VLE が [レイアウト ツリー] ペインにオブジェクトを追加する方法、[レイアウト] ペインで表現する方法、[プロパティ] ペインでオブジェクトのプロパティを編集する方法も定義します。このセクションでは、Curl API オブジェクトの 1 つである Scrollbar の VLE オブジェクト エディタについて説明します。VLE オブジェクト エディタを作成する前に、次の点を考慮する必要があります。
  1. プロパティ シートで Scrollbar のどのプロパティをユーザーが編集できるようにするか、およびこれらのプロパティ エディタにはそれぞれどのようなユーザー インターフェイスが必要か。
  2. [プロパティ] ペインから Scrollbar のどのイベント ハンドラをユーザーが編集できるようにするか。
  3. VLE ユーザーが VLE で新しい Scrollbar をインスタンス化するときに、どの Scrollbar プロパティで既定値を設定しておくか。
  4. Scrollbar アイコンを既存のパレット タブまたは新規パレットのどちらに表示するか。
  5. Scrollbar アイコンにどのような画像を表示するか。
次のコードは、簡単な Scrollbar の VLE エディタを定義しています。このコードはファイルにも収められていて、IDE エディタで調べたり、VLE にロードして使用することができます。「VLE 機能拡張ファイルのインストールとアンインストール」を参照してください。
このサンプル コードは、次のファイルに含まれています。
d:\automated-build-temp\build\win32-atom\docs\default\examples\layout-editor\Scrollbar.zip
アーカイブからファイルを解凍したら、scrollbar-ext.scurl を探します。IDE エディタでファイルを確認し、[パレット機能拡張] ダイアログを使ってこのファイルを VLE にロードし、動作させてみてください。
{import * from CURL.GUI.STANDARD}

{let public constant direction-pmd:PropertyMetaData =
    {OptionMetaData
        "direction",
        {EnumerationEditor
            {StringArray "vertical", "horizontal"}
        },
        help-text =
            {message
                Specifies whether a Scrollbar stretches out
                vertically or horizontally.
            }
    }
}

{define-class public DesignTimeScrollbar {inherits DesignTimeGraphic}

  {constructor public {default}
    {construct-super
        {ObjectMetaData
            {MetaDataCategory
                name = "General",
                design-name-pmd,
                direction-pmd
            },
            standard-geometry-meta-data-category,
            {MetaDataCategory
                name = "Colors",
                color-pmd,
                control-color-pmd,
                background-pmd
            },
            standard-border-meta-data-category,
            {EventHandlerCategory
                {EventHandlerMetaData "Adjustment"}
            },
            default-selected-property = "design-name",
            default-selected-event = "Adjustment"
        }
    }
  }

  {getter public open {initial-properties}:{Array-of PropertyDescriptor}
    {return
        {new {Array-of PropertyDescriptor},
            {PropertyDescriptor direction-pmd, "\"horizontal\""}
        }
    }
  }

  {getter public {runtime-class-name}:String
    {return "Scrollbar"}
  }

  {method protected {new-object element:LayoutElement}:GraphicOptions
    {return {Scrollbar}}
  }

  {getter public {palette-tab-name}:String
    {return "Controls"}
  }

  {getter protected {palette-graphic}:Graphic
    {return 
        {image
            blocking? = true,
            source = {url "images/scrollbar-icon.gif"}
        }
    }
  }
}

{register-palette-item DesignTimeScrollbar}
このファイルの 4 つのステートメントは、オブジェクト エディタを定義する 4 つのステップに対応しています。
  1. import ステートメントを使って、必要な実行環境を設定します。
  2. オブジェクトのプロパティ情報を提供します。この例では、定数 direction-pmd を定義するコードで、direction プロパティの情報を提供しています。colorbackground などの Scrollbar プロパティは他の多数の Curl API にも存在しますが、これらについては VLE で PropertyMetaData オブジェクトが既に定義されています。したがって VLE 実行コードでこれらを再定義する必要はありません。事前定義されているこれらのオブジェクトの完全なリストは、「事前定義されている PropertyMetaData オブジェクト」を参照してください。
  3. DesignTimeGraphic のサブクラスを定義して、VLE が新しいオブジェクト タイプをどのように扱うかを記述します。この例では、サブクラスは DesignTimeScrollbar と呼ばれています。慣例により、オブジェクトの実行時の名前が Name の場合、これを編集する DesignTimeGraphic のサブクラスには、DesignTimeName という名前を使います。
  4. register-palette-item プロシージャを呼び出して、新しいパレット アイコンを実際に追加します。
VLE 機能拡張ファイルを 1 つ作成し、1 つ以上のオブジェクト エディタを定義することができます。 この場合、VLE 機能拡張ファイルで複数の DesignTimeGraphic サブクラスを定義し、それぞれに register-palette-items の呼び出しを含めます。機能拡張により複数のアイコンが同じ [パレット] タブに置かれる場合、タブ内で左から右へ表示される順序は、機能拡張ファイル内の register-palette-items 呼び出しの順序に一致します。
以下の複数のセクションでは、これらの手順についてさらに詳しく解説します。

CURL.GUI.STANDARD のインポート

VLE は、COM.CURL.LAYOUT-EDITOR.EDITOR パッケージが既にインポートされている環境内で機能拡張ファイルを評価します。このパッケージでは、DesignTimeGraphicregister-palette-item および前述の機能拡張ファイルの例で使われているその他の VLE 特有の API アイテムが定義されています。このパッケージの実装を確認するには、次の VLE ソース ファイルを開いて、ファイルの終わりにある include ステートメントのリストに現れるファイルを調べてください。
d:\automated-build-temp\build\win32-atom\ide\vle\editor\load.scurl
ただし、COM.CURL.LAYOUT-EDITOR.EDITORCURL.GUI.STANDARD スーパーパッケージから標準 Curl グラフィックス API をエクスポートしていません。この機能拡張ファイルでは、これらの API の一部 (GraphicScrollbar および image など) が参照されているので、このファイルは CURL.GUI.STANDARD のインポートから始まっています。

direction オプションのプロパティ メタデータの定義

Scrollbarmaxminthumb-length および direction などの多数のオプションを提供しています。この例では、このパレット機能拡張で定義する Scrollbar エディタに、direction オプションだけ編集できる機能を搭載します。
PropertyMetaData オブジェクトは、VLE で編集可能な各オブジェクト プロパティがデザイン時にどのように扱われるかを記述するものです。OptionMetaDataPropertyMetaData のサブクラスで、ローカルまたは非ローカル オプションのプロパティの記述に使われます。direction はローカル オプションなので、サンプル コードでは OptionMetaData のコンストラクタを呼び出しています。この例に示されるような簡単なケースでは、このコンストラクタは 2 つの位置引数と 1 つのキーワード引数を取ります。
direction オプションは Orientation 型で、Orientation は次の 2 つの有効な値を持つ列挙型です。
VLE では EnumerationEditor クラスが用意されています。これは、列挙型の値を持つオプションを編集するための PropertyEditor のサブクラスです。この例では EnumerationEditor を使ってこのオプションのプロパティ エディタを生成しています。

編集可能な Scrollbar プロパティの定義

DesignTimeScrollbar クラスでは、Scrollbar 自体の編集に関する VLE の動作を指定します。このクラスのコンストラクタはスーパークラスのコンストラクタを呼び出し、ObjectMetaData 引数を渡します。ObjectMetaData では、VLE で Scrollbar 型のオブジェクトが編集される際のプロパティ シートの内容を指定します。オブジェクトのメタデータには、PropertyMetaData オブジェクトのグループがいくつか含まれます。各グループは MetaDataCategory と呼ばれ、オブジェクトの個々のプロパティを表す 1 つまたは複数の PropertyMetaData オブジェクトがこのグループに含まれます。
EventHandlerCategory はこれらのグループの 1 つですが、特別に扱われます。このカテゴリのプロパティはすべて、[プロパティ] ペインの [イベント ハンドラ] タブに表示されます。その他のプロパティはすべて [プロパティ] ペインの [プロパティ] タブに表示されます。下図に示されるように、この Scrollbar エディタは[プロパティ] タブ内で、[全般][配置][色] および [境界] の 4 つのプロパティ グループを定義します。これらのグループにはそれぞれ独自の MetaDataCategory オブジェクトが存在します。
Figure: The property sheet for the simple Scrollbar editor
[全般] グループには design-name プロパティが含まれ(ほとんどの場合、常に含めるようにします)、direction も含まれています。direction プロパティ エディタは、前述の direction-pmd プロパティ メタデータ オブジェクトで指定されています。design-name プロパティ エディタは design-name-pmd オブジェクトによって指定されています。このオブジェクト自体は COM.CURL.LAYOUT-EDITOR.EDITOR パッケージで定義されています。このパッケージでは、Curl 言語のグラフィカル オブジェクトに関するその他多数の共通プロパティについて、プロパティのメタデータ オブジェクトが定義されています。これらの事前定義されているプロパティ メタデータ オブジェクトのリストは、「事前定義されている PropertyMetaData オブジェクト」を参照してください。
[配置] グループは standard-geometry-meta-data-category によって指定されています。これは標準 VLE で事前定義されている MetaDataCategory オブジェクトで、widthheighthorigin および vorigin オプションが含まれています。ほとんどの場合、このグループに必要なオプションのセットはこれだけです。
[色] グループには、Scrollbar の外観に影響を与えるカラー関連オプション (colorcontrol-color および background) が含まれています。これらの 3 つのオプションのプロパティ メタデータ オブジェクトは、COM.CURL.LAYOUT-EDITOR.EDITORcolor-pmdcontrol-color-pmd および background-pmd として既に定義されています。
[境界] グループは standard-border-meta-data-category によって指定されています。standard-geometry-meta-data-category と同様に、これは COM.CURL.LAYOUT-EDITOR.EDITOR で既に定義されている MetaDataCategory オブジェクトです。グラフィカル オブジェクトの境界線や余白を制御する標準 Curl オプションのエディタが含まれています。

編集可能な Scrollbar イベント ハンドラの定義

最後に、この簡単な Scrollbar エディタでは、1 つのイベント タイプのイベント ハンドラ エディタを提供しています。このイベント タイプは、Scrollbar の値が変更された場合に発生する Adjustment イベントです。Scrollbar プロパティ シートのこの部分は、[境界] グループの指定に続く EventHandlerCategory 式によって指定されています。

最初に選択されるプロパティ エディタの指定

VLE が Scrollbar のプロパティ シートを初めて表示するときには、プロパティ エディタの 1 つがアクティブな状態になります。この最初に選択されるプロパティは、ObjectMetaData コンストラクタの default-selected-property キーワード引数によって指定します。この例では design-name です。同様に、VLE が Scrollbar のプロパティ シートの [イベント ハンドラ] タブを初めて表示するときには、いずれかのイベント ハンドラが選択された状態になります。最初に選択されるイベント タイプは default-selected-event キーワード引数によって指定します。この例では Adjustment です。

プロパティの初期値の指定

DesignTimeGraphic サブクラスの initial-properties メソッドでは、VLE ユーザーがオブジェクトのインスタンスを新規作成してレイアウトに置くときに使われる、特定プロパティの初期値を指定します。この例では、direction オプションを "horizontal" に設定して新しい Scrollbar をそれぞれ作成するように DesignTimeScrollbar で指定しています。VLE ユーザーは、オブジェクトを作成した後で [プロパティ値を元に戻す] コマンドを使い、このオプションを設定解除できます。この初期値は次の行で指定されています。
{PropertyDescriptor direction-pmd, "\"horizontal\""}
initial-properties ゲッターは、初期のプロパティ値を指定する PropertyDescriptor の配列を返します。空の配列が返される場合もあります。この配列の各要素は、次のような呼び出しで生成されます。
{PropertyDescriptor pmd, source-code}
この要素により、pmd で示されるプロパティに関して、設定された値を使ってスクロール バーを作成するように VLE に指示します。source-code 引数には String 値で、ここには VLE が初期プロパティ値を計算するために評価するソース コードを含めます。この例では、プロパティを設定するソース コードは次のとおりです。
direction = "horizontal"
したがって、このキーワード引数の値の部分は "horizontal" になります。皆さんは、この式には、次のようにもう 1 組の引用符があるのではと予想されているのではないでしょうか。
{PropertyDescriptor direction-pmd, "\"horizontal\""}
事実その通りで、必要なソース コード自体に引用符が含まれているため、追加の引用符が必要になります。"\"horizontal\"" の外側にある引用符のペアは、これが String リテラルであるために必要で、バックスラッシュ文字でエスケープされている内側のペアは、この String リテラルの値自体がその先頭と末尾に引用符文字が付いたソース コードであるため必要になります。 複雑なケースでは、stringify マクロを使ってこのような source-code 文字列を作成すると便利です。

簡単な Scrollbar エディタの完成

この簡単な Scrollbar エディタを利用できるようにするために、いくつかの情報項目を指定する必要があります。これらの各項目は、それぞれ異なる DesignTimeGraphic のメソッドか、DesignTimeScrollbar のゲッターをオーバーライドすることによって制御します。

プロパティの指定

このセクションでは、VLE 機能拡張でプロパティ メタデータを定義する場合に VLE がサポートしているその他の方法について説明します。このサポートは複数の形で存在します。

事前定義されている PropertyMetaData オブジェクト

次の表には、COM.CURL.LAYOUT-EDITOR.EDITOR パッケージで定義済みの便利な PropertyMetaData オブジェクトが一覧表示されています。
似たようなプロパティの PropertyMetaData オブジェクトを定義する方法については、次の VLE ソース ファイルを開いてこれらの変数の定義を調べることにより詳細な情報が得られます。
d:\automated-build-temp\build\win32-atom\ide\vle\editor\design-time-class.scurl
カスタム PropertyMetaData オブジェクトの作成の詳細は、「独自の PropertyMetaData オブジェクトの定義」を参照してください。
PropertyMetaData 変数の名前実行時に制御するプロパティ
background-pmdbackground オプション。
border-color-pmdborder-color オプション。
border-style-pmdborder-style オプション。
border-width-pmdborder-width オプション。
check-button-style-pmdCheckButton.style アクセッサ。
check-button-value-pmdbool 値コントロールの value アクセッサ。
color-pmdcolor オプション。
command-button-style-pmdCommandButton.style アクセッサ。
control-appearance-changeable?-pmdcontrol-appearance-changeable? オプション。
control-color-pmdcontrol-color オプション。
control-content-background-pmdcontrol-content-background オプション。
design-name-pmdこのオブジェクトの値を格納するように定義されているフィールドが存在する場合にその名前を制御する。
discrete-select-in-range?-pmddiscrete-select-in-range? オプション。
enabled?-pmdenabled? オプション。
font-family-pmdfont-family オプション。
font-size-pmdfont-size オプション。
font-style-pmdfont-style オプション。
font-weight-pmdfont-weight オプション。
halign-pmdhalign オプション。Frame.halign および VBox.halign の使用例を参照。
height-pmdheight オプション。
horigin-pmdhorigin オプション。
hstretch?-pmdhstretch? オプション。Frame.hstretch? および VBox.hstretch? の使用例を参照。
label-pmdラベルを持つコントロール (CommandButton など) の label アクセッサ。
listbox-style-pmdListBox.style アクセッサ。
list-item-creation-proc-pmdlist-item-creation-proc フィールド。
margin-pmdmargin オプション。
max-chars-pmdmax-chars アクセッサ。
mnemonic-pmdmnemonic オプション。
name-pmdname オプション。
prompt-pmdprompt アクセッサ。TextField.promptDropdownList.prompt および ComboBox.prompt の使用例を参照。
radio-button-style-pmdRadioButton.style オプション。
reactive-label-pmdこのプロパティを持つコントロール (CommandButton など) の reactive-label アクセッサ。
searchable?-pmdsearchable? オプション。
selection-policy-pmdselection-policy アクセッサ。
spacing-pmdspacing オプション。
tab-index-pmdtab-index オプション。
tooltip-pmdtooltip オプション。
use-look-and-feel?-pmduse-look-and-feel? アクセッサ。
valign-pmdvalign オプション。Frame.valign および HBox.valign の使用例を参照。
vorigin-pmdvorigin オプション。
vstretch?-pmdvstretch? オプション。Frame.vstretch? および HBox.vstretch? の使用例を参照。
width-pmdwidth オプション。

事前定義されている MetaDataCategory オブジェクト

COM.CURL.LAYOUT-EDITOR.EDITOR パッケージでは以下の MetaDataCategory オブジェクトが定義されています。

事前定義されているプロパティ エディタ

COM.CURL.LAYOUT-EDITOR.EDITOR パッケージには、最も一般的なデータ型のプロパティ エディタがすぐに使える状態で用意されています。独自の PropertyMetaData オブジェクトを定義するときにこれらのエディタを使うことができます。Scrollbar の例では、事前定義の EnumerationEditor を使って direction-pmd を定義しました。
プロパティ エディタは VLE に次のようなサービスを提供します。
さまざまなプロパティ エディタは、実行時に処理する値の型、認識できる source-code パターンの範囲、およびプロパティ値の表示や編集用に提供するユーザー インターフェイスがそれぞれ異なります。通常プロパティ エディタのユーザー インターフェイスは、次のようなプロパティ値の編集方法を提供します。
次の表に示されるプロパティ エディタには、VLE 機能拡張を定義する際に便利な機能が搭載されています。これらはすべて COM.CURL.LAYOUT-EDITOR.EDITOR で定義されています。
プロパティ エディタ クラスの名前実行時のデータ型使用例またはコメント
BoolPropertyEditorboolenabled?
BorderStyleEditorBorderStyleGraphic.border-style
CharPropertyEditorcharPasswordField.echo-char
ColorPropertyEditorFillPatterncolor
DistancePropertyEditor距離border-width オプションなど、実行時のデータ型が距離 (1pt1pixel など) であるプロパティを編集します。
EnumerationEditor列挙体
paragraph-justify など、列挙型の値を持つプロパティを編集します。
IntPropertyEditor整数CommandButton.tab-index
FontFamilyEditorStringTextFlowBoxfont-family オプション。
FontStyleEditorFontStyleTextFlowBoxfont-style オプション。
FontWeightEditorFontWeightTextFlowBoxfont-weight オプション。
RichTextPropertyEditorVisual または Graphic
CommandButton.label など、リッチテキスト マークアップを含むテキストが実行時のデータ型であるプロパティを編集します。このプロパティ エディタは、ソース コードの分析方法において特に精巧で、bolditalic のようなリッチテキストのマークアップ オペレータを含むコードを表示することができます。だたし、このプロパティ エディタが提供するネイティブ エディタでは単純な文字列しか編集できないので、インライン コード エディタを使ってリッチテキスト プロパティの値を編集しなければなりません。
SimpleCodeEditorこれは、ソース コードを認識しない、変形プロパティ エディタです。したがって、SimpleCodeEditor は常にインライン コード エディタを表示し、ネイティブ エディタを表示しません。プロトタイプの段階では、このプロパティ エディタの一時的な使用が役に立つ場合がありますが、VLE 機能拡張を広範なユーザーグループに提供する前に、より洗練されたプロパティ エディタに置き換えることをお勧めします。
StringPropertyEditorStringTextField.prompt
TextDistancePropertyEditor距離DistancePropertyEditor が認識する任意のデータ型と、em のような font-size の相対距離を編集します。このプロパティ エディタは、font-size や text-left-margin などのプロパティで使われます。
UrlPropertyEditorUrlimagesource プロパティ。
これらのプロパティ エディタはすべて、次のような構文を使って OptionMetaData または NonOptionMetaData の引数に指定できます。
{OptionMetaData property-name, property-editor-class-name.instance}
次に例を示します。
{OptionMetaData "enabled?", BoolPropertyEditor.instance}
特別なクラスである EnumerationEditor を使って、任意の列挙型のプロパティ エディタを作成することができます。前述の例題では、このクラスを使って direction オプションの direction-pmd PropertyMetaData オブジェクトを作成しています。この例から推測できるように、値が v1v2v3 などの名前を持つ列挙型に対しては、通常は次のようにプロパティ エディタを作成します。
{EnumerationEditor {StringArray "v1", "v2", "v3", ...}}
COM.CURL.LAYOUT-EDITOR.EDITOR には上の図で省略されたエディタもいくつか定義されていますが、これらは非常に特別で、VLE の機能拡張にあまり役立つものではありません。事前定義されているプロパティ エディタの完全なセットを調べるには、次の VLE ソース ファイルを開いてください。
d:\automated-build-temp\build\win32-atom\ide\vle\editor\property-editors.scurl
独自の PropertyEditor サブクラスの定義は高度なトピックで、この概要では詳細には解説していませんが、次の VLE ソース ファイルで PropertyEditor クラスの定義を参照して、
d:\automated-build-temp\build\win32-atom\ide\vle\editor\property-editors.scurl
このファイルの詳細なコメントに含まれている情報を参考にすることができます。これらのコメントを読み、このファイルで実装されている PropertyEditor サブクラスを調べることにより、独自のプロパティ エディタを作成する方法を学ぶことができます。

独自の PropertyMetaData オブジェクトの定義

組み込み Curl API の VLE 機能拡張」で解説されている Scrollbar エディタでは、direction オプションに対して独自の PropertyMetaData オブジェクトが定義されています。他のプロパティに対しても、独自の PropertyMetaData オブジェクトを定義するには以下を実行する必要があります。

OptionMetaData オブジェクトの定義

実行時にオプションとして定義されるプロパティには OptionMetaData を使う必要があります。上述の例で direction-pmd の定義に示されているように、OptionMetaData コンストラクタを最も簡単に呼び出す方法としては引数を 2 つだけ使います。
{OptionMetaData property-name, property-editor}
ここで、property-name はプロパティの名前を提供する String で、property-editor は、このプロパティ用のプロパティ シートの行の動作を制御する PropertyEditor オブジェクトをその値に持つ式になります。この PropertyEditor は、「事前定義されているプロパティ エディタ」の説明に従って指定します。
Scrollbar の例ではキーワード引数も 1 つ指定されています。以下に示すオプションのキーワード引数はすべて OptionMetaData コンストラクタに提供することができます。

NonOptionMetaData オブジェクトの定義

CommandButton.style のように、実行時にオプションとして定義されないプロパティは NonOptionMetaData オブジェクトによって記述します。NonOptionMetaData は広範囲にわたる状況に対応するため、OptionMetaData よりも本質的に複雑なものです。OptionMetaData の場合と同様に、基本的な NonOptionMetaData コンストラクタの呼び出しでは、property-nameproperty-editor の位置引数を 2 つだけ指定します。以下に示すような、さまざまなキーワード引数も利用できます。

DesignTimeGraphic のサブクラス化

このセクションでは、DesignTimeGraphic のその他のいくつかの便利な機能について説明します。

オブジェクト ネーミングの高度な制御

DesignTimeScrollbar オブジェクト エディタの例では、VLE で編集するオブジェクトは Scrollbar 型です。実行時には、このオブジェクトは Scrollbar クラスの既定のコンストラクタを使って作成されます。これは最も一般的なパターンです。VLE 機能拡張ファイルでこのパターンをサポートするには、runtime-class-name ゲッターをオーバーライドして、該当するクラスの名前を返すだけで済みます。上述の例でもこれを行っています。
だたし、状況がかなり複雑な場合があります。例えば、submit-button API の VLE パレット アイコンを考えてみましょう。submit-button はクラスではなく、CommandButton を返すプロシージャです。この場合、クラス名とコンストラクタ式を別々に制御できるようにする必要があります。DesignTimeGraphic には、この目的でオーバーライドできる design-time-class-name というゲッターがあります。さらに、ツールヒントを提供したり、オブジェクトを [レイアウト ツリー] ペインに表示する目的で独自のオブジェクトのインスタンスにラベルを付ける場合は、クラス名以外の名前を選択することが推奨されています。この目的で class-name-for-display ゲッターを使うことができます。これらのゲッターはそれぞれ String を返します。次の表はこれらのプロパティの要約です。
ゲッターの名前既定値目的
runtime-class-nameなし。実行時にオブジェクトのインスタンスを作成するコンストラクタ式のオペレータに対応するソース コードを返します。
design-time-class-nameruntime-class-name が返す値。実行時のオブジェクト型を記述する式に対応するソース コードを返します。通常これはクラス名です。
class-name-for-displaydesign-time-class-name が返す値。VLE のユーザー インターフェイス内のオブジェクト ラベルに使われる文字列を返します。
例えば、submit-button の VLE アイコンを定義する場合に、VLE のユーザー インターフェイスにある submit-button オブジェクトのラベルとして、「Submit Button」を使うとします。この場合、DesignTimeGraphic サブクラスの定義内で次のようにゲッターを定義します。
{getter public {runtime-class-name}:String
  {return "submit-button"}
}

{getter public {design-time-class-name}:String
  {return "CommandButton"}
}

{getter public {class-name-for-display}:String
  {return "Submit Button"}
}
ところで、runtime-class-namedesign-time-class-name という名前は、この章の他のセクションで使われている 実行時デザイン時 の概念とは関係がありません。これらの名前は VLE の以前のバージョンで確定されたもので、互換性のためにそのまま使われています。

コンテナ オブジェクトのエディタ

この章では、リーフ型 オブジェクト、つまり VLE で編集される他のオブジェクトのコンテナとしての役目を持たない、追加のオブジェクト型を扱えるような VLE の機能拡張に焦点をあてています。VLE の機能を拡張して新しいタイプのコンテナ オブジェクトを扱うことも可能ですが、これは高度なトピックで、この概要で説明する範囲には含まれていません。新しいコンテナ クラスを VLE に追加する必要がある場合は、まず VLE に組み込まれている VBox エディタの実装を調べることから始めてみてください。これには、次の VLE ソース ファイルで DesignTimeVBoxDesignVBox クラスおよびそのサブクラスの定義に目を通します。
d:\automated-build-temp\build\win32-atom\ide\vle\editor\standard-design-time-classes.scurl
これを行う場合は、「互換性に関する注意」の内容も忘れないでください。

VLE で使われる実行時パッケージの定義

Scrollbar に基づく例では、標準 Curl API で既に定義されているオブジェクトを編集できるように VLE 機能を拡張する方法が示されています。しかし、オブジェクトの定義も自分で提供し、これを編集できるように VLE 機能を拡張することを望まれるのがほどんどの場合です。このセクションでは、VLE 機能拡張で使われるオブジェクトの実行時の動作をどのように定義するかを示し、その過程で留意すべき大切な点について解説します。
新しい実行時のオブジェクト型の定義を含むパッケージを作成する必要があります。通常このパッケージには、VLE ユーザーがアプリケーション作成に使う Graphic のサブクラスを 1 つまたは複数含めます。
パッケージは次の場所でインポートする必要があります。
get-the-applet などの一部の Curl API は、アプレットの一部として実行されたときだけ呼び出すことができます。VLE 自体はアプレットではないので、VLE で使われる実行時パッケージ内でこれらの API を 直接または間接的に使用することはできません。
Curl IDE のプロジェクトでパッケージを定義すると、IDE はパッケージのマニフェストを作成します。ただし、このパッケージが他のパッケージをインポートせず、manifest-url を使ってリソースにアクセスしない場合は、マニフェストがなくてもパッケージは適切に機能します。パッケージがこれらの操作のいずれかを実行する場合は、マニフェストがなければパッケージは適切に機能しません。さらに、アプレットでパッケージをインポートする場合は、このパッケージで使われるマニフェストを含むマニフェストか、またはそのようなマニフェストに委任するマニフェストを使うようにアプレットを構成する必要があります。アプレットが IDE プロジェクトに属する場合、このようなマニフェストの作成は自動的に処理されますが、アプレットがプロジェクトに属さない場合、アプレットのプログラマーは明示的にマニフェストを作成しなければなりません。
マニフェスト不要のパッケージは任意のアプレットにインポートできます。マニフェストを必要とするパッケージは、適切なマニフェストが存在するアプレットにしかインポートできません。マニフェストの使用は Curl のプログラミングで推奨されている慣行で、これは IDE とそのプロジェクト メカニズムでもサポートされています。ただし、パッケージを作成し、VLE 機能拡張で必要な実行時オブジェクト型を定義する場合にこの慣行を実行すると、そのパッケージはマニフェストが必要になり、その使用はマニフェストが指定されているアプリケーションに制限されてしまいます。したがって、パッケージ作成状況を詳細に理解した上で、作成する VLE 機能拡張の使用においてこのような制限が許容できるかどうかを判断する必要があります。
次の Curl ソース コードでは、非常に簡単な progress bar (進捗状況バー) オブジェクトを実装するパッケージを定義しています。このパッケージはマニフェスト経由でリソースにアクセスしないので、マニフェストは不要です。
このコードは、次の場所にサンプルの一部として含まれています。
d:\automated-build-temp\build\win32-atom\docs\default\examples\layout-editor\ProgressBar.zip
アーカイブからファイルを解凍して、progressbar-ext/progress/load.scurl を参照してください。
{curl 8.0 package}
{package COM.WIDGETS.PROGRESS, version = "1.0"}

{import * from CURL.GUI.STANDARD}

{define-class public DocProgressBar {inherits Fill}

  || This field holds a value from 0 to 1, representing
  || the amount of progress to be displayed.
  field private _progress:double = 0.0
  
  || This getter fetches the current progress value.
  {getter public {progress}:double
    {return self._progress}
  }

  || This setter changes the current progress value,
  || ensuring that it is in the proper range, and
  || signals that a a redraw is necessary.
  {setter public {progress new-value:double}:void
    set self._progress = {max 0.0, {min 1.0, new-value}}
    {self.request-draw}
  }
  
  {constructor public {default ...}
    {construct-super ...}
  }
  
  {method public {draw r:Renderer2d}:void
    let bounds:GRect = {self.layout.get-bounds}
    let width:Distance = self._progress * bounds.width
    {r.render-rectangle
        -bounds.lextent,
        -bounds.ascent,
        width,
        bounds.height,
        fill-pattern = self.color
    }
  }
  
}

ユーザー定義パッケージのデザイン時の動作の定義

DocProgressBar オブジェクト用の VLE オブジェクト エディタの定義は、先の例の Scrollbar エディタの定義プロセスに似ています。これは次の VLE 機能拡張ファイルで定義されています。このコードは、ProgressBar.zip 内の progressbar-ext.scurl サンプル ファイルに収められています。
{import * from CURL.GUI.STANDARD}
{import * from COM.WIDGETS.PROGRESS,
    location = "progress/load.scurl"
}

{define-class public final DesignTimeProgressBar 
  {inherits DesignTimeGraphic}

  {constructor public {default}
    {construct-super
        {ObjectMetaData
            default-selected-property = "design-name",
            default-selected-event = "PointerPress",
            {MetaDataCategory
                name = "General",
                design-name-pmd,
                name-pmd
            },
            standard-geometry-meta-data-category,
            {MetaDataCategory
                name = "Colors",
                color-pmd,
                background-pmd
            },
            standard-border-meta-data-category,
            {EventHandlerCategory
                {EventHandlerMetaData "PointerEnter"},
                {EventHandlerMetaData "PointerLeave"},
                {EventHandlerMetaData "PointerPress"},
                {EventHandlerMetaData "PointerRelease"}
            }
        }
    }
  }

  {getter public open {initial-properties}:{Array-of PropertyDescriptor}
    {return
        {new {Array-of PropertyDescriptor},
            {PropertyDescriptor background-pmd, "\"silver\""},
            {PropertyDescriptor color-pmd, "\"blue\""},
            {PropertyDescriptor border-color-pmd, "\"black\""},
            {PropertyDescriptor border-width-pmd, "1pt"},
            {PropertyDescriptor width-pmd, "144pt"},
            {PropertyDescriptor height-pmd, "24pt"}
        }
    }
  }

  {getter public {runtime-class-name}:String
    {return "DocProgressBar"}
  }

  {method protected {new-object element:LayoutElement}:GraphicOptions
    {return {DocProgressBar}}
  }

  {getter public open {palette-tab-name}:String
    {return "Widgets"}
  }

  {getter protected {palette-graphic}:Graphic
    {return 
        {image
            blocking? = true,
            source = {url "images/progressbar-icon.gif"}
        }
    }
  }

  {method public open 
    {collect-import-descriptors layout-file:LayoutToolFile}:void
    {layout-file.add-import-descriptor
        {CopyImportDescriptor
            "COM.WIDGETS.PROGRESS",
            version = "1.0",
            {url "progress/load.scurl"},
            dest-path = "progress-1-0/load.scurl"
        }
    }
  }
}

{register-palette-item DesignTimeProgressBar}
この VLE 機能拡張ファイルと前述の Scrollbar の例には少しずつ異なる点がいくつかあります。initial-properties ゲッターで挙げられているプロパティのセットが異なり、palette-tab-name では、DocProgressBar アイコンが [コントロール] タブではなく [Widgets] という名前の新しいパレット タブに表示されるよう指定されていますが、これらは既に説明した VLE 機能拡張の異なる適用方法にすぎません。この例と前述の例にはもっと重要な相違点が 2 つあります。

記述子のインポート

作成したパッケージに関する機能拡張を VLE に追加した場合、VLE においては、その機能拡張を使うソース コード ファイルがパッケージを利用できるようにする必要が生じます。これを行うには、上記の DocProgressBar の例で示されるように、DesignTimeGraphic サブクラスの collect-import-descriptors メソッドをオーバーライドして VLE に必要な情報を提供します。VLE は提供された情報を使って、編集中のソース コードに適切な import ステートメントを挿入し、ソース ファイルが実行時パッケージを簡単にインストールできる場所にそのコピーをインストールします。
collect-import-descriptors メソッドは 1 つの引数として LayoutToolFile を取ります。DocProgressBar の例では、この引数は layout-file と呼ばれています。collect-import-descriptors メソッドの本体には、LayoutToolFileadd-import-descriptor メソッド呼び出しが 1 つまたは複数含まれています。これらの各呼び出しの引数は ImportDescriptor です。ImportDescriptor には 2 種類あります。
ソース ファイルの書き出しや IDE ソース エディタへのファイル転送を VLE に要求すると、まずファイル内でインスタンス化されるすべてのオブジェクトをスキャンして、それぞれに対して collect-import-descriptors メソッドを呼び出します。VLE はその結果の ImportDescriptor オブジェクトを収集します。これらのオブジェクトは、VLE が書き出すソース ファイルに import ステートメントを書き加えるよう指示します。次に、VLE は ImportDescriptor を再度スキャンして、アプリケーションに関連する実行時パッケージをインストールする (他のファイルを適切に更新する) よう各記述子に要求します。import ステートメント生成および実行時パッケージのインストールの詳細は、ImportDescriptor の種類によって異なります。以下のセクションではこれらについて説明します。

CopyImportDescriptor

DocProgressBar の例では CopyImportDescriptor が使われています。これは、実行時パッケージがマニフェストを必要としないためです。CopyImportDescriptor コンストラクタの一般的な呼び出しは次のように行われます。
     {CopyImportDescriptor
          package-name,
          [version = version,]
          source-url,
          dest-path = dest-path
     }
package-name は実行時パッケージの名前です。version 引数はオプションですが、指定した場合は、ソース コードで生成される import ステートメントの version 引数にこの version を含めます。source-url は、パッケージのソース コードの場所を示す Url です。DocProgressBar の例でも示されているように、通常この引数には相対 URL を指定します。この URL は VLE 機能拡張ファイルの場所を基準とする相対パスです。したがって、前述の例にある引数を見ると、
    {url "progress/load.scurl"}
progress という名前のディレクトリがあり、これは VLE 機能拡張ファイルと兄弟の位置関係にあることを示しています。このディレクトリには load.scurl という名前のファイルがあり、DocProgressBar クラスを定義するパッケージがそこに含まれています。最後に、dest-path 引数は String 型の値で、VLE が生成する import ステートメントの location キーワード引数を制御します。

import ステートメントの生成

import ステートメントの生成中、CopyImportDescriptor は、package-name をパッケージ名とし、version (指定された場合) をそのバージョンとする import ステートメントがソース コードに既に含まれているかどうかを調べます。そのような import ステートメントが含まれている場合、ソース コードでそのまま変更されずに残ります。含まれていない場合、次に実行されるアクションは、現在 IDE でプロジェクトが開かれていて、そのプロジェクトのディレクトリ ツリーに現在 VLE で編集中のファイルが存在するかどうかによって異なります。
このファイルが現在開いているプロジェクトのディレクトリ ツリーに存在しない場合 (おそらく、現在開いているプロジェクトがないため)、次の形式の import ステートメントが生成されます。
    {import * from package-name,
         version = version,
         location = dest-path
    }
例えば、DesignTimeProgressBar で指定されている CopyImportDescriptor は次の import ステートメントを生成します。
    {import * from COM.WIDGETS.PROGRESS,
         version = "1.0",
         location = "progress-1-0/load.scurl"
    }
このファイルが現在開いているプロジェクトのディレクトリ ツリー内に存在する場合、VLE はこのファイルが実行時にプロジェクト マニフェストを使ってリソースを発見すると判断し、import ステートメントから location 引数を省きます。結果の import ステートメントは次のようになります。
    {import * from package-name,
         version = version
    }

実行時パッケージのインストール

VLE が実行時パッケージをインストールする際のアクションは、現在開いているプロジェクトのディレクトリ ツリーにソース ファイルが存在するかどうかによって異なります。

DelegateImportDescriptor

マニフェストを必要とする実行時パッケージには、DelegateImportDescriptor を使うことができます。DelegateImportDescriptor は、編集するソース コード ファイルが含まれているプロジェクトのマニフェストに、delegate-to 宣言を書き込むよう VLE に指示します。この delegate-to 宣言は、実行時パッケージが必要とするマニフェストへの委譲を表します。
DelegateImportDescriptor コンストラクタの一般的な呼び出しは次のように行われます。
        {DelegateImportDescriptor
              package-name,
              version = version,
              manifest-url
        }
DelegateImportDescriptor では、CopyImportDescriptor と同じ方法で package-nameversion 引数が処理されます。manifest-url は、パッケージが必要とするマニフェストの場所を示す Url です。

import ステートメントの生成

import ステートメントの生成中、DelegateImportDescriptor は、package-name をパッケージ名とし、version (指定された場合) をそのバージョンとする import ステートメントがソース コードに既に含まれているかどうかを調べます。そのような import ステートメントが含まれている場合、ソース コードでそのまま変更されずに残ります。含まれていない場合、次に実行されるアクションは、現在 IDE でプロジェクトが開かれていて、そのプロジェクトのディレクトリ ツリーに現在 VLE で編集中のファイルが存在するかどうかによって異なります。この動作は CopyImportDescriptor の場合と同じです。
import ステートメントが location キーワード引数を伴う場合は、その引数が削除されます。
指定パッケージ名とバージョンの import ステートメントがソース コードにまだ含まれていない場合、VLE は import ステートメントを追加します。この import ステートメントが location キーワード引数を伴うことはありません。

実行時パッケージのインストール

VLE が実行時パッケージをインストールする際のアクションは、現在開いているプロジェクトのディレクトリ ツリーにソース ファイルが存在するかどうかによって異なります。

実行時パッケージがマニフェストを必要とする場合の VLE 機能拡張の定義

マニフェストを必要とする実行時パッケージに基づいて VLE 機能拡張を定義する場合は、いくつかの異なる手順を実行する必要があります。既に説明したとおり、CopyImportDescriptor の代わりに DelegateImportDescriptor を使用する必要があります。また、VLE 機能拡張自体の定義においても異なるアプローチを使う必要があります。その理由は、前述の DesignTimeProgressBar の例では COM.WIDGETS.PROGRESS をインポートする import ステートメントで location キーワードを使いましたが、この COM.WIDGETS.PROGRESS パッケージがマニフェストを必要とする場合、これは不正になるためです。
このような問題すべてを解決する最良の方法は、以下の手順に従うことです。
  1. プロジェクトを作成します。このプロジェクトには、実行時パッケージと、VLE 機能拡張を定義するファイルの両方を含めます。その他のパッケージ、アプレット、および他の VLE 機能拡張ファイルも含めることができます。
  2. 実行時パッケージをこのプロジェクトの一部として作成します。
  3. 同じプロジェクトの一部として、デザイン時に使用するパッケージを別に作成します。このパッケージでは、実行時パッケージをインポートし、DesignTimeGraphic サブクラスをパブリック クラスとして定義します。
  4. 同じプロジェクトのトップレベルのディレクトリにファイルを作成し、手順 (3) のデザイン時パッケージをインポートして、そのパッケージで定義されている DesignTimeGraphic サブクラスそれぞれに対して register-palette-item を呼び出します。このファイルが実際の VLE 機能拡張ファイルです。
この手順を特定の問題に応用した例として、次のコードでは、DesignTimeProgressBar を定義するデザイン時パッケージ (手順 (3) で説明) を定義しています。このファイルは、COM.WIDGETS.PROGRESS パッケージがマニフェストを必要とするかどうかに関係なく使用できます。これに対して、前述の VLE 機能拡張例では、COM.WIDGETS.PROGRESS にマニフェストが必要な場合には機能しなくなります。
このコードは、次の場所にサンプルの一部として含まれています。
d:\automated-build-temp\build\win32-atom\docs\default\examples\layout-editor\ProgressBarManifest.zip
アーカイブからファイルを解凍して、progressbar-ext/progress/designtime-load.scurl を参照してください。
{curl 8.0 package}
{package COM.WIDGETS.PROGRESS-VLE-EXT}

{import * from CURL.GUI.STANDARD}
{import * from COM.CURL.LAYOUT-EDITOR.EDITOR}
{import * from COM.WIDGETS.PROGRESS}

{define-class public final DesignTimeProgressBar 
  {inherits DesignTimeGraphic}

  {constructor public {default}
    {construct-super
        {ObjectMetaData
            default-selected-property = "design-name",
            default-selected-event = "PointerPress",
            {MetaDataCategory
                name = "General",
                design-name-pmd,
                name-pmd
            },
            standard-geometry-meta-data-category,
            {MetaDataCategory
                name = "Colors",
                color-pmd,
                background-pmd
            },
            standard-border-meta-data-category,
            {EventHandlerCategory
                {EventHandlerMetaData "PointerEnter"},
                {EventHandlerMetaData "PointerLeave"},
                {EventHandlerMetaData "PointerPress"},
                {EventHandlerMetaData "PointerRelease"}
            }
        }
    }
  }

  {getter public open {initial-properties}:{Array-of PropertyDescriptor}
    {return
        {new {Array-of PropertyDescriptor},
            {PropertyDescriptor background-pmd, "\"silver\""},
            {PropertyDescriptor color-pmd, "\"blue\""},
            {PropertyDescriptor border-color-pmd, "\"black\""},
            {PropertyDescriptor border-width-pmd, "1pt"},
            {PropertyDescriptor width-pmd, "144pt"},
            {PropertyDescriptor height-pmd, "24pt"}
        }
    }
  }

  {getter public {runtime-class-name}:String
    {return "DocProgressBar"}
  }

  {method protected {new-object element:LayoutElement}:GraphicOptions
    {return {DocProgressBar}}
  }

  {getter public open {palette-tab-name}:String
    {return "Widgets"}
  }

  {getter protected {palette-graphic}:Graphic
    {return 
        {image
            blocking? = true,
            source = {url "images/progressbar-icon.gif"}
        }
    }
  }

  {method public open {collect-import-descriptors layout-file:LayoutToolFile}:void
    {layout-file.add-import-descriptor
        {DelegateImportDescriptor
            "COM.WIDGETS.PROGRESS",
            version = "1.0",
            {url "../manifest.mcurl"}
        }
    }
  }
}
このパッケージを使用する実際の VLE 機能拡張ファイル (手順 (4) で説明) は非常に簡単なものです。次に示すとおりで、これだけです。
{import * from COM.WIDGETS.PROGRESS-VLE-EXT}

{register-palette-item DesignTimeProgressBar}
上記 2 つのファイルに関して、以下の点に注意してください。

ローカライズ可能な VLE 機能拡張ファイルの作成

VLE 自体は、Curl ローカライゼーション API を使い、現在のホスト ロケールに応じてユーザー インターフェイスをカスタマイズしています。これらの API を使って複数言語の VLE 機能拡張を作成することができます。これには以下を実行する必要があります。
次の VLE 機能拡張ファイルで DesignTimeGraphic サブクラスの定義を参照してください。上記のガイドラインに従ってローカライズされたパレット アイテムの定義例が含まれています。
d:\automated-build-temp\build\win32-atom\ide\vle\editor\standard-design-time-classes.scurl

VLE 機能拡張ファイルのデバッグ

VLE を通常の使い方で、つまり Curl IDE の制御下で稼動する場合、VLE 機能拡張のデバッグを行うのにふさわしい環境は提供されません。これには次の 2 つの理由があります。
VLE 機能拡張の開発およびデバッグ プロセスを能率的に行うために推奨されている手順を以下に示します。
VLE をアプレットとして実行するには、ブラウザのアドレス フィールドに次のファイル名を入力します。
d:\automated-build-temp\build\win32-atom\ide\vle\run-layout-tool.curl
空のブラウザ ウィンドウが表示され、その後すぐに VLE ウィンドウが表示されるはずです。これを最初に実行するときには、VLE コードをアプレット用にコンパイルするためにかなり時間をとられる場合がありますが、その後は VLE ウィンドウの表示にほどんど時間がかかりません。VLE 機能拡張ファイル、または機能拡張でインポートされるパッケージに変更を加えるたびに、空のブラウザ ウィンドウで [更新] をクリックして最新の VLE インスタンスに更新し、最後に行った変更をテストできるようにします。
アプレットの実行中、VLE は通常の環境で IDE が実行するサービスにアクセスできません。結果として一部の動作が変わります。以下の変更点に注意してください。