工具箱

工具箱是用户获取积木的位置。通常显示在工作区的一侧。有时包含类别,有时则没有。

本页主要重点介绍如何指定工具箱的结构(即工具箱包含哪些类别以及包含哪些块)。如需详细了解如何更改工具箱的界面,请查看“自定义 Blockly 工具箱”Codelab2021 年 Toolbox API 讲座

表现形式

Blockly 可让您使用几种不同的格式指定工具箱的结构。新建议的格式使用 JSON,旧格式则使用 XML。

您可以通过以下不同方式指定上述工具箱:

2020 年 9 月的版本开始,可以使用 JSON 定义工具箱。

var toolbox = {
   
"kind": "flyoutToolbox",
   
"contents": [
     
{
       
"kind": "block",
       
"type": "controls_if"
     
},
     
{
       
"kind": "block",
       
"type": "controls_whileUntil"
     
}
   
]
 
};
var workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox});
<xml id="toolbox" style="display: none">
 
<block type="controls_if"></block>
 
<block type="controls_whileUntil"></block>
</xml>
<script>
 
var workspace = Blockly.inject('blocklyDiv',
     
{toolbox: document.getElementById('toolbox')});
</script>
var toolbox = '<xml>' +
   
'<block type="controls_if"></block>' +
   
'<block type="controls_whileUntil"></block>' +
   
'</xml>';
var workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox});

类别

工具箱中的组成要素可以按类别进行整理。

您可以通过以下方法定义上述工具箱,该工具箱包含两个类别(“Control”和“Logic”),每个类别都包含块:

JSONXML
{
 
"kind": "categoryToolbox",
 
"contents": [
   
{
     
"kind": "category",
     
"name": "Control",
     
"contents": [
       
{
         
"kind": "block",
         
"type": "controls_if"
       
},
     
]
   
},
   
{
     
"kind": "category",
     
"name": "Logic",
     
"contents": [
       
{
         
"kind": "block",
         
"type": "logic_compare"
       
},
       
{
         
"kind": "block",
         
"type": "logic_operation"
       
},
       
{
         
"kind": "block",
         
"type": "logic_boolean"
       
}
     
]
   
}
 
]
}
<xml id="toolbox" style="display: none">
 
<category name="Control">
   
<block type="controls_if"></block>
 
<category name="Logic">
   
<block type="logic_compare"></block>
   
<block type="logic_operation"></block>
   
<block type="logic_boolean"></block>
 
</category>
</xml>

嵌套类别

类别可以嵌套在其他类别中。下面是两个顶级类别(“核心”和“自定义”),第二个类别包含两个子类别,每个子类别都包含块:

请注意,一个类别可以同时包含子类别和屏蔽。在上面的示例中,“自定义”有两个子类别(“移动”和“回合”),以及自己的一个子类别(“开始”)。

JSONXML
{
 
"kind": "categoryToolbox",
 
"contents": [
   
{
     
"kind": "category",
     
"name": "Core",
     
"contents": [
       
{
         
"kind": "block",
         
"type": "controls_if"
       
},
       
{
         
"kind": "block",
         
"type": "logic_compare"
       
},
     
]
   
},
   
{
     
"kind": "category",
     
"name": "Custom",
     
"contents": [
       
{
         
"kind": "block",
         
"type": "start"
       
},
       
{
         
"kind": "category",
         
"name": "Move",
         
"contents": [
           
{
             
"kind": "block",
             
"type": "move_forward"
           
}
         
]
       
},
       
{
         
"kind": "category",
         
"name": "Turn",
         
"contents": [
           
{
             
"kind": "block",
             
"type": "turn_left"
           
}
         
]
       
}
     
]
   
}
 
]
}
<xml id="toolbox" style="display: none">
 
<category name="Core">
   
<block type="controls_if"></block>
   
<block type="logic_compare"></block>
 
</category>
 
<category name="Custom">
   
