From 77185906d8c8102fcd82867d4be60b0deca288c1 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Mon, 16 Sep 2019 12:13:32 -0700 Subject: [PATCH] Renderers define which paths a block has. (#2981) * Move render elements into a path object. --- blockly_compressed.js | 158 ++++++++++++++------------- blockly_uncompressed.js | 17 ++- core/block_render_svg.js | 58 ---------- core/block_svg.js | 26 +++-- core/renderers/common/constants.js | 2 +- core/renderers/common/debugger.js | 4 +- core/renderers/common/drawer.js | 21 ++-- core/renderers/common/info.js | 8 +- core/renderers/common/path_object.js | 96 ++++++++++++++++ core/renderers/common/renderer.js | 13 +++ core/renderers/geras/drawer.js | 9 +- core/renderers/geras/highlighter.js | 17 +-- core/renderers/geras/info.js | 6 +- core/renderers/geras/path_object.js | 91 +++++++++++++++ core/renderers/geras/renderer.js | 11 ++ core/renderers/measurables/rows.js | 2 +- core/renderers/thrasos/info.js | 6 +- core/renderers/zelos/constants.js | 21 ++++ core/renderers/zelos/info.js | 6 +- core/touch.js | 2 +- 20 files changed, 376 insertions(+), 198 deletions(-) create mode 100644 core/renderers/common/path_object.js create mode 100644 core/renderers/geras/path_object.js diff --git a/blockly_compressed.js b/blockly_compressed.js index 2bbd14ba2..45c403ef1 100644 --- a/blockly_compressed.js +++ b/blockly_compressed.js @@ -209,9 +209,8 @@ Blockly.Connection.prototype.targetBlock=function(){return this.isConnected()?th Blockly.Connection.prototype.setCheck=function(a){a?(Array.isArray(a)||(a=[a]),this.check_=a,this.onCheckChanged_()):this.check_=null;return this};Blockly.Connection.prototype.getCheck=function(){return this.check_};Blockly.Connection.prototype.setShadowDom=function(a){this.shadowDom_=a};Blockly.Connection.prototype.getShadowDom=function(){return this.shadowDom_};Blockly.Connection.prototype.neighbours_=function(a){return[]}; Blockly.Connection.prototype.getParentInput=function(){for(var a=null,b=this.sourceBlock_,c=b.inputList,d=0;d=this.connections_.length)return-1;for(var c=a.y_,d=b;0<=d&&this.connections_[d].y_==c;){if(this.connections_[d]==a)return d;d--}for(;ba.y_)c=d;else{b=d;break}}return b}; Blockly.ConnectionDB.prototype.removeConnection_=function(a){if(!a.inDB_)throw Error("Connection not in database.");var b=this.findConnection(a);if(-1==b)throw Error("Unable to find connection in connectionDB.");a.inDB_=!1;this.connections_.splice(b,1)}; @@ -247,7 +246,7 @@ Blockly.RenderedConnection.prototype.bumpAwayFrom_=function(a){if(!this.sourceBl (e=a.x_-Blockly.SNAP_RADIUS-Math.floor(Math.random()*Blockly.BUMP_RANDOMNESS)-this.x_);b.moveBy(e,f);d||b.removeSelect()}}};Blockly.RenderedConnection.prototype.moveTo=function(a,b){this.inDB_&&this.db_.removeConnection_(this);this.x_=a;this.y_=b;this.hidden_||this.db_.addConnection(this)};Blockly.RenderedConnection.prototype.moveBy=function(a,b){this.moveTo(this.x_+a,this.y_+b)};Blockly.RenderedConnection.prototype.moveToOffset=function(a){this.moveTo(a.x+this.offsetInBlock_.x,a.y+this.offsetInBlock_.y)}; Blockly.RenderedConnection.prototype.setOffsetInBlock=function(a,b){this.offsetInBlock_.x=a;this.offsetInBlock_.y=b};Blockly.RenderedConnection.prototype.getOffsetInBlock=function(){return this.offsetInBlock_}; Blockly.RenderedConnection.prototype.tighten_=function(){var a=this.targetConnection.x_-this.x_,b=this.targetConnection.y_-this.y_;if(0!=a||0!=b){var c=this.targetBlock(),d=c.getSvgRoot();if(!d)throw Error("block is not rendered.");d=Blockly.utils.getRelativeXY(d);c.getSvgRoot().setAttribute("transform","translate("+(d.x-a)+","+(d.y-b)+")");c.moveConnections_(-a,-b)}};Blockly.RenderedConnection.prototype.closest=function(a,b){return this.dbOpposite_.searchForClosest(this,a,b)}; -Blockly.RenderedConnection.prototype.highlight=function(){var a=this.type==Blockly.INPUT_VALUE||this.type==Blockly.OUTPUT_VALUE?Blockly.utils.svgPaths.moveBy(0,-5)+Blockly.utils.svgPaths.lineOnAxis("v",5)+Blockly.blockRendering.getConstants().PUZZLE_TAB.pathDown+Blockly.utils.svgPaths.lineOnAxis("v",5):Blockly.utils.svgPaths.moveBy(-5,0)+Blockly.utils.svgPaths.lineOnAxis("h",5)+Blockly.blockRendering.getConstants().NOTCH.pathLeft+Blockly.utils.svgPaths.lineOnAxis("h",5);var b=this.sourceBlock_.getRelativeToSurfaceXY(); +Blockly.RenderedConnection.prototype.highlight=function(){var a=this.sourceBlock_.workspace.getRenderer().getConstants();a=this.type==Blockly.INPUT_VALUE||this.type==Blockly.OUTPUT_VALUE?Blockly.utils.svgPaths.moveBy(0,-5)+Blockly.utils.svgPaths.lineOnAxis("v",5)+a.PUZZLE_TAB.pathDown+Blockly.utils.svgPaths.lineOnAxis("v",5):Blockly.utils.svgPaths.moveBy(-5,0)+Blockly.utils.svgPaths.lineOnAxis("h",5)+a.NOTCH.pathLeft+Blockly.utils.svgPaths.lineOnAxis("h",5);var b=this.sourceBlock_.getRelativeToSurfaceXY(); Blockly.Connection.highlightedPath_=Blockly.utils.dom.createSvgElement("path",{"class":"blocklyHighlightedConnectionPath",d:a,transform:"translate("+(this.x_-b.x)+","+(this.y_-b.y)+")"+(this.sourceBlock_.RTL?" scale(-1 1)":"")},this.sourceBlock_.getSvgRoot())}; Blockly.RenderedConnection.prototype.unhideAll=function(){this.setHidden(!1);var a=[];if(this.type!=Blockly.INPUT_VALUE&&this.type!=Blockly.NEXT_STATEMENT)return a;var b=this.targetBlock();if(b){if(b.isCollapsed()){var c=[];b.outputConnection&&c.push(b.outputConnection);b.nextConnection&&c.push(b.nextConnection);b.previousConnection&&c.push(b.previousConnection)}else c=b.getConnections_(!0);for(var d=0;d} - */ - this.steps = []; - - /** - * The highlight on the primary outline of the block. - * @type {!Array.} - */ - this.highlightSteps = []; - - /** - * The holes in the block for inline inputs. - * @type {!Array.} - */ - this.inlineSteps = []; - - /** - * The highlights on holes in the block for inline inputs. - * @type {!Array.} - */ - this.highlightInlineSteps = []; -}; - // UI constants for rendering blocks. /** * Vertical space between elements. @@ -108,30 +74,6 @@ Blockly.BlockSvg.prototype.getHeightWidth = function() { return {height: height, width: width}; }; -/** - * Update the block's SVG paths based on the paths that were computed during - * this render pass. - * @param {!Blockly.BlockSvg.PathObject} pathObject The object containing - * partially constructed SVG paths, which will be modified by this function. - * @private - */ -Blockly.BlockSvg.prototype.setPaths_ = function(pathObject) { - var pathString = pathObject.steps.join(' ') + '\n' + - pathObject.inlineSteps.join(' '); - this.svgPath_.setAttribute('d', pathString); - this.svgPathDark_.setAttribute('d', pathString); - - pathString = pathObject.highlightSteps.join(' ') + '\n' + - pathObject.highlightInlineSteps.join(' '); - this.svgPathLight_.setAttribute('d', pathString); - if (this.RTL) { - // Mirror the block's path. - this.svgPath_.setAttribute('transform', 'scale(-1 1)'); - this.svgPathLight_.setAttribute('transform', 'scale(-1 1)'); - this.svgPathDark_.setAttribute('transform', 'translate(1,1) scale(-1 1)'); - } -}; - /** * Position an new block correctly, so that it doesn't move the existing block * when connected to it. diff --git a/core/block_svg.js b/core/block_svg.js index 43a4ac726..a5c4b1dad 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -28,6 +28,7 @@ goog.provide('Blockly.BlockSvg'); goog.require('Blockly.Block'); goog.require('Blockly.blockAnimations'); +goog.require('Blockly.blockRendering.IPathObject'); goog.require('Blockly.ContextMenu'); goog.require('Blockly.Events'); goog.require('Blockly.Events.Ui'); @@ -65,26 +66,35 @@ Blockly.BlockSvg = function(workspace, prototypeName, opt_id) { this.svgGroup_.translate_ = ''; /** + * The renderer's path object. + * @type {Blockly.blockRendering.IPathObject} + * @package + */ + this.pathObject = + workspace.getRenderer().makePathObject(this.svgGroup_); + + // The next three paths are set only for backwards compatibility reasons. + /** + * The dark path of the block. * @type {SVGElement} * @private */ - this.svgPathDark_ = Blockly.utils.dom.createSvgElement('path', - {'class': 'blocklyPathDark', 'transform': 'translate(1,1)'}, - this.svgGroup_); + this.svgPathDark_ = this.pathObject.svgPathDark || null; /** + * The primary path of the block. * @type {SVGElement} * @private */ - this.svgPath_ = Blockly.utils.dom.createSvgElement('path', - {'class': 'blocklyPath'}, this.svgGroup_); + this.svgPath_ = this.pathObject.svgPath || null; /** + * The light path of the block. * @type {SVGElement} * @private */ - this.svgPathLight_ = Blockly.utils.dom.createSvgElement('path', - {'class': 'blocklyPathLight'}, this.svgGroup_); + this.svgPathLight_ = this.pathObject.svgPathLight || null; + this.svgPath_.tooltip = this; /** @type {boolean} */ @@ -1537,7 +1547,7 @@ Blockly.BlockSvg.prototype.positionNearConnection = function(sourceConnection, } }; -/* +/** * Render the block. * Lays out and reflows a block based on its contents and settings. * @param {boolean=} opt_bubble If false, just render this block. diff --git a/core/renderers/common/constants.js b/core/renderers/common/constants.js index f2dad7b80..23a0ab679 100644 --- a/core/renderers/common/constants.js +++ b/core/renderers/common/constants.js @@ -70,7 +70,7 @@ Blockly.blockRendering.ConstantProvider = function() { /** * Rounded corner radius. - * @const + * @type {number} */ this.CORNER_RADIUS = 8; diff --git a/core/renderers/common/debugger.js b/core/renderers/common/debugger.js index b0b4ff709..965f749c8 100644 --- a/core/renderers/common/debugger.js +++ b/core/renderers/common/debugger.js @@ -278,7 +278,9 @@ Blockly.blockRendering.Debug.prototype.drawRenderedRow = function(row, cursorY, Blockly.blockRendering.Debug.prototype.drawRowWithElements = function(row, cursorY, isRtl) { for (var i = 0, elem; (elem = row.elements[i]); i++) { if (Blockly.blockRendering.Types.isSpacer(elem)) { - this.drawSpacerElem(elem, row.height, isRtl); + this.drawSpacerElem( + /** @type {Blockly.blockRendering.InRowSpacer} */ (elem), + row.height, isRtl); } else { this.drawRenderedElem(elem, isRtl); } diff --git a/core/renderers/common/drawer.js b/core/renderers/common/drawer.js index f8a14793a..2592d2171 100644 --- a/core/renderers/common/drawer.js +++ b/core/renderers/common/drawer.js @@ -76,13 +76,7 @@ Blockly.blockRendering.Drawer.prototype.draw = function() { this.drawOutline_(); this.drawInternals_(); - - var pathObject = new Blockly.BlockSvg.PathObject(); - - pathObject.steps = [this.outlinePath_]; - pathObject.inlineSteps = [this.inlinePath_]; - - this.block_.setPaths_(pathObject); + this.block_.pathObject.setPaths(this.outlinePath_ + '\n' + this.inlinePath_); if (Blockly.blockRendering.useDebugger) { this.block_.renderingDebugger.drawDebug(this.block_, this.info_); } @@ -91,8 +85,8 @@ Blockly.blockRendering.Drawer.prototype.draw = function() { /** * Save sizing information back to the block - * Most of the rendering information can be thrown away at the end of the render. - * Anything that needs to be kept around should be set in this function. + * Most of the rendering information can be thrown away at the end of the + * render. Anything that needs to be kept around should be set in this function. * @protected */ Blockly.blockRendering.Drawer.prototype.recordSizeOnBlock_ = function() { @@ -300,10 +294,13 @@ Blockly.blockRendering.Drawer.prototype.drawInternals_ = function() { for (var i = 0, row; (row = this.info_.rows[i]); i++) { for (var j = 0, elem; (elem = row.elements[j]); j++) { if (Blockly.blockRendering.Types.isInlineInput(elem)) { - this.drawInlineInput_(elem); + this.drawInlineInput_( + /** @type {!Blockly.blockRendering.InlineInput} */ (elem)); } else if (Blockly.blockRendering.Types.isIcon(elem) || Blockly.blockRendering.Types.isField(elem)) { - this.layoutField_(elem); + this.layoutField_( + /** @type {!Blockly.blockRendering.Field|!Blockly.blockRendering.Icon} */ + (elem)); } } } @@ -350,7 +347,7 @@ Blockly.blockRendering.Drawer.prototype.layoutField_ = function(fieldInfo) { /** * Add steps for an inline input. - * @param {Blockly.blockRendering.InlineInput} input The information about the + * @param {!Blockly.blockRendering.InlineInput} input The information about the * input to render. * @protected */ diff --git a/core/renderers/common/info.js b/core/renderers/common/info.js index 0cfef0ed3..fac77896f 100644 --- a/core/renderers/common/info.js +++ b/core/renderers/common/info.js @@ -213,7 +213,7 @@ Blockly.blockRendering.RenderInfo.prototype.createRows_ = function() { } } - var lastInput = undefined; + var lastInput = null; // Loop across all of the inputs on the block, creating objects for anything // that needs to be rendered and breaking the block up into visual rows. for (var i = 0, input; (input = this.block_.inputList[i]); i++) { @@ -279,8 +279,8 @@ Blockly.blockRendering.RenderInfo.prototype.addInput_ = function(input, activeRo /** * Decide whether to start a new row between the two Blockly.Inputs. - * @param {!Blockly.Input} input The first input to consider - * @param {Blockly.Input} lastInput The input that follows. + * @param {!Blockly.Input} input The first input to consider + * @param {Blockly.Input} lastInput The input that follows. * @return {boolean} True if the next input should be rendered on a new row. * @protected */ @@ -403,7 +403,7 @@ Blockly.blockRendering.RenderInfo.prototype.alignRowElements_ = function() { for (var i = 0, row; (row = this.rows[i]); i++) { if (row.hasStatement) { this.alignStatementRow_( - /** @type {Blockly.RenderedConnection} */ (row)); + /** @type {!Blockly.blockRendering.InputRow} */ (row)); } else { var currentWidth = row.width; var desiredWidth = this.width - this.startX; diff --git a/core/renderers/common/path_object.js b/core/renderers/common/path_object.js new file mode 100644 index 000000000..e5a22282b --- /dev/null +++ b/core/renderers/common/path_object.js @@ -0,0 +1,96 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2019 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview An object that owns a block's rendering SVG elements. + * @author fenichel@google.com (Rachel Fenichel) + */ + +'use strict'; + +goog.provide('Blockly.blockRendering.IPathObject'); +goog.provide('Blockly.blockRendering.PathObject'); + +goog.require('Blockly.utils.dom'); + +/** + * An interface for a block's path object. + * @param {!SVGElement} _root The root SVG element. + * @interface + * @package + */ +Blockly.blockRendering.IPathObject = function(_root) {}; + +/** + * An object that handles creating and setting each of the SVG elements + * used by the renderer. + * @param {!SVGElement} root The root SVG element. + * @constructor + * @implements {Blockly.blockRendering.IPathObject} + * @package + */ +Blockly.blockRendering.PathObject = function(root) { + this.svgRoot = root; + + /** + * The primary path of the block. + * @type {SVGElement} + * @package + */ + this.svgPath = Blockly.utils.dom.createSvgElement('path', + {'class': 'blocklyPath'}, this.svgRoot); + + // The light and dark paths need to exist (for now) because there is colouring + // code in block_svg that depends on them. But we will always set them to + // display: none, and eventually we want to remove them entirely. + + /** + * The light path of the block. + * @type {SVGElement} + * @package + */ + this.svgPathLight = Blockly.utils.dom.createSvgElement('path', + {'class': 'blocklyPathLight'}, this.svgRoot); + + /** + * The dark path of the block. + * @type {SVGElement} + * @package + */ + this.svgPathDark = Blockly.utils.dom.createSvgElement('path', + {'class': 'blocklyPathDark', 'transform': 'translate(1,1)'}, + this.svgRoot); +}; + +/** + * Set the path generated by the renderer onto the respective SVG element. + * @param {string} pathString The path. + * @package + */ +Blockly.blockRendering.PathObject.prototype.setPaths = function(pathString) { + this.svgPath.setAttribute('d', pathString); + if (this.RTL) { + // Mirror the block's path. + this.svgPath.setAttribute('transform', 'scale(-1 1)'); + } + + this.svgPathLight.style.display = 'none'; + this.svgPathDark.style.display = 'none'; +}; diff --git a/core/renderers/common/renderer.js b/core/renderers/common/renderer.js index 9cc0d135d..1582d0824 100644 --- a/core/renderers/common/renderer.js +++ b/core/renderers/common/renderer.js @@ -29,6 +29,8 @@ goog.provide('Blockly.blockRendering.Renderer'); goog.require('Blockly.blockRendering.ConstantProvider'); goog.require('Blockly.blockRendering.Debug'); goog.require('Blockly.blockRendering.Drawer'); +goog.require('Blockly.blockRendering.IPathObject'); +goog.require('Blockly.blockRendering.PathObject'); goog.require('Blockly.blockRendering.RenderInfo'); @@ -96,6 +98,16 @@ Blockly.blockRendering.Renderer.prototype.makeDebugger_ = function() { return new Blockly.blockRendering.Debug(); }; +/** + * Create a new instance of a renderer path object. + * @param {!SVGElement} root The root SVG element. + * @return {!Blockly.blockRendering.IPathObject} The renderer path object. + * @package + */ +Blockly.blockRendering.Renderer.prototype.makePathObject = function(root) { + return new Blockly.blockRendering.PathObject(root); +}; + /** * Get the current renderer's constant provider. We assume that when this is * called, the renderer has already been initialized. @@ -121,3 +133,4 @@ Blockly.blockRendering.Renderer.prototype.render = function(block) { info.measure(); this.makeDrawer_(block, info).draw(); }; + diff --git a/core/renderers/geras/drawer.js b/core/renderers/geras/drawer.js index 79865a376..c4cd209c2 100644 --- a/core/renderers/geras/drawer.js +++ b/core/renderers/geras/drawer.js @@ -29,6 +29,7 @@ goog.provide('Blockly.geras.Drawer'); goog.require('Blockly.blockRendering.ConstantProvider'); goog.require('Blockly.blockRendering.Drawer'); goog.require('Blockly.geras.Highlighter'); +goog.require('Blockly.geras.PathObject'); goog.require('Blockly.geras.RenderInfo'); goog.require('Blockly.utils.object'); goog.require('Blockly.utils.svgPaths'); @@ -59,13 +60,9 @@ Blockly.geras.Drawer.prototype.draw = function() { this.drawOutline_(); this.drawInternals_(); - var pathObject = new Blockly.BlockSvg.PathObject(); - pathObject.steps = [this.outlinePath_]; - pathObject.inlineSteps = [this.inlinePath_]; - pathObject.highlightSteps = [this.highlighter_.getSteps()]; - pathObject.highlightInlineSteps = [this.highlighter_.getInlineSteps()]; + this.block_.pathObject.setPaths(this.outlinePath_ + '\n' + this.inlinePath_, + this.highlighter_.getPath()); - this.block_.setPaths_(pathObject); if (Blockly.blockRendering.useDebugger) { this.block_.renderingDebugger.drawDebug(this.block_, this.info_); } diff --git a/core/renderers/geras/highlighter.js b/core/renderers/geras/highlighter.js index f1a6a5c36..355839ba9 100644 --- a/core/renderers/geras/highlighter.js +++ b/core/renderers/geras/highlighter.js @@ -90,21 +90,12 @@ Blockly.geras.Highlighter = function(info) { }; /** - * Get the steps for the main highlight path. - * @return {string} The steps for the main highlight path. + * Get the steps for the highlight path. + * @return {string} The steps for the highlight path. * @package */ -Blockly.geras.Highlighter.prototype.getSteps = function() { - return this.steps_; -}; - -/** - * Get the steps for the inline highlight path. - * @return {string} The steps for the inline highlight path. - * @package - */ -Blockly.geras.Highlighter.prototype.getInlineSteps = function() { - return this.inlineSteps_; +Blockly.geras.Highlighter.prototype.getPath = function() { + return this.steps_ + '\n' + this.inlineSteps_; }; Blockly.geras.Highlighter.prototype.drawTopCorner = function(row) { diff --git a/core/renderers/geras/info.js b/core/renderers/geras/info.js index 688cc8aa0..84d559172 100644 --- a/core/renderers/geras/info.js +++ b/core/renderers/geras/info.js @@ -158,13 +158,13 @@ Blockly.geras.RenderInfo.prototype.getInRowSpacing_ = function(prev, next) { // Spacing between an icon and an icon or field. if (Blockly.blockRendering.Types.isIcon(prev) && - !Blockly.blockRendering.Types.isInput(next)) { + next && !Blockly.blockRendering.Types.isInput(next)) { return this.constants_.LARGE_PADDING; } // Spacing between an inline input and a field. if (Blockly.blockRendering.Types.isInlineInput(prev) && - !Blockly.blockRendering.Types.isInput(next)) { + next && !Blockly.blockRendering.Types.isInput(next)) { // Editable field after inline input. if (next.isEditable) { return this.constants_.MEDIUM_PADDING; @@ -206,7 +206,7 @@ Blockly.geras.RenderInfo.prototype.getInRowSpacing_ = function(prev, next) { // Spacing between two fields of the same editability. if (!Blockly.blockRendering.Types.isInput(prev) && - !Blockly.blockRendering.Types.isInput(next) && + next && !Blockly.blockRendering.Types.isInput(next) && (prev.isEditable == next.isEditable)) { return this.constants_.LARGE_PADDING; } diff --git a/core/renderers/geras/path_object.js b/core/renderers/geras/path_object.js new file mode 100644 index 000000000..5a275af57 --- /dev/null +++ b/core/renderers/geras/path_object.js @@ -0,0 +1,91 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2019 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview An object that owns a block's rendering SVG elements. + * @author fenichel@google.com (Rachel Fenichel) + */ + +'use strict'; + +goog.provide('Blockly.geras.PathObject'); + +goog.require('Blockly.blockRendering.IPathObject'); +goog.require('Blockly.utils.dom'); + + +/** + * An object that handles creating and setting each of the SVG elements + * used by the renderer. + * @param {!SVGElement} root The root SVG element. + * @constructor + * @implements {Blockly.blockRendering.IPathObject} + * @package + */ +Blockly.geras.PathObject = function(root) { + this.svgRoot = root; + + // The order of creation for these next three matters, because that + // effectively sets their z-indices. + + /** + * The dark path of the block. + * @type {SVGElement} + * @package + */ + this.svgPathDark = Blockly.utils.dom.createSvgElement('path', + {'class': 'blocklyPathDark', 'transform': 'translate(1,1)'}, + this.svgRoot); + + /** + * The primary path of the block. + * @type {SVGElement} + * @package + */ + this.svgPath = Blockly.utils.dom.createSvgElement('path', + {'class': 'blocklyPath'}, this.svgRoot); + + + /** + * The light path of the block. + * @type {SVGElement} + * @package + */ + this.svgPathLight = Blockly.utils.dom.createSvgElement('path', + {'class': 'blocklyPathLight'}, this.svgRoot); +}; + +/** + * Set each of the paths generated by the renderer onto the respective SVG element. + * @param {string} mainPath The main path. + * @param {string} highlightPath The highlight path. + * @package + */ +Blockly.geras.PathObject.prototype.setPaths = function(mainPath, highlightPath) { + this.svgPath.setAttribute('d', mainPath); + this.svgPathDark.setAttribute('d', mainPath); + this.svgPathLight.setAttribute('d', highlightPath); + if (this.RTL) { + // Mirror the block's path. + this.svgPath.setAttribute('transform', 'scale(-1 1)'); + this.svgPathLight.setAttribute('transform', 'scale(-1 1)'); + this.svgPathDark.setAttribute('transform', 'translate(1,1) scale(-1 1)'); + } +}; diff --git a/core/renderers/geras/renderer.js b/core/renderers/geras/renderer.js index b1f7fe07c..50de0cb13 100644 --- a/core/renderers/geras/renderer.js +++ b/core/renderers/geras/renderer.js @@ -30,6 +30,7 @@ goog.require('Blockly.blockRendering'); goog.require('Blockly.blockRendering.Renderer'); goog.require('Blockly.geras.Drawer'); goog.require('Blockly.geras.HighlightConstantProvider'); +goog.require('Blockly.geras.PathObject'); goog.require('Blockly.geras.RenderInfo'); goog.require('Blockly.utils.object'); @@ -89,6 +90,16 @@ Blockly.geras.Renderer.prototype.makeDrawer_ = function(block, info) { /** @type {!Blockly.geras.RenderInfo} */ (info)); }; +/** + * Create a new instance of a renderer path object. + * @param {!SVGElement} root The root SVG element. + * @return {!Blockly.geras.PathObject} The renderer path object. + * @package + * @override + */ +Blockly.geras.Renderer.prototype.makePathObject = function(root) { + return new Blockly.geras.PathObject(root); +}; /** * Create a new instance of the renderer's highlight constant provider. diff --git a/core/renderers/measurables/rows.js b/core/renderers/measurables/rows.js index 5d8795654..10c25f7a0 100644 --- a/core/renderers/measurables/rows.js +++ b/core/renderers/measurables/rows.js @@ -315,7 +315,7 @@ Blockly.blockRendering.TopRow.prototype.hasLeftSquareCorner = function(block) { var prevBlock = block.getPreviousBlock(); return !!block.outputConnection || - hasHat || (prevBlock && prevBlock.getNextBlock() == block); + hasHat || (prevBlock ? prevBlock.getNextBlock() == block : false); }; /** diff --git a/core/renderers/thrasos/info.js b/core/renderers/thrasos/info.js index aaa257118..1f3acd2f9 100644 --- a/core/renderers/thrasos/info.js +++ b/core/renderers/thrasos/info.js @@ -158,13 +158,13 @@ Blockly.thrasos.RenderInfo.prototype.getInRowSpacing_ = function(prev, next) { // Spacing between an icon and an icon or field. if (Blockly.blockRendering.Types.isIcon(prev) && - !Blockly.blockRendering.Types.isInput(next)) { + next && !Blockly.blockRendering.Types.isInput(next)) { return this.constants_.LARGE_PADDING; } // Spacing between an inline input and a field. if (Blockly.blockRendering.Types.isInlineInput(prev) && - !Blockly.blockRendering.Types.isInput(next)) { + next && !Blockly.blockRendering.Types.isInput(next)) { // Editable field after inline input. if (next.isEditable) { return this.constants_.MEDIUM_PADDING; @@ -193,7 +193,7 @@ Blockly.thrasos.RenderInfo.prototype.getInRowSpacing_ = function(prev, next) { // Spacing between two fields of the same editability. if (!Blockly.blockRendering.Types.isInput(prev) && - !Blockly.blockRendering.Types.isInput(next) && + next && !Blockly.blockRendering.Types.isInput(next) && (prev.isEditable == next.isEditable)) { return this.constants_.LARGE_PADDING; } diff --git a/core/renderers/zelos/constants.js b/core/renderers/zelos/constants.js index d9d34efcd..8050dfa20 100644 --- a/core/renderers/zelos/constants.js +++ b/core/renderers/zelos/constants.js @@ -43,18 +43,39 @@ Blockly.zelos.ConstantProvider = function() { this.GRID_UNIT = 4; + /** + * @override + */ this.CORNER_RADIUS = 1 * this.GRID_UNIT; + /** + * @override + */ this.NOTCH_WIDTH = 9 * this.GRID_UNIT; + /** + * @override + */ this.NOTCH_HEIGHT = 2 * this.GRID_UNIT; + /** + * @override + */ this.NOTCH_OFFSET_LEFT = 3 * this.GRID_UNIT; + /** + * @override + */ this.MIN_BLOCK_HEIGHT = 12 * this.GRID_UNIT; + /** + * @override + */ this.DARK_PATH_OFFSET = 0; + /** + * @override + */ this.TAB_OFFSET_FROM_TOP = 0; }; diff --git a/core/renderers/zelos/info.js b/core/renderers/zelos/info.js index 2d7cfd6dc..921ec5796 100644 --- a/core/renderers/zelos/info.js +++ b/core/renderers/zelos/info.js @@ -187,13 +187,13 @@ Blockly.zelos.RenderInfo.prototype.getInRowSpacing_ = function(prev, next) { // Spacing between an icon and an icon or field. if (Blockly.blockRendering.Types.isIcon(prev) && - !Blockly.blockRendering.Types.isInput(next)) { + next && !Blockly.blockRendering.Types.isInput(next)) { return this.constants_.LARGE_PADDING; } // Spacing between an inline input and a field. if (Blockly.blockRendering.Types.isInlineInput(prev) && - !Blockly.blockRendering.Types.isInput(next)) { + next && !Blockly.blockRendering.Types.isInput(next)) { // Editable field after inline input. if (next.isEditable) { return this.constants_.MEDIUM_PADDING; @@ -225,7 +225,7 @@ Blockly.zelos.RenderInfo.prototype.getInRowSpacing_ = function(prev, next) { // Spacing between two fields of the same editability. if (!Blockly.blockRendering.Types.isInput(prev) && - !Blockly.blockRendering.Types.isInput(next) && + next && !Blockly.blockRendering.Types.isInput(next) && (prev.isEditable == next.isEditable)) { return this.constants_.LARGE_PADDING; } diff --git a/core/touch.js b/core/touch.js index 8b791492e..1464b355a 100644 --- a/core/touch.js +++ b/core/touch.js @@ -124,7 +124,7 @@ Blockly.longStart_ = function(e, gesture) { /** * Nope, that's not a long-press. Either touchend or touchcancel was fired, * or a drag hath begun. Kill the queued long-press task. - * @private + * @package */ Blockly.longStop_ = function() { if (Blockly.longPid_) {