下拉菜单字段

下拉菜单字段存储字符串作为其值,将字符串存储为文本。该值是一个与语言无关的键,将用于访问文本,并且在 Blockly 语言之间切换时不会被翻译。该文本是将向用户显示的人类可读字符串。

恣意创作

下拉菜单构造函数包含菜单生成器和可选的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 上默认为 \u25BC (▼),否则默认为 \u25BE (▾)。

这是一个全局属性,因此设置后会修改所有下拉菜单字段。

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