Campos desplegables

El campo desplegable almacena una cadena como su valor y una cadena como su texto. El valor es una clave neutral en el idioma que se usará para acceder al texto y no se traducirá cuando Blockly cambie de idioma. El texto es una cadena legible que se le mostrará al usuario.

Creación

El constructor de menú desplegable incluye un generador de menú y un validator 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 neutra del lenguaje.

Menús desplegables de texto simples

Abrir el menú desplegable con dos opciones de texto

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 de idioma neutro permite que la configuración del menú desplegable se conserve entre los idiomas. Por ejemplo, una versión en inglés de un bloque definiría [['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 en 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, aunque un menú desplegable puede tener una combinación de opciones de texto e imagen, una opción individual no puede contener actualmente una imagen y texto.

Campo desplegable que contiene imágenes y texto

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

Campo desplegable con los días de la semana

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');
  });

Para ello, se usa una extensión 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 de [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.

Serialización

JSON

El JSON de un campo desplegable se ve de la siguiente manera:

{
  "fields": {
    "FIELDNAME": "LANGUAGE-NEUTRAL-KEY"
  }
}

En la que FIELDNAME es una cadena que hace referencia a un campo desplegable y el valor es el valor que se debe aplicar al campo. El valor debe ser una clave de opción neutra del lenguaje.

XML

El XML de un campo desplegable se ve de la siguiente manera:

<field name="FIELDNAME">LANGUAGE-NEUTRAL-KEY</field>

Cuando el atributo name del campo contiene una cadena que hace referencia a un campo desplegable, y el texto interno es el valor que se debe aplicar al campo. El texto interno debe ser una clave de opción válida y neutra del lenguaje.

Personalización

La propiedad Blockly.FieldDropdown.ARROW_CHAR se puede usar para cambiar el carácter Unicode que representa la flecha desplegable.

Campo desplegable con flecha personalizada

El valor predeterminado de la propiedad ARROW_CHAR es \u25BC (▼) en Android y \u25BE (▾) en los demás casos.

Esta es una propiedad global, por lo que modificará todos los campos desplegables cuando se configure.

Se puede usar la propiedad Blockly.FieldDropdown.MAX_MENU_HEIGHT_VH para cambiar la altura máxima del menú. Se define como un porcentaje de la altura del viewport, que es la ventana.

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 desplegables cuando se configure.

Coincidencia de prefijo/sufijo

Si todas las opciones del menú desplegable comparten palabras de prefijo o sufijo comunes, estas palabras se excluyen automáticamente y se insertan como texto estático. Por ejemplo, a continuación, se muestran dos formas de crear el mismo bloque (la primera sin coincidencia de sufijo y la segunda con):

Sin coincidencia de sufijo:

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 coincidencia de sufijo:

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');
  }
};

Campo desplegable con

Una de las ventajas de este enfoque es que el bloque es más fácil de traducir a otros idiomas. El código anterior tiene las strings 'hello', 'world' y 'computer', mientras que el código revisado tiene las strings 'hello world' y 'hello computer'. A los traductores les resulta mucho más fácil traducir frases que palabras aisladas.

Otra ventaja de este enfoque es que el orden de las palabras a menudo cambia entre los idiomas. Imagina un idioma que use 'world hello' y 'computer hello'. El algoritmo de coincidencia de sufijo detectará el 'hello' común y lo mostrará después del menú desplegable.

Sin embargo, a veces la coincidencia de prefijo o sufijo falla. En algunos casos, dos palabras siempre deberían ir juntas y el prefijo no se debería excluir. Por ejemplo, se podría decir que 'drive red car' y 'drive red truck' solo deberían tener factor de 'drive', no 'drive red'. Se puede usar el espacio de no separación de Unicode '\u00A0' 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 en los que no se separan con espacios las palabras individuales. El chino es un buen ejemplo. La string '訪問中國' significa 'visit China'. Ten en cuenta la falta de espacios entre las palabras. En conjunto, los dos últimos caracteres ('中國') son la palabra para 'China', pero si se dividirían, significarían 'centre' y 'country', respectivamente. Para que la coincidencia de prefijo o sufijo funcione en idiomas como el chino, solo debes insertar un espacio donde debería estar la pausa. Por ejemplo, '訪問 中國' y '訪問 美國' generarían "visit [China/USA]", mientras que '訪問 中 國' y '訪問 美 國' resultarían en "visit [centre/beautiful] country".

Crea un validador de menú desplegable

El valor de un campo desplegable es una string independiente del lenguaje, por lo que cualquier validador debe aceptar una string y mostrar una string que sea una opción disponible, null o undefined.

Si el validador muestra algo más, el comportamiento de Blockly no está definido y tu programa puede fallar.

Por ejemplo, podrías definir un campo desplegable con tres opciones y un validador como el siguiente:

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 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');
  }
}