Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: upgrade block defs to have JSO serialization hooks #5329

Merged
merged 9 commits into from
Aug 12, 2021
44 changes: 43 additions & 1 deletion blocks/lists.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ Blockly.Blocks['lists_create_with'] = {
},
/**
* Create XML to represent list inputs.
* Backwards compatible serialization implementation.
* @return {!Element} XML storage element.
* @this {Blockly.Block}
*/
Expand All @@ -141,13 +142,31 @@ Blockly.Blocks['lists_create_with'] = {
},
/**
* Parse XML to restore the list inputs.
* Backwards compatible serialization implementation.
* @param {!Element} xmlElement XML storage element.
* @this {Blockly.Block}
*/
domToMutation: function(xmlElement) {
this.itemCount_ = parseInt(xmlElement.getAttribute('items'), 10);
this.updateShape_();
},
/**
* Returns the state of this block as a JSON serializable object.
* @return {{itemCount: number}} The state of this block, ie the item count.
*/
saveExtraState: function() {
return {
'itemCount': this.itemCount_,
};
},
/**
* Applies the given state to this block.
* @param {*} state The state to apply to this block, ie the item count.
*/
loadExtraState: function(state) {
this.itemCount_ = state['itemCount'];
this.updateShape_();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@BeksOmega : any particular reason you call updateShape_ here instead of rebuildShape_, as domToMutation does?

(See this comment reopening issue #2037 for why I am curious.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Best guess is that I didn't understand what rebuildShape_ was trying to work around, and I figured updateShape_ was more efficient. I don't think there's any behavioral reason to prefer updateShape_

},
/**
* Populate the mutator's dialog with this block's components.
* @param {!Blockly.Workspace} workspace Mutator's workspace.
Expand Down Expand Up @@ -424,6 +443,12 @@ Blockly.Blocks['lists_getIndex'] = {
var isAt = (xmlElement.getAttribute('at') != 'false');
this.updateAt_(isAt);
},

// This block does not need JSO serialization hooks (saveExtraState and
// loadExtraState) because the state of this object is already encoded in the
// dropdown values.
// XML hooks are kept for backwards compatibility.

/**
* Switch between a value block and a statement block.
* @param {boolean} newStatement True if the block should be a statement.
Expand Down Expand Up @@ -584,6 +609,12 @@ Blockly.Blocks['lists_setIndex'] = {
var isAt = (xmlElement.getAttribute('at') != 'false');
this.updateAt_(isAt);
},

// This block does not need JSO serialization hooks (saveExtraState and
// loadExtraState) because the state of this object is already encoded in the
// dropdown values.
// XML hooks are kept for backwards compatibility.

/**
* Create or delete an input for the numeric index.
* @param {boolean} isAt True if the input should exist.
Expand Down Expand Up @@ -684,6 +715,12 @@ Blockly.Blocks['lists_getSublist'] = {
this.updateAt_(1, isAt1);
this.updateAt_(2, isAt2);
},

// This block does not need JSO serialization hooks (saveExtraState and
// loadExtraState) because the state of this object is already encoded in the
// dropdown values.
// XML hooks are kept for backwards compatibility.

/**
* Create or delete an input for a numeric index.
* This block has two such inputs, independent of each other.
Expand Down Expand Up @@ -857,5 +894,10 @@ Blockly.Blocks['lists_split'] = {
*/
domToMutation: function(xmlElement) {
this.updateType_(xmlElement.getAttribute('mode'));
}
},

