שדות נפתחים

בשדה הנפתח נשמרים מחרוזת כערך שלה ומחרוזת כטקסט. הוא מפתח ניטרלי שפה שישמש לגישה לטקסט לא יתורגמו אם תבוצע מעבר בין שפות ב-Blockly. הטקסט הוא מחרוזת להצגה למשתמש, שאנשים יכולים לקרוא.

יצירה

ה-constructor של התפריט הנפתח מקבל את מחולל התפריטים, כלי התיקוף. במחולל התפריטים יש אבל היא בעצם מערך של אפשרויות, שכל אפשרות מכילה חלק קריא לאנשים, ומחרוזת ניטרלית.

תפריטים נפתחים פשוטים לטקסט

פתיחת תפריט נפתח עם שתי אפשרויות טקסט

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]].

תפריטים נפתחים של תמונות

האפשרויות בתפריט הנפתח יכולות להיות גם תמונות במקום טקסט. אובייקטי תמונה הם צוין באמצעות המאפיינים src, width, height ו-alt.

שימו לב שלמרות שתפריט נפתח יכול לכלול שילוב של אפשרויות טקסט ואפשרויות תמונה, אפשרות מסוימת לא יכולה כרגע להכיל גם תמונה וגם טקסט.

שדה בתפריט נפתח שמכיל תמונות וטקסט

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 היא \u25BC (▼) ב-Android וב-\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".

יצירת תפריט נפתח לאימות

הערך של שדה בתפריט נפתח הוא מחרוזת נייטרלית, לכן כל מאמת צריך לקבל מחרוזת ולהחזיר מחרוזת שאפשרית, 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');
  }
}