<block type="start"></block>
   
<category name="Move">
     
<block type="move_forward"></block>
   
</category>
   
<category name="Turn">
     
<block type="turn_left"></block>
   
</category>
 
</category>
</xml>

动态类别

动态类别是指在每次打开时根据函数动态重新填充的类别。

Blockly 允许您通过注册的字符串键将类别与函数相关联,从而支持此功能。该函数应返回相应类别内容(包括块、按钮、标签等)的定义。内容可以指定为 JSON 或 XML,但建议使用 JSON。

另请注意,该函数是作为参数提供的目标工作区,因此动态类别中的块可以根据工作区的状态。

从 2021 年 9 月的版本开始,您可以在不使用 'blockxml' 的情况下指定块的状态。

// Returns an array of objects.
var coloursFlyoutCallback = function(workspace) {
 
// Returns an array of hex colours, e.g. ['#4286f4', '#ef0447']
 
var colourList = getPalette();
 
var blockList = [];
 
for (var i = 0; i < colourList.length; i++) {
    blockList
.push({
     
'kind': 'block',
     
'type': 'colour_picker',
     
'fields': {
       
'COLOUR': colourList[i]
     
}
   
});
 
}
 
return blockList;
};

// Associates the function with the string 'COLOUR_PALETTE'
myWorkspace
.registerToolboxCategoryCallback(
   
'COLOUR_PALETTE', coloursFlyoutCallback);

在 2021 年 9 月的版本之前,您必须使用 'blockxml' 属性指定块的状态。

// Returns an array of objects.
var coloursFlyoutCallback = function(workspace) {
 
// Returns an array of hex colours, e.g. ['#4286f4', '#ef0447']
 
var colourList = getPalette();
 
var blockList = [];
 
for (var i = 0; i < colourList.length; i++) {
    blockList
.push({
     
'kind': 'block',
     
'type': 'colour_picker', // Type is optional if you provide blockxml
     
'blockxml': '<block type="colour_picker">' +
         
'<field name="COLOUR">' + colourList[i] + '</field>' +
         
'</block>'
   
});
 
}
 
return blockList;
};

// Associates the function with the string 'COLOUR_PALETTE'
myWorkspace
.registerToolboxCategoryCallback(
   
'COLOUR_PALETTE', coloursFlyoutCallback);
// Returns an arry of XML nodes.
var coloursFlyoutCallback = function(workspace) {
 
// Returns an array of hex colours, e.g. ['#4286f4', '#ef0447']
 
var colourList = getPalette();
 
var blockList = [];
 
for (var i = 0; i < colourList.length; i++) {
   
var block = document.createElement('block');
    block
.setAttribute('type', 'colour_picker');
   
var field = document.createElement('field');
    field
.setAttribute('name', 'COLOUR');
    field
.innerText = colourList[i];
    block
.appendChild(field);
    blockList
.push(block);
 
}
 
return blockList;
};

// Associates the function with the string 'COLOUR_PALETTE'
myWorkspace
.registerToolboxCategoryCallback(
   
'COLOUR_PALETTE', coloursFlyoutCallback);

将动态类别函数与字符串键(也称为已注册)关联后,您可以将此字符串键分配给类别定义的 custom 属性,使类别变为动态形式。

JSONXML
{
 
"kind": "category",
 
"name": "Colours",
 
"custom": "COLOUR_PALETTE"
}
<category name="Colours" custom="COLOUR_PALETTE"></category>

内置动态类别

Blockly 提供三种动态类别。

  • 'VARIABLE' 可为非类型变量创建一个类别。
  • 'VARIABLE_DYNAMIC' 用于为类型化变量创建一个类别。它包含用于创建字符串、数字和颜色的按钮。
  • 'PROCEDURE' 用于为函数块创建一个类别。

