定义屏蔽设置

块定义描述了块的外观和行为,包括文本、颜色、形状以及块可以连接到的其他块。

JSON 格式与 JavaScript API

Blockly 有两种定义块的方式:JSON 对象和 JavaScript 函数。JSON 格式旨在简化针对具有不同词序的语言开发应用的本地化过程。JSON 格式是定义块的首选方法。

但是,JSON 格式无法直接定义更改器或验证器等高级功能。这些必须使用 JavaScript(通常作为扩展程序)编写。

使用 Blockly 原始 JavaScript 实现的应用也可以将块定义直接写入较低级别的 Blockly API 函数调用,如下面的各种 JavaScript 示例所示。

JSON

Blockly.defineBlocksWithJsonArray([{
  "type": "string_length",
  "message0": 'length of %1',
  "args0": [
    {
      "type": "input_value",
      "name": "VALUE",
      "check": "String"
    }
  ],
  "output": "Number",
  "colour": 160,
  "tooltip": "Returns number of letters in the provided text.",
  "helpUrl": "http://www.w3schools.com/jsref/jsref_length_string.asp"
}]);

JavaScript

Blockly.Blocks['string_length'] = {
  init: function() {
    this.appendValueInput('VALUE')
        .setCheck('String')
        .appendField('length of');
    this.setOutput(true, 'Number');
    this.setColour(160);
    this.setTooltip('Returns number of letters in the provided text.');
    this.setHelpUrl('http://www.w3schools.com/jsref/jsref_length_string.asp');
  }
};

init 函数用于创建块的形状。在此函数的上下文中,关键字 this 是正在创建的实际块。

两个示例都加载了相同的“string_length”块。

在 Web 上,JSON 格式是使用 initJson 函数加载的。这也允许在 Blockly 网页中混合使用这两种格式。最好尽可能使用 JSON 定义块,并且只对 JSON 不支持的块定义部分使用 JavaScript。

下例中的块主要使用 JSON 定义,但使用 JavaScript API 进行了扩展,以提供动态提示。

JavaScript

var mathChangeJson = {
  "message0": "change %1 by %2",
  "args0": [
    {"type": "field_variable", "name": "VAR", "variable": "item", "variableTypes": [""]},
    {"type": "input_value", "name": "DELTA", "check": "Number"}
  ],
  "previousStatement": null,
  "nextStatement": null,
  "colour": 230
};

Blockly.Blocks['math_change'] = {
  init: function() {
    this.jsonInit(mathChangeJson);
    // Assign 'this' to a variable for use in the tooltip closure below.
    var thisBlock = this;
    this.setTooltip(function() {
      return 'Add a number to variable "%1".'.replace('%1',
          thisBlock.getFieldValue('VAR'));
    });
  }
};

方块颜色

块的主要颜色通过 JSON colour 属性、block.setColour(..) 函数或使用主题并定义块样式来定义。

JSON

{
  // ...,
  "colour": 160,
}

JavaScript

init: function() {
  // ...
  this.setColour(160);
}

如需了解详情,请参阅块颜色指南

语句连接

用户可以使用 nextStatementpreviousStatement 连接器创建序列块。在 Blockly 的标准布局中,这些连接位于顶部和底部,并且块垂直堆叠。

具有前一个连接器的块不能有输出连接器,反之亦然。术语语句块是指没有值输出的块。语句块通常既有上一个连接,又有下一个连接。

nextStatementpreviousStatement 连接可以是类型化连接,但标准块不使用此功能。

下次连接

在代码块底部创建一个点,以便在其下方堆叠其他语句。具有下一个连接但没有上一个连接的块通常表示事件,可以配置为带有帽子进行呈现。

JSON

非类型化:

{
  ...,
  "nextStatement": null,
}

类型化(罕见):

{
  "nextStatement": "Action",
  ...
}

JavaScript

非类型化:

this.setNextStatement(true);  // false implies no next connector, the default

类型化(罕见):

this.setNextStatement(true, 'Action');

之前的连接

在代码块顶部创建一个凹口,以便将其连接为语句堆栈。

具有先前连接的块不能具有输出连接。

JSON

非类型化:

{
  ...,
  "previousStatement": null,
}

类型化(罕见):

{
  "previousStatement": "Action",
  ...
}

JavaScript

非类型化:

this.setPreviousStatement(true);  // false implies no previous connector, the default

类型化(罕见):

this.setPreviousStatement(true, 'Action');

块输出

