ブロック ファクトリにプラグイン フィールドを追加する

Blockly デベロッパー ツールでは、ブロックを使用してカスタム ブロックを作成できます。コア Blockly に付属しているフィールドに加え、プラグインとして公開されるフィールドがサポートされています。カスタム フィールドを作成した場合は、このガイドに沿ってそのサポートをブロック ファクトリに追加できます。カスタム フィールドは、サポートを追加する前に npm で公開されている必要があります。また、Blockly の変更に対応するためにフィールドの更新を確約する必要があります。そうしないと、将来、ブロック ファクトリからフィールドを削除する必要が生じる可能性があります。

Block Factory での開発

ブロック ファクトリのソースコードは、examples/developer-tools ディレクトリの blockly-samples リポジトリにあります。

ブロックリー サンプルでデベロッパー ツールに変更を送信するには、ブロックリーサンプルで開発する際の一般的な手順に従う必要があります。ただし、プラグインを使用する場合とは異なり、npm install はブロックリー サンプルのルートレベルではなく、examples/developer-tools ディレクトリから直接実行する必要があります。

プラグインをインストールする

ブロック ファクトリでカスタム フィールドをプレビューに表示するには、カスタム フィールドをインストールする必要があります。フィールドを、developer-tools の npm 依存関係として追加します。次に、その ID を登録するか、developer-tools/src/blocks/index.ts で必要なその他の設定作業を行います。

フィールドのブロックを作成する

ブロック ファクトリではブロックを使用してカスタム ブロックを作成するため、カスタム フィールドを表すブロックが必要です。

ブロック定義を作成する

フィールドのブロックを設計する必要があります。メタを取得する場合は、Block Factory を使用して設計することもできます。このブロックでは、デフォルト値や名前など、フィールドに必要な設定をユーザーが構成できるようにする必要があります。このブロック定義を developer-tools/src/blocks/fields.ts に追加し、developer-tools/src/blocks/index.ts にインポートします。

ツールボックスにブロックを追加する

次に、このブロックをツールボックスの定義に追加して、ユーザーがアクセスできるようにします。ツールボックスの定義は developer-tools/src/toolbox.ts にあります。ブロックが [項目]カテゴリに追加され

コード生成ツール

ブロック ファクトリは、Blockly ですでに使い慣れているコード生成システムを使用して動作します。各ブロックには、ブロック ファクトリによって生成された出力のタイプごとにブロックコード ジェネレータがあり、親ブロックは子ブロックのコードを正しい出力にアセンブルします。カスタム フィールドのサポートを追加するには、コード生成ツールのクラスごとにブロックコード ジェネレータ関数を追加する必要があります。

output-generators/fields ディレクトリにフィールド ブロックのファイルを作成します。このファイルに、次の各生成ツールのブロック コード ジェネレータを追加します。このファイルを blocks/index.ts ファイルにインポートして、ブロックコード ジェネレータ関数がアプリケーションに読み込まれるようにします。

JavaScript の定義

javascriptDefinitionGenerator は、カスタム フィールドを含むブロックの JavaScript 定義に含めるコードを作成します。通常、これはブロックコード ジェネレータが .appendField(new YourFieldConstructor(arg1, arg2), 'userSpecifiedName') のようなコード行を返す必要があることを意味します。このコード行にはセミコロンは含まれません。複数のフィールドを含む入力には、appendField への複数の呼び出しが連結されるためです。コンストラクタの引数は、ユーザーがフィールド ブロックに設定した値から取得されます。この FieldAngle のブロックコード ジェネレータの例を次に示します。

javascriptDefinitionGenerator.forBlock['field_angle'] = function (
  block: Blockly.Block,
  generator: JavascriptDefinitionGenerator,
): string {
  const name = generator.quote_(block.getFieldValue('FIELDNAME'));
  const angle = block.getFieldValue('ANGLE');
  return `.appendField(new FieldAngle(${angle}), ${name})`;
};

ユーザーがブロック ファクトリ ツールボックスの [フィールド] カテゴリからドラッグしたアングル ブロックには、次の 2 つのフィールドがあります。

  • FIELDNAME: ユーザーはカスタム ブロックにフィールドの名前を設定できます。
  • ANGLE: ユーザーはデフォルトの角度値を設定できます。

このブロックコード ジェネレータでは、デフォルトの角度値を取得し、それを唯一の引数として FieldAngle コンストラクタに渡します。フィールド名は常に 2 番目の引数として appendField に渡されます。

JSON 定義

