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