4.3 定义块

优质
小牛编辑
119浏览
2023-12-01

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

定义自定义块后,每个平台都会以不同的方式加载这些定义,详细介绍了 Web和 Android特定的配置指南。

JSON格式与JavaScript API

Blockly有两种定义块的方法:JSON对象和JavaScript函数。JSON格式是跨平台的,因此可以使用相同的代码在Web,Android和iOS上定义块。此外,JSON格式旨在简化开发具有不同单词排序的语言时的本地化过程。JSON格式是定义块的首选方法。

但是,JSON格式无法直接定义高级功能,如mutator或验证器。这些必须用平台的本机代码,JavaScript,Java或Swift编写,通常作为 扩展。

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

JSON格式:

{
  "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上,使用该initJson函数加载JSON格式。这也允许在Blockly网页中混合使用这两种格式。最好尽可能使用JSON定义块,并仅将JavaScript用于JSON不支持的块定义部分。

下面是一个主要使用JSON定义的块的示例,但是使用JavaScript API进行扩展以提供动态工具提示。

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(..)函数或使用主题定义块样式定义。

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

声明连接

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

具有前置连接器的块不能具有输出连接器,反之亦然。术语语句块指的是没有值输出的块。语句块通常具有前置连接和后续连接。

nextStatement并且previousStatement可以键入连接 ,但标准块不使用此功能。

后续连接

在块的底部创建一个接口,以便其他语句可以堆叠在它下面。具有后续连接但没有前置连接的块通常表示事件,并且可以配置为使用帽子进行渲染 。


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

{
  "nextStatement": "Action",
  ...
}
//无类型:
this.setNextStatement(true);  // false implies no next connector, the default

//键入(罕见):
this.setNextStatement(true, 'Action');

前置连接

在块的顶部创建一个凹口,以便它可以作为一堆语句连接。

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

无类型:
{
  ...,
  "previousStatement": null,
}

键入(罕见):
{
  "previousStatement": "Action",
  ...
}
//无类型:
this.setPreviousStatement(true);  // false implies no previous connector, the default

//键入(罕见):
this.setPreviousStatement(true, 'Action');

块输出

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

无类型:
{
  // ...,
  "output": null,
}

类型:
{
  // ...,
  "output": "Number",
}
//无类型:
init: function() {
  // ...
  this.setOutput(true);
}

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

具有输出连接器的块也不能具有先前的语句缺口。

块输入

块具有一个或多个输入,其中每个输入是可以在连接中结束的标签和字段序列。有三种类型的输入,匹配连接类型:

  • 值输入:连接到一个输出连接一个的 值块。阿math_arithmetic块(加法,减法)是具有两个值输入一个块的一个例子。
  • 声明输入:连接到 先前连接一个的语句块。while循环的嵌套部分是语句输入的示例。
  • 虚拟输入:没有块连接。当块配置为使用外部值输入时,类似于换行符。

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

JSON中的输入和字段

JSON定义块被构造为插消息字符串的序列(message0,message1,...),其中每一内插标记(%1,%2,...)是一个字段或一输入端(因此在输入连接器呈现,消息内)在匹配的JSON argsN数组中。此格式旨在简化国际化。

{
  "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自动更改字段的顺序,创建虚拟输入,并从外部输入切换到内部输入。

  1. ARGS 每个消息字符串与args相同数字的数组配对。例如,message0顺其自在args0。插值标记(%1,, %2......)指的是args数组的项目。每个对象都有一个 type字符串。其余参数因类型而异:

    • 领域:
      • field_input
      • field_dropdown
      • field_checkbox
      • field_colour
      • field_number
      • field_angle
      • field_variable
      • field_date
      • field_label
      • field_image.
    • 输入:
      • input_value
      • input_statement
      • input_dummy

    每个对象也可能有一个alt字段。在Blockly无法识别对象的情况下type,则alt在该位置使用该对象。例如,如果将一个名为new的字段field_time添加到Blockly,则使用此字段的块可用于为旧版本的Blockly alt定义field_input回退:

     {
     "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"(虚拟输入添加)示例 。

  2. lastDummyAlign0

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

    在下面的例子message0是"send email to %1 subject %2 secure %3" 和Blockly自动添加用于第三行的伪输入。设置 lastDummyAlign0为"RIGHT"强制此行右对齐。

    在设计RTL(阿拉伯语和希伯来语)块时,左右颠倒。因此"RIGHT"会将字段对齐到左侧。

  3. message1, args1, lastDummyAlign1

    一些块自然地分成两个或更多个单独的部分。考虑这个有两行的重复块:

    如果使用单个消息描述此块,则message0属性为"repeat %1 times %2 do %3"。这个字符串对于翻译来说很尴尬,很难解释%2替换意味着什么。该%2虚拟输入也不会甚至在一些语言需要的。并且可能有多个块希望共享第二行的文本。更好的方法是让JSON使用多个消息和args属性:

     {
     "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格式定义任意数量的message,args和lastDummyAlign属性,从0开始并按顺序递增。请注意,块工厂无法将消息拆分为多个部分,但手动执行此操作非常简单。

JavaScript中的输入和字段

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

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

每个方法都可以使用代码生成器使用的标识符字符串。虚拟输入很少需要引用,标识符通常不设置。

如上所示,每个方法都通过方法链返回输入对象以进行配置。有三个功能用于配置输入。

  1. setCheck

     input.setCheck('Number');

    此可选功能用于连接输入的类型检查。如果给定参数null(默认值),则此输入可以连接到任何块。有关详细信息,请参阅《4.11类型检查》。

  2. setAlign

     input.setAlign(Blockly.ALIGN_RIGHT);

    此可选功能用于对齐字段(见下文)。存在可以作为参数传递给该功能被传入三个自描述值: Blockly.ALIGN_LEFT,Blockly.ALIGN_RIGHT,和Blockly.ALIGN_CENTRE。注意英国拼写的“中心”。左对齐是默认值。

    在设计RTL(阿拉伯语和希伯来语)块时,左右颠倒。因此Blockly.ALIGN_RIGHT会将字段对齐到左侧。

  3. appendField

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

     input.appendField('hello');

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

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

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

    该appendField('hello')调用实际上是使用显式FieldLabel构造函数的快捷方式:appendField(new Blockly.FieldLabel('hello'))。人们希望使用构造函数的唯一时间是指定类名,以便可以使用CSS规则对文本进行样式设置。

内联与外部

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

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

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

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

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

字段

字段定义块中的UI元素。这些包括字符串标签,图像和文字数据的输入, 如字符串和数字。最简单的例子是math_number块,可以是field_input(web)或field_number(Android)来键入数字。

Blockly提供了许多内置字段,包括文本输入,颜色选择器和图像。您还可以创建自己的字段,详情参阅《4.14字段》

提示

当用户将鼠标悬停在块上时,工具提示提供即时帮助。如果文本很长,它将自动换行。

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

在JavaScript API中,工具提示也可以定义为函数而不是静态字符串。这允许动态帮助。请参阅有关math_arithmetic工具提示的示例,该工具提示会根据选择的下拉选项而更改。

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示例。

帮助网址

块可以有一个与之关联的帮助页面。通过右键单击块并从上下文菜单中选择“帮助”,可以向Blockly for Web的用户使用此选项。如果此值为则null则菜单将显示为灰色。

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

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

更改监听器和验证器

块可以具有更改侦听器函数,这些函数在对工作空间的任何更改时调用(包括与块无关的那些)。这些主要用于设置块的警告文本或工作区外的类似用户通知。

通过使用函数调用setOnChange来添加该函数,如果您计划在所有平台上使用它,则可以在init期间或通过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.');
    }
  });
});
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.');
      }
    });
  }
}

