Поле раскрывающегося списка сохраняет строку в качестве значения и строку в качестве текста. Значение представляет собой независимый от языка ключ, который будет использоваться для доступа к тексту и не будет переводиться при переключении Blockly между языками. Текст представляет собой удобочитаемую строку, которая будет отображаться пользователю.
Раскрывающееся поле
Раскрывающееся поле с открытым редактором
Раскрывающееся поле в свернутом блоке
Создание
Конструктор раскрывающегося списка включает в себя генератор меню и дополнительный валидатор . Генератор меню обладает большой гибкостью, но по сути представляет собой массив параметров, каждый из которых содержит удобочитаемую часть и независимую от языка строку.
Простые раскрывающиеся текстовые списки
JSON
{
"type": "example_dropdown",
"message0": "drop down: %1",
"args0": [
{
"type": "field_dropdown",
"name": "FIELDNAME",
"options": [
[ "first item", "ITEM1" ],
[ "second item", "ITEM2" ]
]
}
]
}
JavaScript
Blockly.Blocks['example_dropdown'] = {
init: function() {
this.appendDummyInput()
.appendField('drop down:')
.appendField(new Blockly.FieldDropdown([
['first item', 'ITEM1'],
['second item', 'ITEM2']
]), 'FIELDNAME');
}
};
Отделение удобочитаемой информации от независимого от языка ключа позволяет сохранять настройки раскрывающегося меню между языками. Например, английская версия блока может определять [['left', 'LEFT'], ['right', 'RIGHT]]
тогда как немецкая версия того же блока будет определять [['links', 'LEFT'], ['rechts', 'RIGHT]]
.
Раскрывающиеся списки изображений
Опции в раскрывающемся меню также могут представлять собой изображения вместо текста. Объекты изображения задаются свойствами src
, width
, height
и alt
.
Обратите внимание: хотя раскрывающийся список может содержать как текстовые параметры, так и параметры изображения, отдельный параметр в настоящее время не может содержать одновременно изображение и текст.
JSON
{
"type": "image_dropdown",
"message0": "flag %1",
"args0": [
{
"type": "field_dropdown",
"name": "FLAG",
"options": [
["none", "NONE"],
[{"src": "canada.png", "width": 50, "height": 25, "alt": "Canada"}, "CANADA"],
[{"src": "usa.png", "width": 50, "height": 25, "alt": "USA"}, "USA"],
[{"src": "mexico.png", "width": 50, "height": 25, "alt": "Mexico"}, "MEXICO"]
]
}
]
}
JavaScript
Blockly.Blocks['image_dropdown'] = {
init: function() {
var input = this.appendDummyInput()
.appendField('flag');
var options = [
['none', 'NONE'],
[{'src': 'canada.png', 'width': 50, 'height': 25, 'alt': 'Canada'}, 'CANADA'],
[{'src': 'usa.png', 'width': 50, 'height': 25, 'alt': 'USA'}, 'USA'],
[{'src': 'mexico.png', 'width': 50, 'height': 25, 'alt': 'Mexico'}, 'MEXICO']
];
input.appendField(new Blockly.FieldDropdown(options), 'FLAG');
}
};
Динамические раскрывающиеся списки
JSON
{
"type": "dynamic_dropdown",
"message0": "day %1",
"args0": [
{
"type": "input_dummy",
"name": "INPUT"
}
],
"extensions": ["dynamic_menu_extension"]
}
Blockly.Extensions.register('dynamic_menu_extension',
function() {
this.getInput('INPUT')
.appendField(new Blockly.FieldDropdown(
function() {
var options = [];
var now = Date.now();
for(var i = 0; i < 7; i++) {
var dateString = String(new Date(now)).substring(0, 3);
options.push([dateString, dateString.toUpperCase()]);
now += 24 * 60 * 60 * 1000;
}
return options;
}), 'DAY');
});
Это делается с помощью расширения JSON.
JavaScript
Blockly.Blocks['dynamic_dropdown'] = {
init: function() {
var input = this.appendDummyInput()
.appendField('day')
.appendField(new Blockly.FieldDropdown(
this.generateOptions), 'DAY');
},
generateOptions: function() {
var options = [];
var now = Date.now();
for(var i = 0; i < 7; i++) {
var dateString = String(new Date(now)).substring(0, 3);
options.push([dateString, dateString.toUpperCase()]);
now += 24 * 60 * 60 * 1000;
}
return options;
}
};
Раскрывающийся список также может быть снабжен функцией вместо списка статических параметров, что позволяет сделать параметры динамическими. Функция должна возвращать массив параметров в том же формате [human-readable-value, language-neutral-key]
что и статические параметры. Каждый раз, когда щелкают раскрывающийся список, запускается функция и параметры пересчитываются.
Сериализация
JSON
JSON для раскрывающегося поля выглядит так:
{
"fields": {
"FIELDNAME": "LANGUAGE-NEUTRAL-KEY"
}
}
Где FIELDNAME
— это строка, ссылающаяся на раскрывающееся поле, а значение — это значение, которое следует применить к полю. Значение должно быть независимым от языка ключом опции.
XML
XML для раскрывающегося поля выглядит так:
<field name="FIELDNAME">LANGUAGE-NEUTRAL-KEY</field>
Где атрибут name
поля содержит строку, ссылающуюся на раскрывающееся поле, а внутренний текст — это значение, которое следует применить к полю. Внутренний текст должен представлять собой допустимый ключ опции, не зависящий от языка.
Кастомизация
Стрелка раскрывающегося списка
Свойство Blockly.FieldDropdown.ARROW_CHAR
можно использовать для изменения символа Юникода, представляющего стрелку раскрывающегося списка.
Свойство ARROW_CHAR
по умолчанию имеет значение \u25BC
(▼) на Android и \u25BE
(▾) в противном случае.
Это глобальное свойство, поэтому при его установке будут изменяться все поля раскрывающегося списка.
Высота меню
Свойство Blockly.FieldDropdown.MAX_MENU_HEIGHT_VH
можно использовать для изменения максимальной высоты меню. Он определяется как процент от высоты области просмотра, при этом область просмотра является окном.
Свойство MAX_MENU_HEIGHT_VH
по умолчанию равно 0,45.
Это глобальное свойство, поэтому при его установке будут изменяться все поля раскрывающегося списка.
Соответствие префикса/суффикса
Если все параметры раскрывающегося меню имеют общий префикс и/или суффикс, эти слова автоматически выделяются и вставляются как статический текст. Например, вот два способа создать один и тот же блок (первый без сопоставления суффиксов, а второй с):
Без сопоставления суффиксов:
JSON
{
"type": "dropdown_no_matching",
"message0": "hello %1",
"args0": [
{
"type": "field_dropdown",
"name": "MODE",
"options": [
["world", "WORLD"],
["computer", "CPU"]
]
}
]
}
JavaScript
Blockly.Blocks['dropdown_no_matching'] = {
init: function() {
var options = [
['world', 'WORLD'],
['computer', 'CPU']
];
this.appendDummyInput()
.appendField('hello')
.appendField(new Blockly.FieldDropdown(options), 'MODE');
}
};
С соответствием суффикса:
JSON
{
"type": "dropdown_with_matching",
"message0": "%1",
"args0": [
{
"type": "field_dropdown",
"name": "MODE",
"options": [
["hello world", "WORLD"],
["hello computer", "CPU"]
]
}
]
}
JavaScript
Blockly.Blocks['dropdown_with_matching'] = {
init: function() {
var options = [
['hello world', 'WORLD'],
['hello computer', 'CPU']
];
this.appendDummyInput()
.appendField(new Blockly.FieldDropdown(options), 'MODE');
}
};
Одним из преимуществ этого подхода является то, что блок легче перевести на другие языки. В более раннем коде есть строки 'hello'
, 'world'
и 'computer'
, тогда как в обновленном коде есть строки 'hello world'
и 'hello computer'
. Переводчикам гораздо легче переводить фразы, чем отдельные слова.
Еще одним преимуществом этого подхода является то, что порядок слов часто меняется в разных языках. Представьте себе язык, в котором используются 'world hello'
и 'computer hello'
. Алгоритм сопоставления суффиксов обнаружит обычное 'hello'
и отобразит его после раскрывающегося списка.
Однако иногда сопоставление префикса и суффикса не удается. В некоторых случаях два слова всегда должны идти вместе, и префикс не следует исключать. Например 'drive red car'
и 'drive red truck'
возможно, следует исключать только 'drive'
, а не 'drive red'
. Неразрывный пробел Юникода '\u00A0'
можно использовать вместо обычного пробела для подавления сопоставления префикса и суффикса. Таким образом, приведенный выше пример можно исправить с помощью 'drive red\u00A0car'
и 'drive red\u00A0truck'
.
Еще одно место, где не удается выполнить сопоставление префикса и суффикса, — это языки, в которых отдельные слова не разделяются пробелами. Китайский язык является хорошим примером. Строка '訪問中國'
означает 'visit China'
, обратите внимание на отсутствие пробелов между словами. В совокупности последние два символа ( '中國'
) обозначают 'China'
, но если разделить их, они будут означать 'centre'
и 'country'
соответственно. Чтобы сопоставление префиксов и суффиксов работало в таких языках, как китайский, просто вставьте пробел там, где должен быть разрыв. Например '訪問 中國'
и '訪問 美國'
приведут к "visit [China/USA]"
, тогда как '訪問 中 國'
и '訪問 美 國'
приведут к "visit [centre/beautiful] country"
.
Создание выпадающего валидатора
Значение раскрывающегося поля представляет собой независимую от языка строку, поэтому любые валидаторы должны принимать строку и возвращать строку , которая является доступной опцией , null
или undefined
.
Если ваш валидатор возвращает что-нибудь еще, поведение Blockly не определено и ваша программа может выйти из строя.
Например, вы можете определить раскрывающееся поле с тремя параметрами и валидатором следующим образом:
validate: function(newValue) {
this.getSourceBlock().updateConnections(newValue);
return newValue;
},
init: function() {
var options = [
['has neither', 'NEITHER'],
['has statement', 'STATEMENT'],
['has value', 'VALUE'],
];
this.appendDummyInput()
// Pass the field constructor the options list, the validator, and the name.
.appendField(new Blockly.FieldDropdown(options, this.validate), 'MODE');
}
validate
всегда возвращает переданное значение, но вызывает вспомогательную функцию updateConnection
, которая добавляет или удаляет входные данные на основе значения раскрывающегося списка:
updateConnections: function(newValue) {
this.removeInput('STATEMENT', /* no error */ true);
this.removeInput('VALUE', /* no error */ true);
if (newValue == 'STATEMENT') {
this.appendStatementInput('STATEMENT');
} else if (newValue == 'VALUE') {
this.appendValueInput('VALUE');
}
}