Champs de menu déroulant

Le champ déroulant stocke une chaîne en tant que valeur et une chaîne en tant que texte. La valeur est une clé indépendante de la langue qui sera utilisée pour accéder au texte et ne sera pas traduite lorsque Blockly changera de langue. Il s'agit d'une chaîne lisible qui sera présentée à l'utilisateur.

Création

Le constructeur de menu déroulant contient un générateur de menu et un validator facultatif. Le générateur de menu offre une grande flexibilité, mais il s'agit essentiellement d'un tableau d'options, chaque option contenant une partie lisible et une chaîne indépendante de la langue.

Menus déroulants de texte simple

Ouvrir le menu déroulant avec deux options de texte

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

En séparant les informations lisibles par l'humain et la clé neutre de par rapport à la langue, le paramètre du menu déroulant est conservé entre les langues. Par exemple, une version anglaise d'un bloc peut définir [['left', 'LEFT'], ['right', 'RIGHT]], tandis qu'une version allemande de ce même bloc définit [['links', 'LEFT'], ['rechts', 'RIGHT]].

Menus déroulants des images

Les options d'un menu déroulant peuvent également être des images au lieu de texte. Les objets image sont spécifiés avec les propriétés src, width, height et alt.

Bien qu'un menu déroulant puisse contenir à la fois des options de texte et d'image, une option individuelle ne peut actuellement pas contenir à la fois une image et du texte.

Champ déroulant contenant des images et du texte

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

Listes déroulantes dynamiques

Champ déroulant avec les jours de la semaine

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

Pour ce faire, utilisez une extension 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;
  }
};

Un menu déroulant peut également être fourni avec une fonction au lieu d'une liste d'options statiques, ce qui permet aux options d'être dynamiques. La fonction doit renvoyer un tableau d'options au même format [human-readable-value, language-neutral-key] que les options statiques. Chaque fois que l'utilisateur clique sur le menu déroulant, la fonction est exécutée et les options sont recalculées.

sérialisation

JSON

Le fichier JSON d'un champ de liste déroulante se présente comme suit:

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

FIELDNAME est une chaîne faisant référence à un champ de liste déroulante, et la valeur est la valeur à appliquer au champ. La valeur doit être une clé d'option neutre en termes de langue.

XML

Le code XML d'un champ de liste déroulante se présente comme suit:

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

L'attribut name du champ contient une chaîne faisant référence à un champ de liste déroulante, et le texte intérieur est la valeur à appliquer au champ. Le texte interne doit être une clé d'option valide et neutre de la langue.

Fonctionnalités

La propriété Blockly.FieldDropdown.ARROW_CHAR permet de modifier le caractère Unicode représentant la flèche du menu déroulant.

Champ déroulant avec flèche personnalisée

Dans le cas contraire, la propriété ARROW_CHAR est définie par défaut sur \u25BC (▼) sur Android, ou sur \u25BE (▾).

Comme il s'agit d'une propriété globale, tous les champs de menu déroulant seront modifiés lorsqu'ils seront définis.

La propriété Blockly.FieldDropdown.MAX_MENU_HEIGHT_VH peut être utilisée pour modifier la hauteur maximale du menu. Elle est définie en tant que pourcentage de la hauteur de la fenêtre d'affichage, la fenêtre étant la fenêtre.

La propriété MAX_MENU_HEIGHT_VH est définie par défaut sur 0,45.

Comme il s'agit d'une propriété globale, tous les champs de menu déroulant seront modifiés lorsqu'ils seront définis.

Correspondance du préfixe/suffixe

Si toutes les options du menu déroulant ont des préfixes et/ou suffixes communs, ces mots sont automatiquement exclus et insérés sous forme de texte statique. Par exemple, voici deux façons de créer le même bloc (la première sans correspondance des suffixes et la seconde avec):

Sans mise en correspondance des suffixes:

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

Avec la mise en correspondance des suffixes:

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

Champ déroulant avec

L'un des avantages de cette approche est que le bloc est plus facile à traduire dans d'autres langues. Le code précédent contient les chaînes 'hello', 'world' et 'computer', tandis que le code révisé contient les chaînes 'hello world' et 'hello computer'. Les traducteurs ont beaucoup plus de temps à traduire des expressions que des mots isolés.

Un autre avantage de cette approche est que l'ordre des mots varie souvent d'une langue à l'autre. Prenons l'exemple d'une langue qui utilise 'world hello' et 'computer hello'. L'algorithme de mise en correspondance des suffixes détecte la 'hello' commune et l'affiche après la liste déroulante.

Cependant, la mise en correspondance du préfixe/suffixe échoue parfois. Dans certains cas, deux mots doivent toujours être aller ensemble et le préfixe ne doit pas être pris en compte. Par exemple, 'drive red car' et 'drive red truck' ne devraient probablement comporter que 'drive', et non 'drive red'. L'espace insécable Unicode '\u00A0' peut être utilisé à la place d'un espace standard pour supprimer la mise en correspondance de préfixe/suffixe. Ainsi, l'exemple ci-dessus peut être corrigé avec 'drive red\u00A0car' et 'drive red\u00A0truck'.

La mise en correspondance des préfixes/suffixes échoue également dans les langues qui ne séparent pas les mots individuels par des espaces. Le chinois en est un bon exemple. La chaîne '訪問中國' signifie 'visit China'. Notez l'absence d'espaces entre les mots. Collectivement, les deux derniers caractères ('中國') correspondent au mot 'China', mais en cas de division, ils signifient respectivement 'centre' et 'country'. Pour que la correspondance des préfixes/suffixes fonctionne dans des langues telles que le chinois, il vous suffit d'insérer un espace à l'endroit où la coupure doit se trouver. Par exemple, '訪問 中國' et '訪問 美國' afficheront "visit [China/USA]", tandis que '訪問 中 國' et '訪問 美 國' généreraient "visit [centre/beautiful] country".

Créer un programme de validation de liste déroulante

La valeur d'un champ de menu déroulant est une chaîne neutre en termes de langue. Les validateurs doivent donc accepter une chaîne et renvoyer une chaîne disponible en option, null ou undefined.

Si votre programme de validation affiche autre chose, cela signifie que le comportement de Blockly n'est pas défini et que votre programme risque de planter.

Par exemple, vous pouvez définir un champ déroulant avec trois options et un programme de validation comme celui-ci:

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 renvoie toujours la valeur transmise, mais appelle la fonction d'assistance updateConnection, qui ajoute ou supprime des entrées en fonction de la valeur du menu déroulant:

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