一个块可能具有单个输出,表示为前缘的公形拼图连接器。输出连接到值输入。带有输出的块通常称为值块

JSON

非类型化:

{
  // ...,
  "output": null,
}

输入:

{
  // ...,
  "output": "Number",
}

JavaScript

非类型化:

init: function() {
  // ...
  this.setOutput(true);
}

输入:

init: function() {
  // ...
  this.setOutput(true, 'Number');
}

带有输出连接器的块不能同时具有前面的语句凹口。

屏蔽输入内容

一个块具有一个或多个输入,其中每个输入具有一系列字段,并且可能以连接结尾。内置输入有多种类型。

  • 值输入:连接到值块输出连接math_arithmetic 块(加法、减法)是包含两个值输入的块的示例。
  • 语句输入:连接到语句块上一个连接。when 循环的嵌套部分是语句输入的一个示例。
  • 虚拟输入:没有块连接。当块配置为使用外部值输入时,则充当换行符。
  • 结束行输入:没有块连接,并且始终充当换行符。

您还可以创建自定义输入,以支持自定义呈现

JSON 格式和 JavaScript API 使用略有不同的模型来描述其输入。

JSON 中的输入和字段

JSON 定义的块的结构为一系列插值消息字符串(message0message1...),其中每个插值令牌(%1%2...)都是匹配的 JSON argsN 数组中的一个字段或输入端(因此,输入连接器在消息中渲染)。此格式旨在简化国际化。

JSON

{
  "message0": "set %1 to %2",
  "args0": [
    {
      "type": "field_variable",
      "name": "VAR",
      "variable": "item",
      "variableTypes": [""]
    },
    {
      "type": "input_value",
      "name": "VALUE"
    }
  ]
}

插值令牌必须与 args0 数组完全匹配:不重复,没有遗漏。令牌可能会以任何顺序存在,这允许不同的语言更改块的布局。

插值令牌两侧的文本会去掉空格。使用 % 字符的文本(例如,引用百分比时)应使用 %%,以免将其解释为插值令牌。

参数和参数类型的顺序决定了块的形状。更改其中某个字符串可能会完全更改代码块的布局。这在词序不同于英语的语言中尤为重要。假设有一种语言,其中 "set %1 to %2"(如上例中使用)需要反转为 "put %2 in %1"。更改这一个字符串(并保持 JSON 的其余部分不变)会生成以下代码块:

块式自动更改字段顺序、创建虚拟输入,并从外部输入切换到内部输入。

Blockly 还会自动将消息字符串中的所有换行符 (\n) 替换为末尾行输入。

JSON

{
  "message0": "set %1\nto %2",
  "args0": [
    {
      "type": "field_variable",
      "name": "VAR",
      "variable": "item",
      "variableTypes": [""]
    },
    {
      "type": "input_value",
      "name": "VALUE"
    }
  ]
}

参数

每个消息字符串都与一个相同编号的 args 数组配对。例如,message0 搭配 args0。插值令牌(%1%2...)引用 args 数组的项。每个对象都有一个 type 字符串。其余参数因类型而异:

您还可以定义自己的自定义字段自定义输入,并将其作为参数传递。

每个对象还可以有一个 alt 字段。如果 Blockly 无法识别对象的 type,则会改用 alt 对象。例如,如果将名为 field_time 的新字段添加到 Blockly 中,则使用此字段的块可以使用 alt 为旧版 Blockly 定义 field_input 回退:

JSON

{
  "message0": "sound alarm at %1",
  "args0": [
    {
      "type": "field_time",
      "name": "TEMPO",
      "hour": 9,
      "minutes": 0,
      "alt":
        {
          "type": "field_input",
          "name": "TEMPOTEXT",
          "text": "9:00"
        }
    }
  ]
}

alt 对象可能有自己的 alt 对象,因此允许链接。最后,如果 Blockly 无法在 args0 数组中创建对象(在尝试任何 alt 对象之后),则会直接跳过该对象。

如果 message 字符串以输入中未包含的文本或字段结尾,系统会自动将虚拟输入添加到块的末尾。因此,如果块上的最后一个输入是虚拟输入,则可以从 args 数组中将其省略,并且无需插值到 message 中。通过自动添加尾随虚拟输入,翻译人员可以更改 message,而无需修改 JSON 的其余部分。请参阅本页前面的示例 "set %1 to %2"(无虚拟输入)和 "put %2 in %1"(添加了虚拟输入)。

implicitAlign0

在极少数情况下,自动创建的尾随虚拟输入需要与 "RIGHT""CENTRE" 对齐。如果未指定,则默认值为 "LEFT"