// This block does not need JSO serialization hooks (saveExtraState and
// loadExtraState) because the state of this object is already encoded in the
// dropdown values.
// XML hooks are kept for backwards compatibility.
};
30 changes: 30 additions & 0 deletions blocks/logic.js
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ Blockly.Constants.Logic.CONTROLS_IF_MUTATOR_MIXIN = {

/**
* Create XML to represent the number of else-if and else inputs.
* Backwards compatible serialization implementation.
* @return {Element} XML storage element.
* @this {Blockly.Block}
*/
Expand All @@ -321,6 +322,7 @@ Blockly.Constants.Logic.CONTROLS_IF_MUTATOR_MIXIN = {
},
/**
* Parse XML to restore the else-if and else inputs.
* Backwards compatible serialization implementation.
* @param {!Element} xmlElement XML storage element.
* @this {Blockly.Block}
*/
Expand All @@ -329,6 +331,34 @@ Blockly.Constants.Logic.CONTROLS_IF_MUTATOR_MIXIN = {
this.elseCount_ = parseInt(xmlElement.getAttribute('else'), 10) || 0;
this.rebuildShape_();
},
/**
* Returns the state of this block as a JSON serializable object.
* @return {?{elseIfCount: (number|undefined), haseElse: (boolean|undefined)}}
* The state of this block, ie the else if count and else state.
*/
saveExtraState: function() {
if (!this.elseifCount_ && !this.elseCount_) {
return null;
}
var state = Object.create(null);
if (this.elseifCount_) {
state['elseIfCount'] = this.elseifCount_;
}
if (this.elseCount_) {
state['hasElse'] = true;
}
return state;
},
/**
* Applies the given state to this block.
* @param {*} state The state to apply to this block, ie the else if count and
* else state.
*/
loadExtraState: function(state) {
this.elseifCount_ = state['elseIfCount'] || 0;
this.elseCount_ = state['hasElse'] ? 1 : 0;
this.updateShape_();
},
/**
* Populate the mutator's dialog with this block's components.
* @param {!Blockly.Workspace} workspace Mutator's workspace.
Expand Down
17 changes: 16 additions & 1 deletion blocks/math.js
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,7 @@ Blockly.Extensions.register('math_op_tooltip',
Blockly.Constants.Math.IS_DIVISIBLEBY_MUTATOR_MIXIN = {
/**
* Create XML to represent whether the 'divisorInput' should be present.
* Backwards compatible serialization implementation.
* @return {!Element} XML storage element.
* @this {Blockly.Block}
*/
Expand All @@ -457,13 +458,20 @@ Blockly.Constants.Math.IS_DIVISIBLEBY_MUTATOR_MIXIN = {
},
/**
* Parse XML to restore the 'divisorInput'.
* Backwards compatible serialization implementation.
* @param {!Element} xmlElement XML storage element.
* @this {Blockly.Block}
*/
domToMutation: function(xmlElement) {
var divisorInput = (xmlElement.getAttribute('divisor_input') == 'true');
this.updateShape_(divisorInput);
},

// This block does not need JSO serialization hooks (saveExtraState and
// loadExtraState) because the state of this object is already encoded in the
// dropdown values.
// XML hooks are kept for backwards compatibility.

/**
* Modify this block to have (or not have) an input for 'is divisible by'.
* @param {boolean} divisorInput True if this block has a divisor input.
Expand Down Expand Up @@ -531,6 +539,7 @@ Blockly.Constants.Math.LIST_MODES_MUTATOR_MIXIN = {
},
/**
* Create XML to represent the output type.
* Backwards compatible serialization implementation.
* @return {!Element} XML storage element.
* @this {Blockly.Block}
*/
Expand All @@ -541,12 +550,18 @@ Blockly.Constants.Math.LIST_MODES_MUTATOR_MIXIN = {
},
/**
* Parse XML to restore the output type.
* Backwards compatible serialization implementation.
* @param {!Element} xmlElement XML storage element.
* @this {Blockly.Block}
*/
domToMutation: function(xmlElement) {
this.updateType_(xmlElement.getAttribute('op'));
}
},

// This block does not need JSO serialization hooks (saveExtraState and
// loadExtraState) because the state of this object is already encoded in the
// dropdown values.
// XML hooks are kept for backwards compatibility.
};

