Панель инструментов — это место, где пользователи получают блоки. Обычно он отображается на одной стороне рабочей области. Иногда у него есть категории, иногда нет.
На этой странице основное внимание уделяется тому, как указать структуру вашего набора инструментов (т. е. какие категории он имеет и какие блоки они содержат). Если вам нужна дополнительная информация о том, как изменить пользовательский интерфейс вашего набора инструментов, ознакомьтесь с лабораторной работой по настройке кода набора инструментов Blockly и беседой об API-интерфейсах Toolbox 2021 .
Форматы
Blockly позволяет вам указать структуру вашего набора инструментов, используя несколько различных форматов. В новом рекомендуемом формате используется JSON, а в старом формате — XML.
Вот различные способы указания вышеуказанного набора инструментов:
JSON
Начиная с выпуска за сентябрь 2020 года наборы инструментов можно определять с помощью 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});
Категории
Блоки на панели инструментов могут быть организованы по категориям.
Вот способы определения вышеуказанного набора инструментов, который имеет две категории («Управление» и «Логика»), каждая из которых содержит блоки:
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 г., вы можете указывать состояние блоков без использования '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 г. вам приходилось использовать свойство '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 и беседой об API-интерфейсах Toolbox 2021 .
Темы
Темы позволяют вам указать все цвета вашего рабочего пространства одновременно, включая цвета наших категорий.
Чтобы использовать их, вам необходимо связать свою категорию с определенным стилем категории:
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:
- контейнер — класс родительского элемента div для категории.
blocklyToolboxCategory
по умолчанию. - row — класс элемента div, содержащий метку и значок категории.
blocklyTreeRow
по умолчанию. - icon — класс значка категории.
blocklyTreeIcon
по умолчанию. - label — класс метки категории.
blocklyTreeLabel
по умолчанию. - selected — класс, который добавляется в категорию при ее выборе. По умолчанию
blocklyTreeSelected
. - openicon — класс, добавляемый к значку, когда категория имеет вложенные категории и открыта. По умолчанию
blocklyTreeIconOpen
. - Closedicon — класс, добавляемый к значку, когда категория имеет вложенные категории и закрыта. По умолчанию
blocklyTreeIconClosed
.
Вот как вы указываете классы, используя любой формат:
JSON
Установите класс CSS для определенного типа элемента, используя свойство cssConfig.
{
"kind": "category",
"name": "...",
"cssConfig": {
"container": "yourClassName"
}
}
XML
Установите класс CSS для определенного типа элемента, добавив к нему «css-».
<category name="..." css-container="yourClassName"></category>
Доступ
Программный доступ к категории можно получить двумя способами. Вы можете получить к нему доступ по индексу (где 0 — верхняя категория):
var category = toolbox.getToolboxItems()[0];
Или по идентификатору:
var category = toolbox.getToolboxItemById('categoryId');
Если идентификатор указан в определении панели инструментов:
JSON
{
"kind": "category",
"name": "...",
"toolboxitemid": "categoryId"
}
XML
<category name="..." toolboxitemid="categoryId"></category>
Предустановленные блоки
Определение набора инструментов может содержать блоки, поля которых имеют значения по умолчанию, или блоки, которые уже соединены вместе.
Вот четыре блока:
- Простой блок
logic_boolean
без предустановленных значений: - Блок
math_number
, который был изменен для отображения числа 42 вместо значения по умолчанию 0: - Блок
controls_for
, к которому подключены три блокаmath_number
: - Блок
math_arithmetic
, к которому подключены два теневых блокаmath_number
:
Вот определение набора инструментов, содержащее эти четыре блока:
JSON
Начиная с выпуска за сентябрь 2021 г., вы можете указывать состояние блоков без использования '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 г. вам приходилось использовать свойство '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
, поскольку они игнорируются панелью инструментов.
Теневые блоки
Теневые блоки — это блоки-заполнители, выполняющие несколько функций:
- Они указывают значения по умолчанию для своего родительского блока.
- Они позволяют пользователям вводить значения напрямую, без необходимости извлечения числового или строкового блока.
- В отличие от обычного блока, они заменяются, если пользователь помещает блок поверх них.
- Они информируют пользователя о типе ожидаемого значения.
Отключенные блоки
Отключенные блоки нельзя перетащить из панели инструментов. Блоки можно отключать по отдельности, используя необязательное свойство 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, они содержат только идентификатор переменной, которую они представляют, поскольку имя и тип переменной сериализуются отдельно. Однако наборы инструментов не содержат этой информации, поэтому ее необходимо включить непосредственно в поле переменной.
{
"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 пикселя. Это разделение можно изменить с помощью атрибута «пробел», который заменит пробел по умолчанию.
Это позволяет создавать логические группы блоков в наборе инструментов.
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).
Ваша функция должна принимать в качестве аргумента кнопку, на которую была нажата кнопка. Кнопка «Создать переменную...» в категории переменных — хороший пример кнопки с обратным вызовом.
Изменение панели инструментов
Приложение может изменить блоки, доступные в панели инструментов, в любое время с помощью одного вызова функции:
workspace.updateToolbox(newTree);
Как и во время первоначальной настройки, newTree
может быть деревом узлов, строковым представлением или объектом JSON. Единственное ограничение состоит в том, что режим нельзя изменить; то есть, если в первоначально определенном наборе инструментов были категории, то новый набор инструментов также должен иметь категории (хотя категории могут меняться). Аналогично, если изначально определенный набор инструментов не имел категорий, то и новый набор инструментов может не иметь категорий.
Содержимое одной категории можно обновить следующим образом:
var category = workspace.getToolbox().getToolboxItems()[0];
category.updateFlyoutContents(flyoutContents);
Где FlyoutContents может быть списком блоков, определенных с использованием JSON, дерева узлов или строкового представления.
Имейте в виду, что в настоящее время обновление набора инструментов приводит к некоторым незначительным сбросам пользовательского интерфейса:
- В наборе инструментов без категорий все поля, измененные пользователем (например, раскрывающийся список), вернутся к значениям по умолчанию.
Вот живая демонстрация дерева с категориями и группами блоков.