在下面的示例中,message0"send email to %1 subject %2 secure %3",Blockly 会自动为第三行添加虚拟输入。将 implicitAlign0 设置为 "RIGHT" 会强制此行右对齐。这种对齐方式适用于 JSON 块定义中未明确定义的所有输入,包括用于替换消息中换行符 ('\n') 的结束行输入。此外,已弃用的属性 lastDummyAlign0 的行为与 implicitAlign0 相同。

针对 RTL(阿拉伯语和希伯来语)设计块时,左、右互反。因此,"RIGHT" 会向左对齐字段。

message1args1implicitAlign1

一些代码块自然分为两个或更多个独立部分。我们来考虑下面这个重复块,它有两行:

如果使用一条消息来描述此块,则 message0 属性将为 "repeat %1 times %2 do %3"。这个字符串对翻译人员来说比较尴尬,很难解释 %2 替代的含义。在某些语言中,甚至可能不需要 %2 虚拟输入。并且可能有多个块想要共享第二行的文本。更好的方法是让 JSON 使用多个消息和参数属性:

JSON

{
  "type": "controls_repeat_ext",
  "message0": "repeat %1 times",
  "args0": [
    {"type": "input_value", "name": "TIMES", "check": "Number"}
  ],
  "message1": "do %1",
  "args1": [
    {"type": "input_statement", "name": "DO"}
  ],
  "previousStatement": null,
  "nextStatement": null,
  "colour": 120
}

可以用 JSON 格式定义任意数量的 messageargsimplicitAlign 属性,从 0 开始,依序递增。请注意,块工厂无法将消息拆分为多个部分,但手动执行此操作相当简单。

JavaScript 中的输入和字段

JavaScript API 包含每种输入类型的 append 方法:

JavaScript

this.appendEndRowInput()
    .appendField('for each')
    .appendField('item')
    .appendField(new Blockly.FieldVariable());
this.appendValueInput('LIST')
    .setCheck('Array')
    .setAlign(Blockly.inputs.Align.RIGHT)
    .appendField('in list');
this.appendStatementInput('DO')
    .appendField('do');
this.appendDummyInput()
    .appendField('end');

每个附加方法都可以接受代码生成器使用的标识符字符串。虚拟和末行输入很少需要引用,并且标识符通常未设置。

JavaScript API 还包含用于附加自定义输入的通用 appendInput 方法。请注意,在这种情况下,标识符应直接传递给自定义输入的构造函数。

JavaScript

this.appendInput(new MyCustomInput('INPUT_NAME'))
    .appendField('an example label')

所有 appendInput 方法(包括泛型和非泛型)都会返回输入对象,以便使用方法链接进一步配置它们。有三种内置方法用于配置输入。

setCheck

JavaScript

input.setCheck('Number');

此可选函数用于对已连接的输入进行类型检查。如果给定的参数为 null(默认值),则此输入可以连接到任何块。如需了解详情,请参阅类型检查

setAlign

JavaScript

input.setAlign(Blockly.inputs.Align.RIGHT);

此可选函数用于对齐字段(见下文)。有三个自描述值可以作为参数传递给此函数:Blockly.inputs.Align.LEFTBlockly.inputs.Align.RIGHTBlockly.inputs.Align.CENTER

针对 RTL(阿拉伯语和希伯来语)设计块时,左、右互反。因此,Blockly.inputs.Align.RIGHT 会向左对齐字段。

appendField

创建输入并使用 appendInput 附加到块后,您可以选择将任意数量的字段附加到输入。这些字段通常用作标签,以描述每个输入的用途。

JavaScript

input.appendField('hello');

最简单的字段元素是文本。Blockly 的惯例是使用全小写文本,但专有名词(如 Google、SQL)除外。

输入行可以包含任意数量的字段元素。可以将多个 appendField 调用链接在一起,以便高效地向同一输入行添加多个字段。

JavaScript

input.appendField('hello')
     .appendField(new Blockly.FieldLabel('Neil', 'person'));

appendField('hello') 调用实际上是使用显式 FieldLabel 构造函数 appendField(new Blockly.FieldLabel('hello')) 的快捷方式。唯一希望使用构造函数的情况是,指定类名称,以便使用 CSS 规则设置文本的样式。

内嵌与外部

块输入可以呈现为外部或内部。

块定义可以指定一个可选的布尔值,用于控制输入是否为内嵌。如果为 false,则任何值输入都将是外部输入(例如左侧代码块)。如果为 true,则任何值输入都将是内嵌的(如上面的右侧代码块)。

