תוספים וגורמים לשינוי

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

Mutator הוא סוג מיוחד של תוסף שמוסיף עיבוד סידורי בהתאמה אישית, וגם לפעמים ממשק משתמש, לבלוק.

תוספים

תוספים הם פונקציות שרצות על כל בלוק מסוג נתון, כי הבלוק נוצר. הם יכולים להוסיף תצורה מותאמת אישית (למשל, הגדרת ההסבר הקצר על הבלוק) או התנהגות מותאמת אישית (למשל, הוספת האזנה לאירועים לבלוק).

// This extension sets the block's tooltip to be a function which displays
// the parent block's tooltip (if it exists).
Blockly.Extensions.register(
    'parent_tooltip_extension',
    function() { // this refers to the block that the extension is being run on
      var thisBlock = this;
      this.setTooltip(function() {
        var parent = thisBlock.getParent();
        return (parent && parent.getInputsInline() && parent.tooltip) ||
            Blockly.Msg.MATH_NUMBER_TOOLTIP;
      });
    });

תוספים צריכים להיות מסוג 'רישום' כך שיהיה אפשר לשייך אותם למחרוזת מקש. לאחר מכן אפשר להקצות את מפתח המחרוזת הזה לנכס extensions של JSON של סוג הבלוק את ההגדרה כדי להחיל לבלוק.

{
 //...,
 "extensions": ["parent_tooltip_extension",]
}

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

{
  //...,
  "extensions": ["parent_tooltip_extension", "break_warning_extension"],
}

מיקסינים

חסימת האפשרות מספקת גם שיטה נוחה למצבים שבהם רוצים להוסיף חלק מהמאפיינים (property) או פונקציות העזרה (helper) לחסימה, אבל לא מריצים אותם באופן מיידי. הזה היא מאפשרת לרשום מיקסין שמכיל את כל המאפיינים/השיטות הנוספים. אובייקט המיקסין ואז עטופה בפונקציה שמחילה את המיקס בכל פעם שמופע נוצר סוג הבלוק הנתון.

