From 144be4d49f36fdba260a26edbd170ae75bbc37a6 Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Wed, 26 Oct 2016 18:39:56 -0700 Subject: [PATCH] Block highlighting * Separate block selection (editing) from highlighting (execution). * Remove add/removeDragging functions. They clutter the API for no reason. --- core/block_svg.js | 38 +++++++++--------- core/inject.js | 11 ++++-- core/workspace_svg.js | 75 +++++++++++++++++------------------- demos/interpreter/index.html | 2 +- 4 files changed, 63 insertions(+), 63 deletions(-) diff --git a/core/block_svg.js b/core/block_svg.js index 7c92c767a..783d4fff3 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -830,12 +830,14 @@ Blockly.BlockSvg.prototype.setDragging_ = function(adding) { var group = this.getSvgRoot(); group.translate_ = ''; group.skew_ = ''; - this.addDragging(); Blockly.draggingConnections_ = Blockly.draggingConnections_.concat(this.getConnections_(true)); + Blockly.addClass_(/** @type {!Element} */ (this.svgGroup_), + 'blocklyDragging'); } else { - this.removeDragging(); Blockly.draggingConnections_ = []; + Blockly.removeClass_(/** @type {!Element} */ (this.svgGroup_), + 'blocklyDragging'); } // Recurse through all blocks attached under this one. for (var i = 0; i < this.childBlocks_.length; i++) { @@ -1430,6 +1432,21 @@ Blockly.BlockSvg.prototype.setDisabled = function(disabled) { } }; +/** + * Set whether the block is highlighted or not. + * @param {boolean} highlighted True if highlighted. + */ +Blockly.BlockSvg.prototype.setHighlighted = function(highlighted) { + if (highlighted) { + this.svgPath_.setAttribute('filter', + 'url(#' + this.workspace.options.embossFilterId + ')'); + this.svgPathLight_.style.display = 'none'; + } else { + this.svgPath_.removeAttribute('filter'); + this.svgPathLight_.style.display = 'block'; + } +}; + /** * Select this block. Highlight it visually. */ @@ -1453,23 +1470,6 @@ Blockly.BlockSvg.prototype.removeSelect = function() { 'blocklySelected'); }; -/** - * Adds the dragging class to this block. - * Also disables the highlights/shadows to improve performance. - */ -Blockly.BlockSvg.prototype.addDragging = function() { - Blockly.addClass_(/** @type {!Element} */ (this.svgGroup_), - 'blocklyDragging'); -}; - -/** - * Removes the dragging class from this block. - */ -Blockly.BlockSvg.prototype.removeDragging = function() { - Blockly.removeClass_(/** @type {!Element} */ (this.svgGroup_), - 'blocklyDragging'); -}; - // Overrides of functions on Blockly.Block that take into account whether the // block has been rendered. diff --git a/core/inject.js b/core/inject.js index 1f64da49b..c59948002 100644 --- a/core/inject.js +++ b/core/inject.js @@ -104,19 +104,22 @@ Blockly.createDom_ = function(container, options) { */ var defs = Blockly.createSvgElement('defs', {}, svg); + // Each filter/pattern needs a unique ID for the case of multiple Blockly + // instances on a page. Browser behaviour becomes undefined otherwise. + // https://neil.fraser.name/news/2015/11/01/ var rnd = String(Math.random()).substring(2); /* - + - + + result="specOut" /> + k1="0" k2="1" k3="1" k4="0" /> */ var embossFilter = Blockly.createSvgElement('filter', diff --git a/core/workspace_svg.js b/core/workspace_svg.js index fe2ad54bd..64067912b 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -38,6 +38,7 @@ goog.require('Blockly.Workspace'); goog.require('Blockly.Xml'); goog.require('Blockly.ZoomControls'); +goog.require('goog.array'); goog.require('goog.dom'); goog.require('goog.math.Coordinate'); goog.require('goog.userAgent'); @@ -65,6 +66,12 @@ Blockly.WorkspaceSvg = function(options) { * @const */ this.SOUNDS_ = Object.create(null); + /** + * List of currently highlighted blocks. + * @type !Array. + * @private + */ + this.highlightedBlocks_ = []; }; goog.inherits(Blockly.WorkspaceSvg, Blockly.Workspace); @@ -260,7 +267,7 @@ Blockly.WorkspaceSvg.prototype.createDom = function(opt_backgroundClass) { // Determine if there needs to be a category tree, or a simple list of // blocks. This cannot be changed later, since the UI is very different. if (this.options.hasCategories) { - /** + /** * @type {Blockly.Toolbox} * @private */ @@ -537,53 +544,43 @@ Blockly.WorkspaceSvg.prototype.render = function() { }; /** - * Turn the visual trace functionality on or off. - * @param {boolean} armed True if the trace should be on. + * Was used back when block highlighting (for execution) and block selection + * (for editing) were the same thing. + * Any calls of this function can be deleted. + * @deprecated October 2016 */ -Blockly.WorkspaceSvg.prototype.traceOn = function(armed) { - this.traceOn_ = armed; - if (this.traceWrapper_) { - Blockly.unbindEvent_(this.traceWrapper_); - this.traceWrapper_ = null; - } - if (armed) { - this.traceWrapper_ = Blockly.bindEventWithChecks_(this.svgBlockCanvas_, - 'blocklySelectChange', this, function() {this.traceOn_ = false;}); - } +Blockly.WorkspaceSvg.prototype.traceOn = function() { + console.warn('Deprecated call to traceOn, delete this.'); }; /** - * Highlight a block in the workspace. - * @param {?string} id ID of block to find. + * Highlight or unhighlight a block in the workspace. + * @param {?string} id ID of block to highlight/unhighlight, + * or null for no block (used to unhighlight all blocks). + * @param {boolean=} opt_state If undefined, highlight specified block and + * automatically unhighlight all others. If true or false, manually + * highlight/unhighlight the specified block. */ -Blockly.WorkspaceSvg.prototype.highlightBlock = function(id) { - if (this.traceOn_ && Blockly.dragMode_ != Blockly.DRAG_NONE) { - // The blocklySelectChange event normally prevents this, but sometimes - // there is a race condition on fast-executing apps. - this.traceOn(false); - } - if (!this.traceOn_) { - return; - } - var block = null; - if (id) { - block = this.getBlockById(id); - if (!block) { - return; +Blockly.WorkspaceSvg.prototype.highlightBlock = function(id, opt_state) { + if (opt_state === undefined) { + // Unhighlight all blocks. + for (var i = 0, block; block = this.highlightedBlocks_[i]; i++) { + block.setHighlighted(false); } + this.highlightedBlocks_.length = 0; } - // Temporary turn off the listener for selection changes, so that we don't - // trip the monitor for detecting user activity. - this.traceOn(false); - // Select the current block. + // Highlight/unhighlight the specified block. + var block = id ? this.getBlockById(id) : null; if (block) { - block.select(); - } else if (Blockly.selected) { - Blockly.selected.unselect(); + var state = (opt_state === undefined) || opt_state; + // Using Set here would be great, but at the cost of IE10 support. + if (!state) { + goog.array.remove(this.highlightedBlocks_, block); + } else if (this.highlightedBlocks_.indexOf(block) == -1) { + this.highlightedBlocks_.push(block); + } + block.setHighlighted(state); } - // Restore the monitor for user activity after the selection event has fired. - var thisWorkspace = this; - setTimeout(function() {thisWorkspace.traceOn(true);}, 1); }; /** diff --git a/demos/interpreter/index.html b/demos/interpreter/index.html index 86b2dee6a..c72f795a0 100644 --- a/demos/interpreter/index.html +++ b/demos/interpreter/index.html @@ -172,7 +172,6 @@ alert('Ready to execute this code:\n\n' + code); document.getElementById('stepButton').disabled = ''; highlightPause = false; - workspace.traceOn(true); workspace.highlightBlock(null); } @@ -183,6 +182,7 @@ if (!ok) { // Program complete, no more code to execute. document.getElementById('stepButton').disabled = 'disabled'; + workspace.highlightBlock(null); return; } }