在创建新的字段类型之前,请考虑其他 方法 来自定义字段如果您的应用需要 新值类型,或者您想为现有值类型创建新界面, 可能需要创建新的字段类型
如需创建新字段,请执行以下操作:
- 实现构造函数。
- 注册 JSON 密钥并实现
fromJson
。 - 处理块上界面和事件的初始化 监听器。
- 处理事件监听器的处置(系统会为 您)。
- 实现价值处理。
- 为您的字段的值添加文本表示法,以方便访问。
- 添加其他功能,例如: <ph type="x-smartling-placeholder">
- 配置字段的其他方面,例如:
<ph type="x-smartling-placeholder">
- </ph>
- 可修改且可序列化 属性
- Cursor
本部分假定您已阅读并熟悉 字段。
有关自定义字段的示例,请参阅自定义字段 演示 ,了解所有最新动态。
实现构造函数
字段的构造函数负责设置字段的初始值 并视需要设置本地 验证器。自定义 字段的构造函数在源块初始化期间调用,无论 源块是使用 JSON 还是 JavaScript 定义的。因此,自定义 字段在构造期间无权访问源块。
以下代码示例可创建一个名为 GenericField
的自定义字段:
class GenericField extends Blockly.Field {
constructor(value, validator) {
super(value, validator);
this.SERIALIZABLE = true;
}
}
方法签名
字段构造函数通常采用值和本地验证器。该值为
可选;如果没有传递值(或者传递的值使类失败)
验证),则将使用父类的默认值。对于
默认 Field
类,其值为 null
。如果您不想使用该默认
则务必传递合适的值。验证器参数仅
用于可编辑字段,并且通常标记为可选。了解详情
请参阅验证工具部分,
文档。
结构
构造函数内的逻辑应遵循以下流程:
- 调用继承的超级构造函数(所有自定义字段都应从
Blockly.Field
或其某个子类)正确初始化该值 ,并为字段设置本地验证器。 - 如果您的字段可序列化,请在 构造函数。可修改字段必须可序列化,并且字段可修改 因此您可能应该将此属性设为 true,除非您知道 且不应该可序列化
- 可选:应用其他自定义设置(例如标签字段) 允许传递 CSS 类,而该类随后会应用于文本)。
JSON 和注册
在 JSON 块中
定义,
字段由字符串描述(例如 field_number
、field_textinput
)。
Blockly 维护从这些字符串到字段对象的映射,并调用
fromJson
。
调用 Blockly.fieldRegistry.register
以将您的字段类型添加到此映射中,
传入 field 类作为第二个参数:
Blockly.fieldRegistry.register('field_generic', GenericField);
您还需要定义 fromJson
函数。您的实现应
首先解引用任何字符串
表格
使用
replaceMessageReferences,
然后将值传递给构造函数。
GenericField.fromJson = function(options) {
const value = Blockly.utils.parsing.replaceMessageReferences(
options['value']);
return new CustomFields.GenericField(value);
};
正在初始化
构建字段时,它基本上只包含一个值。 初始化是构建 DOM、模型(如果字段 拥有模型),而事件是绑定的。
On-Block 显示屏
在初始化期间,您需要负责创建所需的一切 。
默认值、背景和文本
默认的 initView
函数会创建一个浅色 rect
元素和一个
text
元素。如果您希望字段中既包含上述两项内容
好东西,请先调用父类 initView
函数,然后再添加其余的
DOM 元素如果您希望字段中包含其中一种(但不是全部)
可以使用 createBorderRect_
或 createTextElement_
函数。
自定义 DOM 构建
如果您的字段是通用文本字段(例如 Text
输入),
系统会为您处理 DOM 构建。否则,您需要替换
initView
函数,用于创建您在创建元素期间所需的 DOM 元素
字段的未来呈现效果
例如,下拉列表字段可能同时包含图片和文本。在 initView
中,
会创建一个图片元素和一个文本元素。之后在render_
期间
它会根据元素类型显示当前元素并隐藏另一个元素
选项。
要创建 DOM 元素,可使用
Blockly.utils.dom.createSvgElement
方法,或者使用传统的 DOM 创建方法
方法。
字段的块显示要求如下:
- 所有 DOM 元素都必须是该字段的
fieldGroup_
的子项。字段 系统会自动创建群组 - 所有 DOM 元素都必须位于报告的 字段尺寸内。
请参阅 渲染 部分,详细了解如何自定义和更新区块显示内容。
添加文本符号
如果您想向字段的文本(例如
角度
字段的度数符号),您可以将符号元素(通常包含在
<tspan>
) 直接传递到该字段的 textElement_
。
输入事件
默认情况下,字段会注册提示事件和 mousedown 事件(用于
正在显示
编辑者)。
如果您想监听其他类型的事件(例如,如果您要处理
),您应替换该字段的 bindEvents_
函数。
bindEvents_() {
// Call the superclass function to preserve the default behavior as well.
super.bindEvents_();
// Then register your own additional event listeners.
this.mouseDownWrapper_ =
Blockly.browserEvents.conditionalBind(this.getClickTarget_(), 'mousedown', this,
function(event) {
this.originalMouseX_ = event.clientX;
this.isMouseDown_ = true;
this.originalValue_ = this.getValue();
event.stopPropagation();
}
);
this.mouseMoveWrapper_ =
Blockly.browserEvents.conditionalBind(document, 'mousemove', this,
function(event) {
if (!this.isMouseDown_) {
return;
}
var delta = event.clientX - this.originalMouseX_;
this.setValue(this.originalValue_ + delta);
}
);
this.mouseUpWrapper_ =
Blockly.browserEvents.conditionalBind(document, 'mouseup', this,
function(_event) {
this.isMouseDown_ = false;
}
);
}
要绑定到事件,您通常应使用
Blockly.utils.browserEvents.conditionalBind
函数。这种绑定事件的方法会在
拖动。如果您希望处理程序甚至在拖动过程中运行
您可以使用
Blockly.browserEvents.bind
函数。
处理中
如果您在该字段的 bindEvents_
内注册了任何自定义事件监听器
函数,需要在 dispose
函数内取消注册。
如果您正确初始化
查看
(通过将所有 DOM 元素附加到 fieldGroup_
),则将
系统会自动处置此字段的 DOM。
值处理
→ 有关字段值及其文本的信息,请参阅对 字段。
验证顺序
实现类验证器
字段应仅接受特定值。例如,数字字段只能 颜色字段只接受颜色等 通过类和本地 验证器。类 验证程序遵循的规则与本地验证程序相同,只是它也会运行 在 构造函数 因此,它不应引用源块。
如需实现字段的类验证器,请替换 doClassValidation_
函数。
doClassValidation_(newValue) {
if (typeof newValue != 'string') {
return null;
}
return newValue;
};
处理有效值
如果传递给包含 setValue
的字段的值是有效的,您会收到一个
doValueUpdate_
回调。默认情况下,doValueUpdate_
函数:
- 将
value_
属性设置为newValue
。 - 设置
isDirty_
属性设置为true
。
如果您只需存储该值,并且不想进行任何自定义处理,
您无需替换 doValueUpdate_
。
或者,如果您想执行以下操作:
newValue
的自定义存储空间。- 根据
newValue
更改其他属性。 - 保存当前值是否有效。
您需要替换 doValueUpdate_
:
doValueUpdate_(newValue) {
super.doValueUpdate_(newValue);
this.displayValue_ = newValue;
this.isValueValid_ = true;
}
处理无效值
如果传递给包含 setValue
的字段的值无效,您将收到
doValueInvalid_
回调。默认情况下,doValueInvalid_
函数
什么都不用做。这意味着,默认情况下不会显示无效值。它还
表示不会重新呈现该字段,因为
isDirty_
属性。
如果您希望显示无效值,则应替换 doValueInvalid_
。
在大多数情况下,您应将 displayValue_
属性设置为
值无效,已设置
isDirty_
true
,然后将
render_
让区块显示内容根据 displayValue_
(而不是
value_
。
doValueInvalid_(newValue) {
this.displayValue_ = newValue;
this.isDirty_ = true;
this.isValueValid_ = false;
}
多部分值
当您的字段包含多部分值(例如列表、矢量、对象)时, 可能希望像处理各个值一样处理这些部分。
doClassValidation_(newValue) {
if (FieldTurtle.PATTERNS.indexOf(newValue.pattern) == -1) {
newValue.pattern = null;
}
if (FieldTurtle.HATS.indexOf(newValue.hat) == -1) {
newValue.hat = null;
}
if (FieldTurtle.NAMES.indexOf(newValue.turtleName) == -1) {
newValue.turtleName = null;
}
if (!newValue.pattern || !newValue.hat || !newValue.turtleName) {
this.cachedValidatedValue_ = newValue;
return null;
}
return newValue;
}
在上面的示例中,newValue
的每个属性都进行了单独验证。然后
doClassValidation_
函数的末尾(如果有任何单个属性
无效,该值会先缓存到 cacheValidatedValue_
属性,
返回 null
(无效)。通过逐个验证缓存对象
属性可让
doValueInvalid_
函数来分别处理它们,只需执行
!this.cacheValidatedValue_.property
检查,而不是重新验证每个
属性。
这种验证多部分值的模式也可在 local 验证程序 目前还无法强制执行此模式。
isDirty_
isDirty_
是
setValue
函数以及字段的其他部分,以判断字段是否需要
重新渲染。如果字段的显示值发生了变化,isDirty_
通常应该
设为 true
。
文本
→ 有关字段中的文本使用位置及其不同之处的信息 请参阅对 字段。
如果字段中的文本与字段的值不同,
替换
getText
函数
以提供正确的文本。
getText() {
let text = this.value_.turtleName + ' wearing a ' + this.value_.hat;
if (this.value_.hat == 'Stovepipe' || this.value_.hat == 'Propeller') {
text += ' hat';
}
return text;
}
创建编辑器
如果您定义 showEditor_
函数,Blockly 将自动监听
在适当的时间点击并调用 showEditor_
。您可以
将其封装在两个称为“DropDownDiv”的特殊 div 中
以及悬浮在 Blockly 界面其余部分的 WidgetDiv。
DropDownDiv 与 WidgetDiv
DropDownDiv
用于提供位于已连接 Box 内的编辑器
字段。它会在保持不动的同时,自动将其调整为靠近拍摄场地
在可见边界内。角度选择器和颜色选择器都是
DropDownDiv
。
WidgetDiv
用于
提供不位于机箱内的编辑器。数字字段使用
WidgetDiv,用于通过 HTML 文本输入框覆盖该字段。虽然 DropDownDiv
负责为您处理定位,而 WidgetDiv 不会。元素需要
手动定位。坐标系以相对于
窗口左上角文本输入编辑器是一个很好的例子,
WidgetDiv
。
DropDownDiv 示例代码
showEditor_() {
// Create the widget HTML
this.editor_ = this.dropdownCreate_();
Blockly.DropDownDiv.getContentDiv().appendChild(this.editor_);
// Set the dropdown's background colour.
// This can be used to make it match the colour of the field.
Blockly.DropDownDiv.setColour('white', 'silver');
// Show it next to the field. Always pass a dispose function.
Blockly.DropDownDiv.showPositionedByField(
this, this.disposeWidget_.bind(this));
}
WidgetDiv 示例代码
showEditor_() {
// Show the div. This automatically closes the dropdown if it is open.
// Always pass a dispose function.
Blockly.WidgetDiv.show(
this, this.sourceBlock_.RTL, this.widgetDispose_.bind(this));
// Create the widget HTML.
var widget = this.createWidget_();
Blockly.WidgetDiv.getDiv().appendChild(widget);
}
清理
DropDownDiv 和 WidgetDiv 句柄都破坏了微件 HTML 但您需要手动处理 并应用这些元素
widgetDispose_() {
for (let i = this.editorListeners_.length, listener;
listener = this.editorListeners_[i]; i--) {
Blockly.browserEvents.unbind(listener);
this.editorListeners_.pop();
}
}
dispose
函数在 DropDownDiv
的 null
上下文中调用。已开启
在 WidgetDiv
的上下文中调用它的 WidgetDiv
。无论是哪种情况
最好使用
绑定
函数,如上面的 DropDownDiv
所示
和 WidgetDiv
个示例。
→ 有关处理(而非特定于编辑器)的信息,请参阅 处置。
更新区块显示
render_
函数用于更新该字段的块显示,以匹配
内部值。
常见示例包括:
- 更改文本(下拉菜单)
- 更改颜色(颜色)
默认值
默认的 render_
函数会将显示文本设置为
getDisplayText_
函数。getDisplayText_
函数会返回字段的 value_
属性
转换为字符串,即为了遵循文本上限而截断字符串后
。
如果使用默认的块上显示屏,而默认文本行为
适合您的字段,因此您无需替换 render_
。
默认文本行为适用于字段,但字段位于阻止状态
具有额外的静态元素,您可以调用默认的 render_
函数,但您仍需要覆盖该函数,以更新字段的
尺寸。
默认文本行为不适用于您的字段,或字段的
区块显示屏上还有其他动态元素,您需要自定义
render_
函数。
自定义渲染
如果默认呈现行为不适用于您的字段,您需要 自定义呈现行为这可能涉及设置自定义 显示文字、更改图片元素、更新背景颜色等操作。
所有 DOM 属性更改都是合法的,仅需要注意以下两点:
render_() {
switch(this.value_.hat) {
case 'Stovepipe':
this.stovepipe_.style.display = '';
break;
case 'Crown':
this.crown_.style.display = '';
break;
case 'Mask':
this.mask_.style.display = '';
break;
case 'Propeller':
this.propeller_.style.display = '';
break;
case 'Fedora':
this.fedora_.style.display = '';
break;
}
switch(this.value_.pattern) {
case 'Dots':
this.shellPattern_.setAttribute('fill', 'url(#polkadots)');
break;
case 'Stripes':
this.shellPattern_.setAttribute('fill', 'url(#stripes)');
break;
case 'Hexagons':
this.shellPattern_.setAttribute('fill', 'url(#hexagons)');
break;
}
this.textContent_.nodeValue = this.value_.turtleName;
this.updateSize_();
}
正在更新大小
更新字段的 size_
属性非常重要,因为它会通知
如何定位字段。找出问题的最佳方式
size_
应该是什么样子的。
updateSize_() {
const bbox = this.movableGroup_.getBBox();
let width = bbox.width;
let height = bbox.height;
if (this.borderRect_) {
width += this.constants_.FIELD_BORDER_RECT_X_PADDING * 2;
height += this.constants_.FIELD_BORDER_RECT_X_PADDING * 2;
this.borderRect_.setAttribute('width', width);
this.borderRect_.setAttribute('height', height);
}
// Note how both the width and the height can be dynamic.
this.size_.width = width;
this.size_.height = height;
}
匹配的方块颜色
如果您希望字段中的元素颜色与块的颜色一致
您应替换 applyColour
方法。您需要
通过块的样式属性获取颜色。
applyColour() {
const sourceBlock = this.sourceBlock_;
if (sourceBlock.isShadow()) {
this.arrow_.style.fill = sourceBlock.style.colourSecondary;
} else {
this.arrow_.style.fill = sourceBlock.style.colourPrimary;
}
}
更新可编辑性
updateEditable
函数可用于更改字段的显示方式
具体取决于它是否可以编辑默认函数使其成为
在可修改/无法修改的背景中是否有悬停响应(边框)。
块上显示的显示大小不应根据其可编辑性而改变,但
所有其他更改都允许。
updateEditable() {
if (!this.fieldGroup_) {
// Not initialized yet.
return;
}
super.updateEditable();
const group = this.getClickTarget_();
if (!this.isCurrentlyEditable()) {
group.style.cursor = 'not-allowed';
} else {
group.style.cursor = this.CURSOR;
}
}
序列化
序列化就是 字段状态,以便稍后可以将其重新加载到工作区。
工作区的状态始终包含该字段的值,但也可能包含 包括其他状态,例如字段界面的状态。例如,如果您的 字段是一个可缩放的地图,可让用户选择国家/地区,那么您可以 对缩放级别进行序列化处理。
如果您的字段可序列化,则必须将 SERIALIZABLE
属性设置为
true
。
Blockly 为字段提供了两组序列化钩子。一对钩子 适用于新的 JSON 序列化系统,而另一对适用于 旧 XML 序列化系统。
saveState
和loadState
saveState
和 loadState
是适用于新 JSON 的序列化钩子
序列化系统
在某些情况下,您不需要提供这些参数,因为
是否有效。如果 (1) 您的字段是基础类的直接子类
Blockly.Field
类,(2) 您的值是可序列化的 JSON
type,并且 (3) 只需要
序列化该值,那么默认实现就行了!
否则,您的 saveState
函数应返回可序列化的 JSON
对象/值,用于表示字段的状态。您的loadState
函数应接受相同的 JSON 可序列化对象/值,并将其应用于
字段。
saveState() {
return {
'country': this.getValue(), // Value state
'zoom': this.getZoomLevel(), // UI state
};
}
loadState(state) {
this.setValue(state['country']);
this.setZoomLevel(state['zoom']);
}
完整的序列化和后备数据
saveState
还会接收一个可选参数 doFullSerialization
。这是
通常引用由其他
序列化器(如支持数据模型)。参数表示
在块被反序列化时,引用的状态将不可用,因此
字段应该执行所有的序列化操作。例如,当
或块复制粘贴时。
这有两个常见用例:
- 当单个块被加载到后备数据的工作区时 模型不存在,则该字段自身状态有足够的信息 创建新的数据模型。
- 复制粘贴块时,该字段始终会创建新的后备 而不是引用现有模型。
使用该字段的一个字段是内置变量字段。通常,它会序列化
它所引用的变量的 ID,但如果 doFullSerialization
为 true
它会对其所有状态进行序列化
saveState(doFullSerialization) {
const state = {'id': this.variable_.getId()};
if (doFullSerialization) {
state['name'] = this.variable_.name;
state['type'] = this.variable_.type;
}
return state;
}
loadState(state) {
const variable = Blockly.Variables.getOrCreateVariablePackage(
this.getSourceBlock().workspace,
state['id'],
state['name'], // May not exist.
state['type']); // May not exist.
this.setValue(variable.getId());
}
这个变量字段是为了确保在将变量加载到工作区时 如果其变量不存在,则可以创建要引用的新变量。
toXml
和fromXml
toXml
和 fromXml
是适用于旧 XML 的序列化钩子
序列化系统请仅在必要的情况下(例如,
),否则请使用 saveState
和
loadState
。
您的 toXml
函数应返回一个 XML 节点,该节点表示
字段。您的 fromXml
函数应接受相同的 XML 节点并应用
toXml(fieldElement) {
fieldElement.textContent = this.getValue();
fieldElement.setAttribute('zoom', this.getZoomLevel());
return fieldElement;
}
fromXml(fieldElement) {
this.setValue(fieldElement.textContent);
this.setZoomLevel(fieldElement.getAttribute('zoom'));
}
可修改且可序列化的属性
EDITABLE
属性决定了该字段是否应具有用于指示该字段的界面
可以与之互动默认值为 true
。
SERIALIZABLE
属性决定了是否应序列化该字段。它
默认为 false
。如果此属性为 true
,您可能需要提供
序列化和反序列化函数(请参阅
序列化)。
自定义光标
CURSOR
属性决定了用户将鼠标悬停在上方时看到的光标
字段。它应该是有效的 CSS 游标字符串。默认为光标
由 .blocklyDraggable
(即抓取游标)定义。