mirror of
https://github.com/scratchfoundation/scratch-blocks.git
synced 2025-08-28 22:10:31 -04:00
Ensure that if a procedure call exists, so does its definition.
Specifically, if a call is copied, then the def is deleted, then the call is pasted, the def should be created.
This commit is contained in:
parent
2b191e897e
commit
2dab19c015
5 changed files with 77 additions and 43 deletions
|
@ -59,16 +59,6 @@ Blockly.Blocks['procedures_defnoreturn'] = {
|
|||
this.setStatements_(true);
|
||||
this.statementConnection_ = null;
|
||||
},
|
||||
/**
|
||||
* Initialization of the block has completed, clean up anything that may be
|
||||
* inconsistent as a result of the XML loading.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
validate: function() {
|
||||
var name = Blockly.Procedures.findLegalName(
|
||||
this.getFieldValue('NAME'), this);
|
||||
this.setFieldValue(name, 'NAME');
|
||||
},
|
||||
/**
|
||||
* Add or remove the statement block from this function definition.
|
||||
* @param {boolean} hasStatements True if a statement block is needed.
|
||||
|
@ -244,16 +234,6 @@ Blockly.Blocks['procedures_defnoreturn'] = {
|
|||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Dispose of any callers.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
dispose: function() {
|
||||
var name = this.getFieldValue('NAME');
|
||||
Blockly.Procedures.disposeCallers(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:
|
||||
|
@ -371,13 +351,11 @@ Blockly.Blocks['procedures_defreturn'] = {
|
|||
this.statementConnection_ = null;
|
||||
},
|
||||
setStatements_: Blockly.Blocks['procedures_defnoreturn'].setStatements_,
|
||||
validate: Blockly.Blocks['procedures_defnoreturn'].validate,
|
||||
updateParams_: Blockly.Blocks['procedures_defnoreturn'].updateParams_,
|
||||
mutationToDom: Blockly.Blocks['procedures_defnoreturn'].mutationToDom,
|
||||
domToMutation: Blockly.Blocks['procedures_defnoreturn'].domToMutation,
|
||||
decompose: Blockly.Blocks['procedures_defnoreturn'].decompose,
|
||||
compose: Blockly.Blocks['procedures_defnoreturn'].compose,
|
||||
dispose: Blockly.Blocks['procedures_defnoreturn'].dispose,
|
||||
/**
|
||||
* Return the signature of this procedure definition.
|
||||
* @return {!Array} Tuple containing three elements:
|
||||
|
@ -668,6 +646,72 @@ Blockly.Blocks['procedures_callnoreturn'] = {
|
|||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Procedure calls cannot exist without the corresponding procedure
|
||||
* definition. Enforce this link whenever an event is fired.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
onchange: function(event) {
|
||||
if (!this.workspace || this.workspace.isFlyout) {
|
||||
// Block is deleted or is in a flyout.
|
||||
return;
|
||||
}
|
||||
if (event.type == Blockly.Events.CREATE &&
|
||||
event.ids.indexOf(this.id) != -1) {
|
||||
// Look for the case where a procedure call was created (usually through
|
||||
// paste) and there is no matching definition. In this case, create
|
||||
// an empty definition block with the correct signature.
|
||||
var name = this.getProcedureCall();
|
||||
var def = Blockly.Procedures.getDefinition(name, this.workspace);
|
||||
if (def && (def.type != this.defType_ ||
|
||||
JSON.stringify(def.arguments_) != JSON.stringify(this.arguments_))) {
|
||||
// The signatures don't match.
|
||||
def = null;
|
||||
}
|
||||
if (!def) {
|
||||
Blockly.Events.setGroup(event.group);
|
||||
/**
|
||||
* Create matching definition block.
|
||||
* <xml>
|
||||
* <block type="procedures_defreturn" x="10" y="20">
|
||||
* <mutation name="test">
|
||||
* <arg name="x"></arg>
|
||||
* </mutation>
|
||||
* <field name="NAME">test</field>
|
||||
* </block>
|
||||
* </xml>
|
||||
*/
|
||||
var xml = goog.dom.createDom('xml');
|
||||
var block = goog.dom.createDom('block');
|
||||
block.setAttribute('type', this.defType_);
|
||||
var xy = this.getRelativeToSurfaceXY();
|
||||
var x = xy.x + Blockly.SNAP_RADIUS * (this.RTL ? -1 : 1);
|
||||
var y = xy.y + Blockly.SNAP_RADIUS * 2;
|
||||
block.setAttribute('x', x);
|
||||
block.setAttribute('y', y);
|
||||
var mutation = this.mutationToDom();
|
||||
block.appendChild(mutation);
|
||||
var field = goog.dom.createDom('field');
|
||||
field.setAttribute('name', 'NAME');
|
||||
field.appendChild(document.createTextNode(this.getProcedureCall()));
|
||||
block.appendChild(field);
|
||||
xml.appendChild(block);
|
||||
Blockly.Xml.domToWorkspace(xml, this.workspace);
|
||||
Blockly.Events.setGroup(false);
|
||||
}
|
||||
} else if (event.type == Blockly.Events.DELETE) {
|
||||
// Look for the case where a procedure definition has been deleted,
|
||||
// leaving this block (a procedure call) orphaned. In this case, delete
|
||||
// the orphan.
|
||||
var name = this.getProcedureCall();
|
||||
var def = Blockly.Procedures.getDefinition(name, this.workspace);
|
||||
if (!def) {
|
||||
Blockly.Events.setGroup(event.group);
|
||||
this.dispose(true, false);
|
||||
Blockly.Events.setGroup(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Add menu option to find the definition block for this call.
|
||||
* @param {!Array} options List of menu options to add to.
|
||||
|
@ -683,7 +727,8 @@ Blockly.Blocks['procedures_callnoreturn'] = {
|
|||
def && def.select();
|
||||
};
|
||||
options.push(option);
|
||||
}
|
||||
},
|
||||
defType_: 'procedures_defnoreturn'
|
||||
};
|
||||
|
||||
Blockly.Blocks['procedures_callreturn'] = {
|
||||
|
@ -710,7 +755,10 @@ Blockly.Blocks['procedures_callreturn'] = {
|
|||
mutationToDom: Blockly.Blocks['procedures_callnoreturn'].mutationToDom,
|
||||
domToMutation: Blockly.Blocks['procedures_callnoreturn'].domToMutation,
|
||||
renameVar: Blockly.Blocks['procedures_callnoreturn'].renameVar,
|
||||
customContextMenu: Blockly.Blocks['procedures_callnoreturn'].customContextMenu
|
||||
onchange: Blockly.Blocks['procedures_callnoreturn'].onchange,
|
||||
customContextMenu:
|
||||
Blockly.Blocks['procedures_callnoreturn'].customContextMenu,
|
||||
defType_: 'procedures_defreturn'
|
||||
};
|
||||
|
||||
Blockly.Blocks['procedures_ifreturn'] = {
|
||||
|
|
|
@ -277,7 +277,9 @@ Blockly.onKeyDown_ = function(e) {
|
|||
if (e.keyCode == 86) {
|
||||
// 'v' for paste.
|
||||
if (Blockly.clipboardXml_) {
|
||||
Blockly.Events.setGroup(true);
|
||||
Blockly.clipboardSource_.paste(Blockly.clipboardXml_);
|
||||
Blockly.Events.setGroup(false);
|
||||
}
|
||||
} else if (e.keyCode == 90) {
|
||||
// 'z' for undo 'Z' is for redo.
|
||||
|
|
|
@ -234,19 +234,6 @@ Blockly.Procedures.getCallers = function(name, workspace) {
|
|||
return callers;
|
||||
};
|
||||
|
||||
/**
|
||||
* When a procedure definition is disposed of, find and dispose of all its
|
||||
* callers.
|
||||
* @param {string} name Name of deleted procedure definition.
|
||||
* @param {!Blockly.Workspace} workspace The workspace to delete callers from.
|
||||
*/
|
||||
Blockly.Procedures.disposeCallers = function(name, workspace) {
|
||||
var callers = Blockly.Procedures.getCallers(name, workspace);
|
||||
for (var i = 0; i < callers.length; i++) {
|
||||
callers[i].dispose(true, false);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* When a procedure definition changes its parameters, find and edit all its
|
||||
* callers.
|
||||
|
@ -282,7 +269,8 @@ Blockly.Procedures.mutateCallers = function(defBlock) {
|
|||
* @return {Blockly.Block} The procedure definition block, or null not found.
|
||||
*/
|
||||
Blockly.Procedures.getDefinition = function(name, workspace) {
|
||||
var blocks = workspace.getAllBlocks();
|
||||
// Assume that a procedure definition is a top block.
|
||||
var blocks = workspace.getTopBlocks(false);
|
||||
for (var i = 0; i < blocks.length; i++) {
|
||||
if (blocks[i].getProcedureDef) {
|
||||
var tuple = blocks[i].getProcedureDef();
|
||||
|
|
|
@ -740,7 +740,7 @@ Blockly.WorkspaceSvg.prototype.onMouseWheel_ = function(e) {
|
|||
* containing the blocks on the workspace.
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.getBlocksBoundingBox = function() {
|
||||
var topBlocks = this.getTopBlocks();
|
||||
var topBlocks = this.getTopBlocks(false);
|
||||
// There are no blocks, return empty rectangle.
|
||||
if (!topBlocks.length) {
|
||||
return {x: 0, y: 0, width: 0, height: 0};
|
||||
|
|
|
@ -532,10 +532,6 @@ Blockly.Xml.domToBlockHeadless_ = function(xmlBlock, workspace) {
|
|||
}
|
||||
block.setShadow(true);
|
||||
}
|
||||
// Give the block a chance to clean up any initial inputs.
|
||||
if (block.validate) {
|
||||
block.validate();
|
||||
}
|
||||
return block;
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue