mirror of
https://github.com/google/blockly.git
synced 2026-01-11 19:07:08 +01:00
Add undo/redo.
Some bugs in undoing function argument changes.
This commit is contained in:
@@ -123,18 +123,7 @@ Blockly.Blocks['lists_create_with'] = {
|
||||
this.updateShape_();
|
||||
// Reconnect any child blocks.
|
||||
for (var i = 0; i < this.itemCount_; i++) {
|
||||
var connectionChild = connections[i];
|
||||
if (connectionChild) {
|
||||
var parent = connectionChild.targetBlock();
|
||||
var connectionParent = this.getInput('ADD' + i).connection;
|
||||
if (connectionParent.targetConnection != connectionChild &&
|
||||
(!parent || parent == this)) {
|
||||
if (connectionParent.targetConnection) {
|
||||
connectionParent.disconnect();
|
||||
}
|
||||
connectionParent.connect(connectionChild);
|
||||
}
|
||||
}
|
||||
Blockly.Mutator.reconnect(connections[i], this, 'ADD' + i);
|
||||
}
|
||||
},
|
||||
/**
|
||||
|
||||
@@ -152,16 +152,10 @@ Blockly.Blocks['controls_if'] = {
|
||||
this.updateShape_();
|
||||
// Reconnect any child blocks.
|
||||
for (var i = 1; i <= this.elseifCount_; i++) {
|
||||
if (valueConnections[i]) {
|
||||
this.getInput('IF' + i).connection.connect(valueConnections[i]);
|
||||
}
|
||||
if (statementConnections[i]) {
|
||||
this.getInput('DO' + i).connection.connect(statementConnections[i]);
|
||||
}
|
||||
}
|
||||
if (elseStatementConnection) {
|
||||
this.getInput('ELSE').connection.connect(elseStatementConnection);
|
||||
Blockly.Mutator.reconnect(valueConnections[i], this, 'IF' + i);
|
||||
Blockly.Mutator.reconnect(statementConnections[i], this, 'DO' + i);
|
||||
}
|
||||
Blockly.Mutator.reconnect(elseStatementConnection, this, 'ELSE');
|
||||
},
|
||||
/**
|
||||
* Store pointers to any connected child blocks.
|
||||
|
||||
@@ -141,9 +141,7 @@ Blockly.Blocks['text_join'] = {
|
||||
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]);
|
||||
}
|
||||
Blockly.Mutator.reconnect(connections[i], this, 'ADD' + i);
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -168,29 +166,27 @@ Blockly.Blocks['text_join'] = {
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
updateShape_: function() {
|
||||
// Delete everything.
|
||||
if (this.getInput('EMPTY')) {
|
||||
if (this.itemCount_ && this.getInput('EMPTY')) {
|
||||
this.removeInput('EMPTY');
|
||||
} else {
|
||||
var i = 0;
|
||||
while (this.getInput('ADD' + i)) {
|
||||
this.removeInput('ADD' + i);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
// Rebuild block.
|
||||
if (this.itemCount_ == 0) {
|
||||
} else if (!this.itemCount_ && !this.getInput('EMPTY')) {
|
||||
this.appendDummyInput('EMPTY')
|
||||
.appendField(this.newQuote_(true))
|
||||
.appendField(this.newQuote_(false));
|
||||
} else {
|
||||
for (var i = 0; i < this.itemCount_; i++) {
|
||||
}
|
||||
// Add new inputs.
|
||||
for (var i = 0; i < this.itemCount_; i++) {
|
||||
if (!this.getInput('ADD' + i)) {
|
||||
var input = this.appendValueInput('ADD' + i);
|
||||
if (i == 0) {
|
||||
input.appendField(Blockly.Msg.TEXT_JOIN_TITLE_CREATEWITH);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Remove deleted inputs.
|
||||
while (this.getInput('ADD' + i)) {
|
||||
this.removeInput('ADD' + i);
|
||||
i++;
|
||||
}
|
||||
},
|
||||
newQuote_: Blockly.Blocks['text'].newQuote_
|
||||
};
|
||||
|
||||
@@ -97,7 +97,7 @@ Blockly.Events.fire = function(event) {
|
||||
* @private
|
||||
*/
|
||||
Blockly.Events.fireNow_ = function() {
|
||||
var queue = Blockly.Events.filter(Blockly.Events.FIRE_QUEUE_);
|
||||
var queue = Blockly.Events.filter(Blockly.Events.FIRE_QUEUE_, true);
|
||||
Blockly.Events.FIRE_QUEUE_.length = 0;
|
||||
for (var i = 0, event; event = queue[i]; i++) {
|
||||
var workspace = Blockly.Workspace.getById(event.workspaceId);
|
||||
@@ -110,10 +110,15 @@ Blockly.Events.fireNow_ = function() {
|
||||
/**
|
||||
* Filter the queued events and merge duplicates.
|
||||
* @param {!Array.<!Blockly.Events.Abstract>} queueIn Array of events.
|
||||
* @param {boolean} forward True if forward (redo), false if backward (undo).
|
||||
* @return {!Array.<!Blockly.Events.Abstract>} Array of filtered events.
|
||||
*/
|
||||
Blockly.Events.filter = function(queueIn) {
|
||||
Blockly.Events.filter = function(queueIn, forward) {
|
||||
var queue = goog.array.clone(queueIn);
|
||||
if (!forward) {
|
||||
// Undo is merged in reverse order.
|
||||
queue.reverse();
|
||||
}
|
||||
// Merge duplicates. O(n^2), but n should be very small.
|
||||
for (var i = 0, event1; event1 = queue[i]; i++) {
|
||||
for (var j = i + 1, event2; event2 = queue[j]; j++) {
|
||||
@@ -144,6 +149,10 @@ Blockly.Events.filter = function(queueIn) {
|
||||
queue.splice(i, 1);
|
||||
}
|
||||
}
|
||||
if (!forward) {
|
||||
// Restore undo order.
|
||||
queue.reverse();
|
||||
}
|
||||
// Move mutation events to the top of the queue.
|
||||
// Intentionally skip first event.
|
||||
for (var i = 1, event; event = queue[i]; i++) {
|
||||
@@ -251,6 +260,8 @@ Blockly.Events.Create.prototype.run = function(forward) {
|
||||
var block = Blockly.Block.getById(this.blockId);
|
||||
if (block) {
|
||||
block.dispose(false, true);
|
||||
} else {
|
||||
console.warn("Can't delete non-existant block: " + this.blockId);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -285,6 +296,8 @@ Blockly.Events.Delete.prototype.run = function(forward) {
|
||||
var block = Blockly.Block.getById(this.blockId);
|
||||
if (block) {
|
||||
block.dispose(false, true);
|
||||
} else {
|
||||
console.warn("Can't delete non-existant block: " + this.blockId);
|
||||
}
|
||||
} else {
|
||||
var workspace = Blockly.Workspace.getById(this.workspaceId);
|
||||
@@ -334,6 +347,7 @@ Blockly.Events.Change.prototype.isNull = function() {
|
||||
Blockly.Events.Change.prototype.run = function(forward) {
|
||||
var block = Blockly.Block.getById(this.blockId);
|
||||
if (!block) {
|
||||
console.warn("Can't change non-existant block: " + this.blockId);
|
||||
return;
|
||||
}
|
||||
var value = forward ? this.newValue : this.oldValue;
|
||||
@@ -375,6 +389,8 @@ Blockly.Events.Change.prototype.run = function(forward) {
|
||||
Blockly.Events.fire(new Blockly.Events.Change(
|
||||
block, 'mutation', null, oldMutation, value));
|
||||
break;
|
||||
default:
|
||||
console.warn("Unknown change type: " + this.element);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -448,6 +464,7 @@ Blockly.Events.Move.prototype.isNull = function() {
|
||||
Blockly.Events.Move.prototype.run = function(forward) {
|
||||
var block = Blockly.Block.getById(this.blockId);
|
||||
if (!block) {
|
||||
console.warn("Can't move non-existant block: " + this.blockId);
|
||||
return;
|
||||
}
|
||||
var parentId = forward ? this.newParentId : this.oldParentId;
|
||||
@@ -457,6 +474,7 @@ Blockly.Events.Move.prototype.run = function(forward) {
|
||||
if (parentId) {
|
||||
parentBlock = Blockly.Block.getById(parentId);
|
||||
if (!parentBlock) {
|
||||
console.warn("Can't connect to non-existant block: " + parentId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -473,14 +491,14 @@ Blockly.Events.Move.prototype.run = function(forward) {
|
||||
var input = parentBlock.getInput(inputName);
|
||||
if (input) {
|
||||
parentConnection = input.connection;
|
||||
} else {
|
||||
console.warn("Can't connect to non-existant input: " + inputName);
|
||||
}
|
||||
} else if (blockConnection.type == Blockly.PREVIOUS_STATEMENT) {
|
||||
parentConnection = parentBlock.nextConnection;
|
||||
}
|
||||
if (parentConnection) {
|
||||
blockConnection.connect(parentConnection);
|
||||
} else {
|
||||
console.warn("Can't connect to non-existant input: " + inputName);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -340,3 +340,35 @@ Blockly.Mutator.prototype.dispose = function() {
|
||||
this.block_.mutator = null;
|
||||
Blockly.Icon.prototype.dispose.call(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Reconnect an block to a mutated input.
|
||||
* @param {Blockly.Connection} connectionChild Connection on child block.
|
||||
* @param {!Blockly.Block} block Parent block.
|
||||
* @param {string} inputName Name of input on parent block.
|
||||
*/
|
||||
Blockly.Mutator.reconnect = function(connectionChild, block, inputName) {
|
||||
if (!connectionChild) {
|
||||
return;
|
||||
}
|
||||
var connectionParent = block.getInput(inputName).connection;
|
||||
var currentParent = connectionChild.targetBlock();
|
||||
if ((!currentParent || currentParent == block) &&
|
||||
connectionParent.targetConnection != connectionChild) {
|
||||
if (connectionParent.targetConnection) {
|
||||
// There's already something connected here. Get rid of it.
|
||||
connectionParent.disconnect();
|
||||
}
|
||||
connectionParent.connect(connectionChild);
|
||||
}
|
||||
};
|
||||
|
||||
// Export symbols that would otherwise be renamed by Closure compiler.
|
||||
if (!goog.global['Blockly']) {
|
||||
goog.global['Blockly'] = {};
|
||||
}
|
||||
if (!goog.global['Blockly']['Mutator']) {
|
||||
goog.global['Blockly']['Mutator'] = {};
|
||||
}
|
||||
goog.global['Blockly']['Mutator']['reconnect'] = Blockly.Mutator.reconnect;
|
||||
|
||||
|
||||
@@ -209,22 +209,27 @@ Blockly.Workspace.prototype.remainingCapacity = function() {
|
||||
* @param {boolean} redo False if undo, true if redo.
|
||||
*/
|
||||
Blockly.Workspace.prototype.undo = function(redo) {
|
||||
var sourceStack = redo ? this.redoStack_ : this.undoStack_;
|
||||
var event = sourceStack.pop();
|
||||
var inputStack = redo ? this.redoStack_ : this.undoStack_;
|
||||
var outputStack = redo ? this.undoStack_ : this.redoStack_;
|
||||
var event = inputStack.pop();
|
||||
if (!event) {
|
||||
return;
|
||||
}
|
||||
var events = [event];
|
||||
// Do another undo/redo if the next one is of the same group.
|
||||
while (sourceStack.length && event.group &&
|
||||
event.group == sourceStack[sourceStack.length - 1].group) {
|
||||
events.push(sourceStack.pop());
|
||||
while (inputStack.length && event.group &&
|
||||
event.group == inputStack[inputStack.length - 1].group) {
|
||||
events.push(inputStack.pop());
|
||||
}
|
||||
events = Blockly.Events.filter(events);
|
||||
// Push these popped events on the opposite stack.
|
||||
for (var i = 0, event; event = events[i]; i++) {
|
||||
outputStack.push(event);
|
||||
}
|
||||
events = Blockly.Events.filter(events, redo);
|
||||
Blockly.Events.recordUndo = false;
|
||||
for (var i = 0, event; event = events[i]; i++) {
|
||||
console.log(event);
|
||||
event.run(redo);
|
||||
(redo ? this.undoStack_ : this.redoStack_).push(event);
|
||||
}
|
||||
Blockly.Events.recordUndo = true;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user