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

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

ブロック ファクトリーでの開発

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

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

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

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

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

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

ブロック定義を作成する

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

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

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

コード生成ツール

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

output-generators/fields ディレクトリにフィールド ブロックのファイルを作成します。このファイルに、次の各 Generator のブロックコード ジェネレータを追加します。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})`;
};

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

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

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

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);
};

コード ヘッダー

コード ヘッダー ジェネレータは、Block Factory に表示されるコード ヘッダー出力を作成します。この出力は、ユーザーがコードを読み込む方法に応じて、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 回のみ表示されます。ブロックコード ジェネレータは空の文字列を返します。

ジェネレータ スタブ

最後に、フィールドの生成ツール スタブを作成する生成ツールがあります。このブロックコード ジェネレータでは、ユーザーがコードを生成するコードを記述するのに役立つコードを生成するコードを記述します。混乱しましたか?思ったより簡単です。

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

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 を実行して、ブロック ファクトリを開始できます。フィールド カテゴリからブロックをドラッグして、ブロックの入力に追加し、出力の変化を確認できます。ブロックのプレビューが正しく表示され、各出力セクションのコードが正しいことを確認します。