下拉式選單欄位

下拉式欄位會將字串儲存為值,並將字串儲存為文字。這個值為語言中立的鍵,會用於存取文字,不會在語言間切換時獲得翻譯。文字是容易理解的字串,會對使用者顯示。

創作風潮

下拉式選單建構函式會採用選單產生器和選用的validator。選單產生器具有彈性,但基本上是選項陣列,每個選項包含人類可讀的部分,以及語言中立字串。

簡易文字下拉式選單

開啟內含兩個文字選項的下拉式選單

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

將使用者可讀的資訊與語言中立鍵分開,可讓下拉式選單設定在不同的語言之間保留。舉例來說,英文區塊的英文版本可能會定義 [['left', 'LEFT'], ['right', 'RIGHT]],而相同區塊的德文版本則定義 [['links', 'LEFT'], ['rechts', 'RIGHT]]

圖片下拉式選單

下拉式選單中的選項可能是圖片而非文字。圖片物件是以 srcwidthheightalt 屬性指定。

請注意,雖然下拉式選單可以混合使用文字選項和圖片選項,但個別選項目前不能同時包含圖片和文字。

包含圖片和文字的下拉式選單欄位

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

動態下拉式選單

含有星期幾的下拉式選單欄位

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

方法是使用 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;
  }
};

您也可以提供函式而非靜態選項清單,藉此提供動態選項。該函式應傳回與靜態選項相同的 [human-readable-value, language-neutral-key] 格式陣列。每當您點選下拉式選單時,系統就會執行函式並重新計算選項。

序列化

JSON

下拉式欄位的 JSON 如下所示:

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

其中 FIELDNAME 是參照下拉式選單欄位的字串,而值是套用至該欄位的值。這個值應為中立選項鍵。

XML

下拉式選單欄位的 XML 如下所示:

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

其中,欄位的 name 屬性包含參照下拉式選單欄位的字串,而內部文字是套用至該欄位的值。內部文字應為有效的中立選項鍵。

自訂

Blockly.FieldDropdown.ARROW_CHAR 屬性可用來變更代表下拉式選單箭頭的萬國碼 (Unicode) 字元。

含有自訂箭頭的下拉式選單欄位

否則,ARROW_CHAR 屬性在 Android 和 \u25BE (▾) 上預設為 \u25BC (▼)。

這是全域屬性,因此會在設定時修改所有下拉式選單欄位。

Blockly.FieldDropdown.MAX_MENU_HEIGHT_VH 屬性可用來變更選單的高度上限。定義為可視區域高度的百分比,也就是視窗的可視區域。

MAX_MENU_HEIGHT_VH 屬性預設為 0.45。

這是全域屬性,因此會在設定時修改所有下拉式選單欄位。

前置字串/後置字串比對

如果所有下拉式選單選項都共用常見的前置字元和/或後置字詞,系統會自動排除這些字詞,並以靜態文字的形式插入這些字詞。舉例來說,以下是建立相同區塊的兩種方法 (第一項不含後置字串比對,第二種則加上後置字串):

沒有後置字元比對:

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

使用後置字串比對時:

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

下拉式選單欄位,內含

這種做法的優點之一,就是該區塊更容易翻譯成其他語言。先前的程式碼包含 'hello''world''computer' 字串,而修改後的程式碼含有 'hello world''hello computer' 字串。相較於單個字詞,譯者翻譯詞組所需的時間高出許多。

這種做法的另一個優點是,字詞順序通常會隨著語言不同而改變。假設某個語言使用 'world hello''computer hello'。後置字串比對演算法會偵測常見的 'hello',並在下拉式選單顯示後顯示。

不過,有時前置字串/後置字串比對會失敗。在某些情況下,兩個字詞應一律一起在一起,且不應去分解前置字串。舉例來說,'drive red car''drive red truck' 應該只可排除 'drive',而非 'drive red'。Unicode 非分行空格 '\u00A0' 可用來取代一般空格,以抑制前置字串/後置字串比對器。因此,上述範例可以使用 'drive red\u00A0car''drive red\u00A0truck' 修正。

另一個前置字串/後置字串比對失敗的位置,則是並未使用空格分隔個別字詞的語言。中文就是不錯的例子。'訪問中國' 字串代表 'visit China',請注意字詞之間沒有空格。最後兩個字元 ('中國') 是 'China' 的字詞,如果分割分別代表 'centre''country'。如要在中文等語言中使用前置字串/後置字串比對,只要插入換行符號即可。例如,'訪問 中國''訪問 美國' 會產生 "visit [China/USA]",而 '訪問 中 國''訪問 美 國' 則為 "visit [centre/beautiful] country"

建立下拉式選單驗證工具

下拉式選單欄位的值是語言中立的字串,因此任何驗證工具都必須接受字串並傳回可選用選項nullundefined 的字串。

如果驗證工具傳回任何其他結果,就表示 Blockly 的行為未定義,且程式可能會當機。

例如,您可以定義含有三個選項的下拉式選單欄位,以及類似下方的驗證工具:

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 一律會傳回傳遞的值,但會呼叫輔助函式 updateConnection,並根據下拉式選單值新增或移除輸入:

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