Add Create, Delete, and Change events.

Not counting change for mutators.
This commit is contained in:
Neil Fraser 2016-01-20 19:11:03 -08:00
parent 36fe3994c9
commit c429949a4e
17 changed files with 209 additions and 78 deletions

View file

@ -121,6 +121,10 @@ Blockly.Block = function(workspace, prototypeName, opt_id) {
// Record initial inline state.
/** @type {boolean|undefined} */
this.inputsInlineDefault = this.inputsInline;
if (Blockly.Events.isEnabled() && !this.isShadow()) {
var xmlBlock = Blockly.Xml.blockToDom(this);
Blockly.Events.fire(new Blockly.Events.Create(workspace, xmlBlock));
}
};
/**
@ -160,6 +164,10 @@ Blockly.Block.prototype.colour_ = '#000000';
*/
Blockly.Block.prototype.dispose = function(healStack, animate) {
this.unplug(healStack, false);
if (Blockly.Events.isEnabled() && !this.isShadow()) {
Blockly.Events.fire(new Blockly.Events.Delete(this));
}
Blockly.Events.disable();
// This block is now at the top of the workspace.
// Remove this block from the workspace's list of top-most blocks.
@ -197,6 +205,7 @@ Blockly.Block.prototype.dispose = function(healStack, animate) {
}
// Remove from block database.
delete Blockly.Block.BlockDB_[this.id];
Blockly.Events.enable();
};
/**
@ -483,6 +492,11 @@ Blockly.Block.prototype.setShadow = function(shadow) {
return; // No change.
}
this.isShadow_ = shadow;
if (Blockly.Events.isEnabled() && !shadow) {
// Fire a creation event.
var xmlBlock = Blockly.Xml.blockToDom(this);
Blockly.Events.fire(new Blockly.Events.Create(this.workspace, xmlBlock));
}
};
/**
@ -737,11 +751,14 @@ Blockly.Block.prototype.setOutput = function(newBoolean, opt_check) {
* @param {boolean} newBoolean True if inputs are horizontal.
*/
Blockly.Block.prototype.setInputsInline = function(newBoolean) {
this.inputsInline = newBoolean;
if (this.rendered) {
this.render();
this.bumpNeighbours_();
this.workspace.fireChangeEvent();
if (this.inputsInline != newBoolean) {
Blockly.Events.fire(new Blockly.Events.Change(
this, 'inline', null, this.inputsInline, newBoolean));
this.inputsInline = newBoolean;
if (this.rendered) {
this.render();
this.bumpNeighbours_();
}
}
};
@ -777,7 +794,11 @@ Blockly.Block.prototype.getInputsInline = function() {
* @param {boolean} disabled True if disabled.
*/
Blockly.Block.prototype.setDisabled = function(disabled) {
this.disabled = disabled;
if (this.disabled != disabled) {
Blockly.Events.fire(new Blockly.Events.Change(
this, 'disabled', null, this.disabled, disabled));
this.disabled = disabled;
}
};
/**
@ -812,6 +833,8 @@ Blockly.Block.prototype.isCollapsed = function() {
*/
Blockly.Block.prototype.setCollapsed = function(collapsed) {
if (this.collapsed_ != collapsed) {
Blockly.Events.fire(new Blockly.Events.Change(
this, 'collapsed', null, this.collapsed_, collapsed));
this.collapsed_ = collapsed;
}
};
@ -1207,6 +1230,8 @@ Blockly.Block.prototype.getCommentText = function() {
*/
Blockly.Block.prototype.setCommentText = function(text) {
if (this.comment != text) {
Blockly.Events.fire(new Blockly.Events.Change(
this, 'comment', null, this.comment, text || ''));
this.comment = text;
}
};

View file

@ -227,7 +227,6 @@ Blockly.BlockSvg.terminateDrag_ = function() {
selected.bumpNeighbours_, Blockly.BUMP_DELAY, selected);
// Fire an event to allow scrollbars to resize.
Blockly.fireUiEvent(window, 'resize');
selected.workspace.fireChangeEvent();
}
}
Blockly.dragMode_ = 0;
@ -388,7 +387,6 @@ Blockly.BlockSvg.prototype.setCollapsed = function(collapsed) {
// all their functions and store them next to each other. Expanding and
// bumping causes all their definitions to go out of alignment.
}
this.workspace.fireChangeEvent();
};
/**
@ -1089,11 +1087,8 @@ Blockly.BlockSvg.INNER_BOTTOM_LEFT_CORNER_HIGHLIGHT_LTR =
* the next statement with the previous statement. Otherwise, dispose of
* all children of this block.
* @param {boolean} animate If true, show a disposal animation and sound.
* @param {boolean=} opt_dontRemoveFromWorkspace If true, don't remove this
* block from the workspace's list of top blocks.
*/
Blockly.BlockSvg.prototype.dispose = function(healStack, animate,
opt_dontRemoveFromWorkspace) {
Blockly.BlockSvg.prototype.dispose = function(healStack, animate) {
Blockly.Field.startCache();
// Terminate onchange event calls.
if (this.onchangeWrapper_) {
@ -1116,12 +1111,14 @@ Blockly.BlockSvg.prototype.dispose = function(healStack, animate,
// Stop rerendering.
this.rendered = false;
Blockly.BlockSvg.superClass_.dispose.call(this, healStack);
Blockly.Events.disable();
var icons = this.getIcons();
for (var i = 0; i < icons.length; i++) {
icons[i].dispose();
}
Blockly.Events.enable();
Blockly.BlockSvg.superClass_.dispose.call(this, healStack);
goog.dom.removeNode(this.svgGroup_);
// Sever JavaScript to DOM connections.
@ -1509,14 +1506,12 @@ Blockly.BlockSvg.prototype.setMutator = function(mutator) {
* @param {boolean} disabled True if disabled.
*/
Blockly.BlockSvg.prototype.setDisabled = function(disabled) {
if (this.disabled == disabled) {
return;
if (this.disabled != disabled) {
Blockly.BlockSvg.superClass_.setDisabled.call(this, disabled);
if (this.rendered) {
this.updateDisabled();
}
}
Blockly.BlockSvg.superClass_.setDisabled.call(this, disabled);
if (this.rendered) {
this.updateDisabled();
}
this.workspace.fireChangeEvent();
};
/**

View file

@ -116,6 +116,14 @@ Blockly.Comment.prototype.createEditor_ = function() {
Blockly.bindEvent_(this.textarea_, 'wheel', this, function(e) {
e.stopPropagation();
});
Blockly.bindEvent_(this.textarea_, 'change', this, function(e) {
if (this.text_ != this.textarea_.value) {
Blockly.Events.fire(new Blockly.Events.Change(
this.block_, 'comment', null, this.text_, this.textarea_.value));
this.text_ = this.textarea_.value;
}
});
return this.foreignObject_;
};
@ -243,6 +251,8 @@ Blockly.Comment.prototype.getText = function() {
*/
Blockly.Comment.prototype.setText = function(text) {
if (this.text_ != text) {
Blockly.Events.fire(new Blockly.Events.Change(
this.block_, 'comment', null, this.text_, text));
this.text_ = text;
}
if (this.textarea_) {
@ -254,6 +264,9 @@ Blockly.Comment.prototype.setText = function(text) {
* Dispose of this comment.
*/
Blockly.Comment.prototype.dispose = function() {
if (Blockly.Events.isEnabled()) {
this.setText(''); // Fire event to delete comment.
}
this.block_.comment = null;
Blockly.Icon.prototype.dispose.call(this);
};

View file

@ -29,16 +29,39 @@ goog.provide('Blockly.Events');
/**
* Allow change events to be created and fired.
* @type {boolean}
* @type {number}
* @private
*/
Blockly.Events.enabled = true;
Blockly.Events.disabled_ = 0;
/**
* Name of event that creates a block.
* @const
*/
Blockly.Events.CREATE = 'create';
/**
* Name of event that deletes a block.
* @const
*/
Blockly.Events.DELETE = 'delete';
/**
* Name of event that changes a block.
* @const
*/
Blockly.Events.CHANGE = 'change';
/**
* Create a custom event and fire it.
* @param {Object} detail Custom data for event.
*/
Blockly.Events.fire = function(detail) {
var workspace = Blockly.Workspace.getById(detail.workspace);
if (!Blockly.Events.isEnabled()) {
return; // No events allowed.
}
console.log(detail);
var workspace = Blockly.Workspace.getById(detail.workspaceId);
if (workspace.rendered) {
// Create a custom event in a browser-compatible way.
if (typeof CustomEvent == 'function') {
@ -52,3 +75,91 @@ Blockly.Events.fire = function(detail) {
workspace.getCanvas().dispatchEvent(evt);
}
};
/**
* Stop sending events. Every call to this function MUST also call enable.
*/
Blockly.Events.disable = function() {
Blockly.Events.disabled_++;
};
/**
* Start sending events. Unless events were already disabled when the
* corresponding call to disable was made.
*/
Blockly.Events.enable = function() {
Blockly.Events.disabled_--;
};
/**
* Returns whether events may be fired or not.
* @return {boolean} True if enabled.
*/
Blockly.Events.isEnabled = function() {
return Blockly.Events.disabled_ == 0;
};
/**
* Abstract class for a change event.
* @constructor
*/
Blockly.Events.Abstract = function() {};
/**
* Class for a block creation event.
* @param {!Blockly.Workspace} workspace The workspace.
* @param {!Element} xml XML DOM.
* @extends {Blockly.Events.Abstract}
* @constructor
*/
Blockly.Events.Create = function(workspace, xml) {
this.type = Blockly.Events.CREATE;
this.workspaceId = workspace.id;
this.xml = xml;
};
goog.inherits(Blockly.Events.Create, Blockly.Events.Abstract);
/**
* Class for a block deletion event.
* @param {!Blockly.Block} block The deleted block.
* @extends {Blockly.Events.Abstract}
* @constructor
*/
Blockly.Events.Delete = function(block) {
this.type = Blockly.Events.DELETE;
this.workspaceId = block.workspace.id;
this.blockId = block.id;
this.oldXml = Blockly.Xml.blockToDom(block);
var parent = block.getParent();
if (parent) {
this.oldParentId = parent.id;
for (var i = 0, input; input = parent.inputList[i]; i++) {
if (input.connection && input.connection.targetBlock() == block) {
this.oldInput = input.name;
break;
}
}
}
};
goog.inherits(Blockly.Events.Delete, Blockly.Events.Abstract);
/**
* Class for a block change event.
* @param {!Blockly.Block} block The deleted block.
* @param {string} element One of 'field', 'comment', 'disabled', etc.
* @param {?string} name Name of input or field affected, or null.
* @param {string} oldValue Previous value of element.
* @param {string} newValue New value of element.
* @extends {Blockly.Events.Abstract}
* @constructor
*/
Blockly.Events.Change = function(block, element, name, oldValue, newValue) {
this.type = Blockly.Events.CHANGE;
this.workspaceId = block.workspace.id;
this.blockId = block.id;
this.element = element;
this.name = name;
this.oldValue = oldValue;
this.newValue = newValue;
};
goog.inherits(Blockly.Events.Create, Blockly.Events.Abstract);

View file

@ -42,7 +42,7 @@ goog.require('goog.userAgent');
*/
Blockly.Field = function(text) {
this.size_ = new goog.math.Size(0, 25);
this.setText(text);
this.setValue(text);
};
/**
@ -139,6 +139,10 @@ Blockly.Field.prototype.init = function(block) {
Blockly.bindEvent_(this.fieldGroup_, 'mouseup', this, this.onMouseUp_);
// Force a render.
this.updateTextNode_();
if (Blockly.Events.isEnabled()) {
Blockly.Events.fire(new Blockly.Events.Change(
this.sourceBlock_, 'field', this.name, '', this.getValue()));
}
};
/**
@ -326,7 +330,6 @@ Blockly.Field.prototype.setText = function(text) {
if (this.sourceBlock_ && this.sourceBlock_.rendered) {
this.sourceBlock_.render();
this.sourceBlock_.bumpNeighbours_();
this.sourceBlock_.workspace.fireChangeEvent();
}
};
@ -386,6 +389,10 @@ Blockly.Field.prototype.setValue = function(newText) {
if (oldText == newText) {
return;
}
if (this.sourceBlock_ && Blockly.Events.isEnabled()) {
Blockly.Events.fire(new Blockly.Events.Change(
this.sourceBlock_, 'field', this.name, oldText, newText));
}
this.setText(newText);
};

View file

@ -206,7 +206,7 @@ Blockly.FieldAngle.prototype.onMouseMove = function(e) {
}
angle = String(angle);
Blockly.FieldTextInput.htmlInput_.value = angle;
this.setText(angle);
this.setValue(angle);
this.validate_();
};

View file

@ -91,9 +91,6 @@ Blockly.FieldCheckbox.prototype.setValue = function(strBool) {
if (this.checkElement_) {
this.checkElement_.style.display = newState ? 'block' : 'none';
}
if (this.sourceBlock_ && this.sourceBlock_.rendered) {
this.sourceBlock_.workspace.fireChangeEvent();
}
}
};

View file

@ -45,11 +45,9 @@ goog.require('goog.ui.ColorPicker');
* @constructor
*/
Blockly.FieldColour = function(colour, opt_changeHandler) {
Blockly.FieldColour.superClass_.constructor.call(this, '\u00A0\u00A0\u00A0');
Blockly.FieldColour.superClass_.constructor.call(this, colour);
this.setText(Blockly.Field.NBSP + Blockly.Field.NBSP + Blockly.Field.NBSP);
this.setChangeHandler(opt_changeHandler);
// Set the initial state.
this.setValue(colour);
};
goog.inherits(Blockly.FieldColour, Blockly.Field);
@ -103,13 +101,15 @@ Blockly.FieldColour.prototype.getValue = function() {
* @param {string} colour The new colour in '#rrggbb' format.
*/
Blockly.FieldColour.prototype.setValue = function(colour) {
if (this.sourceBlock_ && Blockly.Events.isEnabled() &&
this.colour_ != colour) {
Blockly.Events.fire(new Blockly.Events.Change(
this.sourceBlock_, 'field', this.name, this.colour_, colour));
}
this.colour_ = colour;
if (this.borderRect_) {
this.borderRect_.style.fill = colour;
}
if (this.sourceBlock_ && this.sourceBlock_.rendered) {
this.sourceBlock_.workspace.fireChangeEvent();
}
};
/**

View file

@ -54,10 +54,9 @@ Blockly.FieldDropdown = function(menuGenerator, opt_changeHandler) {
this.setChangeHandler(opt_changeHandler);
this.trimOptions_();
var firstTuple = this.getOptions_()[0];
this.value_ = firstTuple[1];
// Call parent's constructor.
Blockly.FieldDropdown.superClass_.constructor.call(this, firstTuple[0]);
Blockly.FieldDropdown.superClass_.constructor.call(this, firstTuple[1]);
};
goog.inherits(Blockly.FieldDropdown, Blockly.Field);
@ -268,6 +267,10 @@ Blockly.FieldDropdown.prototype.setValue = function(newValue) {
if (newValue === null || newValue === this.value_) {
return; // No change if null.
}
if (this.sourceBlock_ && Blockly.Events.isEnabled()) {
Blockly.Events.fire(new Blockly.Events.Change(
this.sourceBlock_, 'field', this.name, this.value_, newValue));
}
this.value_ = newValue;
// Look up and display the human-readable text.
var options = this.getOptions_();
@ -311,7 +314,6 @@ Blockly.FieldDropdown.prototype.setText = function(text) {
if (this.sourceBlock_ && this.sourceBlock_.rendered) {
this.sourceBlock_.render();
this.sourceBlock_.bumpNeighbours_();
this.sourceBlock_.workspace.fireChangeEvent();
}
};

View file

@ -42,7 +42,7 @@ goog.require('goog.math.Size');
Blockly.FieldLabel = function(text, opt_class) {
this.size_ = new goog.math.Size(0, 17.5);
this.class_ = opt_class;
this.setText(text);
this.setValue(text);
};
goog.inherits(Blockly.FieldLabel, Blockly.Field);

View file

@ -78,7 +78,7 @@ Blockly.FieldTextInput.prototype.dispose = function() {
* @param {?string} text New text.
* @override
*/
Blockly.FieldTextInput.prototype.setText = function(text) {
Blockly.FieldTextInput.prototype.setValue = function(text) {
if (text === null) {
return; // No change if null.
}
@ -90,7 +90,7 @@ Blockly.FieldTextInput.prototype.setText = function(text) {
text = validated;
}
}
Blockly.Field.prototype.setText.call(this, text);
Blockly.Field.prototype.setValue.call(this, text);
};
/**
@ -120,9 +120,7 @@ Blockly.FieldTextInput.prototype.showEditor_ = function(opt_quietInput) {
newValue = override;
}
}
if (newValue !== null) {
this.setText(newValue);
}
this.setValue(newValue);
return;
}
@ -172,7 +170,7 @@ Blockly.FieldTextInput.prototype.onHtmlInputKeyDown_ = function(e) {
if (e.keyCode == enterKey) {
Blockly.WidgetDiv.hide();
} else if (e.keyCode == escKey) {
this.setText(htmlInput.defaultValue);
htmlInput.value = htmlInput.defaultValue;
Blockly.WidgetDiv.hide();
} else if (e.keyCode == tabKey) {
Blockly.WidgetDiv.hide();
@ -200,6 +198,7 @@ Blockly.FieldTextInput.prototype.onHtmlInputChange_ = function(e) {
// Chrome only (version 26, OS X).
this.sourceBlock_.render();
}
this.resizeEditor_();
};
/**
@ -275,7 +274,7 @@ Blockly.FieldTextInput.prototype.widgetDispose_ = function() {
text = text1;
}
}
thisField.setText(text);
thisField.setValue(text);
thisField.sourceBlock_.rendered && thisField.sourceBlock_.render();
Blockly.unbindEvent_(htmlInput.onKeyDownWrapper_);
Blockly.unbindEvent_(htmlInput.onKeyUpWrapper_);

View file

@ -87,14 +87,13 @@ Blockly.FieldVariable.prototype.init = function(block) {
// Dropdown has already been initialized once.
return;
}
Blockly.FieldVariable.superClass_.init.call(this, block);
if (!this.getValue()) {
// Variables without names get uniquely named for this workspace.
var workspace =
block.isInFlyout ? block.workspace.targetWorkspace : block.workspace;
this.setValue(Blockly.Variables.generateUniqueName(workspace));
}
Blockly.FieldVariable.superClass_.init.call(this, block);
};
/**
@ -110,6 +109,11 @@ Blockly.FieldVariable.prototype.getValue = function() {
* Set the variable name.
* @param {string} newValue New text.
*/
Blockly.FieldVariable.prototype.setValue = function(newValue) {
if (this.sourceBlock_ && Blockly.Events.isEnabled()) {
Blockly.Events.fire(new Blockly.Events.Change(
this.sourceBlock_, 'field', this.name, this.value_, newValue));
}
this.value_ = newValue;
this.setText(newValue);
};

View file

@ -455,7 +455,6 @@ Blockly.Flyout.prototype.show = function(xmlList) {
Blockly.fireUiEventNow(window, 'resize');
this.reflowWrapper_ = Blockly.bindEvent_(this.workspace_.getCanvas(),
'blocklyWorkspaceChange', this, this.reflow);
this.workspace_.fireChangeEvent();
};
/**
@ -694,8 +693,8 @@ Blockly.Flyout.prototype.getRect = function() {
}
// Fix scale if nested in zoomed workspace.
var scale = this.targetWorkspace_ == mainWorkspace ? 1 : mainWorkspace.scale;
return new goog.math.Rect(x, -BIG_NUM,
BIG_NUM + this.width_ * scale, BIG_NUM * 2);
return new goog.math.Rect(x, -BIG_NUM,
BIG_NUM + this.width_ * scale, BIG_NUM * 2);
};
/**

View file

@ -296,8 +296,6 @@ Blockly.Mutator.prototype.workspaceChanged_ = function() {
this.block_.render();
}
this.resizeBubble_();
// The source block may have changed, notify its workspace.
this.block_.workspace.fireChangeEvent();
goog.Timer.callOnce(
this.block_.bumpNeighbours_, Blockly.BUMP_DELAY, this.block_);
}

View file

@ -77,7 +77,6 @@ Blockly.Workspace.SCAN_ANGLE = 3;
*/
Blockly.Workspace.prototype.addTopBlock = function(block) {
this.topBlocks_.push(block);
this.fireChangeEvent();
};
/**
@ -96,7 +95,6 @@ Blockly.Workspace.prototype.removeTopBlock = function(block) {
if (!found) {
throw 'Block not present in workspace\'s list of top-most blocks.';
}
this.fireChangeEvent();
};
/**
@ -193,13 +191,6 @@ Blockly.Workspace.prototype.remainingCapacity = function() {
return this.options.maxBlocks - this.getAllBlocks().length;
};
/**
* Something on this workspace has changed.
*/
Blockly.Workspace.prototype.fireChangeEvent = function() {
// NOP.
};
/**
* Database of all workspaces.
* @private

View file

@ -463,20 +463,6 @@ Blockly.WorkspaceSvg.prototype.highlightBlock = function(id) {
setTimeout(function() {thisWorkspace.traceOn(true);}, 1);
};
/**
* Fire a change event for this workspace. Changes include new block, dropdown
* edits, mutations, connections, etc. Groups of simultaneous changes (e.g.
* a tree of blocks being deleted) are merged into one event.
* Applications may hook workspace changes by listening for
* 'blocklyWorkspaceChange' on workspace.getCanvas().
*/
Blockly.WorkspaceSvg.prototype.fireChangeEvent = function() {
if (this.rendered && this.svgBlockCanvas_) {
var details = {workspace: this.id};
Blockly.Events.fire(details);
}
};
/**
* Paste the provided block onto the workspace.
* @param {!Element} xmlBlock XML block element.
@ -691,7 +677,6 @@ Blockly.WorkspaceSvg.prototype.cleanUp_ = function() {
}
// Fire an event to allow scrollbars to resize.
Blockly.fireUiEvent(window, 'resize');
this.fireChangeEvent();
};
/**

View file

@ -295,6 +295,7 @@ Blockly.Xml.domToWorkspace = function(workspace, xml) {
*/
Blockly.Xml.domToBlock = function(workspace, xmlBlock) {
// Create top-level block.
Blockly.Events.disable();
var topBlock = Blockly.Xml.domToBlockHeadless_(workspace, xmlBlock);
if (workspace.rendered) {
// Hide connections to speed up assembly.
@ -319,6 +320,10 @@ Blockly.Xml.domToBlock = function(workspace, xmlBlock) {
// Fire an event to allow scrollbars to resize.
Blockly.fireUiEvent(window, 'resize');
}
Blockly.Events.enable();
if (Blockly.Events.isEnabled() && !topBlock.isShadow()) {
Blockly.Events.fire(new Blockly.Events.Create(workspace, xmlBlock));
}
return topBlock;
};