Files
2024-07-19 10:16:00 +08:00

1164 lines
44 KiB
JavaScript

import * as Blockly from 'blockly/core';
const CLASS_HUE = 345;//'#af5180'//330;
const PROPERTY_HUE = 345;
const METHOD_HUE = 345;
const OBJECT_HUE = 345;
export const class_make = {
init: function () {
this.appendDummyInput()
.appendField(Blockly.Msg.CREATE_CLASS)
.appendField(new Blockly.FieldTextInput(""), "VAR");
this.appendStatementInput("data")
.setCheck(null)
.setAlign(Blockly.inputs.Align.RIGHT);
this.setColour(CLASS_HUE);
this.setTooltip("");
this.setHelpUrl("");
},
class_getVars: function () {
var varValue = this.getFieldValue('VAR');
if (varValue == null) {
return [];
}
return varValue.split(",");
},
class_renameVar: function (oldName, newName) {
if (Blockly.Names.equals(oldName, this.getFieldValue('VAR'))) {
this.setFieldValue(newName, 'VAR');
}
}
};
export const class_make_with_base = {
init: function () {
this.appendValueInput("NAME")
.appendField(Blockly.Msg.CREATE_CLASS)
.appendField(new Blockly.FieldTextInput(""), "VAR")
.appendField(" " + Blockly.Msg.FATHER_CLASS + ":");
this.appendStatementInput("data")
.setCheck(null)
.setAlign(Blockly.inputs.Align.RIGHT);
this.setColour(CLASS_HUE);
this.setTooltip("");
this.setHelpUrl("");
},
class_getVars: function () {
var varValue = this.getFieldValue('VAR');
if (varValue == null) {
return [];
}
return varValue.split(",");
},
class_renameVar: function (oldName, newName) {
if (Blockly.Names.equals(oldName, this.getFieldValue('VAR'))) {
this.setFieldValue(newName, 'VAR');
}
}
};
export const class_get = {
init: function () {
this.setColour(CLASS_HUE);
this.appendDummyInput()
.appendField(Blockly.Msg.MIXPY_CLASS)
.appendField(new Blockly.FieldTextInput(''), 'VAR');
this.setOutput(true);
this.setTooltip(Blockly.Msg.VARIABLES_GET_TOOLTIP);
},
class_getVars: function () {
return [this.getFieldValue('VAR')];
},
class_renameVar: function (oldName, newName) {
if (Blockly.Names.equals(oldName, this.getFieldValue('VAR'))) {
this.setFieldValue(newName, 'VAR');
}
}
};
export const property_set = {
init: function () {
this.setColour(PROPERTY_HUE);
this.appendValueInput('VALUE');
this.appendValueInput('DATA')
.appendField(Blockly.Msg.MIXPY_ATTRIBUTE_OF)
.appendField(new Blockly.FieldTextInput(''), 'VAR')
.appendField(" " + Blockly.Msg.MIXLY_VALUE2 + " ");
this.setInputsInline(true);
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setTooltip(Blockly.Msg.VARIABLES_SET_TOOLTIP);
},
property_getVars: function () {
var varValue = this.getFieldValue('VAR');
if (varValue == null) {
return [];
}
return varValue.split(",");
},
property_renameVar: function (oldName, newName) {
if (Blockly.Names.equals(oldName, this.getFieldValue('VAR'))) {
this.setFieldValue(newName, 'VAR');
}
}
};
export const property_get = {
init: function () {
this.setColour(PROPERTY_HUE);
this.appendValueInput('VALUE');
this.appendDummyInput()
.appendField(Blockly.Msg.MIXPY_ATTRIBUTE_OF)
.appendField(new Blockly.FieldTextInput(''), 'VAR');
this.setInputsInline(true);
this.setOutput(true);
this.setTooltip(Blockly.Msg.VARIABLES_GET_TOOLTIP);
},
property_getVars: function () {
return [this.getFieldValue('VAR')];
},
property_renameVar: function (oldName, newName) {
if (Blockly.Names.equals(oldName, this.getFieldValue('VAR'))) {
this.setFieldValue(newName, 'VAR');
}
}
};
/**
* Common HSV hue for all blocks in this category.
*/
export const method_procedures_defnoreturn = {
/**
* Block for defining a procedure with no return value.
* @this Blockly.Block
*/
init: function () {
this.setColour(METHOD_HUE);
var nameField = new Blockly.FieldTextInput(
Blockly.Msg.PROCEDURES_DEFNORETURN_PROCEDURE,
Blockly.Class.prorename);
nameField.setSpellcheck(false);
this.appendDummyInput()
.appendField(Blockly.Msg.MIXPY_CREATE_METHOD)
.appendField(Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE)
.appendField(nameField, 'NAME')
.appendField('', 'PARAMS');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setMutator(new Blockly.icons.MutatorIcon(['method_procedures_mutatorarg'], this));
this.setTooltip(Blockly.Msg.PROCEDURES_DEFNORETURN_TOOLTIP);
this.arguments_ = [];
this.argumentstype_ = [];//新增
this.setStatements_(true);
this.statementConnection_ = null;
},
/**
* Add or remove the statement block from this function definition.
* @param {boolean} hasStatements True if a statement block is needed.
* @this Blockly.Block
*/
setStatements_: function (hasStatements) {
if (this.hasStatements_ === hasStatements) {
return;
}
if (hasStatements) {
this.appendStatementInput('STACK')
.appendField(Blockly.Msg.CONTROLS_REPEAT_INPUT_DO);
if (this.getInput('RETURN')) {
this.moveInputBefore('STACK', 'RETURN');
}
} else {
this.removeInput('STACK', true);
}
this.hasStatements_ = hasStatements;
},
/**
* Update the display of parameters for this procedure definition block.
* Display a warning if there are duplicately named parameters.
* @private
* @this Blockly.Block
*/
updateParams_: function () {
// Check for duplicated arguments.
var badArg = false;
var hash = {};
for (var i = 0; i < this.arguments_.length; i++) {
if (hash['arg_' + this.arguments_[i].toLowerCase()]) {
badArg = true;
break;
}
hash['arg_' + this.arguments_[i].toLowerCase()] = true;
}
if (badArg) {
this.setWarningText(Blockly.Msg.PROCEDURES_DEF_DUPLICATE_WARNING);
} else {
this.setWarningText(null);
}
// Merge the arguments into a human-readable list.
var paramString = '';
if (this.arguments_.length) {
paramString = Blockly.Msg.PROCEDURES_BEFORE_PARAMS +
' ' + this.arguments_.join(', ');
}
// The params field is deterministic based on the mutation,
// no need to fire a change event.
Blockly.Events.disable();
this.setFieldValue(paramString, 'PARAMS');
Blockly.Events.enable();
},
/**
* Create XML to represent the argument inputs.
* @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.
* @this Blockly.Block
*/
mutationToDom: function () {
var container = document.createElement('mutation');
for (var i = 0; i < this.arguments_.length; i++) {
var parameter = document.createElement('arg');
parameter.setAttribute('name', this.arguments_[i]);
//parameter.setAttribute('vartype', this.argumentstype_[i]);//新增
container.appendChild(parameter);
}
// Save whether the statement input is visible.
if (!this.hasStatements_) {
container.setAttribute('statements', 'false');
}
return container;
},
/**
* Parse XML to restore the argument inputs.
* @param {!Element} xmlElement XML storage element.
* @this Blockly.Block
*/
domToMutation: function (xmlElement) {
this.arguments_ = [];
this.argumentstype_ = [];//新增
for (var i = 0; xmlElement.childNodes[i]; i++) {
let childNode = xmlElement.childNodes[i];
if (childNode.nodeName.toLowerCase() == 'arg') {
this.arguments_.push(childNode.getAttribute('name'));
//this.argumentstype_.push(childNode.getAttribute('vartype'));//新增
}
}
this.updateParams_();
Blockly.Class.mutateCallers(this);
// Show or hide the statement input.
this.setStatements_(xmlElement.getAttribute('statements') !== 'false');
},
/**
* Populate the mutator's dialog with this block's components.
* @param {!Blockly.Workspace} workspace Mutator's workspace.
* @return {!Blockly.Block} Root block in mutator.
* @this Blockly.Block
*/
decompose: function (workspace) {
var containerBlock = workspace.newBlock('method_procedures_mutatorcontainer');
containerBlock.initSvg();
// Check/uncheck the allow statement box.
if (this.getInput('RETURN')) {
containerBlock.setFieldValue(this.hasStatements_ ? 'TRUE' : 'FALSE',
'STATEMENTS');
} else {
containerBlock.getInput('STATEMENT_INPUT').setVisible(false);
}
// Parameter list.
var connection = containerBlock.getInput('STACK').connection;
for (var i = 0; i < this.arguments_.length; i++) {
var paramBlock = workspace.newBlock('method_procedures_mutatorarg');
paramBlock.initSvg();
paramBlock.setFieldValue(this.arguments_[i], 'NAME');
//paramBlock.setFieldValue(this.argumentstype_[i], 'TYPEVAR');//新增
// Store the old location.
paramBlock.oldLocation = i;
connection.connect(paramBlock.previousConnection);
connection = paramBlock.nextConnection;
}
// Initialize procedure's callers with blank IDs.
//Blockly.Class.promutateCallers(this);
return containerBlock;
},
/**
* Reconfigure this block based on the mutator dialog's components.
* @param {!Blockly.Block} containerBlock Root block in mutator.
* @this Blockly.Block
*/
compose: function (containerBlock) {
// Parameter list.
this.arguments_ = [];
this.paramIds_ = [];
this.argumentstype_ = [];//新增
var paramBlock = containerBlock.getInputTargetBlock('STACK');
while (paramBlock) {
this.arguments_.push(paramBlock.getFieldValue('NAME'));
//this.argumentstype_.push(paramBlock.getFieldValue('TYPEVAR'));//新增
this.paramIds_.push(paramBlock.id);
paramBlock = paramBlock.nextConnection &&
paramBlock.nextConnection.targetBlock();
}
this.updateParams_();
Blockly.Class.promutateCallers(this);
// Show/hide the statement input.
var hasStatements = containerBlock.getFieldValue('STATEMENTS');
if (hasStatements !== null) {
hasStatements = hasStatements == 'TRUE';
if (this.hasStatements_ != hasStatements) {
if (hasStatements) {
this.setStatements_(true);
// Restore the stack, if one was saved.
this.statementConnection_ && this.statementConnection_.reconnect(this, 'STACK');
this.statementConnection_ = null;
} else {
// Save the stack, then disconnect it.
var stackConnection = this.getInput('STACK').connection;
this.statementConnection_ = stackConnection.targetConnection;
if (this.statementConnection_) {
var stackBlock = stackConnection.targetBlock();
stackBlock.unplug();
stackBlock.bumpNeighbours_();
}
this.setStatements_(false);
}
}
}
},
/**
* Dispose of any callers.
* @this Blockly.Block
*/
/*
dispose: function() {
var name = this.getFieldValue('NAME');
Blockly.Class.prodisposeCallers(name, this.workspace);
// Call parent's destructor.
this.constructor.prototype.dispose.apply(this, arguments);
},*/
/**
* Return the signature of this procedure definition.
* @return {!Array} Tuple containing three elements:
* - the name of the defined procedure,
* - a list of all its arguments,
* - that it DOES NOT have a return value.
* @this Blockly.Block
*/
method_getProcedureDef: function () {
var surround_parent = this.getSurroundParent();
var arg_data = '';
for (var i = 0; i < this.arguments_.length; i++) {
arg_data = arg_data + '_' + this.arguments_[i];
}
if (surround_parent && ((surround_parent.type == 'class_make') || (surround_parent.type == 'class_make_with_base'))) {
var class_name = surround_parent.getFieldValue('VAR');
return [this.getFieldValue('NAME'), this.arguments_, false, this.getFieldValue('NAME') + '_' + class_name, this.getFieldValue('NAME') + arg_data];
}
return [this.getFieldValue('NAME'), this.arguments_, false, this.getFieldValue('NAME'), this.getFieldValue('NAME') + arg_data];
},
/**
* Return all variables referenced by this block.
* @return {!Array.<string>} List of variable names.
* @this Blockly.Block
*/
getVars: function () {
return this.arguments_;
},
/**
* Notification that a variable is renaming.
* If the name matches one of this block's variables, rename it.
* @param {string} oldName Previous name of variable.
* @param {string} newName Renamed variable.
* @this Blockly.Block
*/
renameVar: function (oldName, newName) {
var change = false;
for (var i = 0; i < this.arguments_.length; i++) {
if (Blockly.Names.equals(oldName, this.arguments_[i])) {
this.arguments_[i] = newName;
change = true;
}
}
if (change) {
this.updateParams_();
// Update the mutator's variables if the mutator is open.
if (this.mutator.isVisible()) {
var blocks = this.mutator.workspace_.getAllBlocks();
for (var i = 0; blocks[i]; i++) {
let block = blocks[i];
if (block.type == 'method_procedures_mutatorarg' &&
Blockly.Names.equals(oldName, block.getFieldValue('NAME'))) {
block.setFieldValue(newName, 'NAME');
}
}
}
}
},
/**
* Add custom menu options to this block's context menu.
* @param {!Array} options List of menu options to add to.
* @this Blockly.Block
*/
customContextMenu: function (options) {
// Add option to create caller.
var option = { enabled: true };
var name = this.getFieldValue('NAME');
option.text = Blockly.Msg.PROCEDURES_CREATE_DO.replace('%1', name);
var xmlMutation = Blockly.utils.xml.createElement('mutation');
xmlMutation.setAttribute('name', name);
for (var i = 0; i < this.arguments_.length; i++) {
var xmlArg = Blockly.utils.xml.createElement('arg');
xmlArg.setAttribute('name', this.arguments_[i]);
//xmlArg.setAttribute('type', this.argumentstype_[i]);//新增
xmlMutation.appendChild(xmlArg);
}
var xmlBlock = Blockly.utils.xml.createElement('block', null, xmlMutation);
xmlBlock.setAttribute('type', this.callType_);
option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock);
options.push(option);
// Add options to create getters for each parameter.
if (!this.isCollapsed()) {
for (var i = 0; i < this.arguments_.length; i++) {
var option = { enabled: true };
var name = this.arguments_[i];
option.text = Blockly.Msg.VARIABLES_SET_CREATE_GET.replace('%1', name);
var xmlField = Blockly.utils.xml.createElement('field', null, name);
xmlField.setAttribute('name', 'VAR');
xmlField.setAttribute('type', 'TYPEVAR');//新增
var xmlBlock = Blockly.utils.xml.createElement('block', null, xmlField);
xmlBlock.setAttribute('type', 'variables_get');
option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock);
options.push(option);
}
}
},
callType_: 'method_procedures_callnoreturn'
};
export const method_procedures_defreturn = {
/**
* Block for defining a procedure with a return value.
* @this Blockly.Block
*/
init: function () {
this.setColour(METHOD_HUE);
var nameField = new Blockly.FieldTextInput(
Blockly.Msg.PROCEDURES_DEFRETURN_PROCEDURE,
Blockly.Class.prorename);
nameField.setSpellcheck(false);
this.appendDummyInput()
.appendField(Blockly.Msg.MIXPY_CREATE_METHOD)
.appendField(Blockly.Msg.PROCEDURES_DEFRETURN_TITLE)
.appendField(nameField, 'NAME')
.appendField('', 'PARAMS');
this.appendValueInput('RETURN')
.setAlign(Blockly.inputs.Align.RIGHT)
.appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setMutator(new Blockly.icons.MutatorIcon(['method_procedures_mutatorarg'], this));
this.setTooltip(Blockly.Msg.PROCEDURES_DEFRETURN_TOOLTIP);
this.arguments_ = [];
this.setStatements_(true);
this.statementConnection_ = null;
},
setStatements_: method_procedures_defnoreturn.setStatements_,
updateParams_: method_procedures_defnoreturn.updateParams_,
mutationToDom: method_procedures_defnoreturn.mutationToDom,
domToMutation: method_procedures_defnoreturn.domToMutation,
decompose: method_procedures_defnoreturn.decompose,
compose: method_procedures_defnoreturn.compose,
dispose: method_procedures_defnoreturn.dispose,
/**
* Return the signature of this procedure definition.
* @return {!Array} Tuple containing three elements:
* - the name of the defined procedure,
* - a list of all its arguments,
* - that it DOES have a return value.
* @this Blockly.Block
*/
method_getProcedureDef: function () {
var surround_parent = this.getSurroundParent();
var arg_data = '';
for (var i = 0; i < this.arguments_.length; i++) {
arg_data = arg_data + '_' + this.arguments_[i];
}
if (surround_parent && ((surround_parent.type == 'class_make') || (surround_parent.type == 'class_make_with_base'))) {
var class_name = surround_parent.getFieldValue('VAR');
return [this.getFieldValue('NAME'), this.arguments_, true, this.getFieldValue('NAME') + '_' + class_name, this.getFieldValue('NAME') + arg_data];
}
return [this.getFieldValue('NAME'), this.arguments_, true, this.getFieldValue('NAME'), this.getFieldValue('NAME') + arg_data];
},
getVars: method_procedures_defnoreturn.getVars,
renameVar: method_procedures_defnoreturn.renameVar,
customContextMenu: method_procedures_defnoreturn.customContextMenu,
callType_: 'method_procedures_callreturn'
};
export const method_procedures_mutatorcontainer = {
/**
* Mutator block for procedure container.
* @this Blockly.Block
*/
init: function () {
this.setColour(METHOD_HUE);
this.appendDummyInput()
.appendField(Blockly.Msg.PROCEDURES_MUTATORCONTAINER_TITLE);
this.appendStatementInput('STACK');
this.appendDummyInput('STATEMENT_INPUT')
.appendField(Blockly.Msg.PROCEDURES_ALLOW_STATEMENTS)
.appendField(new Blockly.FieldCheckbox('TRUE'), 'STATEMENTS');
this.setTooltip(Blockly.Msg.PROCEDURES_MUTATORCONTAINER_TOOLTIP);
this.contextMenu = false;
}
};
export const method_procedures_mutatorarg = {
/**
* Mutator block for procedure argument.
* @this Blockly.Block
*/
init: function () {
this.setColour(METHOD_HUE);
this.appendDummyInput()
.appendField(Blockly.Msg.PROCEDURES_BEFORE_PARAMS)
//.appendField(new Blockly.FieldDropdown([[Blockly.Msg.MIXLY_NUMBER, 'number'], [Blockly.Msg.LANG_MATH_STRING, 'string'], [Blockly.Msg.LANG_MATH_BOOLEAN, 'boolean'], [Blockly.Msg.MIXLY_MICROBIT_JS_TYPE_ARRAY_NUMBER, 'Array<number>'], [Blockly.Msg.MIXLY_MICROBIT_JS_TYPE_ARRAY_STRING, 'Array<string>']]), 'TYPEVAR')
.appendField(new Blockly.FieldTextInput('x', this.validator_), 'NAME');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setTooltip(Blockly.Msg.PROCEDURES_MUTATORARG_TOOLTIP);
this.contextMenu = false;
},
/**
* Obtain a valid name for the procedure.
* Merge runs of whitespace. Strip leading and trailing whitespace.
* Beyond this, all names are legal.
* @param {string} newVar User-supplied name.
* @return {?string} Valid name, or null if a name was not specified.
* @private
* @this Blockly.Block
*/
validator_: function (newVar) {
newVar = newVar.replace(/[\s\xa0]+/g, ' ').replace(/^ | $/g, '');
return newVar || null;
}
};
export const method_procedures_callnoreturn = {
/**
* Block for calling a procedure with no return value.
* @this Blockly.Block
*/
init: function () {
this.setHelpUrl(Blockly.Msg.PROCEDURES_CALLNORETURN_HELPURL);
this.setColour(METHOD_HUE);
this.appendValueInput('DATA');
this.appendDummyInput('TOPROW')
.appendField(Blockly.Msg.MIXPY_EXECUTION_METHOD)
.appendField(this.id, 'NAME');
this.setInputsInline(true);
this.setPreviousStatement(true);
this.setNextStatement(true);
// Tooltip is set in method_renameProcedure.
this.arguments_ = [];
this.quarkConnections_ = {};
this.quarkIds_ = null;
},
/**
* Returns the name of the procedure this block calls.
* @return {string} Procedure name.
* @this Blockly.Block
*/
method_getProcedureCall: function () {
// The NAME field is guaranteed to exist, null will never be returned.
return /** @type {string} */ (this.getFieldValue('NAME'));
},
/**
* Notification that a procedure is renaming.
* If the name matches this block's procedure, rename it.
* @param {string} oldName Previous name of procedure.
* @param {string} newName Renamed procedure.
* @this Blockly.Block
*/
method_renameProcedure: function (oldName, newName) {
if (Blockly.Names.equals(oldName, this.method_getProcedureCall())) {
this.setFieldValue(newName, 'NAME');
this.setTooltip(
(this.outputConnection ? Blockly.Msg.PROCEDURES_CALLRETURN_TOOLTIP :
Blockly.Msg.PROCEDURES_CALLNORETURN_TOOLTIP)
.replace('%1', newName));
}
},
/**
* Notification that the procedure's parameters have changed.
* @param {!Array.<string>} paramNames New param names, e.g. ['x', 'y', 'z'].
* @param {!Array.<string>} paramIds IDs of params (consistent for each
* parameter through the life of a mutator, regardless of param renaming),
* e.g. ['piua', 'f8b_', 'oi.o'].
* @private
* @this Blockly.Block
*/
setProcedureParameters_: function (paramNames, paramIds) {
// Data structures:
// this.arguments = ['x', 'y']
// Existing param names.
// this.quarkConnections_ {piua: null, f8b_: Blockly.Connection}
// Look-up of paramIds to connections plugged into the call block.
// this.quarkIds_ = ['piua', 'f8b_']
// Existing param IDs.
// Note that quarkConnections_ may include IDs that no longer exist, but
// which might reappear if a param is reattached in the mutator.
var defBlock = Blockly.Class.progetDefinition(this.method_getProcedureCall(),
this.workspace);
const mutatorIcon = defBlock && defBlock.getIcon(Blockly.icons.MutatorIcon.TYPE);
const mutatorOpen =
mutatorIcon && mutatorIcon.bubbleIsVisible();
if (!mutatorOpen) {
this.quarkConnections_ = {};
this.quarkIds_ = null;
}
if (!paramIds) {
// Reset the quarks (a mutator is about to open).
return;
}
if (this.arguments_.length) {
if (Blockly.Names.equals(this.arguments_, paramNames)) {
// No change.
this.quarkIds_ = paramIds;
return;
}
}
if (paramIds.length != paramNames.length) {
throw 'Error: paramNames and paramIds must be the same length.';
}
this.setCollapsed(false);
if (!this.quarkIds_) {
// Initialize tracking for this block.
this.quarkConnections_ = {};
if (paramNames.join('\n') == this.arguments_.join('\n')) {
// No change to the parameters, allow quarkConnections_ to be
// populated with the existing connections.
this.quarkIds_ = paramIds;
} else {
this.quarkIds_ = [];
}
}
// Switch off rendering while the block is rebuilt.
var savedRendered = this.rendered;
this.rendered = false;
// Update the quarkConnections_ with existing connections.
for (var i = 1; i < this.arguments_.length; i++) {
var input = this.getInput('ARG' + i);
if (input) {
var connection = input.connection.targetConnection;
this.quarkConnections_[this.quarkIds_[i]] = connection;
if (mutatorOpen && connection &&
paramIds.indexOf(this.quarkIds_[i]) == -1) {
// This connection should no longer be attached to this block.
connection.disconnect();
connection.getSourceBlock().bumpNeighbours_();
}
}
}
// Rebuild the block's arguments.
this.arguments_ = [].concat(paramNames);
this.updateShape_();
this.quarkIds_ = paramIds;
// Reconnect any child blocks.
if (this.quarkIds_) {
for (var i = 1; i < this.arguments_.length; i++) {
var quarkId = this.quarkIds_[i];
if (quarkId in this.quarkConnections_) {
var connection = this.quarkConnections_[quarkId];
if (connection && !connection.reconnect(this, 'ARG' + i)) {
// Block no longer exists or has been attached elsewhere.
delete this.quarkConnections_[quarkId];
}
}
}
}
// Restore rendering and show the changes.
this.rendered = savedRendered;
if (this.rendered) {
this.render();
}
},
/**
* Modify this block to have the correct number of arguments.
* @private
* @this Blockly.Block
*/
updateShape_: function () {
for (var i = 1; i < this.arguments_.length; i++) {
var field = this.getField('ARGNAME' + i);
if (field) {
// Ensure argument name is up to date.
// The argument name field is deterministic based on the mutation,
// no need to fire a change event.
Blockly.Events.disable();
field.setValue(this.arguments_[i]);
Blockly.Events.enable();
} else {
// Add new input.
field = new Blockly.FieldLabel(this.arguments_[i]);
var input = this.appendValueInput('ARG' + i)
.setAlign(Blockly.inputs.Align.RIGHT)
.appendField(field, 'ARGNAME' + i);
input.init();
}
}
// Remove deleted inputs.
while (this.getInput('ARG' + i)) {
this.removeInput('ARG' + i);
i++;
}
// Add 'with:' if there are parameters, remove otherwise.
var topRow = this.getInput('TOPROW');
if (topRow) {
if (this.arguments_.length - 1) {
if (!this.getField('WITH')) {
topRow.appendField(Blockly.Msg.PROCEDURES_CALL_BEFORE_PARAMS, 'WITH');
topRow.init();
}
} else {
if (this.getField('WITH')) {
topRow.removeField('WITH');
}
}
}
},
/**
* Create XML to represent the (non-editable) name and arguments.
* @return {!Element} XML storage element.
* @this Blockly.Block
*/
mutationToDom: function () {
var container = document.createElement('mutation');
container.setAttribute('name', this.method_getProcedureCall());
for (var i = 0; i < this.arguments_.length; i++) {
var parameter = document.createElement('arg');
parameter.setAttribute('name', this.arguments_[i]);
container.appendChild(parameter);
}
return container;
},
/**
* Parse XML to restore the (non-editable) name and parameters.
* @param {!Element} xmlElement XML storage element.
* @this Blockly.Block
*/
domToMutation: function (xmlElement) {
var name = xmlElement.getAttribute('name');
this.method_renameProcedure(this.method_getProcedureCall(), name);
var args = [];
var paramIds = [];
for (var i = 0; xmlElement.childNodes[i]; i++) {
let childNode = xmlElement.childNodes[i];
if (childNode.nodeName.toLowerCase() == 'arg') {
args.push(childNode.getAttribute('name'));
paramIds.push(childNode.getAttribute('paramId'));
}
}
this.setProcedureParameters_(args, paramIds);
},
/**
* Notification that a variable is renaming.
* If the name matches one of this block's variables, rename it.
* @param {string} oldName Previous name of variable.
* @param {string} newName Renamed variable.
* @this Blockly.Block
*/
renameVar: function (oldName, newName) {
for (var i = 1; i < this.arguments_.length; i++) {
if (Blockly.Names.equals(oldName, this.arguments_[i])) {
this.arguments_[i] = newName;
this.getField('ARGNAME' + i).setValue(newName);
}
}
},
/**
* Add menu option to find the definition block for this call.
* @param {!Array} options List of menu options to add to.
* @this Blockly.Block
*/
customContextMenu: function (options) {
var option = { enabled: true };
option.text = Blockly.Msg.PROCEDURES_HIGHLIGHT_DEF;
var name = this.method_getProcedureCall();
var workspace = this.workspace;
option.callback = function () {
var def = Blockly.Class.progetDefinition(name, workspace);
def && def.select();
};
options.push(option);
}
};
export const method_procedures_callreturn = {
/**
* Block for calling a procedure with a return value.
* @this Blockly.Block
*/
init: function () {
this.setHelpUrl(Blockly.Msg.PROCEDURES_CALLRETURN_HELPURL);
this.setColour(METHOD_HUE);
this.appendValueInput('DATA');
this.appendDummyInput('TOPROW')
.appendField(Blockly.Msg.MIXPY_EXECUTION_METHOD)
.appendField(Blockly.Msg.PROCEDURES_CALLRETURN_CALL)
.appendField('', 'NAME');
this.setOutput(true);
this.setInputsInline(true);
// Tooltip is set in domToMutation.
this.arguments_ = [];
this.quarkConnections_ = {};
this.quarkIds_ = null;
},
method_getProcedureCall: method_procedures_callnoreturn.method_getProcedureCall,
method_renameProcedure: method_procedures_callnoreturn.method_renameProcedure,
setProcedureParameters_:
method_procedures_callnoreturn.setProcedureParameters_,
updateShape_: method_procedures_callnoreturn.updateShape_,
mutationToDom: method_procedures_callnoreturn.mutationToDom,
domToMutation: method_procedures_callnoreturn.domToMutation,
renameVar: method_procedures_callnoreturn.renameVar,
customContextMenu: method_procedures_callnoreturn.customContextMenu
};
export const method_procedures_ifreturn = {
/**
* Block for conditionally returning a value from a procedure.
* @this Blockly.Block
*/
init: function () {
this.setColour(METHOD_HUE);
this.appendValueInput('CONDITION')
.setCheck(Boolean)
.appendField(Blockly.Msg.CONTROLS_IF_MSG_IF);
this.appendValueInput('VALUE')
.appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);
this.setInputsInline(true);
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setTooltip(Blockly.Msg.PROCEDURES_IFRETURN_TOOLTIP);
this.hasReturnValue_ = true;
},
/**
* Create XML to represent whether this block has a return value.
* @return {!Element} XML storage element.
* @this Blockly.Block
*/
mutationToDom: function () {
var container = document.createElement('mutation');
container.setAttribute('value', Number(this.hasReturnValue_));
return container;
},
/**
* Parse XML to restore whether this block has a return value.
* @param {!Element} xmlElement XML storage element.
* @this Blockly.Block
*/
domToMutation: function (xmlElement) {
var value = xmlElement.getAttribute('value');
this.hasReturnValue_ = (value == 1);
if (!this.hasReturnValue_) {
this.removeInput('VALUE');
this.appendDummyInput('VALUE')
.appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);
}
},
/**
* 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 () {
var legal = false;
// Is the block nested in a procedure?
var block = this;
do {
if (this.FUNCTION_TYPES.indexOf(block.type) != -1) {
legal = true;
break;
}
block = block.getSurroundParent();
} while (block);
if (legal) {
// If needed, toggle whether this block has a return value.
if (block.type == 'method_procedures_defnoreturn' && this.hasReturnValue_) {
this.removeInput('VALUE');
this.appendDummyInput('VALUE')
.appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);
this.hasReturnValue_ = false;
} else if (block.type == 'method_procedures_defreturn' &&
!this.hasReturnValue_) {
this.removeInput('VALUE');
this.appendValueInput('VALUE')
.appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);
this.hasReturnValue_ = true;
}
this.setWarningText(null);
} else {
this.setWarningText(Blockly.Msg.PROCEDURES_IFRETURN_WARNING);
}
},
/**
* List of block types that are functions and thus do not need warnings.
* To add a new function type add this to your code:
* procedures_ifreturn.FUNCTION_TYPES.push('custom_func');
*/
FUNCTION_TYPES: ['method_procedures_defnoreturn', 'method_procedures_defreturn']
};
export const method_procedures_return = {
/**
* Block for conditionally returning a value from a procedure.
* @this Blockly.Block
*/
init: function () {
this.setColour(METHOD_HUE);
// this.appendValueInput('CONDITION')
// .setCheck(Boolean)
// .appendField(Blockly.Msg.CONTROLS_IF_MSG_IF);
this.appendValueInput('VALUE')
.appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);
this.setInputsInline(true);
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setTooltip(Blockly.Msg.PROCEDURES_IFRETURN_TOOLTIP);
this.hasReturnValue_ = true;
},
/**
* Create XML to represent whether this block has a return value.
* @return {!Element} XML storage element.
* @this Blockly.Block
*/
mutationToDom: function () {
var container = document.createElement('mutation');
container.setAttribute('value', Number(this.hasReturnValue_));
return container;
},
/**
* Parse XML to restore whether this block has a return value.
* @param {!Element} xmlElement XML storage element.
* @this Blockly.Block
*/
domToMutation: function (xmlElement) {
var value = xmlElement.getAttribute('value');
this.hasReturnValue_ = (value == 1);
if (!this.hasReturnValue_) {
this.removeInput('VALUE');
this.appendDummyInput('VALUE')
.appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);
}
},
/**
* 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 () {
var legal = false;
// Is the block nested in a procedure?
var block = this;
do {
if (this.FUNCTION_TYPES.indexOf(block.type) != -1) {
legal = true;
break;
}
block = block.getSurroundParent();
} while (block);
if (legal) {
// If needed, toggle whether this block has a return value.
if (block.type == 'method_procedures_defnoreturn' && this.hasReturnValue_) {
this.removeInput('VALUE');
this.appendDummyInput('VALUE')
.appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);
this.hasReturnValue_ = false;
} else if (block.type == 'method_procedures_defreturn' &&
!this.hasReturnValue_) {
this.removeInput('VALUE');
this.appendValueInput('VALUE')
.appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);
this.hasReturnValue_ = true;
}
this.setWarningText(null);
} else {
this.setWarningText(Blockly.Msg.PROCEDURES_IFRETURN_WARNING);
}
},
/**
* List of block types that are functions and thus do not need warnings.
* To add a new function type add this to your code:
* procedures_ifreturn.FUNCTION_TYPES.push('custom_func');
*/
FUNCTION_TYPES: ['method_procedures_defnoreturn', 'method_procedures_defreturn']
};
export const object_set = {
init: function () {
this.appendDummyInput("EMPTY")
.appendField(Blockly.Msg.MIXPY_OBJECT)
.appendField(new Blockly.FieldTextInput(""), "VAR11")
.appendField(" " + Blockly.Msg.MIXLY_VALUE2 + " " + Blockly.Msg.MIXPY_CLASS)
.appendField(new Blockly.FieldTextInput(""), "VAR10");
this.itemCount_ = 0;
this.updateShape_();
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(OBJECT_HUE);
this.setMutator(new Blockly.icons.MutatorIcon(['object_set_with_item'], this));
this.setTooltip("");
this.setHelpUrl("");
},
class_getVars: function () {
return [this.getFieldValue('VAR10')];
},
class_renameVar: function (oldName, newName) {
if (Blockly.Names.equals(oldName, this.getFieldValue('VAR10'))) {
this.setFieldValue(newName, 'VAR10');
}
},
object_getVars: function () {
var varValue = this.getFieldValue('VAR11');
if (varValue == null) {
return [];
}
return varValue.split(",");
},
object_renameVar: function (oldName, newName) {
if (Blockly.Names.equals(oldName, this.getFieldValue('VAR11'))) {
this.setFieldValue(newName, 'VAR11');
}
},
mutationToDom: function () {
var container = document.createElement('mutation');
container.setAttribute('items', this.itemCount_);
return container;
},
domToMutation: function (xmlElement) {
this.itemCount_ = parseInt(xmlElement.getAttribute('items'), 10);
this.updateShape_();
},
decompose: function (workspace) {
var containerBlock =
workspace.newBlock('object_set_with_container');
containerBlock.initSvg();
var connection = containerBlock.getInput('STACK').connection;
for (var i = 0; i < this.itemCount_; i++) {
var itemBlock = workspace.newBlock('object_set_with_item');
itemBlock.initSvg();
connection.connect(itemBlock.previousConnection);
connection = itemBlock.nextConnection;
}
return containerBlock;
},
compose: function (containerBlock) {
var itemBlock = containerBlock.getInputTargetBlock('STACK');
// Count number of inputs.
var connections = [];
var i = 0;
while (itemBlock) {
connections[i] = itemBlock.valueConnection_;
itemBlock = itemBlock.nextConnection &&
itemBlock.nextConnection.targetBlock();
i++;
}
this.itemCount_ = i;
this.updateShape_();
// Reconnect any child blocks.
for (var i = 0; i < this.itemCount_; i++) {
if (connections[i]) {
this.getInput('ADD' + i).connection.connect(connections[i]);
}
}
},
saveConnections: function (containerBlock) {
var itemBlock = containerBlock.getInputTargetBlock('STACK');
var i = 0;
while (itemBlock) {
var input = this.getInput('ADD' + i);
itemBlock.valueConnection_ = input && input.connection.targetConnection;
i++;
itemBlock = itemBlock.nextConnection &&
itemBlock.nextConnection.targetBlock();
}
},
updateShape_: function () {
// Delete everything.
var i = 0;
while (this.getInput('ADD' + i)) {
this.removeInput('ADD' + i);
i++;
}
// Rebuild block.
if (this.itemCount_ != 0) {
for (var i = 0; i < this.itemCount_; i++) {
var input = this.appendValueInput('ADD' + i);
input.setAlign(Blockly.inputs.Align.RIGHT)
input.appendField(Blockly.Msg.MIXLY_PARAMS + (i + 1) + ":");
}
}
}
};
export const object_set_with_item = {
/**
* Mutator bolck for adding items.
* @this Blockly.Block
*/
init: function () {
this.setColour(OBJECT_HUE);
this.appendDummyInput()
.appendField(Blockly.Msg.MIXPY_ADD_PARAMETERS);
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setTooltip(Blockly.Msg.LISTS_CREATE_WITH_ITEM_TOOLTIP);
this.contextMenu = false;
}
};
export const object_set_with_container = {
/**
* Mutator block for list container.
* @this Blockly.Block
*/
init: function () {
this.setColour(OBJECT_HUE);
this.appendDummyInput()
.appendField(Blockly.Msg.MIXLY_PARAMS);
this.appendStatementInput('STACK');
this.setTooltip("");
this.contextMenu = false;
}
};
export const object_get = {
init: function () {
this.setColour(OBJECT_HUE);
this.appendDummyInput()
.appendField(Blockly.Msg.MIXPY_OBJECT)
.appendField(new Blockly.FieldTextInput(''), 'VAR');
this.setOutput(true);
this.setTooltip(Blockly.Msg.VARIABLES_GET_TOOLTIP);
},
object_getVars: function () {
return [this.getFieldValue('VAR')];
},
object_renameVar: function (oldName, newName) {
if (Blockly.Names.equals(oldName, this.getFieldValue('VAR'))) {
this.setFieldValue(newName, 'VAR');
}
}
};