שדה התפריט הנפתח שומר מחרוזת כערך ומחרוזת כטקסט. הערך הוא מפתח ניטרלי לשפה שישמש לגישה לטקסט ולא יתורגם כשעוברים בין שפות ב-Blockly. הטקסט הוא מחרוזת שאנשים יכולים לקרוא, שתוצג למשתמש.
שדה של תפריט נפתח
שדה נפתח עם עורך פתוח
שדה נפתח בבלוק מכווץ
יצירה
הפונקציה ליצירת תפריט נפתח מקבלת מחולל תפריטים ומאמת אופציונלי. מחולל התפריט הוא מערך של אפשרויות (שכל אחת מהן מכילה חלק שקריא למשתמשים ומחרוזת שאינה תלויה בשפה) או פונקציה שמייצרת מערך של אפשרויות. החלק שקריא לבני אדם בכל אפשרות יכול להיות מחרוזת, תמונה או רכיב HTML, והמערך יכול להכיל שילוב של אפשרויות מסוגים שונים.
תפריטים נפתחים עם טקסט פשוט
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');
}
};
תפריטים נפתחים של HTML
אפשר להשתמש בכל רכיב HTML כאפשרות, כל עוד הוא לא גדול מדי ולא מנסה לטפל באירועים של עכבר או מקלדת. (באחריותכם לפעול בהתאם לכללים האלה – Blockly לא אוכפת אותם).
כשהתפריט הנפתח פתוח, הרשימה מציגה את רכיב ה-HTML. כשהתפריט סגור והרכיב הוא האפשרות שנבחרה, מוצגת הרשימה (בסדר יורד לפי העדפה) של מאפיין title
של הרכיב, מאפיין aria-label
או מאפיין innerText
.
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'
.
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
הוא \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');
}
}