プルダウン フィールド

プルダウン フィールドには、値として文字列が、テキストとして文字列が格納されます。値は、テキストへのアクセスに使用される言語に依存しないキーであり、Blockly が言語間で切り替えられても翻訳されません。テキストは、ユーザーに表示される人が読める形式の文字列です。

「drop down:」というラベルの付いたブロック、[first] が選択されたプルダウン フィールド、「item」というラベル。

同じブロックでプルダウンが開いている状態。ドロップダウンには「first」と「second」という項目が含まれています。

折りたたまれた後の同じブロック。「drop down: first item」というラベルが付いており、右端がギザギザになっていて、折りたたまれていることを示しています。

作成

ドロップダウン コンストラクタは、メニュー ジェネレータとオプションのバリデータを受け取ります。メニュー ジェネレータは、オプションの配列(各オプションには人が読める部分と言語に依存しない文字列が含まれます)またはオプションの配列を生成する関数のいずれかです。各オプションの人間が読める部分は、文字列、画像、HTML 要素にすることができ、配列には異なる型のオプションを混在させることができます。

シンプルなテキストのプルダウン

2 つのテキスト オプションを含むプルダウンを開く

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

HTML プルダウン

オプションは、大きすぎず、マウスやキーボードのイベントを処理しようとしない限り、任意の HTML 要素にすることができます。(これらのルールに従うのはユーザーの責任です。Blockly はこれらのルールを強制しません)。

プルダウンを開くと、リストに HTML 要素が表示されます。閉じられていて、要素が選択されたオプションである場合、リストには要素の title 属性、aria-label 属性、または innerText プロパティが(優先度の高い順に)表示されます。

テキストと 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);
  });

これは、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');
  }
};

動的なプルダウン

曜日を含むプルダウン フィールド

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

これは、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] 形式のオプションの配列を返す必要があります。プルダウンがクリックされるたびに、関数が実行され、オプションが再計算されます。

区切り文字(セパレータ)

文字列 'separator' を使用して、プルダウン メニューのオプション間に線を追加します。

2 番目と 3 番目のオプションの間に線があるプルダウン フィールド

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

シリアル化

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 です。

これはグローバル プロパティであるため、設定するとすべてのドロップダウン フィールドが変更されます。

接頭辞/接尾辞の一致

プルダウン メニューのすべてのオプションに共通の接頭辞や接尾辞がある場合、これらの単語は自動的に因数分解され、静的テキストとして挿入されます。たとえば、同じブロックを作成する方法は 2 つあります(1 つ目はサフィックス マッチングなし、2 つ目はサフィックス マッチングあり)。

サフィックス マッチングなし:

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

プルダウン フィールド

このアプローチの利点の 1 つは、ブロックを他の言語に簡単に翻訳できることです。以前のコードには 'hello''world''computer' の文字列が含まれていますが、修正後のコードには 'hello world''hello computer' の文字列が含まれています。翻訳者は、単語を個別に翻訳するよりも、フレーズを翻訳する方がはるかに簡単です。

このアプローチのもう 1 つの利点は、言語間で語順が頻繁に変わることです。'world hello''computer hello' を使用する言語を考えてみましょう。接尾辞照合アルゴリズムは共通の 'hello' を検出し、プルダウンの後に表示します。

ただし、接頭辞/接尾辞のマッチングが失敗することがあります。2 つの単語が常に一緒に使用され、接頭辞を分離すべきでない場合もあります。たとえば、'drive red car''drive red truck''drive red' ではなく 'drive' のみをファクタリングする必要があります。Unicode の改行なしスペース '\u00A0' を通常のスペースの代わりに使用して、接頭辞/接尾辞マッチャーを抑制できます。したがって、上記の例は 'drive red\u00A0car''drive red\u00A0truck' で修正できます。

接頭辞/接尾辞のマッチングが失敗するもう 1 つの場所は、個々の単語をスペースで区切らない言語です。中国語が良い例です。文字列 '訪問中國''visit China' を意味します。単語間にスペースがないことに注意してください。最後の 2 文字('中國')は 'China' という単語ですが、分割するとそれぞれ 'centre''country' を意味します。中国語などの言語で接頭辞/接尾辞のマッチングを機能させるには、区切りを入れるべき場所にスペースを挿入します。たとえば、'訪問 中國''訪問 美國'"visit [China/USA]" になり、'訪問 中 國''訪問 美 國'"visit [centre/beautiful] country" になります。

プルダウン検証ツールを作成する

プルダウン フィールドの値は言語に依存しない文字列であるため、バリデーターは文字列を受け取り、使用可能なオプションnull、または undefined を返す必要があります。

検証ツールがそれ以外の値を返した場合、Blockly の動作は未定義となり、プログラムがクラッシュする可能性があります。

たとえば、3 つのオプションとバリデーターを含むプルダウン フィールドを次のように定義できます。

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

「neither」、「statement」、「value」の 3 つの項目を含むプルダウン フィールドを示すアニメーション GIF。[neither] が選択されている場合、入力はありません。[statement] が選択されている場合は、ステートメントの入力があります。「value」が接続されている場合、「value」入力があります。