JSON

{
  // ...,
  "inputsInline": true
}

JavaScript

init: function() {
  // ...
  this.setInputsInline(true);
}

如果未定义,Blockly 将利用一些启发法来猜测哪种模式是最佳的。假设 Blockly 做出了正确的选择,最好将此字段保留为未定义状态,因为不同的语言翻译可以自动采用不同的模式。请参阅本页前面的 "set %1 to %2"(外部输入)和 "put %2 in %1"(内嵌输入)的 JSON 示例。

当某个块可能包含小型输入(例如数字)时,请使用内嵌输入。如果启用了 collapse 配置,则用户可以通过上下文菜单切换此选项(如果工具箱包含类别,则默认为 true)。

字段

字段定义块中的大部分界面元素。其中包括字符串标签、图片和字面量数据(如字符串和数字)的输入。最简单的示例是 math_number 代码块,它使用 field_input 让用户输入数字。

字段使用 appendField 附加到块。

Blockly 提供了许多内置字段,包括文本输入、颜色选择器和图片。您也可以创建自己的字段。

→ 详细了解内置字段

→ 详细了解如何创建自定义字段

图标

图标定义块上的界面元素,用于显示有关块的“元”信息。

图标是使用 addIcon 附加到块的。

Blockly 提供了许多内置图标,包括评论图标和警告图标。您也可以创建自己的图标。

→ 详细了解如何创建自定义图标

提示

用户将鼠标悬停在木块上时,提示功能可提供即时帮助。 如果文本过长,系统会自动换行。

JSON

{
  // ...,
  "tooltip": "Tooltip text."
}

JavaScript

init: function() {
  this.setTooltip("Tooltip text.");
}

在 JavaScript API 中,提示还可以定义为函数,而不是静态字符串。这样可以提供动态帮助。有关提示示例,该提示会根据所选的下拉菜单选项而变化,请参阅 math_arithmetic

JavaScript

Blockly.Blocks['math_arithmetic'] = {
  init: function() {
    // ...

    // Assign 'this' to a variable for use in the tooltip closure below.
    var thisBlock = this;
    this.setTooltip(function() {
      var mode = thisBlock.getFieldValue('OP');
      var TOOLTIPS = {
        'ADD': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_ADD,
        'MINUS': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_MINUS,
        'MULTIPLY': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_MULTIPLY,
        'DIVIDE': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_DIVIDE,
        'POWER': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_POWER
      };
      return TOOLTIPS[mode];
    });
  }
};

使用 JavaScript API,块可以指定一个函数,而不是一个返回提示字符串的静态字符串。这样,您就可以使用动态提示。 如需查看示例,请参阅 math_arithmetic

自定义

您还可以通过提供自定义渲染函数来自定义提示的外观。创建一个接受两个参数的函数:

  • 首先是 <div> 元素,您将在其中渲染内容
  • 其上显示的是用户将鼠标悬停在哪个元素上

在函数的正文中,您可以将任何喜欢的内容呈现到 div 中。如需获取在鼠标悬停的块上定义的提示字符串,您可以调用 Blockly.Tooltip.getTooltipOfObject(element);,其中 element 是上面的第二个参数。

最后,注册此函数,以便 Blockly 可以在适当的时间调用它:

Blockly.Tooltip.setCustomTooltip(yourFnHere);

如需查看示例,请参阅自定义提示演示

“帮助”网址

组成要素可以有与之关联的帮助页面。Blockly for Web 的用户可通过以下方式使用该功能:右键点击相应代码块,然后从上下文菜单中选择“帮助”。如果此值为 null,则菜单会显示为灰色。

JSON

{
  // ...,
  "helpUrl": "https://en.wikipedia.org/wiki/For_loop"
}

JavaScript

init: function() {
  // ...
  this.setHelpUrl('https://en.wikipedia.org/wiki/For_loop');
}

使用 JavaScript API,代码块可以指定一个函数,而不是一个返回网址字符串的静态字符串,从而允许动态帮助。

更改监听器和验证器

块可以具有更改监听器函数,当对工作区(包括与块无关的函数)进行任何更改时,就会调用这些函数。它们主要用于在工作区之外设置代码块的警告文本或类似用户通知。

通过使用函数调用 setOnChange 来添加该函数。如果您计划在所有平台上使用该函数,则可以在初始化期间或通过 JSON 扩展程序添加该函数。

JSON

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