JSONXML
{
 
"kind": "category",
 
"name": "Variables",
 
"custom": "VARIABLE"
},
{
 
"kind": "category",
 
"name": "Variables",
 
"custom": "VARIABLE_DYNAMIC"
},
{
 
"kind": "category",
 
"name": "Functions",
 
"custom": "PROCEDURE"
}
<category name="Variables" custom="VARIABLE"></category>
<category name="Variables" custom="VARIABLE_DYNAMIC"></category>
<category name="Functions" custom="PROCEDURE"></category>

注意:整个 Blockly 代码库中都使用“过程”一词,但学生发现“函数”一词更容易理解。抱歉,信息不一致。

正在停用

停用的类别不允许用户打开该类别,并且在键盘导航期间系统会跳过该类别。

var category = toolbox.getToolboxItems()[0];
category
.setDisabled('true');

停用类别后,系统会在 DOM 元素中添加 'disabled' 属性,以便您控制已停用类别的外观。

.blocklyToolboxCategory[disabled="true"] {
 
opacity: .5;
}

正在隐藏

隐藏的类别不会在工具箱中显示。隐藏的类别稍后可通过 JavaScript 显示。

{
 
"kind": "category",
 
"name": "...",
 
"hidden": "true"
}
<category name="..." hidden="true"></category>
var category = toolbox.getToolboxItems()[0];
category
.hide();
// etc...
category
.show();

正在展开

这仅适用于包含其他嵌套类别的类别。

展开后的类别会显示其子类别。默认情况下,嵌套类别处于收起状态,需要点击才能展开。

JSONXML
{
 
"kind": "category",
 
"name": "...",
 
"expanded": "true"
}
<category name="..." expanded="true"></sep>

样式

Blockly 提供了一个默认的类别界面,并为其提供了一些基本的样式设置选项。如果您想了解如何对界面进行更高级的样式设置/配置,请参阅“自定义 Blockly 工具箱”Codelab2021 年 Toolbox API 讲座

主题

借助主题,您可以一次性指定工作区的所有颜色,包括类别的颜色。

要使用它们,您必须将类别与特定类别样式相关联:

JSONXML
{
 
"kind": "category",
 
"name": "Logic",
 
"categorystyle": "logic_category"
}
<category name="Logic" categorystyle="logic_category"></category>

颜色

您也可以直接指定颜色,但不建议这样做。颜色是一个指定色调的字符串化数字 (0-360)。请注意英式拼写。

JSONXML
{
 
"contents": [
   
{
     
"kind": "category",
     
"name": "Logic",
     
"colour": "210"
   
},
   
{
     
"kind": "category",
     
"name": "Loops",
     
"colour": "120"
   
}
 
]
}
<xml id="toolbox" style="display: none">
 
<category name="Logic" colour="210">...</category>
 
<category name="Loops" colour="120">...</category>
 
<category name="Math" colour="230">...</category>
 
<category name="Colour" colour="20">...</category>
 
<category name="Variables" colour="330" custom="VARIABLE"></category>
 
<category name="Functions" colour="290" custom="PROCEDURE"></category>
</xml>

请注意,我们还支持使用可本地化的颜色引用

类别 CSS

如果您需要更强大的自定义功能,Blockly 还允许您为默认界面的不同元素指定 CSS 类。然后,您可以使用 CSS 为这些元素设置样式。

可以对以下元素类型应用 CSS 类:

  • container - 类别的父 div 的类。默认值为 blocklyToolboxCategory
  • 行 - 包含类别标签和图标的 div 的类。默认值为 blocklyTreeRow
  • icon - 类别图标的类。默认为 blocklyTreeIcon
  • label - 类别标签的类。默认为 blocklyTreeLabel
  • selected - 选择某个类别后添加至该类别的类。默认值为 blocklyTreeSelected
  • openicon - 当类别具有嵌套类别并处于打开状态时添加到图标的类。默认为 blocklyTreeIconOpen
  • closeicon - 当类别具有嵌套类别并关闭时,添加到图标的类。默认为 blocklyTreeIconClosed