/**
Expand Down
92 changes: 91 additions & 1 deletion blocks/procedures.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ Blockly.Blocks['procedures_defnoreturn'] = {
},
/**
* Create XML to represent the argument inputs.
* Backwards compatible serialization implementation.
* @param {boolean=} opt_paramIds If true include the IDs of the parameter
* quarks. Used by Blockly.Procedures.mutateCallers for reconnection.
* @return {!Element} XML storage element.
Expand Down Expand Up @@ -124,6 +125,7 @@ Blockly.Blocks['procedures_defnoreturn'] = {
},
/**
* Parse XML to restore the argument inputs.
* Backwards compatible serialization implementation.
* @param {!Element} xmlElement XML storage element.
* @this {Blockly.Block}
*/
Expand All @@ -150,6 +152,54 @@ Blockly.Blocks['procedures_defnoreturn'] = {
// Show or hide the statement input.
this.setStatements_(xmlElement.getAttribute('statements') !== 'false');
},
/**
* Returns the state of this block as a JSON serializable object.
* @return {?{params: (!Array<{name: string, id: string}>|undefined),
* hasStatements: (boolean|undefined)}} The state of this block, eg the
* parameters and statements.
*/
saveExtraState: function() {
if (!this.argumentVarModels_.length && this.hasStatements_) {
return null;
}
var state = Object.create(null);
if (this.argumentVarModels_.length) {
state['params'] = [];
for (var i = 0; i < this.argumentVarModels_.length; i++) {
state['params'].push({
// We don't need to serialize the name, but just in case we decide
// to separate params from variables.
'name': this.argumentVarModels_[i].name,
'id': this.argumentVarModels_[i].getId()
});
}
}
if (!this.hasStatements_) {
state['hasStatements'] = false;
}
return state;
},
/**
* Applies the given state to this block.
* @param {*} state The state to apply to this block, eg the parameters and
* statements.
*/
loadExtraState: function(state) {
this.arguments_ = [];
this.argumentVarModels_ = [];
if (state['params']) {
for (var i = 0; i < state['params'].length; i++) {
var param = state['params'][i];
var variable = Blockly.Variables.getOrCreateVariablePackage(
this.workspace, param['id'], param['name'], '');
this.arguments_.push(variable.name);
this.argumentVarModels_.push(variable);
}
}
this.updateParams_();
Blockly.Procedures.mutateCallers(this);
this.setStatements_(state['hasStatements'] === false ? false : true);
},
/**
* Populate the mutator's dialog with this block's components.
* @param {!Blockly.Workspace} workspace Mutator's workspace.
Expand Down Expand Up @@ -436,6 +486,8 @@ Blockly.Blocks['procedures_defreturn'] = {
updateParams_: Blockly.Blocks['procedures_defnoreturn'].updateParams_,
mutationToDom: Blockly.Blocks['procedures_defnoreturn'].mutationToDom,
domToMutation: Blockly.Blocks['procedures_defnoreturn'].domToMutation,
saveExtraState: Blockly.Blocks['procedures_defnoreturn'].saveExtraState,
loadExtraState: Blockly.Blocks['procedures_defnoreturn'].loadExtraState,
decompose: Blockly.Blocks['procedures_defnoreturn'].decompose,
compose: Blockly.Blocks['procedures_defnoreturn'].compose,
/**
Expand Down Expand Up @@ -776,6 +828,7 @@ Blockly.Blocks['procedures_callnoreturn'] = {
},
/**
* Create XML to represent the (non-editable) name and arguments.
* Backwards compatible serialization implementation.
* @return {!Element} XML storage element.
* @this {Blockly.Block}
*/
Expand All @@ -791,6 +844,7 @@ Blockly.Blocks['procedures_callnoreturn'] = {
},
/**
* Parse XML to restore the (non-editable) name and parameters.
* Backwards compatible serialization implementation.
* @param {!Element} xmlElement XML storage element.
* @this {Blockly.Block}
*/
Expand All @@ -807,6 +861,34 @@ Blockly.Blocks['procedures_callnoreturn'] = {
}
this.setProcedureParameters_(args, paramIds);
},
/**
* Returns the state of this block as a JSON serializable object.
* @return {{name: string, params:(!Array<string>|undefined)}} The state of
* this block, ie the params and procedure name.
*/
saveExtraState: function() {
var state = Object.create(null);
state['name'] = this.getProcedureCall();
if (this.arguments_.length) {
state['params'] = this.arguments_;
}
return state;
},
/**
* Applies the given state to this block.
* @param {*} state The state to apply to this block, ie the params and
* procedure name.
*/
loadExtraState: function(state) {
this.renameProcedure(this.getProcedureCall(), state['name']);
const params = state['params'];
if (params) {
const ids = [];
ids.length = params.length;
ids.fill(null);
this.setProcedureParameters_(params, ids);
}
},
/**
* Return all variables referenced by this block.
* @return {!Array<string>} List of variable names.
Expand Down Expand Up @@ -975,6 +1057,8 @@ Blockly.Blocks['procedures_callreturn'] = {
updateShape_: Blockly.Blocks['procedures_callnoreturn'].updateShape_,
mutationToDom: Blockly.Blocks['procedures_callnoreturn'].mutationToDom,
domToMutation: Blockly.Blocks['procedures_callnoreturn'].domToMutation,
saveExtraState: Blockly.Blocks['procedures_callnoreturn'].saveExtraState,
loadExtraState: Blockly.Blocks['procedures_callnoreturn'].loadExtraState,
getVars: Blockly.Blocks['procedures_callnoreturn'].getVars,
getVarModels: Blockly.Blocks['procedures_callnoreturn'].getVarModels,
onchange: Blockly.Blocks['procedures_callnoreturn'].onchange,
Expand Down Expand Up @@ -1026,14 +1110,20 @@ Blockly.Blocks['procedures_ifreturn'] = {
.appendField(Blockly.Msg['PROCEDURES_DEFRETURN_RETURN']);
}
},

// This block does not need JSO serialization hooks (saveExtraState and
// loadExtraState) because the state of this block is already encoded in the
// block's position in the workspace.
// XML hooks are kept for backwards compatibility.

/**
* Called whenever anything on the workspace changes.
* Add warning if this flow block is not nested inside a loop.
* @param {!Blockly.Events.Abstract} _e Change event.
* @this {Blockly.Block}
*/
onchange: function(_e) {
if (!this.workspace.isDragging || this.workspace.isDragging()) {
if (this.workspace.isDragging && this.workspace.isDragging()) {
return; // Don't change state at the start of a drag.
}
var legal = false;
Expand Down
Loading