拡張機能とミューテータ

拡張機能は、特定の型の各ブロックで実行される関数で、 作成されます。多くの場合、これらはブロックにカスタムの設定や動作を追加します。

ミューテーターは、カスタム シリアル化を追加する特別な種類の拡張機能です。 UI やブロックなどです。

拡張機能

拡張機能は、特定の型の各ブロックで実行される関数で、 作成されます。カスタム設定(ブロックのツールチップの設定など)を追加するか、 カスタム動作を指定できます(例: ブロックへのイベント リスナーの追加)。

// This extension sets the block's tooltip to be a function which displays
// the parent block's tooltip (if it exists).
Blockly.Extensions.register(
    'parent_tooltip_extension',
    function() { // this refers to the block that the extension is being run on
      var thisBlock = this;
      this.setTooltip(function() {
        var parent = thisBlock.getParent();
        return (parent && parent.getInputsInline() && parent.tooltip) ||
            Blockly.Msg.MATH_NUMBER_TOOLTIP;
      });
    });

拡張機能は「登録」する必要がある文字列に関連付けることができるように ] キーを押します。次に、この文字列キーを BigQuery の extensions プロパティに ブロックタイプの JSON 定義を使用して、 ブロックに追加します。

{
 //...,
 "extensions": ["parent_tooltip_extension",]
}

複数の広告表示オプションをまとめて追加することもできます。なお、extensions プロパティは配列である必要があります。

{
  //...,
  "extensions": ["parent_tooltip_extension", "break_warning_extension"],
}

ミックスイン

Blockly には、スペースに追加したい状況がある場合に すぐには実行せず、一部のプロパティやヘルパー関数をブロックに追加しましょう。この ミックスインを登録して、 追加のプロパティとメソッドをすべて含むオブジェクトです。Mixin オブジェクト という関数でラップされ、2 つの関数が ブロックタイプが作成されます。

