השדה הנפתח שומר מחרוזת בתור הערך שלה ומחרוזת בתור הטקסט שלה. הערך הוא מפתח נייטרלי, שישמש לגישה לטקסט, ולא יתורגם כשעוברים בין שפות ב-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]]
.
תפריטים נפתחים של תמונות
האפשרויות בתפריט הנפתח יכולות להיות גם תמונות במקום טקסט. אובייקטים של תמונות מצוינים באמצעות המאפיינים 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'
. אפשר להשתמש ברווח '\u00A0'
מסוג Unicode שאינו נשבר, במקום ברווח רגיל, כדי להשמיט את
התו להתאמה של הקידומת/הסיומת. לכן אפשר לתקן את הדוגמה שלמעלה באמצעות
'drive red\u00A0car'
ו-'drive red\u00A0truck'
.
מקום נוסף שבו התאמת קידומת/סיומת נכשלת הוא בשפות שלא מפרידות מילים בודדות באמצעות רווחים. דוגמה טובה לכך היא סינית. המחרוזת '訪問中國'
פירושה 'visit China'
. חשוב לשים לב שאין רווחים בין המילים.
יחד, שני התווים האחרונים ('中國'
) הם המילה של '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');
}
}