فیلدهای کشویی

فیلد کشویی یک رشته را به عنوان مقدار و یک رشته را به عنوان متن ذخیره می‌کند. مقدار، یک کلید بی‌طرف از نظر زبان است که برای دسترسی به متن استفاده می‌شود و هنگام تغییر زبان Blockly ترجمه نمی‌شود. متن، یک رشته قابل خواندن توسط انسان است که به کاربر نمایش داده می‌شود.

یک بلوک با برچسب "drop down:"، یک فیلد کشویی با "first" انتخاب شده، و برچسب "item".

همان بلوک با منوی کشویی باز. منوی کشویی شامل آیتم‌های "first" و "second" است.

همان بلوک پس از جمع شدن. برچسب "drop down: first item" را دارد و یک لبه سمت راست ناهموار برای نشان دادن جمع شدن آن.

خلقت

سازنده‌ی منوی کشویی یک تولیدکننده‌ی منو و یک اعتبارسنج اختیاری را دریافت می‌کند. تولیدکننده‌ی منو یا آرایه‌ای از گزینه‌ها است (که در آن هر گزینه شامل یک بخش قابل خواندن توسط انسان و یک رشته‌ی بی‌طرف از نظر زبان است) یا تابعی است که آرایه‌ای از گزینه‌ها را تولید می‌کند. بخش قابل خواندن توسط انسان در هر گزینه می‌تواند یک رشته، یک تصویر یا یک عنصر HTML باشد و آرایه می‌تواند شامل ترکیبی از گزینه‌هایی با انواع مختلف باشد.

منوهای کشویی متنی ساده

باز کردن منوی کشویی با دو گزینه متنی

جی‌سون

{
  "type": "example_dropdown",
  "message0": "drop down: %1",
  "args0": [
    {
      "type": "field_dropdown",
      "name": "FIELDNAME",
      "options": [
        [ "first item", "ITEM1" ],
        [ "second item", "ITEM2" ]
      ]
    }
  ]
}

جاوا اسکریپت

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]] را تعریف کند.

منوی کشویی تصاویر

گزینه‌های موجود در یک منوی کشویی می‌توانند تصاویر باشند که به صورت اشیاء با ویژگی‌های src ، width ، height و alt نمایش داده می‌شوند.

فیلد کشویی حاوی تصویر و متن

جی‌سون

{
  "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"]
      ]
    }
  ]
}

جاوا اسکریپت

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

جی‌سون