Blockly.Extensions.register('warning_on_change', function() {
  // Example validation upon block change:
  this.setOnChange(function(changeEvent) {
    if (this.getInput('NUM').connection.targetBlock()) {
      this.setWarningText(null);
    } else {
      this.setWarningText('Must have an input block.');
    }
  });
});

JavaScript

Blockly.Blocks['block_type'] = {
  init: function() {
    // Example validation upon block change:
    this.setOnChange(function(changeEvent) {
      if (this.getInput('NUM').connection.targetBlock()) {
        this.setWarningText(null);
      } else {
        this.setWarningText('Must have an input block.');
      }
    });
  }
}

系统会调用该函数,传入更改事件。 在该函数内,this 是指块实例。

由于系统会在进行任何更改时调用该函数(如果使用的话),开发者应确保监听器快速运行。此外,还应注意工作区更改,这些更改可能会级联或环回监听器。

有关示例,请参阅 controls_flow_statementslogic_compareprocedures_ifreturn 代码块。

请注意,可修改字段有自己的事件监听器,用于验证输入和产生副作用。

赋值函数

借助 Mutator,高级块能够改变形状,最值得注意的是,用户可以通过打开对话框来添加、移除或重新排列组件。可以使用 mutator 键通过 JSON 添加更改器。

JSON

{
  // ...,
  "mutator":"if_else_mutator"
}

按块配置

块实例有许多属性,用于配置它们对用户的行为方式。这些参数可用于限制工作区以反映域的某些属性(例如,只有一个“start”事件),或专注于用户的操作(例如教程)。

可删除状态

block.setDeletable(false);

如果此政策设为 false,用户将无法删除相应屏蔽设置。在可修改的工作区中,块默认设置为可删除。

所有屏蔽(甚至是不可删除的屏蔽)都可以以编程方式删除:

block.dispose();

可修改的状态

block.setEditable(false);

如果设置为 false,用户将无法更改该块的字段(例如下拉菜单和文本输入)。在可修改的工作区中,块默认处于可修改状态。

移动状态

block.setMovable(false);

如果设置为 false,用户将无法直接移动图块。如果某个不可移动块是另一个块的子块,则不能与该块断开连接,不过,如果移动父级块,该块将会随其父块一起移动。块在可修改的工作区上默认为可移动。

任何块(即使是不可移动的块)在位于工作区中后,可以通过程序化方式移动。

block.moveBy(dx, dy)

块在工作区中的起始位置默认为 (0, 0)。

禁止使用数据流量

this.data = '16dcb3a4-bd39-11e4-8dfc-aa07a5b093db';

data 是附加到块的可选任意字符串。当块序列化时,数据字符串会随之一起序列化。这包括复制或复制/粘贴块的情况。

这通常用于将块与外部资源相关联。

序列化为 JSON 后,数据将作为顶级属性存储在块中:

{
  "type": "my_block",
  "data": "16dcb3a4-bd39-11e4-8dfc-aa07a5b093db",
  // etc..
}

序列化为 XML(旧的冰箱式序列化系统)时,数据字符串将存储在代码块内的 <data></data> 标记中:

<block type="my_block">
  <data>16dcb3a4-bd39-11e4-8dfc-aa07a5b093db</data>
  <!-- etc... -->
</block>

破坏

块具有 destroy 钩子,当它们从工作区中删除时,系统会调用该钩子。这可用于销毁与分块关联且不再需要的任何后备数据模型/外部资源。

JSON

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

Blockly.Extensions.registerMixin('destroy', {
  destroy: function() {
    this.myResource.dispose();
  }
});

JavaScript

Blockly.Blocks['block_type'] = {
  destroy: function() {
    this.myResource.dispose();
  }
}

destroy 方法会在处置块的父级之后、其任何子级或字段之前被调用。

上下文菜单

默认情况下,代码块具有右键点击上下文菜单,用户可以执行添加注释或复制代码块等操作。

您可以通过以下方式停用单个块的上下文菜单:

block.contextMenu = false;

您还可以自定义菜单中显示的选项。如需为所有代码块自定义菜单,请参阅上下文菜单文档。如需为单个代码块自定义菜单,您可以实现 customContextMenu。此函数接受一系列菜单选项并适当修改,这意味着您可以添加和移除项。

每个菜单选项都是一个包含以下三个属性的对象:

  • text 是显示文本。
  • enabled 是一个布尔值。停用后,该选项会显示,但带有灰色文字。
  • callback 是点击选项时要调用的函数。