Commit 00be41cd authored by Evan W. Patton's avatar Evan W. Patton

Make if block mutator robust to undo/redo operations

This commit reconciles our implementation of the `if` block with the
implementation in Blockly. In particular, our implementation only
works under the assumption that the user is making changes via the
mutator and causes graphical problems when the undo/redo feature in
Blockly is used. The Blockly version correctly handles undo/redo, but
the mutator is slightly different in its implementation. This change
maintains our visual presentation of the mutator but uses the Blockly
logic for updating.

Fixes #973

Change-Id: Ib335d4cd7f132f8bf21f9c691ee7a7e53f28e674
parent 6deba8bc
......@@ -84,24 +84,9 @@ Blockly.Blocks['controls_if'] = {
return container;
},
domToMutation: function (xmlElement) {
if (xmlElement.getAttribute('elseif') === null) {
this.elseifCount_ = 0;
} else {
this.elseifCount_ = window.parseInt(xmlElement.getAttribute('elseif'), 10);
}
this.elseCount_ = window.parseInt(xmlElement.getAttribute('else'), 10);
for (var x = 1; x <= this.elseifCount_; x++) {
this.appendValueInput('IF' + x)
.setCheck(Blockly.Blocks.Utilities.YailTypeToBlocklyType("boolean", Blockly.Blocks.Utilities.INPUT))
.appendField(Blockly.Msg.LANG_CONTROLS_IF_MSG_ELSEIF);
this.appendStatementInput('DO' + x)
.appendField(Blockly.Msg.LANG_CONTROLS_IF_MSG_THEN);
}
if (this.elseCount_) {
this.appendStatementInput('ELSE')
.appendField(Blockly.Msg.LANG_CONTROLS_IF_MSG_ELSE);
}
this.elseifCount_ = parseInt(xmlElement.getAttribute('elseif'), 10) || 0
this.elseCount_ = window.parseInt(xmlElement.getAttribute('else'), 10) || 0;
this.updateShape_();
},
decompose: function (workspace) {
var containerBlock = workspace.newBlock('controls_if_if');
......@@ -121,56 +106,41 @@ Blockly.Blocks['controls_if'] = {
return containerBlock;
},
compose: function (containerBlock) {
// Disconnect the else input blocks and destroy the inputs.
if (this.elseCount_) {
this.removeInput('ELSE');
}
this.elseCount_ = 0;
// Disconnect all the elseif input blocks and destroy the inputs.
for (var x = this.elseifCount_; x > 0; x--) {
this.removeInput('IF' + x);
this.removeInput('DO' + x);
}
this.elseifCount_ = 0;
// Rebuild the block's optional inputs.
var clauseBlock = containerBlock.getInputTargetBlock('STACK');
this.elseifCount_ = 0;
this.elseCount_ = 0;
var valueConnections = [null];
var statementConnections = [null];
var elseStatementConnection = null;
while (clauseBlock) {
switch (clauseBlock.type) {
case 'controls_if_elseif':
this.elseifCount_++;
var ifInput = this.appendValueInput('IF' + this.elseifCount_)
.setCheck(Blockly.Blocks.Utilities.YailTypeToBlocklyType("boolean", Blockly.Blocks.Utilities.INPUT))
.appendField(Blockly.Msg.LANG_CONTROLS_IF_MSG_ELSEIF);
var doInput = this.appendStatementInput('DO' + this.elseifCount_);
doInput.appendField(Blockly.Msg.LANG_CONTROLS_IF_MSG_THEN);
// Reconnect any child blocks.
if (clauseBlock.valueConnection_) {
ifInput.connection.connect(clauseBlock.valueConnection_);
}
if (clauseBlock.statementConnection_) {
doInput.connection.connect(clauseBlock.statementConnection_);
}
valueConnections.push(clauseBlock.valueConnection_);
statementConnections.push(clauseBlock.statementConnection_);
break;
case 'controls_if_else':
this.elseCount_++;
var elseInput = this.appendStatementInput('ELSE');
elseInput.appendField(Blockly.Msg.LANG_CONTROLS_IF_MSG_ELSE);
// Reconnect any child blocks.
if (clauseBlock.statementConnection_) {
elseInput.connection.connect(clauseBlock.statementConnection_);
}
elseStatementConnection = clauseBlock.statementConnection_;
break;
default:
throw 'Unknown block type.';
}
clauseBlock = clauseBlock.nextConnection &&
clauseBlock.nextConnection.targetBlock();
clauseBlock.nextConnection.targetBlock();
}
this.updateShape_();
// Reconnect any child blocks.
for (var i = 1; i <= this.elseifCount_; i++) {
Blockly.Mutator.reconnect(valueConnections[i], this, 'IF' + i);
Blockly.Mutator.reconnect(statementConnections[i], this, 'DO' + i);
}
Blockly.Mutator.reconnect(elseStatementConnection, this, 'ELSE');
},
saveConnections: function (containerBlock) {
// Store a pointer to any connected child blocks.
var inputDo;
var clauseBlock = containerBlock.getInputTargetBlock('STACK');
var clauseBlock = containerBlock.getInput('STACK').connection.targetBlock();
var x = 1;
while (clauseBlock) {
switch (clauseBlock.type) {
......@@ -195,7 +165,31 @@ Blockly.Blocks['controls_if'] = {
clauseBlock.nextConnection.targetBlock();
}
},
typeblock: [{translatedName: Blockly.Msg.LANG_CONTROLS_IF_IF_TITLE_IF}]
typeblock: [{translatedName: Blockly.Msg.LANG_CONTROLS_IF_IF_TITLE_IF}],
updateShape_: function() {
// Delete everything.
if (this.getInput('ELSE')) {
this.removeInput('ELSE');
}
var i = 1;
while (this.getInput('IF' + i)) {
this.removeInput('IF' + i);
this.removeInput('DO' + i);
i++;
}
// Rebuild block.
for (var i = 1; i <= this.elseifCount_; i++) {
this.appendValueInput('IF' + i)
.setCheck(Blockly.Blocks.Utilities.YailTypeToBlocklyType('boolean', Blockly.Blocks.Utilities.INPUT))
.appendField(Blockly.Msg.LANG_CONTROLS_IF_MSG_ELSEIF);
this.appendStatementInput('DO' + i)
.appendField(Blockly.Msg.LANG_CONTROLS_IF_MSG_THEN);
}
if (this.elseCount_) {
this.appendStatementInput('ELSE')
.appendField(Blockly.Msg.CONTROLS_IF_MSG_ELSE);
}
}
};
Blockly.Blocks['controls_if_if'] = {
......
......@@ -432,10 +432,17 @@ Blockly.Events.filter = function(queueIn, forward) {
event.element == 'warningOpen')) {
// Merge change events.
hash[key].newValue = event.newValue;
} else {
// Collision, but newer events should merge into this event to maintain order
hash[key] = event;
queue2.push(event);
}
}
}
queue = queue2;
// After merging, it is possible that the product of merging two events where isNull() returned
// false now returns true. This is one last pass to remove these null events on the filtered
// queue.
queue = queue2.filter(function(e) { return !e.isNull(); });
if (!forward) {
// Restore undo order.
queue.reverse();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment