工具箱

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

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

预设块

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

以下是四个代码块:

  1. 不含预设值的简单 logic_boolean 代码块:
  2. math_number 块,已修改为显示数字 42(而不是默认值 0):
  3. 与三个 math_number 块连接的 controls_for 块:
  4. 具有两个与之相连的 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()));

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

影子方块

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 定义的块列表、节点树或字符串表示形式。

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

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

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