Drop-down-Felder

Im Drop-down-Feld werden ein String als Wert und ein String als Text gespeichert. Der Wert ist ein sprachneutraler Schlüssel, der für den Zugriff auf den Text verwendet und nicht übersetzt wird, wenn Blockly zwischen den Sprachen gewechselt wird. Der Text ist ein für Menschen lesbarer String, der dem Nutzer angezeigt wird.

Erstellung

Der Drop-down-Konstruktor übernimmt einen Menügenerator und einen optionalen validator. Der Menügenerator ist sehr flexibel, besteht jedoch im Wesentlichen aus einem Array von Optionen, wobei jede Option einen für Menschen lesbaren Teil und einen sprachneutralen String enthält.

Einfache Text-Dropdown-Menüs

Drop-down-Menü mit zwei Textoptionen öffnen

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

Wenn die visuell lesbaren Informationen vom sprachneutralen Schlüssel getrennt bleiben, bleibt die Einstellung des Drop-down-Menüs zwischen den Sprachen erhalten. Beispielsweise kann für eine englische Version eines Blocks [['left', 'LEFT'], ['right', 'RIGHT]] definiert werden, während in einer deutschen Version desselben Blocks [['links', 'LEFT'], ['rechts', 'RIGHT]] definiert wird.

Drop-down-Menüs für Bilder

Optionen in einem Dropdown-Menü können auch Bilder anstelle von Text enthalten. Bildobjekte werden mit den Attributen src, width, height und alt angegeben.

Obwohl ein Drop-down-Menü eine Mischung aus Text- und Bildoptionen enthalten kann, kann eine einzelne Option derzeit nicht sowohl ein Bild als auch Text enthalten.

Drop-down-Feld mit Bildern und Text

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

Dynamische Drop-down-Menüs

Drop-down-Feld mit Wochentagen

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

Dies geschieht mithilfe einer JSON-Erweiterung.

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

Ein Drop-down-Menü kann auch mit einer Funktion anstelle einer Liste statischer Optionen bereitgestellt werden. Dadurch sind die Optionen dynamisch. Die Funktion sollte ein Array mit Optionen im selben [human-readable-value, language-neutral-key]-Format wie statische Optionen zurückgeben. Bei jedem Klick auf das Drop-down-Menü wird die Funktion ausgeführt und die Optionen werden neu berechnet.

Serialisierung

JSON

Der JSON-Code für ein Drop-down-Feld sieht so aus:

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

Dabei ist FIELDNAME ein String, der auf ein Drop-down-Feld verweist, und der Wert ist der Wert, der auf das Feld angewendet werden soll. Der Wert sollte ein sprachneutraler Optionsschlüssel sein.

XML

Der XML-Code für ein Drop-down-Feld sieht so aus:

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

Dabei enthält das Attribut name des Felds einen String, der auf ein Drop-down-Feld verweist, und der innere Text ist der Wert, der auf das Feld angewendet werden soll. Der innere Text sollte ein gültiger sprachneutraler Optionsschlüssel sein.

Anpassbare

Mit dem Attribut Blockly.FieldDropdown.ARROW_CHAR kann das Unicode-Zeichen für den Drop-down-Pfeil geändert werden.

Drop-down-Feld mit benutzerdefiniertem Pfeil

Die Property ARROW_CHAR ist unter Android standardmäßig auf \u25BC (▼) und andernfalls auf \u25BE (▾) festgelegt.

Da dies eine globale Eigenschaft ist, werden alle Drop-down-Felder geändert, wenn sie festgelegt wird.

Mit dem Attribut Blockly.FieldDropdown.MAX_MENU_HEIGHT_VH kann die maximale Höhe des Menüs geändert werden. Sie wird als Prozentsatz der Höhe des Darstellungsbereichs definiert, wobei der Darstellungsbereich das Fenster ist.

Der Standardwert für die Eigenschaft MAX_MENU_HEIGHT_VH ist „0,45“.

Da dies eine globale Eigenschaft ist, werden alle Drop-down-Felder geändert, wenn sie festgelegt wird.

Abgleich von Präfixen und Suffixen

Wenn alle Optionen des Drop-down-Menüs ein gemeinsames Präfix und/oder Suffixe haben, werden diese Wörter automatisch herausgefiltert und als statischen Text eingefügt. Es gibt beispielsweise zwei Möglichkeiten, denselben Block zu erstellen (davon eine ohne Abgleich des Suffixes und die zweite mit):

Ohne Suffixabgleich:

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

Mit Suffixabgleich:

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

Drop-down-Feld mit

Ein Vorteil dieses Ansatzes besteht darin, dass der Block einfacher in andere Sprachen übersetzt werden kann. Der frühere Code hat die Strings 'hello', 'world' und 'computer', während der überarbeitete Code die Strings 'hello world' und 'hello computer' enthält. Übersetzer können Sätze viel einfacher übersetzen als Wörter, die isoliert sind.

Ein weiterer Vorteil dieses Ansatzes besteht darin, dass sich die Wortreihenfolge in einer Sprache häufig ändert. Beispiel: 'world hello' und 'computer hello' werden verwendet. Der Algorithmus zum Suffixabgleich erkennt den gemeinsamen 'hello' und zeigt ihn nach dem Drop-down-Menü an.

Manchmal schlägt der Abgleich von Präfixen und Suffixen jedoch fehl. Es gibt einige Fälle, in denen zwei Wörter immer zusammen stehen sollten und das Präfix nicht außer Acht gelassen werden sollte. Beispielsweise sollte für 'drive red car' und 'drive red truck' nur 'drive' weggelassen werden, nicht 'drive red'. Der geschützte Unicode-Bereich '\u00A0' kann anstelle eines regulären Leerzeichens verwendet werden, um den Präfix-/Suffix-Matcher zu unterdrücken. Daher kann das obige Beispiel mit 'drive red\u00A0car' und 'drive red\u00A0truck' korrigiert werden.

Ein anderer Fall, bei dem der Präfix-/Suffixabgleich fehlschlägt, sind Sprachen, in denen einzelne Wörter nicht durch Leerzeichen getrennt werden. Chinesisch ist ein gutes Beispiel. Der String '訪問中國' steht für 'visit China'. Beachten Sie, dass zwischen den Wörtern keine Leerzeichen vorhanden sind. Zusammen sind die letzten beiden Zeichen ('中國') das Wort für 'China'. Bei Aufteilung bedeuten sie jedoch 'centre' bzw. 'country'. Damit der Abgleich von Präfixen und Suffixen in Sprachen wie Chinesisch funktioniert, fügen Sie an der Stelle, an der die Pause stehen soll, ein Leerzeichen ein. Beispielsweise würden '訪問 中國' und '訪問 美國' zu "visit [China/USA]" führen, während '訪問 中 國' und '訪問 美 國' zu "visit [centre/beautiful] country" führen.

Drop-down-Validator erstellen

Der Wert eines Drop-down-Felds ist ein sprachneutraler String. Daher müssen alle Validierungen einen String akzeptieren und einen String zurückgeben, der als Option verfügbar ist, null oder undefined.

Wenn Ihre Validierung etwas anderes zurückgibt, ist das Verhalten von Blockly nicht definiert und Ihr Programm kann abstürzen.

Sie könnten beispielsweise ein Drop-down-Feld mit drei Optionen und eine Validierung wie diese definieren:

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 gibt immer den übergebenen Wert zurück, ruft jedoch die Hilfsfunktion updateConnection auf, die Eingaben basierend auf dem Drop-down-Wert hinzufügt oder entfernt:

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