jsonDefinitionGenerator も同様ですが、フィールドに対応する JSON ブロック定義の部分が出力されます。通常、このコードは以下を含む JSON オブジェクトです。

  • type: Blockly フィールド レジストリのフィールド名に対応します。
  • name: ユーザーはカスタム ブロックにフィールドの名前を設定できます。
  • フィールドの JSON 初期化メソッドで必要なその他のカスタム プロパティ。

ここでもう一度 FieldAngle の例を示します。

jsonDefinitionGenerator.forBlock['field_angle'] = function (
  block: Blockly.Block,
  generator: JsonDefinitionGenerator,
): string {
  const code = {
    type: 'field_angle',
    name: block.getFieldValue('FIELDNAME'),
    angle: block.getFieldValue('ANGLE'),
  };
  return JSON.stringify(code);
};

コードヘッダー

コードヘッダー ジェネレータは、ブロック ファクトリに表示されるコードヘッダー出力を作成します。この出力は、ユーザーのコード読み込み方法に応じて esmodule のインポートとスクリプトタグを切り替えられます。そのため、実際には 2 つの異なるジェネレータ インスタンス(ケースごとに 1 つずつ)があります。それぞれにブロックコードジェネレータを 追加する必要がありますFieldAngle の例を次に示します。

importHeaderGenerator.forBlock['field_angle'] = function (
  block: Blockly.Block,
  generator: CodeHeaderGenerator,
): string {
  generator.addHeaderLine(
    `import {registerFieldAngle, FieldAngle} from '@blockly/field-angle';`,
  );
  generator.addHeaderLine(`registerFieldAngle();`);
  return '';
};

scriptHeaderGenerator.forBlock['field_angle'] = function (
  block: Blockly.Block,
  generator: CodeHeaderGenerator,
): string {
  generator.addHeaderLine(
    `<script src="https://unpkg.com/@blockly/field-angle"></script>`,
  );
  generator.addHeaderLine(`registerFieldAngle();`);
  return '';
};

これらのジェネレータには addHeaderLine というメソッドがあり、このフィールドをコードで使用する前に呼び出すコード行を指定できます。一般に、これには、フィールドのインポートやスクリプトタグによるフィールドの読み込み、Blockly のフィールド レジストリにフィールドを登録する関数の呼び出しなどの作業が含まれます。

この 2 つのブロックコード ジェネレータでは、すべてのコードを addHeaderLine の呼び出しによって追加する必要があります。この関数により、カスタム フィールド ブロックが 1 つのカスタム ブロックで複数回使用されている場合でも、各ヘッダー行が 1 回だけ表示されます。ブロックコード ジェネレータは空の文字列を返す必要があります。

Generator スタブ

最後は、フィールドの generator スタブを作成するジェネレータです。このブロックコード ジェネレータでは、コードを生成するコードを記述します。コード生成は、ユーザーによるコード生成を支援します。ご不明な点がある場合思ったより簡単です!

カスタム ブロックのジェネレータ スタブには、ブロックのすべてのフィールドを表す既製の変数が含まれています。次に、すべての変数をアセンブルして、カスタム ブロックが返す最終的なコード文字列にするために TODO があります。つまり、通常、ブロックコード ジェネレータは、このカスタム変数を作成する行を返すだけで済みます。たとえば、キャンバスに太陽光を追加するカスタム ブロックを作成するとします。ブロックに angle フィールドを追加し、"SUN_DIRECTION" という名前を付けます。このブロックのジェネレータ スタブには、const angle_sun_direction = block.getFieldValue("SUN_DIRECTION"); という行が含まれます。angle フィールドのブロックコード ジェネレータは、次のコード行を返す必要があります。

generatorStubGenerator.forBlock['field_angle'] = function (
  block: Blockly.Block,
  generator: GeneratorStubGenerator,
): string {
  const name = block.getFieldValue('FIELDNAME');
  const fieldVar = generator.createVariableName('angle', name);
  return `const ${fieldVar} = block.getFieldValue(${generator.quote_(
    name,
  )});\n`;
};

変数の標準化された名前を取得するには、generator.createVariableName を呼び出して、フィールドの型(anglenumber など)とユーザーがフィールドに付けた名前を渡します。

テスト

これらをすべて記述したら、blockly-samples/examples/developer-tools ディレクトリで npm start を実行して、Block Factory を起動できるようになります。フィールド カテゴリからブロックをドラッグしてブロックの入力に追加し、出力の変化を確認できます。ブロックのプレビューが正しく表示されることと、各出力セクションのコードが正しいことを確認します。