المعدِّل هو mixin يضيف تسلسلاً إضافيًا (حالة إضافية يتم حفظها وتحميلها) إلى أحد البلوكات. على سبيل المثال، يحتاج كل من controls_if
وlist_create_with
المضمّنين إلى تسلسل إضافي ليتمكّنا من حفظ عدد المدخلات التي يتضمّنانها. قد يضيف أيضًا واجهة مستخدم تتيح للمستخدم تغيير شكل المربّع.
يُرجى العِلم أنّ تغيير شكل الحزمة لا يعني بالضرورة أنّك بحاجة إلى تسلسل إضافي. على سبيل المثال، يتغيّر شكل الحظر math_number_property
، ولكنّه يفعل ذلك استنادًا إلى حقل قائمة منسدلة، يتم تسلسل قيمته تلقائيًا. وبالتالي، يمكنه استخدام أداة التحقّق من صحة الحقل فقط، ولا يحتاج إلى أداة تعديل.
يمكنك الاطّلاع على صفحة التسلسل لمعرفة المزيد من المعلومات حول الحالات التي تحتاج فيها إلى أداة تعديل والحالات التي لا تحتاج فيها إليها.
توفّر أدوات التعديل أيضًا واجهة مستخدم مدمجة تتيح للمستخدمين تغيير أشكال الكتل إذا وفّرت بعض الطرق الاختيارية.
عمليات ربط التسلسل
تحتوي أدوات التعديل على مجموعتَين من خطافات التسلسل التي تعمل معها. تعمل إحدى مجموعتَي الخطافات مع نظام تسلسل JSON الجديد، بينما تعمل المجموعة الأخرى مع نظام تسلسل XML القديم. يجب تقديم زوج واحد على الأقل من هذه الأزواج.
saveExtraState وloadExtraState
saveExtraState
وloadExtraState
هما وسيطا تسلسل يعملان مع نظام تسلسل 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
. يتم استخدام هذا
من خلال الكتل التي تشير إلى الحالة المتسلسلة بواسطة
مسلسل مختلف (مثل نماذج بيانات الخلفية). تشير المَعلمة إلى أنّ الحالة المشار إليها لن تكون متاحة عند إلغاء تسلسل الحظر، لذا يجب أن يسرّل الحظر جميع حالات النسخ الاحتياطي بنفسه. على سبيل المثال، يكون هذا صحيحًا عند تسلسل حظر فردي، أو عند نسخ حظر ولصقه.
في ما يلي حالتا استخدام شائعتان لذلك:
- عند تحميل كتلة فردية في مساحة عمل لا يتوفّر فيها نموذج البيانات الأساسي، تتضمّن الكتلة معلومات كافية في حالتها الخاصة لإنشاء نموذج بيانات جديد.
- عند نسخ فقرة ولصقها، يتم دائمًا إنشاء نموذج بيانات جديد بدلاً من الإشارة إلى نموذج حالي.
من بين المربّعات التي تستخدم هذه الميزة، مربّعات
@blockly/block-shareable-procedures. عادةً ما يتم تسلسل مرجع إلى نموذج بيانات أساسي يخزّن حالتها.
ولكن إذا كانت المَعلمة doFullSerialization
صحيحة، سيتم تسلسل جميع حالاتها. تستخدم كتل الإجراءات القابلة للمشاركة هذه الطريقة للتأكّد من أنّه عند نسخها ولصقها، يتم إنشاء نموذج بيانات أساسي جديد بدلاً من الإشارة إلى نموذج حالي.
mutationToDom وdomToMutation
mutationToDom
وdomToMutation
هما خطافا تسلسل يعملان مع نظام تسلسل 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
تعرض قيمة فارغة، لن تتم إضافة أي عنصر إضافي إلى ملف XML.
UI Hooks
إذا وفّرت وظائف معيّنة كجزء من أداة التعديل، سيضيف Blockly واجهة مستخدم تلقائية "لأداة التعديل" إلى البلوك.
لست مضطرًا إلى استخدام واجهة المستخدم هذه إذا أردت إضافة تسلسل إضافي. يمكنك استخدام واجهة مستخدم مخصّصة، مثل تلك التي يوفّرها المكوّن الإضافي blocks-plus-minus، أو يمكنك عدم استخدام أي واجهة مستخدم على الإطلاق.
التركيب والتحليل
تعتمد واجهة المستخدِم التلقائية على الدالتَين 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();
}
},
جارٍ التسجيل
المعدِّلات هي نوع خاص من عمليات الدمج، لذا يجب تسجيلها أيضًا قبل أن تتمكّن من استخدامها في تعريف 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"
}
دالة مساعِدة
بالإضافة إلى mixin، قد يسجّل mutator دالة مساعدة. يتم تنفيذ هذه الدالة على كل كتلة من النوع المحدّد بعد إنشائها وإضافة mixinObj
إليها. يمكن استخدامها لإضافة مشغّلات أو تأثيرات إضافية إلى عملية تغيير.
على سبيل المثال، يمكنك إضافة أداة مساعدة إلى كتلة تشبه القائمة تحدّد العدد الأولي للعناصر:
var helper = function() {
this.itemCount_ = 5;
this.updateShape();
}