Blockly.Extensions.registerMixin('my_mixin', {
  someProperty: 'a cool value',

  someMethod: function() {
    // Do something cool!
  }
))`

אפשר להפנות אל מפתחות מחרוזת שמשויכים למיקסינים ב-JSON כמו אל כל דפדפן אחר לתוסף.

{
 //...,
 "extensions": ["my_mixin"],
}

מוטטורים

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

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

אפשר לראות את הסריאליזציה דף עבור מידע נוסף על המקרים שבהם צריך מוטציה ומתי לא.

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

קטעי הוק (hooks) לסידור טורי

למוטטורים יש שני זוגות של הוקים (hooks) לסידור טורי שהם עובדים איתם. זוג אחד של הוקים פועל עם מערכת העריכה החדשה של JSON, והזוג השני עובד עם במערכת הישנה של יצירת סריאליזציה ל-XML. חובה לספק לפחות אחד מהצמדים האלה.

SaveExtraState ו-loadExtraState

saveExtraState ו-loadExtraState הם קטעי הוק (hooks) לסריאליזציה שפועלים עם מערכת חדשה מסדרת JSON. הפונקציה saveExtraState מחזירה קובץ JSON שאפשר לבצע בו סריאליזציה שמייצג את המצב הנוסף של הבלוק, loadExtraState מקבל את אותו ערך של JSON שמאפשר שימוש בפורמט סידורי ומחיל אותו על הבלוק.

// These are the serialization hooks for the lists_create_with block.
saveExtraState: function() {
  return {
    'itemCount': this.itemCount_,
  };
},

loadExtraState: function(state) {
  this.itemCount_ = state['itemCount'];
  // This is a helper function which adds or removes inputs from the block.
  this.updateShape_();
},

קובץ ה-JSON שמתקבל ייראה כך:

{
  "type": "lists_create_with",
  "extraState": {
    "itemCount": 3 // or whatever the count is
  }
}
אין מדינה

אם הבלוק נמצא במצב ברירת המחדל שלו כשהוא עובר סריאליזציה, השיטה saveExtraState יכולה להחזיר את הערך null כדי לציין זאת. אם השיטה saveExtraState מחזירה null ואז לא נוסף נכס extraState אל קובץ ה-JSON. כך הקובץ יישמר בגודל קטן.

סריאליזציה וגיבוי של נתונים

הפרמטר saveExtraState מקבל גם פרמטר doFullSerialization אופציונלי. הזה משמש את הבלוקים שבו serializer (כמו מודלים של נתוני גיבוי). הפרמטר מאותת המצב שאליו מפנה הבלוק לא יהיה זמין אחרי השמטה של הבלוקים, כך הבלוק צריך להציג סריאליזציה של כל מצב הגיבוי עצמו. לדוגמה, true כאשר בלוק בודד מבצע סריאליזציה, או כאשר מעתיקים בלוק ומדביקים אותו.

הנה שני תרחישים נפוצים לדוגמה:

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

בלוקים מסוימים שמשתמשים באפשרות הזו, חסימות של @blockly/block-shareable-procedures. באופן רגיל הם יוצרים סדרה של הפניה למודל של נתוני גיבוי, ששומר את המצב שלהם. אבל אם הפרמטר doFullSerialization נכון, הם יוצרים סריאליזציה של כל את המדינה שלהם. בלוקים של תהליכים שניתן לשתף משתמשים בהם כדי לוודא שכאשר הם מועתקים ומדביקים אותם, הם יוצרים מודל גיבוי חדש של נתוני גיבוי, במקום להתייחס למודל הקיים.

mutationToDom ו-domToMutation

mutationToDom ו-domToMutation הם קטעי הוק (hooks) לסריאליזציה שפועלים עם במערכת הישנה של יצירת סריאליזציה ל-XML. השתמשו בתוכן המושך הזה רק אם אתם חייבים (למשל: עבודה על בסיס קוד ישן שעדיין לא הועבר), אחרת, saveExtraState וגם loadExtraState

הפונקציה mutationToDom מחזירה צומת XML שמייצג את המצב הנוסף של ו-domToMutation מקבל את אותו צומת XML ומחיל את המצב על את הבלוק.

// These are the old XML serialization hooks for the lists_create_with block.
mutationToDom: function() {
  // You *must* create a <mutation></mutation> element.
  // This element can have children.
  var container = Blockly.utils.xml.createElement('mutation');
  container.setAttribute('items', this.itemCount_);
  return container;
},

domToMutation: function(xmlElement) {
  this.itemCount_ = parseInt(xmlElement.getAttribute('items'), 10);
  // This is a helper function which adds or removes inputs from the block.
  this.updateShape_();
},

ה-XML שיתקבל ייראה כך:

<block type="lists_create_with">
  <mutation items="3"></mutation>
</block>

אם הפונקציה mutationToDom מחזירה null, לא יתבצע רכיב נוסף. נוספו ל-XML.

קטעי הוק (hooks) לממשק משתמש

אם מספקים פונקציות מסוימות כחלק מהשינוי, Blockly יוסיף ברירת מחדל של 'mutator' ממשק המשתמש של הבלוק שלכם.

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

לכתוב ולהפרק

ממשק המשתמש שמוגדר כברירת מחדל מבוסס על הפונקציות compose ו-decompose.

decompose "מתפוצץ" את הבלוק לגושי משנה קטנים יותר שאפשר להעביר שנוספו או נמחקו. הפונקציה הזו צריכה להחזיר 'בלוק עליון' כלומר הבלוק הראשי בסביבת העבודה של המשתנה, שאליו מתחברים בלוקים משניים.

לאחר מכן, compose מפרש את התצורה של בלוקי המשנה ומשתמש בהם כדי לשנות את הבלוק הראשי. הפונקציה הזו צריכה לקבל את 'הבלוק העליון' שהיה שהוחזר על ידי decompose כפרמטר.

שימו לב שהפונקציות האלה "משולבות" לבלוק שעבר שינוי כך this כדי להתייחס לבלוק הזה.

// These are the decompose and compose functions for the lists_create_with block.
decompose: function(workspace) {
  // This is a special sub-block that only gets created in the mutator UI.
  // It acts as our "top block"
  var topBlock = workspace.newBlock('lists_create_with_container');
  topBlock.initSvg();

  // Then we add one sub-block for each item in the list.
  var connection = topBlock.getInput('STACK').connection;
  for (var i = 0; i < this.itemCount_; i++) {
    var itemBlock = workspace.newBlock('lists_create_with_item');
    itemBlock.initSvg();
    connection.connect(itemBlock.previousConnection);
    connection = itemBlock.nextConnection;
  }

  // And finally we have to return the top-block.
  return topBlock;
},

// The container block is the top-block returned by decompose.
compose: function(topBlock) {
  // First we get the first sub-block (which represents an input on our main block).
  var itemBlock = topBlock.getInputTargetBlock('STACK');

  // Then we collect up all of the connections of on our main block that are
  // referenced by our sub-blocks.
  // This relates to the saveConnections hook (explained below).
  var connections = [];
  while (itemBlock && !itemBlock.isInsertionMarker()) {  // Ignore insertion markers!
    connections.push(itemBlock.valueConnection_);
    itemBlock = itemBlock.nextConnection &&
        itemBlock.nextConnection.targetBlock();
  }

  // Then we disconnect any children where the sub-block associated with that
  // child has been deleted/removed from the stack.
  for (var i = 0; i < this.itemCount_; i++) {
    var connection = this.getInput('ADD' + i).connection.targetConnection;
    if (connection && connections.indexOf(connection) == -1) {
      connection.disconnect();
    }
  }

  // Then we update the shape of our block (removing or adding iputs as necessary).
  // `this` refers to the main block.
  this.itemCount_ = connections.length;
  this.updateShape_();

  // And finally we reconnect any child blocks.
  for (var i = 0; i < this.itemCount_; i++) {
    connections[i].reconnect(this, 'ADD' + i);
  }
},

saveConnections

אפשר גם להגדיר פונקציית saveConnections שפועלת עם ממשק המשתמש שמוגדר כברירת מחדל. הפונקציה הזו מאפשרת לשייך ילדים הבלוק הראשי (שקיים בסביבת העבודה הראשית) עם בלוקים משניים שקיימים את סביבת העבודה של השינוי. אחר כך אפשר להשתמש בנתונים האלה כדי לוודא ש-compose מחברת מחדש בצורה תקינה את הצאצאים של הבלוק הראשי כאשר בלוקים משנה מאורגנים מחדש.

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

saveConnections: function(topBlock) {
  // First we get the first sub-block (which represents an input on our main block).
  var itemBlock = topBlock.getInputTargetBlock('STACK');

  // Then we go through and assign references to connections on our main block
  // (input.connection.targetConnection) to properties on our sub blocks
  // (itemBlock.valueConnection_).
  var i = 0;
  while (itemBlock) {
    // `this` refers to the main block (which is being "mutated").
    var input = this.getInput('ADD' + i);
    // This is the important line of this function!
    itemBlock.valueConnection_ = input && input.connection.targetConnection;
    i++;
    itemBlock = itemBlock.nextConnection &&
        itemBlock.nextConnection.targetBlock();
  }
},

מבצע רישום

Mutators הם רק סוג מיוחד של תוספת, לכן הם גם צריכים להיות רשומים לפני שאפשר להשתמש בהם ב-JSON של סוג הבלוק שלכם. להגדרה.

// Function signature.
Blockly.Extensions.registerMutator(name, mixinObj, opt_helperFn, opt_blockList);

// Example call.
Blockly.Extensions.registerMutator(
    'controls_if_mutator',
    { /* mutator methods */ },
    undefined,
    ['controls_if_elseif', 'controls_if_else']);
  • name: מחרוזת לשיוך למוטטור כדי שאפשר יהיה להשתמש בה ב-JSON.
  • mixinObj: אובייקט שמכיל את שיטות המוטציה השונות. לדוגמה saveExtraState וגם loadExtraState
  • opt_helperFn: פונקציית עזרה אופציונלית להריץ את הבלוק אחרי ערבוב המיקסין.
  • opt_blockList: מערך אופציונלי של סוגי בלוקים (כמחרוזות) שיהיו מתווספת באופן זמני בממשק המשתמש של המוטציה המוגדרת כברירת מחדל, אם השיטות של ממשק המשתמש מוגדר.

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

{
  //...
  "mutator": "controls_if_mutator"
}

פונקציית העזרה

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

לדוגמה, אפשר להוסיף רכיב עזר לבלוק דמוי רשימה, שמגדיר את מספר הפריטים הראשוני:

var helper = function() {
  this.itemCount_ = 5;
  this.updateShape();
}