Campi del menu a discesa

Il campo menu a discesa memorizza una stringa come valore e una stringa come testo. Il valore è una chiave indipendente dalla lingua che verrà utilizzata per accedere al testo e non verrà tradotta quando Blockly passa da una lingua all'altra. Il testo è una stringa leggibile che verrà mostrata all'utente.

Un blocco con l'etichetta "menu a discesa:", un campo a discesa con "primo" selezionato
e l'etichetta "elemento".

Lo stesso blocco con il menu a discesa aperto. Il menu a discesa contiene gli elementi "primo"
e "secondo".

Lo stesso blocco dopo essere stato compresso. Ha l'etichetta "menu a discesa: primo elemento"
e un bordo destro frastagliato per indicare che è
compresso.

Creazione

Il costruttore del menu a discesa accetta un generatore di menu e uno strumento di convalida facoltativo . Il generatore di menu è un array di opzioni (dove ogni opzione contiene una parte leggibile e una stringa indipendente dalla lingua) o una funzione che genera un array di opzioni. La parte leggibile di ogni opzione può essere una stringa, un'immagine o un elemento HTML e l'array può contenere un mix di opzioni di tipi diversi.

Menu a discesa di testo semplice

Apri il menu a discesa con due opzioni di testo

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

Mantenere le informazioni leggibili separate dalla chiave indipendente dalla lingua consente di conservare l'impostazione del menu a discesa tra le lingue. Ad esempio, una versione inglese di un blocco può definire [['left', 'LEFT'], ['right', 'RIGHT]], mentre una versione tedesca dello stesso blocco definirebbe [['links', 'LEFT'], ['rechts', 'RIGHT]].

Menu a discesa di immagini

Le opzioni in un menu a discesa possono essere immagini, rappresentate come oggetti con proprietà src, width, height e alt.

Campo a discesa contenente immagini e testo

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

Menu a discesa HTML

Un'opzione può essere qualsiasi elemento HTML, purché non sia troppo grande e non tenti di gestire eventi del mouse o della tastiera. (È tua responsabilità rispettare queste regole: Blockly non le applica.)

Quando il menu a discesa è aperto, l'elenco mostra l'elemento HTML. Quando è chiuso e l'elemento è l'opzione selezionata, l'elenco mostra (in ordine decrescente di preferenza) l'attributo title dell'elemento, l'attributo aria-label o la proprietà innerText.

Campo menu a discesa contenente testo ed elementi HTML

JSON

{
  "type": "flags_with_text_dropdown",
  "message0": "flag with text %1",
  "args0": [
    {
      "type": "field_dropdown",
      "name": "FLAG_WITH_TEXT",
      "options": [
        ["x", "X"], // Placeholder. An empty array throws an exception.
      ]
    }
  ],
  // Use an extension to add the HTML element options.
  "extensions": ["flag_with_text_extension"]
}
Blockly.Extensions.register('flag_with_text_extension',
  function() {
    function createFlagWithTextDiv(text, src) {
      const div = document.createElement('div');
      div.setAttribute('style', 'width: 75px;');
      div.setAttribute('title', text);
      const img = document.createElement('img');
      img.setAttribute('src', src);
      img.setAttribute('style', 'height: 25px; display: block; margin: auto;');
      div.appendChild(img);
      const para = document.createElement('p');
      para.innerText = text;
      para.setAttribute('style', 'text-align: center; margin: 5px;');
      div.appendChild(para);
      return div;
    }

    const canadaDiv = createFlagWithTextDiv('Canada', 'canada.png');
    const usaDiv = createFlagWithTextDiv('USA', 'usa.png');
    const mexicoDiv = createFlagWithTextDiv('Mexico', 'mexico.png');
    const options = [
      ['none', 'NONE'],
      [canadaDiv, 'CANADA'],
      [usaDiv, 'USA'],
      [mexicoDiv, 'MEXICO']
    ];
    this.getField('FLAG_WITH_TEXT').setOptions(options);
  });

Questa operazione viene eseguita utilizzando un'estensione JSON .

JavaScript

function createFlagWithTextDiv(text, src) {
  const div = document.createElement('div');
  div.setAttribute('style', 'width: 75px;');
  div.setAttribute('title', text);
  const img = document.createElement('img');
  img.setAttribute('src', src);
  img.setAttribute('style', 'height: 25px; display: block; margin: auto;');
  div.appendChild(img);
  const para = document.createElement('p');
  para.innerText = text;
  para.setAttribute('style', 'text-align: center; margin: 5px;');
  div.appendChild(para);
  return div;
}

const canadaDiv = createFlagWithTextDiv('Canada', 'canada.png');
const usaDiv = createFlagWithTextDiv('USA', 'usa.png');
const mexicoDiv = createFlagWithTextDiv('Mexico', 'mexico.png');

Blockly.Blocks['flags_with_text_dropdown'] = {
  init: function() {
    const input = this.appendDummyInput()
        .appendField('flag with text');
    const options = [
        ['none', 'NONE'],
        [canadaDiv, 'CANADA'],
        [usaDiv, 'USA'],
        [mexicoDiv, 'MEXICO']
    ];
    input.appendField(new Blockly.FieldDropdown(options), 'FLAG_WITH_TEXT');
  }
};

Menu a discesa dinamici

Campo menu a discesa con i giorni della settimana

JSON

{
  "type": "dynamic_dropdown",
  "message0": "day %1",
  "args0": [
    {
      "type": "field_dropdown",
      "name": "DAY",
      "options": [
        ["x", "X"], // Placeholder. An empty array throws an exception.
      ]
     }
  ],
  // Use an extension to set the menu function.
  "extensions": ["dynamic_menu_extension"]
}
Blockly.Extensions.register('dynamic_menu_extension',
  function() {
    this.getField('DAY').setOptions(
      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;
      });
  });

Questa operazione viene eseguita utilizzando un'estensione 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;
  }
};

