扩展程序和转变器

扩展是在给定类型的每个块上运行的函数,因为块是 创建。这类操作通常会将一些自定义配置或行为添加到代码块中。

赋值函数是一种特殊的扩展程序,可添加自定义序列化,并且 通常是 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;
      });
    });

扩展程序必须“注册”以便将其与字符串相关联 键。然后,您可以将此字符串键分配给您的extensions属性 块类型的 JSON 定义以将 扩展。

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

您也可以一次添加多条附加信息。请注意,extensions 属性必须是数组,即使您只应用一个扩展也是如此。

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

合辑

此外,Blockly 还提供了一种便捷的方法,供您在需要添加 将一些属性/辅助函数添加到块中,但不要立即运行它们。这个 方法是在 Google Cloud 中注册一个mixin 对象,其中包含所有其他属性/方法。Mixin 对象 然后,该函数被封装在一个函数中,该函数会在每次 系统就会创建指定的屏蔽类型

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

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

可以在 JSON 中引用与 mixins 关联的字符串键,就像引用任何其他项一样 。

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

变更器

赋值函数是一种特殊类型的扩展程序,它会添加额外的序列化(额外 保存和加载的状态)。例如,内置的 controls_iflist_create_with 块需要额外的序列化,以便 就可以节省自己拥有的输入量。

请注意,更改砌块的形状并不一定意味着您需要 额外的序列化。例如,math_number_property 代码块会更改为 但它基于一个下拉菜单字段执行该操作,该字段的值已有 序列化。因此,只需使用字段 验证程序 需要一个赋值函数。

有关详情,请参阅序列化 页

Mutator 还提供内置界面,以便用户在发生以下情况时更改方块的形状: 您需要提供一些可选方法。

序列化钩子

变更器有两对与之搭配使用的序列化钩子。一对钩子 适用于新的 JSON 序列化系统,而另一对适用于 旧 XML 序列化系统。您必须至少提供这些键值对中的一个。

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 属性 JSON。这样可以使保存的文件保持小巧。

完整的序列化和后备数据

saveExtraState 还会接收可选的 doFullSerialization 参数。这个 由引用状态(由另一个 序列化器(如支持数据模型)。参数表示 在块被反序列化时,引用的状态将不可用,因此 应将所有后备状态本身序列化。例如, 在序列化单个块或复制粘贴块时为 true。

这有两个常见用例:

  • 当单个块被加载到后备数据的工作区时 模型不存在,但它拥有足够的信息, 创建新的数据模型。
  • 复制粘贴文本块时,它始终会创建新的后备内容 而不是引用现有模型。

一些使用该参数的区块包括 @blockly/block-shareable-procedures 代码块。正常 它们将对存储其状态的后备数据模型的引用序列化。 但是,如果 doFullSerialization 参数为 true,它们会序列化所有 状态可共享的过程块使用此机制来确保 它们会创建新的后备数据模型,而不是引用 现有模型。

mutateToDom 和 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 中

界面钩子

如果您提供某些功能作为赋值函数的一部分,Blockly 会添加一个 默认的“更改器”添加到区块中的界面

如果您想添加额外的序列化,则无需使用此界面。您可以 使用自定义界面,例如 blocks-plus---minus 插件 否则,您根本不需要使用界面!

编写和分解

默认界面依赖于 composedecompose 函数。

decompose“爆炸”将文本块拆分成更小的子块 已添加和删除的信息此函数应返回一个“顶部代码块”也就是 子块连接到的赋值函数工作区中的主块。

然后,compose 会解释子块的配置,并将其用于 修改 main 代码块。此函数应接受“top block”也就是 作为参数由 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 函数, 默认界面此函数让您有机会将 主块(位于主工作区中)和位于主工作区中的子块 来进行更改。然后,您可以使用此数据来确保您的compose 功能正常地重新连接 main 代码块的子项时, 重新组织了子块。

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:可选的辅助函数, 在 Mixin 混合后在该块上运行。
  • opt_blockList:可选的块类型数组(作为字符串),将 添加到默认更改器界面中的浮出控件(如果这些界面方法也 。

请注意,与扩展程序不同,每种块类型只能有一个更改器。

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

辅助函数

增值器可以与 mixin 一起注册辅助函数。此函数 在创建指定类型的每个块后运行,并且 mixinObj 是 已添加。它可用于向变更添加其他触发器或效果。

例如,您可以在类似列表的代码块中添加一个帮助程序, 初始商品数量:

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