Blockly.Extensions.registerMixin('my_mixin', {
  someProperty: 'a cool value',

  someMethod: function() {
    // Do something cool!
  }
))`

Mixin に関連付けられた文字列キーは、他のモジュールと同様に JSON で参照できます。 あります。

{
 //...,
 "extensions": ["my_mixin"],
}

ミューテータ

ミューテーターは特殊なタイプの拡張機能で、シリアル化をさらに追加して ブロックに割り当てられます。たとえば、組み込みの controls_if ブロックと list_create_with ブロックに追加のシリアル化が必要。 保存できる入力の数を削減できます。

ブロックの形状を変更しても、必ずしも 使用します。たとえば、math_number_property ブロックは、 プルダウン フィールドに基づいて行われます。このフィールドの値は、 シリアル化されます。そのため、 バリデータであり、 ミューテーターが必要です。

詳細については、こちらの のページ ミューテータが必要になる場合と必要ない場合について説明します。

ミューテータには、ユーザーが必要に応じてブロックの形状を変更できる組み込みの UI も用意されています。 オプションのメソッドをいくつか提供します。

シリアル化フック

ミューテーターには、連携するシリアル化フックのペアが 2 つあります。1 組のフック 他のペアは新しい JSON シリアル化システムで動作し、もう 1 つのペアは 古い XML シリアル化システムです。これらのペアの少なくとも 1 つを指定する必要があります。

saveExtraState と loadExtraState

saveExtraStateloadExtraState は、 JSON シリアル化システムです。saveExtraState は、シリアル化可能な JSON を返します。 ブロックの追加状態を表す値、loadExtraState 同じ JSON シリアル化可能な値を受け入れ、ブロックに適用します。

// These are the serialization hooks for the lists_create_with block.
saveExtraState: function() {
  return {
    'itemCount': this.itemCount_,
  };
},

loadExtraState: function(state) {
  this.itemCount_ = state['itemCount'];
  // This is a helper function which adds or removes inputs from the block.
  this.updateShape_();
},

結果の JSON は次のようになります。

{
  "type": "lists_create_with",
  "extraState": {
    "itemCount": 3 // or whatever the count is
  }
}
状態なし

ブロックがシリアル化されたときにデフォルトの状態である場合、 saveExtraState メソッドは、これを示すために null を返すことができます。お使いの saveExtraState メソッドが null を返した場合、extraState プロパティは追加されません。 必要があります。これにより、保存ファイルのサイズを小さくすることができます。

完全なシリアル化とバッキング データ

saveExtraState はオプションの doFullSerialization パラメータも受け取ります。この ブロックが別のブロックでシリアル化された状態を serializer(バッキング データモデルなど)を使用します。このパラメータは ブロックがシリアル化解除されたときに参照状態を利用できないため、 すべてのバッキング状態自体をシリアル化する必要があります。たとえば、これは 個々のブロックがシリアル化されたとき、またはブロックをコピーして貼り付けたときには true になります。

一般的なユースケースは次の 2 つです。

  • バッキングデータが保存されるワークスペースに個々のブロックが モデルが存在しない場合は、その状態に十分な情報が 新しいデータモデルを作成します
  • ブロックをコピーして貼り付けると、常に新しいバッキングが作成される データモデルを使用します。

これを使用するブロックの一部は、 @blockly/block-shareable-procedures ブロック。通常 状態を保存するバッキング データモデルへの参照をシリアル化します。 ただし、doFullSerialization パラメータが true の場合、すべての要素がシリアル化されます。 確認できます。共有可能なプロシージャのブロックはこれを使用して、 新しいバッキング データモデルが作成されるため、 必要があります。

mutationToDom と domToMutation

mutationToDomdomToMutation は、 古い XML シリアル化システムです。これらのフックは必要な場合にのみ使用してください(例: まだ移行していない古いコードベースで作業している場合)は、 saveExtraStateloadExtraState

mutationToDom は、追加の状態を表す XML ノードを返します。 domToMutation がその同じ XML ノードを受け入れ、状態を ブロックを解除できます。

// These are the old XML serialization hooks for the lists_create_with block.
mutationToDom: function() {
  // You *must* create a <mutation></mutation> element.
  // This element can have children.
  var container = Blockly.utils.xml.createElement('mutation');
  container.setAttribute('items', this.itemCount_);
  return container;
},

domToMutation: function(xmlElement) {
  this.itemCount_ = parseInt(xmlElement.getAttribute('items'), 10);
  // This is a helper function which adds or removes inputs from the block.
  this.updateShape_();
},

結果の XML は次のようになります。

<block type="lists_create_with">
  <mutation items="3"></mutation>
</block>

mutationToDom 関数が null を返した場合、余分な要素は XML に追加されます。

UI フック

ミューテータの一部として特定の機能を提供する場合、Blockly は デフォルトの「ミューテーター」ブロックに追加します。

シリアル化をさらに追加する場合は、この UI を使用する必要はありません。方法 カスタム UI を使用します。たとえば、 プラグイン UI をまったく使用しないことも可能です。

合成と分解

デフォルト UI は、compose 関数と decompose 関数に依存します。

decomposeが「爆発」しました移動可能な小さなサブブロックに分割できます。 追加または削除できます。この関数は「上部ブロック」を返します。これは サブブロックが接続するミューテータ ワークスペースのメインブロックに配置します。

次に、compose がサブブロックの構成を解釈し、それらを使用して次の処理を行います。 main ブロックを変更します。この関数は「トップブロック」を受け入れます。これは パラメータとして decompose から返されます。

これらの関数は「混在」していることに注意してください。適用できるのは「変更」されたブロックです。this そのブロックを参照できます。

// These are the decompose and compose functions for the lists_create_with block.
decompose: function(workspace) {
  // This is a special sub-block that only gets created in the mutator UI.
  // It acts as our "top block"
  var topBlock = workspace.newBlock('lists_create_with_container');
  topBlock.initSvg();

  // Then we add one sub-block for each item in the list.
  var connection = topBlock.getInput('STACK').connection;
  for (var i = 0; i < this.itemCount_; i++) {
    var itemBlock = workspace.newBlock('lists_create_with_item');
    itemBlock.initSvg();
    connection.connect(itemBlock.previousConnection);
    connection = itemBlock.nextConnection;
  }

  // And finally we have to return the top-block.
  return topBlock;
},

// The container block is the top-block returned by decompose.
compose: function(topBlock) {
  // First we get the first sub-block (which represents an input on our main block).
  var itemBlock = topBlock.getInputTargetBlock('STACK');

  // Then we collect up all of the connections of on our main block that are
  // referenced by our sub-blocks.
  // This relates to the saveConnections hook (explained below).
  var connections = [];
  while (itemBlock && !itemBlock.isInsertionMarker()) {  // Ignore insertion markers!
    connections.push(itemBlock.valueConnection_);
    itemBlock = itemBlock.nextConnection &&
        itemBlock.nextConnection.targetBlock();
  }

  // Then we disconnect any children where the sub-block associated with that
  // child has been deleted/removed from the stack.
  for (var i = 0; i < this.itemCount_; i++) {
    var connection = this.getInput('ADD' + i).connection.targetConnection;
    if (connection && connections.indexOf(connection) == -1) {
      connection.disconnect();
    }
  }

  // Then we update the shape of our block (removing or adding iputs as necessary).
  // `this` refers to the main block.
  this.itemCount_ = connections.length;
  this.updateShape_();

  // And finally we reconnect any child blocks.
  for (var i = 0; i < this.itemCount_; i++) {
    connections[i].reconnect(this, 'ADD' + i);
  }
},

saveConnections

必要に応じて、次を処理する saveConnections 関数を定義することもできます。 使用できます。この関数を使用すると、子孫を main ブロック(メイン ワークスペースに存在する)とサブブロックがあります。 コピーします。このデータを使用して、compose メイン ブロックの子が正しく再接続されます。 再編成されます。

saveConnections は「トップブロック」を受け入れる必要があります。decomposeからの返品 パラメータとして渡します。saveConnections 関数が定義されている場合、Blockly compose を呼び出す前に、このメソッドを呼び出します。

saveConnections: function(topBlock) {
  // First we get the first sub-block (which represents an input on our main block).
  var itemBlock = topBlock.getInputTargetBlock('STACK');

  // Then we go through and assign references to connections on our main block
  // (input.connection.targetConnection) to properties on our sub blocks
  // (itemBlock.valueConnection_).
  var i = 0;
  while (itemBlock) {
    // `this` refers to the main block (which is being "mutated").
    var input = this.getInput('ADD' + i);
    // This is the important line of this function!
    itemBlock.valueConnection_ = input && input.connection.targetConnection;
    i++;
    itemBlock = itemBlock.nextConnection &&
        itemBlock.nextConnection.targetBlock();
  }
},

登録中

ミューテータは特殊な拡張機能なので、これも必ず一致させる必要があります。 ブロックタイプの JSON や 定義をご覧ください。

// Function signature.
Blockly.Extensions.registerMutator(name, mixinObj, opt_helperFn, opt_blockList);

// Example call.
Blockly.Extensions.registerMutator(
    'controls_if_mutator',
    { /* mutator methods */ },
    undefined,
    ['controls_if_elseif', 'controls_if_else']);
  • name: JSON で使用できるようにミューテータに関連付ける文字列。
  • mixinObj: さまざまなミューテーション メソッドを含むオブジェクト。例: saveExtraStateloadExtraState
  • opt_helperFn: 次の処理を行うオプションのヘルパー関数。 ブロックを実行します。
  • opt_blockList: 対象となるブロックタイプの配列(文字列)。省略可 デフォルトのミューテータ UI のフライアウトに追加される(UI メソッドも追加されている場合) 定義します。

拡張機能とは異なり、各ブロックタイプに設定できるミューテーターは 1 つのみです。

{
  //...
  "mutator": "controls_if_mutator"
}

ヘルパー関数

ミックスインとともにミューテータがヘルパー関数を登録することもできます。この関数は、 作成された後、指定されたタイプの各ブロックで実行され、mixinObj が 追加されました。ミューテーションにトリガーや効果を追加するために使用できます。

たとえば、リストのようなブロックにヘルパーを追加して、 最初のアイテム数:

var helper = function() {
  this.itemCount_ = 5;
  this.updateShape();
}