创建自定义过程块要求您执行以下操作:
- 按照使用过程页面中的说明安装 @blockly/block-shareable-procedures 插件。
- 按照概览页面中的说明使用 JSON 序列化系统。
将数据模型添加到工作区
过程定义和过程调用方块都会引用后备数据模型,后者定义了过程的签名(名称、参数和返回)。这样可以更灵活地设计应用(例如,您可以允许在一个工作区中定义过程,并在另一个工作区中引用过程)。
这意味着,您需要将过程数据模型添加到工作区,才能让块正常运行。您可以通过多种方式(例如,自定义界面)实现这一点。
通过让过程定义块在实例化到工作区时动态创建其后备数据模型,@blockly/block-shareable-procedures。如需自行实现,您需要在 init
中创建模型并在 destroy
中将其删除。
import {ObservableProcedureModel} from '@blockly/block-shareable-procedures';
Blockly.Blocks['my_procedure_def'] = {
init: function() {
this.model = new ObservableProcedureModel('default name');
this.workspace.getProcedureMap().add(model);
// etc...
}
destroy: function() {
// Optionally:
// Destroy the model when the definition block is deleted.
this.workpace.getProcedureMap().delete(model.getId());
}
}
返回有关块的信息
您的过程定义和过程调用块需要实现 getProcedureModel
、isProcedureDef
和 getVarModels
方法。这些是 Blockly 代码用于获取有关过程块的信息的钩子。
Blockly.Blocks['my_procedure_def'] = {
getProcedureModel() {
return this.model;
},
isProcedureDef() {
return true;
},
getVarModels() {
// If your procedure references variables
// then you should return those models here.
return [];
},
};
Blockly.Blocks['my_procedure_call'] = {
getProcedureModel() {
return this.model;
},
isProcedureDef() {
return false;
},
getVarModels() {
// If your procedure references variables
// then you should return those models here.
return [];
},
};
有更新时触发重新渲染
您的过程定义和过程调用块需要实现 doProcedureUpdate
方法。这是数据模型调用的钩子,用于指示过程块自行重新渲染。
Blockly.Blocks['my_procedure_def'] = {
doProcedureUpdate() {
this.setFieldValue('NAME', this.model.getName());
this.setFieldValue(
'PARAMS',
this.model.getParameters()
.map((p) => p.getName())
.join(','));
this.setFieldValue(
'RETURN', this.model.getReturnTypes().join(',');
}
};
Blockly.Blocks['my_procedure_call'] = {
doProcedureUpdate() {
// Similar to the def block above...
}
};
添加自定义序列化
过程块的序列化必须完成两项不同的操作。
- 从 JSON 加载时,您的块需要获取对其后备数据模型的引用,因为块和模型会分别进行序列化。
- 复制和粘贴过程块时,该块需要对其过程模型的完整状态进行序列化,以便能够复制/复制它。
这两个操作都通过 saveExtraState
和 loadExtraState
处理。另请注意,只有在使用 JSON 序列化系统时,才支持自定义过程块,因此我们只需要定义 JSON 序列化钩子。
import {
ObservableProcedureModel,
ObservableParameterModel,
isProcedureBlock
} from '@blockly/block-shareable-procedures';
Blockly.Blocks['my_procedure_def'] = {
saveExtraState() {
return {
'procedureId': this.model.getId(),
// These properties are only necessary for pasting.
'name': this.model.getName(),
'parameters': this.model.getParameters().map((p) => {
return {name: p.getName(), p.getId()};
}),
'returnTypes': this.model.getReturnTypes(),
};
},
loadExtraState(state) {
const id = state['procedureId']
const map = this.workspace.getProcedureMap();
// Grab a reference to the existing procedure model.
if (this.model.getId() != id && map.has(id) &&
(this.isInsertionMarker || this.noBlockHasClaimedModel_(id))) {
// Delete the existing model (created in init).
this.workspace.getProcedureMap().delete(model.getId());
// Grab a reference to the new model.
this.model = this.workspace.getProcedureMap()
.get(state['procedureId']);
this.doProcedureUpdate();
return;
}
// There is no existing procedure model (we are likely pasting), so
// generate it from JSON.
this.model
.setName(state['name'])
.setReturnTypes(state['returnTypes']);
for (const [i, param] of state['parameters'].entries()) {
this.model.insertParameter(
i,
new ObservableParameterModel(
this.workspace, param['name'], param['id']));
}
},
// We don't want to reference a model that some other procedure definition
// is already referencing.
noBlockHasClaimedModel_(procedureId) {
const model =
this.workspace.getProcedureMap().get(procedureId);
return this.workspace.getAllBlocks(false).every(
(block) =>
!isProcedureBlock(block) ||
!block.isProcedureDef() ||
block.getProcedureModel() !== model);
},
};
Blockly.Blocks['my_procedure_call'] = {
saveExtraState() {
return {
'procedureId': this.model.getId(),
};
},
loadExtraState(state) {
// Delete our existing model (created in init).
this.workspace.getProcedureMap().delete(model.getId());
// Grab a reference to the new model.
this.model = this.workspace.getProcedureMap()
.get(state['procedureId']);
if (this.model) this.doProcedureUpdate();
},
// Handle pasting after the procedure definition has been deleted.
onchange(event) {
if (event.type === Blockly.Events.BLOCK_CREATE &&
event.blockId === this.id) {
if(!this.model) { // Our procedure definition doesn't exist =(
this.dispose();
}
}
}
};
(可选)修改过程模型/签名
您还可以添加允许用户修改过程模型/签名的功能。调用 insertParameter
、deleteParameter
或 setReturnTypes
方法将自动触发您的块重新渲染(通过 doProcedureUpdate
)。
用于创建界面以修改过程模型的选项包括使用更改器(内置过程块使用)、具有点击处理程序的映像字段,以及 Blockly 外部完全外部的内容等。
将组成要素添加到工具箱
Blockly 的内置动态过程类别专用于 Blockly 的内置过程块。因此,为了能够访问块,您需要定义自己的自定义动态类别,并将其添加到工具箱中。
const proceduresFlyoutCallback = function(workspace) {
const blockList = [];
blockList.push({
'kind': 'block',
'type': 'my_procedure_def',
});
for (const model of
workspace.getProcedureMap().getProcedures()) {
blockList.push({
'kind': 'block',
'type': 'my_procedure_call',
'extraState': {
'procedureId': model.getId(),
},
});
}
return blockList;
};
myWorkspace.registerToolboxCategoryCallback(
'MY_PROCEDURES', proceduresFlyoutCallback);