Remove setProcedureParameters from API (use domToMutation instead).

This commit is contained in:
Neil Fraser 2016-03-22 10:48:13 -07:00
parent d086634394
commit 39fbd24659
6 changed files with 101 additions and 76 deletions

View file

@ -119,6 +119,13 @@ Blockly.Blocks['lists_create_with'] = {
itemBlock = itemBlock.nextConnection &&
itemBlock.nextConnection.targetBlock();
}
// Disconnect any children that don't belong.
for (var i = 0; i < this.itemCount_; i++) {
var connection = this.getInput('ADD' + i).connection.targetConnection;
if (connection && connections.indexOf(connection) == -1) {
connection.disconnect();
}
}
this.itemCount_ = connections.length;
this.updateShape_();
// Reconnect any child blocks.

View file

@ -117,18 +117,30 @@ Blockly.Blocks['procedures_defnoreturn'] = {
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() {
mutationToDom: function(opt_paramIds) {
var container = document.createElement('mutation');
if (opt_paramIds) {
container.setAttribute('name', this.getFieldValue('NAME'));
}
for (var i = 0; i < this.arguments_.length; i++) {
var parameter = document.createElement('arg');
parameter.setAttribute('name', this.arguments_[i]);
if (opt_paramIds && this.paramIds_) {
parameter.setAttribute('paramId', this.paramIds_[i]);
}
container.appendChild(parameter);
}
@ -151,6 +163,7 @@ Blockly.Blocks['procedures_defnoreturn'] = {
}
}
this.updateParams_();
Blockly.Procedures.mutateCallers(this);
// Show or hide the statement input.
this.setStatements_(xmlElement.getAttribute('statements') !== 'false');
@ -185,8 +198,7 @@ Blockly.Blocks['procedures_defnoreturn'] = {
connection = paramBlock.nextConnection;
}
// Initialize procedure's callers with blank IDs.
Blockly.Procedures.mutateCallers(this.getFieldValue('NAME'),
this.workspace, this.arguments_, null);
Blockly.Procedures.mutateCallers(this);
return containerBlock;
},
/**
@ -206,8 +218,7 @@ Blockly.Blocks['procedures_defnoreturn'] = {
paramBlock.nextConnection.targetBlock();
}
this.updateParams_();
Blockly.Procedures.mutateCallers(this.getFieldValue('NAME'),
this.workspace, this.arguments_, this.paramIds_);
Blockly.Procedures.mutateCallers(this);
// Show/hide the statement input.
var hasStatements = containerBlock.getFieldValue('STATEMENTS');
@ -439,15 +450,15 @@ Blockly.Blocks['procedures_callnoreturn'] = {
*/
init: function() {
this.appendDummyInput('TOPROW')
.appendField('', 'NAME');
.appendField(this.id, 'NAME');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(Blockly.Blocks.procedures.HUE);
// Tooltip is set in domToMutation.
// Tooltip is set in renameProcedure.
this.setHelpUrl(Blockly.Msg.PROCEDURES_CALLNORETURN_HELPURL);
this.arguments_ = [];
this.quarkConnections_ = {};
this.quarkArguments_ = null;
this.quarkIds_ = null;
},
/**
* Returns the name of the procedure this block calls.
@ -480,63 +491,69 @@ Blockly.Blocks['procedures_callnoreturn'] = {
* @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) {
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.quarkArguments_ = ['piua', 'f8b_']
// 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.
if (!paramIds) {
// Reset the quarks (a mutator is about to open).
this.quarkConnections_ = {};
this.quarkArguments_ = null;
this.quarkIds_ = null;
return;
}
if (goog.array.equals(this.arguments_, paramNames)) {
// No change.
this.quarkArguments_ = paramIds;
this.quarkIds_ = paramIds;
return;
}
if (paramIds.length != paramNames.length) {
throw 'Error: paramNames and paramIds must be the same length.';
}
this.setCollapsed(false);
if (!this.quarkArguments_) {
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.quarkArguments_ = paramIds;
this.quarkIds_ = paramIds;
} else {
this.quarkArguments_ = [];
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 = this.arguments_.length - 1; i >= 0; i--) {
for (var i = 0; i < this.arguments_.length; i++) {
var input = this.getInput('ARG' + i);
if (input) {
var connection = input.connection.targetConnection;
this.quarkConnections_[this.quarkArguments_[i]] = connection;
this.quarkConnections_[this.quarkIds_[i]] = connection;
if (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.quarkArguments_ = paramIds;
this.quarkIds_ = paramIds;
// Reconnect any child blocks.
if (this.quarkArguments_) {
if (this.quarkIds_) {
for (var i = 0; i < this.arguments_.length; i++) {
var quarkId = this.quarkArguments_[i];
var quarkId = this.quarkIds_[i];
if (quarkId in this.quarkConnections_) {
var connection = this.quarkConnections_[quarkId];
if (!Blockly.Mutator.reconnect(connection, this, 'ARG' + i)) {
@ -558,12 +575,21 @@ Blockly.Blocks['procedures_callnoreturn'] = {
* @this Blockly.Block
*/
updateShape_: function() {
// Add new inputs.
for (var i = 0; i < this.arguments_.length; i++) {
if (!this.getInput('ARG' + 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.ALIGN_RIGHT)
.appendField(this.arguments_[i]);
.appendField(field, 'ARGNAME' + i);
input.init();
}
}
@ -609,25 +635,16 @@ Blockly.Blocks['procedures_callnoreturn'] = {
*/
domToMutation: function(xmlElement) {
var name = xmlElement.getAttribute('name');
this.setFieldValue(name, 'NAME');
this.setTooltip(
(this.outputConnection ? Blockly.Msg.PROCEDURES_CALLRETURN_TOOLTIP :
Blockly.Msg.PROCEDURES_CALLNORETURN_TOOLTIP).replace('%1', name));
var def = Blockly.Procedures.getDefinition(name, this.workspace);
if (def && def.mutator && def.mutator.isVisible()) {
// Initialize caller with the mutator's IDs.
this.setProcedureParameters(def.arguments_, def.paramIds_);
} else {
var args = [];
for (var i = 0, childNode; childNode = xmlElement.childNodes[i]; i++) {
if (childNode.nodeName.toLowerCase() == 'arg') {
args.push(childNode.getAttribute('name'));
}
this.renameProcedure(this.getProcedureCall(), name);
var args = [];
var paramIds = [];
for (var i = 0, childNode; childNode = xmlElement.childNodes[i]; i++) {
if (childNode.nodeName.toLowerCase() == 'arg') {
args.push(childNode.getAttribute('name'));
paramIds.push(childNode.getAttribute('paramId'));
}
// For the second argument (paramIds) use the arguments list as a dummy
// list.
this.setProcedureParameters(args, args);
}
this.setProcedureParameters_(args, paramIds);
},
/**
* Notification that a variable is renaming.
@ -640,7 +657,7 @@ Blockly.Blocks['procedures_callnoreturn'] = {
for (var i = 0; i < this.arguments_.length; i++) {
if (Blockly.Names.equals(oldName, this.arguments_[i])) {
this.arguments_[i] = newName;
this.getInput('ARG' + i).fieldRow[0].setValue(newName);
this.getField('ARGNAME' + i).setValue(newName);
}
}
},
@ -676,12 +693,12 @@ Blockly.Blocks['procedures_callreturn'] = {
this.setHelpUrl(Blockly.Msg.PROCEDURES_CALLRETURN_HELPURL);
this.arguments_ = [];
this.quarkConnections_ = {};
this.quarkArguments_ = null;
this.quarkIds_ = null;
},
getProcedureCall: Blockly.Blocks['procedures_callnoreturn'].getProcedureCall,
renameProcedure: Blockly.Blocks['procedures_callnoreturn'].renameProcedure,
setProcedureParameters:
Blockly.Blocks['procedures_callnoreturn'].setProcedureParameters,
setProcedureParameters_:
Blockly.Blocks['procedures_callnoreturn'].setProcedureParameters_,
updateShape_: Blockly.Blocks['procedures_callnoreturn'].updateShape_,
mutationToDom: Blockly.Blocks['procedures_callnoreturn'].mutationToDom,
domToMutation: Blockly.Blocks['procedures_callnoreturn'].domToMutation,

View file

@ -137,6 +137,13 @@ Blockly.Blocks['text_join'] = {
itemBlock = itemBlock.nextConnection &&
itemBlock.nextConnection.targetBlock();
}
// Disconnect any children that don't belong.
for (var i = 0; i < this.itemCount_; i++) {
var connection = this.getInput('ADD' + i).connection.targetConnection;
if (connection && connections.indexOf(connection) == -1) {
connection.disconnect();
}
}
this.itemCount_ = connections.length;
this.updateShape_();
// Reconnect any child blocks.

View file

@ -251,16 +251,28 @@ Blockly.Procedures.disposeCallers = function(name, workspace) {
/**
* When a procedure definition changes its parameters, find and edit all its
* callers.
* @param {string} name Name of edited procedure definition.
* @param {!Blockly.Workspace} workspace The workspace to delete callers from.
* @param {!Array.<string>} paramNames Array of new parameter names.
* @param {!Array.<string>} paramIds Array of unique parameter IDs.
* @param {!Blockly.Block} defBlock Procedure definition block.
*/
Blockly.Procedures.mutateCallers = function(name, workspace,
paramNames, paramIds) {
var callers = Blockly.Procedures.getCallers(name, workspace);
for (var i = 0; i < callers.length; i++) {
callers[i].setProcedureParameters(paramNames, paramIds);
Blockly.Procedures.mutateCallers = function(defBlock) {
var oldRecordUndo = Blockly.Events.recordUndo;
var name = defBlock.getProcedureDef()[0];
var xmlElement = defBlock.mutationToDom(true);
var callers = Blockly.Procedures.getCallers(name, defBlock.workspace);
for (var i = 0, caller; caller = callers[i]; i++) {
var oldMutationDom = caller.mutationToDom();
var oldMutation = oldMutationDom && Blockly.Xml.domToText(oldMutationDom);
caller.domToMutation(xmlElement);
var newMutationDom = caller.mutationToDom();
var newMutation = newMutationDom && Blockly.Xml.domToText(newMutationDom);
if (oldMutation != newMutation) {
// Fire a mutation on every caller block. But don't record this as an
// undo action since it is deterministically tied to the procedure's
// definition mutation.
Blockly.Events.recordUndo = false;
Blockly.Events.fire(new Blockly.Events.Change(
caller, 'mutation', null, oldMutation, newMutation));
Blockly.Events.recordUndo = oldRecordUndo;
}
}
};

View file

@ -558,30 +558,12 @@ Blockly.genUid = function() {
var length = 20;
var soupLength = Blockly.genUid.soup_.length;
var id = [];
if (Blockly.genUid.crypto_) {
// Cryptographically strong randomness is supported.
var array = new Uint32Array(length);
Blockly.genUid.crypto_.getRandomValues(array);
for (var i = 0; i < length; i++) {
id[i] = Blockly.genUid.soup_.charAt(array[i] % soupLength);
}
} else {
// Fall back to Math.random for IE 10.
for (var i = 0; i < length; i++) {
id[i] = Blockly.genUid.soup_.charAt(Math.random() * soupLength);
}
for (var i = 0; i < length; i++) {
id[i] = Blockly.genUid.soup_.charAt(Math.random() * soupLength);
}
return id.join('');
};
/**
* Determine if window.crypto or global.crypto exists.
* @this {Object}
* @type {=RandomSource}
* @private
*/
Blockly.genUid.crypto_ = this.crypto;
/**
* Legal characters for the unique ID.
* Should be all on a US keyboard. No XML special characters or control codes.

View file

@ -98,17 +98,17 @@ Blockly.ZoomControls.prototype.createDom = function() {
<clippath id="blocklyZoomoutClipPath837493">
<rect width="32" height="32" y="77"></rect>
</clippath>
<image width="96" height="124" x="-64" y="-15" xlink:href="media/sprites.png">
<image width="96" height="124" x="-64" y="-15" xlink:href="media/sprites.png"
clip-path="url(#blocklyZoomoutClipPath837493)"></image>
<clippath id="blocklyZoominClipPath837493">
<rect width="32" height="32" y="43"></rect>
</clippath>
<image width="96" height="124" x="-32" y="-49" xlink:href="media/sprites.png">
<image width="96" height="124" x="-32" y="-49" xlink:href="media/sprites.png"
clip-path="url(#blocklyZoominClipPath837493)"></image>
<clippath id="blocklyZoomresetClipPath837493">
<rect width="32" height="32"></rect>
</clippath>
<image width="96" height="124" y="-92" xlink:href="media/sprites.png">
<image width="96" height="124" y="-92" xlink:href="media/sprites.png"
clip-path="url(#blocklyZoomresetClipPath837493)"></image>
</g>
*/