{
  "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 انجام می‌شود.

جاوا اسکریپت

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

منوی کشویی پویا

فیلد کشویی با روزهای هفته

جی‌سون

{
  "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 انجام می‌شود.

جاوا اسکریپت

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' برای اضافه کردن خط بین گزینه‌ها در یک منوی کشویی استفاده کنید.

فیلد کشویی با خطی بین گزینه‌های دوم و سوم

جی‌سون

{
  "type": "separator_dropdown",
  "message0": "food %1",
  "args0": [
    {
      "type": "field_dropdown",
      "name": "FOOD",
      "options": [
        ["water", "WATER"],
        ["juice", "JUICE"],
        "separator",
        ["salad", "SALAD"],
        ["soup", "SOUP"],
      ]
    }
  ]
}

جاوا اسکریپت

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 برای یک فیلد کشویی به این شکل است:

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

که در آن FIELDNAME رشته‌ای است که به یک فیلد کشویی اشاره می‌کند و value مقداری است که قرار است به فیلد اعمال شود. این مقدار باید یک کلید گزینه‌ی بی‌طرف از نظر زبان باشد.

XML

XML برای یک فیلد کشویی به این شکل است:

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

جایی که ویژگی name فیلد شامل یک رشته است که به یک فیلد کشویی اشاره می‌کند، و متن داخلی مقداری است که برای فیلد اعمال می‌شود. متن داخلی باید یک کلید گزینه معتبر و بی‌طرف از نظر زبان باشد.

سفارشی‌سازی

ویژگی Blockly.FieldDropdown.ARROW_CHAR می‌تواند برای تغییر کاراکتر یونیکد نمایش‌دهنده‌ی فلش کشویی استفاده شود.

فیلد کشویی با فلش سفارشی

ویژگی ARROW_CHAR در اندروید به صورت پیش‌فرض روی \u25BC (▼) و در غیر این صورت روی \u25BE (▾) تنظیم شده است.

این یک ویژگی سراسری است، بنابراین هنگام تنظیم، تمام فیلدهای کشویی را تغییر می‌دهد.

ویژگی Blockly.FieldDropdown.MAX_MENU_HEIGHT_VH می‌تواند برای تغییر حداکثر ارتفاع منو استفاده شود. این ویژگی به صورت درصدی از ارتفاع پنجره نمایش (viewport) تعریف می‌شود، که منظور از پنجره نمایش، پنجره است.

مقدار پیش‌فرض ویژگی MAX_MENU_HEIGHT_VH برابر با ۰.۴۵ است.

این یک ویژگی سراسری است، بنابراین هنگام تنظیم، تمام فیلدهای کشویی را تغییر می‌دهد.

تطبیق پیشوند/پسوند

اگر همه گزینه‌های منوی کشویی کلمات پیشوند و/یا پسوند مشترکی داشته باشند، این کلمات به طور خودکار حذف شده و به عنوان متن ثابت درج می‌شوند. برای مثال، در اینجا دو روش برای ایجاد یک بلوک مشابه وجود دارد (اولی بدون تطبیق پسوند و دومی با):

بدون تطبیق پسوند:

جی‌سون

{
  "type": "dropdown_no_matching",
  "message0": "hello %1",
  "args0": [
    {
      "type": "field_dropdown",
      "name": "MODE",
      "options": [
        ["world", "WORLD"],
        ["computer", "CPU"]
      ]
    }
  ]
}

جاوا اسکریپت

Blockly.Blocks['dropdown_no_matching'] = {
  init: function() {
    var options = [
      ['world', 'WORLD'],
      ['computer', 'CPU']
    ];

    this.appendDummyInput()
        .appendField('hello')
        .appendField(new Blockly.FieldDropdown(options), 'MODE');
  }
};

با تطبیق پسوند:

جی‌سون

{
  "type": "dropdown_with_matching",
  "message0": "%1",
  "args0": [
    {
      "type": "field_dropdown",
      "name": "MODE",
      "options": [
        ["hello world", "WORLD"],
        ["hello computer", "CPU"]
      ]
    }
  ]
}

جاوا اسکریپت

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' . می‌توان از فاصله‌ی غیرشکستنی یونیکد '\u00A0' به جای فاصله‌ی معمولی برای حذف تطبیق‌دهنده‌ی پیشوند/پسوند استفاده کرد. بنابراین، مثال بالا را می‌توان با 'drive red\u00A0car' و 'drive red\u00A0truck' اصلاح کرد.

یکی دیگر از مواردی که تطبیق پیشوند/پسوند با شکست مواجه می‌شود، زبان‌هایی است که کلمات را با فاصله از هم جدا نمی‌کنند. زبان چینی مثال خوبی است. رشته '訪問中國' به معنی 'visit China' است، به عدم وجود فاصله بین کلمات توجه کنید. در مجموع، دو کاراکتر آخر ( '中國' ) کلمه 'China' هستند، اما اگر از هم جدا شوند به ترتیب به معنی 'centre' و 'country' خواهند بود. برای اینکه تطبیق پیشوند/پسوند در زبان‌هایی مانند چینی کار کند، کافیست در جایی که باید فاصله باشد، یک فاصله قرار دهید. به عنوان مثال '訪問 中國' و '訪問 美國' منجر به "visit [China/USA]" می‌شوند، در حالی که '訪問 中 國' و '訪問 美 國' منجر به "visit [centre/beautiful] country" می‌شوند.

ایجاد اعتبارسنج کشویی

مقدار یک فیلد کشویی یک رشته‌ی بی‌طرف از نظر زبان است، بنابراین هر اعتبارسنجی باید یک رشته را بپذیرد و رشته‌ای را برگرداند که یک option ، null یا undefined در دسترس باشد.

اگر اعتبارسنج شما چیز دیگری را برگرداند، رفتار 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');
  }
}

یک GIF متحرک که یک فیلد کشویی با سه مورد را نشان می‌دهد: "هیچ‌کدام"، "عبارت" و "مقدار". وقتی "هیچ‌کدام" انتخاب شود، هیچ ورودی ندارد. وقتی "عبارت" انتخاب شود، یک ورودی عبارت دارد. وقتی "مقدار" متصل شود، یک ورودی "مقدار" دارد.