以下是使用任一格式指定类的方法:

JSONXML

使用 cssConfig 属性设置特定元素类型的 CSS 类。

{
 
"kind": "category",
 
"name": "...",
 
"cssConfig": {
   
"container": "yourClassName"
 
}
}

设置特定元素类型的 CSS 类,方法是在其前面加上“css-”。

<category name="..." css-container="yourClassName"></category>

访问

您可以通过两种方法以编程方式访问类别。您可以通过索引对其进行访问(其中 0 表示顶级类别):

var category = toolbox.getToolboxItems()[0];

或按 ID:

var category = toolbox.getToolboxItemById('categoryId');

在工具箱定义中指定 ID 的位置:

JSONXML
{
 
"kind": "category",
 
"name": "...",
 
"toolboxitemid": "categoryId"
}
<category name="..." toolboxitemid="categoryId"></category>

预设块

工具箱定义中可能包含将字段设置为默认值的块,或者包含已经连接在一起的块。

以下是四个代码块:

  1. 不含预设值的简单 logic_boolean 代码块:
  2. math_number 块,已修改为显示数字 42(而不是默认值 0):
  3. 与三个 math_number 块连接的 controls_for 块:
  4. 具有两个与之相连的 math_number 影子块math_arithmetic 块:

下面是一个包含这四个块的工具箱定义:

从 2021 年 9 月的版本开始,您可以使用 'blockxml' 指定块的状态。

{
 
"kind": "flyoutToolbox",
 
"contents": [
   
{
     
"kind": "block",
     
"type": "logic_boolean"
   
},
   
{
     
"kind": "block",
     
"type": "math_number",
     
"fields": {
       
"NUM": 42
     
}
   
},
   
{
     
"kind": "block",
     
"type": "controls_for",
     
"inputs": {
       
"FROM": {
         
"block": {
           
"type": "math_number",
           
"fields": {
             
"NUM": 1
           
}
         
}
       
},
       
"TO": {
         
"block": {
           
"type": "math_number",
           
"fields": {
             
"NUM": 10
           
}
         
}
       
},
       
"BY": {
         
"block": {
           
"type": "math_number",
           
"fields": {
             
"NUM": 1
           
}
         
}
       
},
     
}
   
},
   
{
     
"kind": "block",
     
"type": "math_arithmetic",
     
"fields": {
       
"OP": "ADD"
     
},
     
"inputs": {
       
"A": {
         
"shadow": {
           
"type": "math_number",
           
"fields": {
             
"NUM": 1
           
}
         
}
       
},
       
"B": {
         
"shadow": {
           
"type": "math_number",
           
"fields": {
             
"NUM": 1
           
}
         
}
       
}
     
}
   
},
 
]
}

在 2021 年 9 月的版本之前,您必须使用 'blockxml' 属性指定块的状态。

{
 
"kind": "flyoutToolbox",
 
"contents": [
   
{
     
"kind": "block",
     
"type": "logic_boolean"
   
},
   
{
     
"kind": "block",
     
"blockxml":
         
'<block type="math_number">' +
         
'<field name="NUM">42</field>' +
         
'</block>'
   
},
   
{
     
"kind": "block",
     
"blockxml":
         
'<block type="controls_for">' +
           
'<value name="FROM">' +
             
'<block type="math_number">' +
               
'<field name="NUM">1</field>' +
             
'</block>' +
           
'</value>' +
           
'<value name="TO">' +
             
'<block type="math_number">' +
               
'<field name="NUM">10</field>' +
             
'</block>' +
           
'</value>' +
           
'<value name="BY">' +
             
'<block type="math_number">' +
               
'<field name="NUM">1</field>' +
             
'</block>' +
           
'</value>' +
         
'</block>'
   
},
   
{
     
"kind": "block",
     
"blockxml":
         
'<block type="math_arithmetic">' +
           
'<field name="OP">ADD</field>' +
           
'<value name="A">' +
             
'<shadow type="math_number">' +
               
'<field name="NUM">1</field>' +
             
'</shadow>' +
           
'</value>' +
           
'<value name="B">' +
             
'<shadow type="math_number">' +
               
'<field name="NUM">1</field>' +
             
'</shadow>' +
           
'</value>' +
         
'</block>'
   
},
 
]
}
<xml id="toolbox" style="display: none">
 