系统调用该函数,传入change事件。函数内部this是指块实例。

因为在任何更改时调用该函数,如果使用,开发人员应确保侦听器快速运行。人们也应该警惕可能级联或循环回监听器的工作空间的更改。

见controls_flow_statements,logic_compare和procedures_ifreturn 为实例块。

请注意,可编辑字段具有自己的事件侦听器,用于输入验证可能会导致副作用。

赋值函数

Mutators允许高级块改变形状,最显着的是由于用户打开一个对话框来添加,删除或重新排列组件。可以通过JSON使用mutator密钥添加Mutators 。

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

每块配置

块实例具有许多属性,用于配置它们对用户的行为方式。这些可以用于约束工作空间以反映域的某些属性(例如,恰好有一个'开始'事件),或者集中用户的努力(例如,教程)。

可删除状态

默认情况下,用户可以删除可编辑工作区上的任何块(不是 readOnly)。有时,使某些块永久固定装置是有用的。例如,教程框架代码。

block.setDeletable(false);

任何块,包括标记为不可删除的块,都可以通过编程方式删除:

block.dispose();

可编辑状态

block.setEditable(false);  

设置为false时,用户将无法更改块的字段(例如,下拉列表和文本输入)。块在可编辑工作区上默认为可编辑。

可动状态

block.setMovable(false); 

设置为false时,用户将无法直接移动块。作为另一个块的子节点的不可移动块可能不会与该块断开连接,但如果移动父节点,它将与其父节点一起移动。

块默认为可在可编辑工作区上移动。

任何块(甚至是不可移动的块)一旦在工作空间中就可以以编程方式移动。在JavaScript中,调用block.moveBy(dx, dy)。除非另行指定,否则工作空间上块的起始位置默认为(0,0)。

Block数据

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

数据是附加到块的可选和任意字符串。当保存为XML时,数据字符串存储在标记中,以便可以将其往返回到块。使用数据字符串将块与外部资源相关联或用于任何其他自定义目的。

请注意,如果块复制或复制/粘贴,则数据字符串也会重复。不可删除的块无法复制或复制/粘贴。