工具箱是用户获取积木的位置。通常显示在工作区的一侧。有时包含类别,有时则没有。
本页主要重点介绍如何指定工具箱的结构(即工具箱包含哪些类别以及包含哪些块)。如需详细了解如何更改工具箱的界面,请查看“自定义 Blockly 工具箱”Codelab 和 2021 年 Toolbox API 讲座。
表现形式
Blockly 可让您使用几种不同的格式指定工具箱的结构。新建议的格式使用 JSON,旧格式则使用 XML。
您可以通过以下不同方式指定上述工具箱:
JSON
从 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
<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>
XML 字符串
var toolbox = '<xml>' +
'<block type="controls_if"></block>' +
'<block type="controls_whileUntil"></block>' +
'</xml>';
var workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox});
类别
工具箱中的组成要素可以按类别进行整理。
您可以通过以下方法定义上述工具箱,该工具箱包含两个类别(“Control”和“Logic”),每个类别都包含块:
JSON
{
"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
<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>
嵌套类别
类别可以嵌套在其他类别中。下面是两个顶级类别(“核心”和“自定义”),第二个类别包含两个子类别,每个子类别都包含块:
请注意,一个类别可以同时包含子类别和屏蔽。在上面的示例中,“自定义”有两个子类别(“移动”和“回合”),以及自己的一个子类别(“开始”)。
JSON
{
"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
<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。
另请注意,该函数是作为参数提供的目标工作区,因此动态类别中的块可以根据工作区的状态。
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);
旧 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', // 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);
XML
// 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
属性,使类别变为动态形式。
JSON
{
"kind": "category",
"name": "Colours",
"custom": "COLOUR_PALETTE"
}
XML
<category name="Colours" custom="COLOUR_PALETTE"></category>
内置动态类别
Blockly 提供三种动态类别。
'VARIABLE'
可为非类型变量创建一个类别。'VARIABLE_DYNAMIC'
用于为类型化变量创建一个类别。它包含用于创建字符串、数字和颜色的按钮。'PROCEDURE'
用于为函数块创建一个类别。
JSON
{
"kind": "category",
"name": "Variables",
"custom": "VARIABLE"
},
{
"kind": "category",
"name": "Variables",
"custom": "VARIABLE_DYNAMIC"
},
{
"kind": "category",
"name": "Functions",
"custom": "PROCEDURE"
}
XML
<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 显示。
JSON
{
"kind": "category",
"name": "...",
"hidden": "true"
}
XML
<category name="..." hidden="true"></category>
JavaScript
var category = toolbox.getToolboxItems()[0];
category.hide();
// etc...
category.show();
正在展开
这仅适用于包含其他嵌套类别的类别。
展开后的类别会显示其子类别。默认情况下,嵌套类别处于收起状态,需要点击才能展开。
JSON
{
"kind": "category",
"name": "...",
"expanded": "true"
}
XML
<category name="..." expanded="true"></sep>
样式
Blockly 提供了一个默认的类别界面,并为其提供了一些基本的样式设置选项。如果您想了解如何对界面进行更高级的样式设置/配置,请参阅“自定义 Blockly 工具箱”Codelab 和 2021 年 Toolbox API 讲座。
主题
借助主题,您可以一次性指定工作区的所有颜色,包括类别的颜色。
要使用它们,您必须将类别与特定类别样式相关联:
JSON
{
"kind": "category",
"name": "Logic",
"categorystyle": "logic_category"
}
XML
<category name="Logic" categorystyle="logic_category"></category>
颜色
您也可以直接指定颜色,但不建议这样做。颜色是一个指定色调的字符串化数字 (0-360)。请注意英式拼写。
JSON
{
"contents": [
{
"kind": "category",
"name": "Logic",
"colour": "210"
},
{
"kind": "category",
"name": "Loops",
"colour": "120"
}
]
}
XML
<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
。
以下是使用任一格式指定类的方法:
JSON
使用 cssConfig 属性设置特定元素类型的 CSS 类。
{
"kind": "category",
"name": "...",
"cssConfig": {
"container": "yourClassName"
}
}
XML
设置特定元素类型的 CSS 类,方法是在其前面加上“css-”。
<category name="..." css-container="yourClassName"></category>
访问
您可以通过两种方法以编程方式访问类别。您可以通过索引对其进行访问(其中 0 表示顶级类别):
var category = toolbox.getToolboxItems()[0];
或按 ID:
var category = toolbox.getToolboxItemById('categoryId');
在工具箱定义中指定 ID 的位置:
JSON
{
"kind": "category",
"name": "...",
"toolboxitemid": "categoryId"
}
XML
<category name="..." toolboxitemid="categoryId"></category>
预设块
工具箱定义中可能包含将字段设置为默认值的块,或者包含已经连接在一起的块。
以下是四个代码块:
- 不含预设值的简单
logic_boolean
代码块:
math_number
块,已修改为显示数字 42(而不是默认值 0):
- 与三个
math_number
块连接的controls_for
块:
- 具有两个与之相连的
math_number
影子块的math_arithmetic
块:
下面是一个包含这四个块的工具箱定义:
JSON
从 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
}
}
}
}
},
]
}
旧 JSON
在 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
<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>
手动写出这些定义可能有点麻烦。不过,您可以将块加载到工作区中,然后运行以下代码来获取定义。这些调用之所以有效,是因为工具箱使用与序列化系统相同的块格式。
JSON
console.log(Blockly.serialization.workspaces.save(Blockly.getMainWorkspace()));
XML
console.log(Blockly.Xml.workspaceToDom(Blockly.getMainWorkspace()));
您还可以移除 x
、y
和 id
属性,因为工具箱会忽略这些属性。
影子方块
Shadow 块是执行多种功能的占位符块:
- 指明其父级块的默认值。
- 它们可让用户直接输入值,而无需提取数字或字符串块。
- 与普通方块不同,如果用户在其上方掉落的方块,主方块就会被替换。
- 用于告知用户预期值的类型。
已停用的屏蔽设置
已停用的图块无法从工具箱中拖动。您可以使用可选的 disabled
属性单独停用各个块。
JSON
{
"kind": "flyoutToolbox",
"contents": [
{
"kind": "block",
"type":"math_number"
},
{
"kind": "block",
"type": "math_arithmetic"
},
{
"kind": "block",
"type": "math_single",
"disabled": "true"
}
]
}
XML
<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 工具箱定义中更改分隔符的类。
JSON
{
"kind": "sep",
"cssConfig": {
"container": "yourClassName"
}
}
XML
<sep css-container="yourClassName"></sep>
在任意两个块之间添加分隔符会在各个块之间形成间隙。 默认情况下,每个块与其下相邻块之间相隔 24 像素。可以使用“gap”属性更改这种分隔,该属性将替换默认的间隔。
这样,您就可以在工具箱中创建块的逻辑组。
JSON
{
"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
<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>
按钮和标签
您可以将按钮或标签放置在工具箱中能够放置砌块的任何位置。
JSON
{
"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
<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 定义的块列表、节点树或字符串表示形式。
请注意,此时更新工具箱会导致一些细微的界面重置:
- 在没有类别的工具箱中,用户更改的所有字段(如下拉菜单)都将恢复为默认设置。
此处是包含类别和屏蔽组的树的实时演示。