<block type="logic_boolean"></block>

 
<block type="math_number">
   
<field name="NUM">42</field>
 
</block>

 
<block type="controls_for">
   
<value name="FROM">
     
<block type="math_number">
       
<field name="NUM">1</field>
     
</block>
   
</value>
   
<value name="TO">
     
<block type="math_number">
       
<field name="NUM">10</field>
     
</block>
   
</value>
   
<value name="BY">
     
<block type="math_number">
       
<field name="NUM">1</field>
     
</block>
   
</value>
 
</block>

 
<block type="math_arithmetic">
   
<field name="OP">ADD</field>
   
<value name="A">
     
<shadow type="math_number">
       
<field name="NUM">1</field>
     
</shadow>
   
</value>
   
<value name="B">
     
<shadow type="math_number">
       
<field name="NUM">1</field>
     
</shadow>
   
</value>
 
</block>
</xml>

手动写出这些定义可能有点麻烦。不过,您可以将块加载到工作区中,然后运行以下代码来获取定义。这些调用之所以有效,是因为工具箱使用与序列化系统相同的块格式。

JSONXML
console.log(Blockly.serialization.workspaces.save(Blockly.getMainWorkspace()));
console.log(Blockly.Xml.workspaceToDom(Blockly.getMainWorkspace()));

您还可以移除 xyid 属性,因为工具箱会忽略这些属性。

影子方块

Shadow 块是执行多种功能的占位符块:

  • 指明其父级块的默认值。
  • 它们可让用户直接输入值,而无需提取数字或字符串块。
  • 与普通方块不同,如果用户在其上方掉落的方块,主方块就会被替换。
  • 用于告知用户预期值的类型。

已停用的屏蔽设置

已停用的图块无法从工具箱中拖动。您可以使用可选的 disabled 属性单独停用各个块。

JSONXML
{
 
"kind": "flyoutToolbox",
 
"contents": [
   
{
     
"kind": "block",
     
"type":"math_number"
   
},
   
{
     
"kind": "block",
     
"type": "math_arithmetic"
   
},
   
{
     
"kind": "block",
     
"type": "math_single",
     
"disabled": "true"
   
}
 
]
}
<xml id="toolbox" style="display: none">
 
<block type="math_number"></block>
 
<block type="math_arithmetic"></block>
 
<block type="math_single" disabled="true"></block>
</xml>

您还可以使用 setEnabled 以编程方式停用或启用块。

变量字段

与简单序列化的变量字段相比,在工具箱中时的变量字段可能需要以不同的方式指定。

特别是,当变量字段正常序列化为 JSON 时,它们仅包含它们所代表的变量的 ID,因为变量的名称和类型是单独序列化的。但是,工具箱不包含该信息,因此需要直接包含在变量字段中。

{
 
"kind": "flyoutToolbox",
 
"content": [
   
{
     
"type": "controls_for",
     
"fields": {
       
"VAR": {
         
"name": "index",
         
"type": "Number"
       
}
     
}
   
}
 
]
}

分隔符

在任何两个类别之间添加分隔符会在两个类别之间创建一行,并在两个类别之间添加额外的空格。

您可以在 JSON 或 XML 工具箱定义中更改分隔符的类。

JSONXML
{
 
"kind": "sep",
 
"cssConfig": {
   
"container": "yourClassName"
 
}
}
<sep css-container="yourClassName"></sep>

