El campo desplegable almacena una cadena como valor y otra como texto. El valor es una clave neutral en cuanto al idioma que se usará para acceder al texto y no se traducirá cuando se cambie de idioma en Blockly. El texto es una cadena legible por humanos que se mostrará al usuario.
Campo desplegable
Campo desplegable con el editor abierto
Campo desplegable en un bloque colapsado
Creación
El constructor de menús desplegables toma un generador de menús y un validador opcional. El generador de menús tiene mucha flexibilidad, pero, en esencia, es un array de opciones, cada una de las cuales contiene una parte legible por humanos y una cadena neutral en cuanto al idioma.
Menús desplegables de texto simple
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');
}
};
Mantener la información legible por humanos separada de la clave neutral en cuanto al idioma permite que se conserve la configuración del menú desplegable entre los idiomas. Por ejemplo, una versión en inglés de un bloque puede definir [['left', 'LEFT'], ['right',
'RIGHT]]
, mientras que una versión en alemán del mismo bloque definiría [['links',
'LEFT'], ['rechts', 'RIGHT]]
.
Menús desplegables de imágenes
Las opciones de un menú desplegable también pueden ser imágenes en lugar de texto. Los objetos de imagen se especifican con las propiedades src
, width
, height
y alt
.
Ten en cuenta que, si bien un menú desplegable puede tener una combinación de opciones de texto y de imagen, una opción individual no puede contener una imagen y texto al mismo tiempo.
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');
}
};
Menús desplegables dinámicos
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');
});
Esto se hace con una extensión de 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;
}
};
También se puede proporcionar un menú desplegable con una función en lugar de una lista de opciones estáticas, lo que permite que las opciones sean dinámicas. La función debe mostrar un array de opciones en el mismo formato [human-readable-value, language-neutral-key]
que las opciones estáticas. Cada vez que se hace clic en el menú desplegable, se ejecuta la función y se vuelven a calcular las opciones.
Separadores
Usa la cadena 'separator'
para agregar una línea entre las opciones de un menú desplegable.
JSON
{
"type": "separator_dropdown",
"message0": "food %1",
"args0": [
{
"type": "field_dropdown",
"name": "FOOD",
"options": [
["water", "WATER"],
["juice", "JUICE"],
"separator",
["salad", "SALAD"],
["soup", "SOUP"],
]
}
]
}
JavaScript
Blockly.Blocks["separator_dropdown"] = {
init: function() {
var input = this.appendDummyInput()
.appendField("food1");
var options = [
["water", "WATER"],
["juice", "JUICE"],
"separator",
["salad", "SALAD"],
["soup", "SOUP"],
];
input.appendField(new Blockly.FieldDropdown(options), "FOOD");
}
};
Serialización
JSON
El JSON de un campo desplegable se ve de la siguiente manera:
{
"fields": {
"FIELDNAME": "LANGUAGE-NEUTRAL-KEY"
}
}
En el que FIELDNAME
es una cadena que hace referencia a un campo desplegable y el valor es el valor que se aplicará al campo. El valor debe ser una clave de opción independiente del idioma.
XML
El XML de un campo desplegable se ve de la siguiente manera:
<field name="FIELDNAME">LANGUAGE-NEUTRAL-KEY</field>
En el que el atributo name
del campo contiene una cadena que hace referencia a un campo desplegable y el texto interno es el valor que se aplicará al campo. El texto interno debe ser una clave de opción válida sin idioma.
Personalización
Flecha del menú desplegable
La propiedad Blockly.FieldDropdown.ARROW_CHAR
se puede usar para cambiar el carácter Unicode que representa la flecha del menú desplegable.
El valor predeterminado de la propiedad ARROW_CHAR
es \u25BC
(▼) en Android y \u25BE
(▾) en otros casos.
Esta es una propiedad global, por lo que modificará todos los campos del menú desplegable cuando se establezca.
Altura del menú
La propiedad Blockly.FieldDropdown.MAX_MENU_HEIGHT_VH
se puede usar para cambiar la altura máxima del menú. Se define como un porcentaje de la altura del viewport, siendo la ventana el viewport.
El valor predeterminado de la propiedad MAX_MENU_HEIGHT_VH
es 0.45.
Esta es una propiedad global, por lo que modificará todos los campos del menú desplegable cuando se establezca.
Coincidencia de prefijos o sufijos
Si todas las opciones del menú desplegable comparten palabras de prefijo o sufijo comunes, estas se eliminan automáticamente y se insertan como texto estático. Por ejemplo, estas son dos formas de crear el mismo bloque (la primera sin coincidencia de sufijos y la segunda con):
Sin concordancia de sufijos:
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');
}
};
Con la concordancia de sufijos:
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');
}
};
Una ventaja de este enfoque es que el bloque es más fácil de traducir a otros idiomas. El código anterior tiene las cadenas 'hello'
, 'world'
y 'computer'
, mientras que el código revisado tiene las cadenas 'hello world'
y 'hello computer'
. Los traductores tienen más facilidad para traducir frases que palabras aisladas.
Otra ventaja de este enfoque es que el orden de las palabras suele cambiar entre los idiomas. Imagina un lenguaje que use 'world hello'
y 'computer hello'
.
El algoritmo de coincidencia de sufijos detectará el 'hello'
común y lo mostrará después del menú desplegable.
Sin embargo, a veces, la coincidencia de prefijos o sufijos falla. Hay algunos casos en los que dos palabras siempre deben ir juntas y el prefijo no debe eliminarse.
Por ejemplo, se podría argumentar que 'drive red car'
y 'drive red truck'
solo deberían tener 'drive'
factorizado, no 'drive red'
. Se puede usar el espacio indentado '\u00A0'
de Unicode en lugar de un espacio normal para suprimir el comparador de prefijos o sufijos. Por lo tanto, el ejemplo anterior se puede corregir con 'drive red\u00A0car'
y 'drive red\u00A0truck'
.
Otro lugar en el que falla la coincidencia de prefijos o sufijos es en los idiomas que no separan palabras individuales con espacios. El chino es un buen ejemplo. La cadena '訪問中國'
significa 'visit China'
. Ten en cuenta la falta de espacios entre las palabras.
En conjunto, los últimos dos caracteres ('中國'
) son la palabra para 'China'
, pero si se dividen, significarían 'centre'
y 'country'
, respectivamente. Para que la coincidencia de prefijos o sufijos funcione en idiomas como el chino, solo debes insertar un espacio donde debería estar la pausa. Por ejemplo, '訪問 中國'
y '訪問 美國'
darían como resultado "visit [China/USA]"
, mientras que '訪問 中 國'
y '訪問 美 國'
darían como resultado "visit [centre/beautiful] country"
.
Cómo crear un validador de menú desplegable
El valor de un campo desplegable es una cadena neutral en cuanto al idioma, por lo que cualquier validador debe aceptar una cadena y mostrar una cadena que sea una opción disponible, null
o undefined
.
Si tu validador muestra cualquier otro valor, el comportamiento de Blockly no se define y es posible que tu programa falle.
Por ejemplo, podrías definir un campo desplegable con tres opciones y un validador de la siguiente manera:
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
siempre muestra el valor que se le pasó, pero llama a la función auxiliar updateConnection
, que agrega o quita entradas según el valor del menú desplegable:
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');
}
}