A un menu a discesa può essere fornita anche una funzione anziché un elenco di opzioni statiche, il che consente di rendere dinamiche le opzioni. La funzione deve restituire un array di opzioni nello stesso formato [human-readable-value, language-neutral-key] delle opzioni statiche. Ogni volta che si fa clic sul menu a discesa, la funzione viene eseguita e le opzioni vengono ricalcolate.

Separatori

Utilizza la stringa 'separator' per aggiungere una riga tra le opzioni in un menu a discesa.

Campo a discesa con una linea tra la seconda e la terza opzione

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

Serializzazione

JSON

Il JSON per un campo menu a discesa è il seguente:

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

Dove FIELDNAME è una stringa che fa riferimento a un campo menu a discesa e il valore è il valore da applicare al campo. Il valore deve essere una chiave di opzione indipendente dalla lingua.

XML

L'XML per un campo menu a discesa è il seguente:

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

Dove l'attributo name del campo contiene una stringa che fa riferimento a un campo menu a discesa e il testo interno è il valore da applicare al campo. Il testo interno deve essere una chiave di opzione indipendente dalla lingua valida.

Personalizzazione

La proprietà Blockly.FieldDropdown.ARROW_CHAR può essere utilizzata per modificare il carattere Unicode che rappresenta la freccia del menu a discesa.

Campo a discesa con freccia personalizzata

Per impostazione predefinita, la proprietà ARROW_CHAR è \u25BC (▼) su Android e \u25BE (▾) in caso contrario.

Questa è una proprietà globale, quindi se impostata modificherà tutti i campi menu a discesa.

La proprietà Blockly.FieldDropdown.MAX_MENU_HEIGHT_VH può essere utilizzata per modificare l'altezza massima del menu. È definita come percentuale dell'altezza della finestra, che è la finestra.

Per impostazione predefinita, la proprietà MAX_MENU_HEIGHT_VH è 0,45.

Questa è una proprietà globale, quindi se impostata modificherà tutti i campi menu a discesa.

Corrispondenza di prefissi/suffissi

Se tutte le opzioni del menu a discesa condividono parole di prefisso e/o suffisso comuni, queste parole vengono automaticamente fattorizzate e inserite come testo statico. Ad esempio, ecco due modi per creare lo stesso blocco (il primo senza corrispondenza di suffissi e il secondo con):

Senza corrispondenza di suffissi:

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 corrispondenza di suffissi:

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 menu a discesa con

Un vantaggio di questo approccio è che il blocco è più facile da tradurre in altre lingue. Il codice precedente contiene le stringhe 'hello', 'world' e 'computer', mentre il codice rivisto contiene le stringhe 'hello world' e 'hello computer'. I traduttori hanno molta più facilità a tradurre frasi che parole isolate.

Un altro vantaggio di questo approccio è che l'ordine delle parole spesso cambia da una lingua all'altra. Immagina una lingua che utilizza 'world hello' e 'computer hello'. L'algoritmo di corrispondenza dei suffissi rileverà il 'hello' comune e lo visualizzerà dopo il menu a discesa.

A volte, però, la corrispondenza di prefissi/suffissi non riesce. In alcuni casi, due parole devono sempre andare insieme e il prefisso non deve essere fattorizzato. Ad esempio 'drive red car' e 'drive red truck' dovrebbero avere solo 'drive' fattorizzato, non 'drive red'. Lo spazio unificatore Unicode space '\u00A0' può essere utilizzato al posto di uno spazio normale per sopprimere il matcher di prefissi/suffissi. Pertanto, l'esempio precedente può essere corretto con 'drive red\u00A0car' e 'drive red\u00A0truck'.

Un altro caso in cui la corrispondenza di prefissi/suffissi non riesce è nelle lingue che non separano le singole parole con spazi. Il cinese è un buon esempio. La stringa '訪問中國' significa 'visit China', nota l'assenza di spazi tra le parole. Collettivamente, gli ultimi due caratteri ('中國') sono la parola per 'China', ma se divisi significherebbero rispettivamente 'centre' e 'country'. Per far funzionare la corrispondenza di prefissi/suffissi in lingue come il cinese, basta inserire uno spazio dove deve essere presente l'interruzione. Ad esempio '訪問 中國' e '訪問 美國' genererebbero "visit [China/USA]", mentre '訪問 中 國' e '訪問 美 國' genererebbero "visit [centre/beautiful] country".

Creazione di uno strumento di convalida del menu a discesa

Il valore di un campo menu a discesa è una stringa indipendente dalla lingua, quindi tutti i validatori devono accettare una stringa e restituire una stringa che sia un'opzione disponibile, null, o undefined.

Se il validatore restituisce qualsiasi altro valore, il comportamento di Blockly non è definito e il programma potrebbe avere un arresto anomalo.

Ad esempio, puoi definire un campo menu a discesa con tre opzioni e uno strumento di convalida come questo:

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 restituisce sempre il valore che gli è stato passato, ma chiama la funzione helper updateConnection che aggiunge o rimuove gli input in base al valore del menu a discesa:

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

Una GIF animata che mostra un campo a discesa con tre elementi: &quot;nessuno&quot;,
&quot;istruzione&quot; e &quot;valore&quot;. Quando è selezionato &quot;Nessuno&quot;, non ha input. Quando
&quot;statement&quot; è selezionato, ha un input di istruzione. Quando &quot;value&quot; è connesso, ha un input &quot;value&quot;.