在任意两个块之间添加分隔符会在各个块之间形成间隙。 默认情况下,每个块与其下相邻块之间相隔 24 像素。可以使用“gap”属性更改这种分隔,该属性将替换默认的间隔。

这样,您就可以在工具箱中创建块的逻辑组。

JSONXML
{
 
"kind": "flyoutToolbox",
 
"contents": [
   
{
     
"kind": "block",
     
"type":"math_number"
   
},
   
{
     
"kind": "sep",
     
"gap": "32"
   
},
   
{
     
"kind": "block",
     
"blockxml": "<block type='math_arithmetic'><field name='OP'>ADD</field></block>"
   
},
   
{
     
"kind": "sep",
     
"gap": "8"
   
},
   
{
     
"kind": "block",
     
"blockxml": "<block type='math_arithmetic'><field name='OP'>MINUS</field></block>"
   
}
 
]
}
<xml id="toolbox" style="display: none">
 
<block type="math_number"></block>
 
<sep gap="32"></sep>
 
<block type="math_arithmetic">
   
<field name="OP">ADD</field>
 
</block>
 
<sep gap="8"></sep>
 
<block type="math_arithmetic">
   
<field name="OP">MINUS</field>
 
</block>
</xml>

按钮和标签

您可以将按钮或标签放置在工具箱中能够放置砌块的任何位置。

JSONXML
{
 
"kind": "flyoutToolbox",
 
"contents": [
   
{
     
"kind": "block",
     
"type":"logic_operation"
   
},
   
{
     
"kind": "label",
     
"text": "A label",
     
"web-class": "myLabelStyle"
   
},
   
{
     
"kind": "label",
     
"text": "Another label"
   
},
   
{
     
"kind": "block",
     
"type": "logic_negate"
   
},
   
{
     
"kind": "button",
     
"text": "A button",
     
"callbackKey": "myFirstButtonPressed"
   
},
   
{
     
"kind": "block",
     
"type": "logic_boolean"
   
}
 
]
}
<xml id="toolbox" style="display: none">
 
<block type="logic_operation"></block>
 
<label text="A label" web-class="myLabelStyle"></label>
 
<label text="Another label"></label>
 
<block type="logic_negate"></block>
 
<button text="A button" callbackKey="myFirstButtonPressed"></button>
 
<block type="logic_boolean"></block>
</xml>
    <style>
   
.myLabelStyle>.blocklyFlyoutLabelText {
     
font-style: italic;
     
fill: green;
   
}
   
</style>

您可以指定要应用于按钮或标签的 CSS 类名称。在上面的示例中,第一个标签使用自定义样式,而第二个标签使用自定义样式。

按钮应具有回调函数;标签则不应该有此类函数。如需为指定的按钮点击设置回调,请使用

yourWorkspace.registerButtonCallback(yourCallbackKey, yourFunction).

您的函数应接受所点击的按钮作为参数。变量类别中的“Create variable...”按钮就是具有回调的按钮的一个很好的例子。

更改工具箱

应用可能会随时通过一个函数调用更改工具箱中的可用块:

workspace.updateToolbox(newTree);

与初始配置期间一样,newTree 可以是节点树、字符串表示法或 JSON 对象。唯一的限制是无法更改模式;也就是说,如果最初定义的工具箱中有类别,则新工具箱也必须包含类别(不过类别可能会更改)。同样,如果最初定义的工具箱没有任何类别,则新工具箱可能没有任何类别。

单个类别的内容可通过以下方式更新:

var category = workspace.getToolbox().getToolboxItems()[0];
category
.updateFlyoutContents(flyoutContents);

其中 flyoutContents 可以是使用 JSON 定义的块列表、节点树或字符串表示形式。

请注意,此时更新工具箱会导致一些细微的界面重置:

  • 在没有类别的工具箱中,用户更改的所有字段(如下拉菜单)都将恢复为默认设置。

此处是包含类别和屏蔽组的树的实时演示