diff --git a/core/block.js b/core/block.js index 2b0ce8d34..f462c2f29 100644 --- a/core/block.js +++ b/core/block.js @@ -934,9 +934,9 @@ Blockly.Block.prototype.setColour = function(colour) { // Set colour just stores these as properties on the block, but never uses // them again. // TODO: see if we can just get rid of these properties on the block. - var parsed = Blockly.blockRendering.Colourer.parseColour(colour); + var parsed = Blockly.utils.colour.parseBlockColour(colour); this.hue_ = parsed.hue; - this.colour_ = parsed.colour; + this.colour_ = parsed.hex; }; /** diff --git a/core/block_svg.js b/core/block_svg.js index 8af8b9e66..98228c024 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -68,25 +68,9 @@ Blockly.BlockSvg = function(workspace, prototypeName, opt_id) { * @type {Blockly.blockRendering.IPathObject} * @package */ - this.pathObject = - workspace.getRenderer().makePathObject(this.svgGroup_); - - /** - * [colourer description] - * @type {[type]} - * @package - */ - this.colourer = - workspace.getRenderer().makeColourer(this, this.pathObject); - - // The next three paths are set only for backwards compatibility reasons. - /** - * The dark path of the block. - * @type {SVGElement} - * @private - */ - this.svgPathDark_ = this.pathObject.svgPathDark || null; + this.pathObject = workspace.getRenderer().makePathObject(this.svgGroup_); + // The next two paths are set only for backwards compatibility reasons. /** * The primary path of the block. * @type {SVGElement} @@ -1035,7 +1019,6 @@ Blockly.BlockSvg.prototype.dispose = function(healStack, animate) { this.svgGroup_ = null; this.svgPath_ = null; this.svgPathLight_ = null; - this.svgPathDark_ = null; Blockly.utils.dom.stopTextWidthCache(); }; @@ -1049,7 +1032,7 @@ Blockly.BlockSvg.prototype.applyColour = function() { return; } - this.colourer.applyColour(this.isShadow()); + this.pathObject.applyColour(this.isShadow()); var icons = this.getIcons(); for (var i = 0; i < icons.length; i++) { @@ -1314,7 +1297,7 @@ Blockly.BlockSvg.prototype.setDeleteStyle = function(enable) { * @return {string} #RRGGBB string. */ Blockly.BlockSvg.prototype.getColour = function() { - return this.colourer.getColour(); + return this.pathObject.primaryColour; }; /** @@ -1323,7 +1306,7 @@ Blockly.BlockSvg.prototype.getColour = function() { */ Blockly.BlockSvg.prototype.setColour = function(colour) { Blockly.BlockSvg.superClass_.setColour.call(this, colour); - this.colourer.setColour(colour); + this.pathObject.setColour(colour); this.applyColour(); }; @@ -1339,7 +1322,7 @@ Blockly.BlockSvg.prototype.setStyle = function(blockStyleName) { if (blockStyle) { this.hat = blockStyle.hat; - this.colourer.setFromStyle(blockStyle); + this.pathObject.setFromStyle(blockStyle); this.applyColour(); } else { throw Error('Invalid style name: ' + blockStyleName); diff --git a/core/field_angle.js b/core/field_angle.js index 1025824e6..dafed3810 100644 --- a/core/field_angle.js +++ b/core/field_angle.js @@ -269,9 +269,9 @@ Blockly.FieldAngle.prototype.showEditor_ = function() { var editor = this.dropdownCreate_(); Blockly.DropDownDiv.getContentDiv().appendChild(editor); - var colourer = this.sourceBlock_.colourer; - Blockly.DropDownDiv.setColour(colourer.getColour(), - colourer.getBorderColour()); + var pathObject = this.sourceBlock_.pathObject; + Blockly.DropDownDiv.setColour(pathObject.primaryColour, + pathObject.tertiaryColour); Blockly.DropDownDiv.showPositionedByField( this, this.dropdownDispose_.bind(this)); diff --git a/core/field_date.js b/core/field_date.js index 9ed4f68bd..b6fff8f2c 100644 --- a/core/field_date.js +++ b/core/field_date.js @@ -130,8 +130,8 @@ Blockly.FieldDate.prototype.render_ = function() { * @package */ Blockly.FieldDate.prototype.applyColour = function() { - this.todayColour_ = this.sourceBlock_.colourer.getColour(); - this.selectedColour_ = this.sourceBlock_.colourer.getShadowColour(); + this.todayColour_ = this.sourceBlock_.pathObject.primaryColour; + this.selectedColour_ = this.sourceBlock_.pathObject.secondaryColour; this.updateEditor_(); }; diff --git a/core/field_dropdown.js b/core/field_dropdown.js index 9c59069f3..179f98e23 100644 --- a/core/field_dropdown.js +++ b/core/field_dropdown.js @@ -472,9 +472,9 @@ Blockly.FieldDropdown.prototype.applyColour = function() { // Update arrow's colour. if (this.sourceBlock_ && this.arrow_) { if (this.sourceBlock_.isShadow()) { - this.arrow_.style.fill = this.sourceBlock_.colourer.getColourShadow(); + this.arrow_.style.fill = this.sourceBlock_.pathObject.secondaryColour; } else { - this.arrow_.style.fill = this.sourceBlock_.colourer.getColour(); + this.arrow_.style.fill = this.sourceBlock_.pathObject.primaryColour; } } }; diff --git a/core/icon.js b/core/icon.js index 5b3a28a23..f703beea2 100644 --- a/core/icon.js +++ b/core/icon.js @@ -141,7 +141,7 @@ Blockly.Icon.prototype.iconClick_ = function(e) { */ Blockly.Icon.prototype.applyColour = function() { if (this.isVisible()) { - this.bubble_.setColour(this.block_.colourer.getColour()); + this.bubble_.setColour(this.block_.pathObject.primaryColour); } }; diff --git a/core/renderers/common/colourer.js b/core/renderers/common/colourer.js deleted file mode 100644 index 563c0fec3..000000000 --- a/core/renderers/common/colourer.js +++ /dev/null @@ -1,131 +0,0 @@ -/** - * @license - * Copyright 2019 Google LLC - * - * 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 - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -goog.provide('Blockly.blockRendering.Colourer'); -goog.provide('Blockly.blockRendering.IColourer'); - -goog.require('Blockly.blockRendering.IPathObject'); - -/** - * An interface for a block's colourer object. - * @param {!SVGElement} _root The root SVG element. - * @interface - */ -Blockly.blockRendering.IColourer = function(block, pathObject) {}; - -/** - * ) - * @param {[type]} block [description] - * @param {[type]} pathObject [description] - * @implements {Blockly.blockRendering.IColourer} - * @constructor - */ -Blockly.blockRendering.Colourer = function(block, pathObject) { - this.block = block; - /** - * The renderer's path object. - * @type {Blockly.blockRendering.IPathObject} - * @package - */ - this.pathObject = /** {Blockly.BlockRender.PathObject} */ (pathObject); - - this.svgPath = this.pathObject.svgPath; - - this.hue_; - this.colour_; - this.colourSecondary_; - this.colourTertiary_; -}; - -Blockly.blockRendering.Colourer.prototype.applyColours = function(isShadow) { - if (isShadow) { - this.svgPath.setAttribute('stroke', 'none'); - this.svgPath.setAttribute('fill', this.colourSecondary_); - } else { - this.svgPath.setAttribute('stroke', this.colourTertiary_); - this.svgPath.setAttribute('fill', this.colour_); - } -}; - -/** - * Change the colour of a block. - * @param {number|string} colour HSV hue value (0 to 360), #RRGGBB string, - * or a message reference string pointing to one of those two values. - */ -Blockly.blockRendering.Colourer.prototype.setColour = function(colour) { - console.log('todo: set colour'); - // this.colour_ = colour; - // this.colourSecondary_ = Blockly.utils.colour.blend('#fff', this.colour, 0.6); - // this.colourTertiary_ = Blockly.utils.colour.blend('#fff', this.colour, 0.3); -}; - -Blockly.blockRendering.Colourer.prototype.setFromStyle = function(blockStyle) { - this.parseColour(blockStyle['colourPrimary']); - this.colourSecondary_ = blockStyle['colourSecondary'] || - Blockly.utils.colour.blend('#fff', this.colour_, 0.6); - this.colourTertiary_ = blockStyle['colourTertiary'] || - Blockly.utils.colour.blend('#fff', this.colour_, 0.3); -}; - -Blockly.blockRendering.Colourer.prototype.getBorderColour = function() { - return this.colourTertiary_; -}; - -Blockly.blockRendering.Colourer.prototype.getShadowColour = function() { - return this.colourSecondary_; -}; - -/** - * Change the colour of a block. - * @param {number|string} colour HSV hue value (0 to 360), #RRGGBB string, - * or a message reference string pointing to one of those two values. - * @return {{hue: ?number, colour: string}} An object containing the colour as - * a #RRGGBB string, and the hue if the input was an HSV hue value. - */ -Blockly.blockRendering.Colourer.parseColour = function(colour) { - var dereferenced = (typeof colour == 'string') ? - Blockly.utils.replaceMessageReferences(colour) : colour; - - var hue = Number(dereferenced); - if (!isNaN(hue) && 0 <= hue && hue <= 360) { - return { - hue: hue, - colour: Blockly.hueToHex(hue) - }; - } else { - var hex = Blockly.utils.colour.parse(dereferenced); - if (hex) { - // Only store hue if colour is set as a hue. - return { - hue: null, - colour: hex - }; - } else { - var errorMsg = 'Invalid colour: "' + dereferenced + '"'; - if (colour != dereferenced) { - errorMsg += ' (from "' + colour + '")'; - } - throw Error(errorMsg); - } - } -}; diff --git a/core/renderers/common/path_object.js b/core/renderers/common/path_object.js index 07b1aa343..34d65733b 100644 --- a/core/renderers/common/path_object.js +++ b/core/renderers/common/path_object.js @@ -74,6 +74,12 @@ Blockly.blockRendering.PathObject = function(root) { this.svgPathDark = Blockly.utils.dom.createSvgElement('path', {'class': 'blocklyPathDark', 'transform': 'translate(1,1)'}, this.svgRoot); + + + this.hue_; + this.primaryColour; + this.secondaryColour; + this.tertiaryColour; }; /** @@ -95,3 +101,67 @@ Blockly.blockRendering.PathObject.prototype.flipRTL = function() { // Mirror the block's path. this.svgPath.setAttribute('transform', 'scale(-1 1)'); }; + +/** + * Apply the stored colours to the block's path, taking into account whether + * the paths belong to a shadow block. + * @param {boolean} isShadow True if the block is a shadow block. + * @package + */ +Blockly.blockRendering.PathObject.prototype.applyColour = function(isShadow) { + if (isShadow) { + this.svgPath.setAttribute('stroke', 'none'); + this.svgPath.setAttribute('fill', this.secondaryColour); + } else { + this.svgPath.setAttribute('stroke', this.tertiaryColour); + this.svgPath.setAttribute('fill', this.primaryColour); + } +}; + +/** + * Update colour properties based on a triplet of colours. + * @param {string} primary The primary colour. + * @param {string} secondary The secondary colour, or null to have the colourer + * generate it. + * @param {string} tertiary The tertiary colour, or null to have the colourer + * generate it. + * @package + */ +Blockly.blockRendering.PathObject.prototype.setColourFromTriplet = function( + primary, secondary, tertiary) { + this.primaryColour = primary; + this.secondaryColour = secondary || + Blockly.utils.colour.blend('#fff', primary, 0.6); + this.tertiaryColour = tertiary || + Blockly.utils.colour.blend('#fff', primary, 0.3); +}; + +/** + * Update colour properties based on a single colour value. + * @param {number|string} colour HSV hue value (0 to 360), #RRGGBB string, + * or a message reference string pointing to one of those two values. + */ +Blockly.blockRendering.PathObject.prototype.setColour = function(colour) { + var parsed = Blockly.utils.colour.parseBlockColour(colour); + if (parsed.hue) { + this.hue_ = parsed.hue; + } + this.setColourFromTriplet(parsed.hex, null, null); +}; + +/** + * Update colour properties based on a block style. + * @param {!Blockly.Theme.BlockStyle} blockStyle The block style to use. + * @package + */ +Blockly.blockRendering.PathObject.prototype.setFromStyle = function( + blockStyle) { + var parsed = + Blockly.utils.colour.parseBlockColour(blockStyle['colourPrimary']); + if (parsed.hue) { + this.hue_ = parsed.hue; + } + this.setColourFromTriplet(parsed.hex, + blockStyle['colourSecondary'], + blockStyle['colourTertiary']); +}; diff --git a/core/renderers/common/renderer.js b/core/renderers/common/renderer.js index c9d19902e..49a83943a 100644 --- a/core/renderers/common/renderer.js +++ b/core/renderers/common/renderer.js @@ -28,8 +28,6 @@ goog.require('Blockly.blockRendering.Drawer'); goog.require('Blockly.blockRendering.IPathObject'); goog.require('Blockly.blockRendering.PathObject'); goog.require('Blockly.blockRendering.RenderInfo'); -goog.require('Blockly.blockRendering.IColourer'); -goog.require('Blockly.blockRendering.Colourer'); goog.require('Blockly.CursorSvg'); goog.requireType('Blockly.blockRendering.Debug'); @@ -127,18 +125,6 @@ Blockly.blockRendering.Renderer.prototype.makePathObject = function(root) { return new Blockly.blockRendering.PathObject(root); }; -/** - * Create a new instance of a renderer path object. - * @param {!Blockly.BlockSvg} block The root SVG element. - * @param {!Blockly.blockRendering.IPathObject} pathObject [description] - * @return {!Blockly.blockRendering.IColourer} The renderer path object. - * @package - */ -Blockly.blockRendering.Renderer.prototype.makeColourer = function(block, - pathObject) { - return new Blockly.blockRendering.Colourer(block, pathObject); -}; - /** * Get the current renderer's constant provider. We assume that when this is * called, the renderer has already been initialized. diff --git a/core/renderers/geras/colourer.js b/core/renderers/geras/colourer.js deleted file mode 100644 index eb8548cd7..000000000 --- a/core/renderers/geras/colourer.js +++ /dev/null @@ -1,119 +0,0 @@ -/** - * @license - * Copyright 2019 Google LLC - * - * 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 - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -goog.provide('Blockly.geras.Colourer'); - -goog.require('Blockly.geras.PathObject'); - - -/** - * ) - * @param {[type]} block [description] - * @param {[type]} pathObject [description] - * @implements {Blockly.blockRendering.IColourer} - * @constructor - */ -Blockly.geras.Colourer = function(block, pathObject) { - /** - * The renderer's path object. - * @type {Blockly.geras.PathObject} - * @package - */ - this.pathObject = /** {Blockly.geras.PathObject} */ (pathObject); - - this.svgPath = this.pathObject.svgPath; - this.svgPathLight = this.pathObject.svgPathLight; - this.svgPathDark = this.pathObject.svgPathDark; - this.svgPath.setAttribute('stroke', 'none'); - this.svgPathLight.style.display = ''; - this.svgPathDark.setAttribute('display', ''); - - this.colour_; - this.colourTertiary_; - this.colourDark_; - this.colourSecondary_; - this.styleName_; -}; - -Blockly.geras.Colourer.prototype.applyColour = function(isShadow) { - if (isShadow) { - this.svgPathLight.style.display = 'none'; - this.svgPathDark.setAttribute('fill', this.colourSecondary_); - this.svgPath.setAttribute('stroke', 'none'); - this.svgPath.setAttribute('fill', this.colourSecondary_); - } else { - this.svgPathLight.style.display = ''; - this.svgPathDark.style.display = ''; - this.svgPath.setAttribute('stroke', 'none'); - this.svgPathLight.setAttribute('stroke', this.colourTertiary_); - this.svgPathDark.setAttribute('fill', this.colourDark_); - this.svgPath.setAttribute('fill', this.colour_); - } -}; - -/** - * Get the colour of a block. - * @return {string} #RRGGBB string. - */ -Blockly.geras.Colourer.prototype.getColour = function() { - return this.colour_; -}; - -Blockly.geras.Colourer.prototype.getShadowColour = function() { - return this.colourSecondary_; -}; - -Blockly.geras.Colourer.prototype.getBorderColour = function() { - return this.colourTertiary_; -}; - -Blockly.geras.Colourer.prototype.setColourFromTriplet = function(primary, - secondary, tertiary) { - this.colour_ = primary; - this.colourSecondary_ = secondary || - Blockly.utils.colour.blend('#fff', primary, 0.6); - this.colourTertiary_ = tertiary || - Blockly.utils.colour.blend('#fff', primary, 0.3); - this.colourDark_ = tertiary || - Blockly.utils.colour.blend('#000', primary, 0.2); -}; - -/** - * Change the colour of a block. - * @param {number|string} colour HSV hue value (0 to 360), #RRGGBB string, - * or a message reference string pointing to one of those two values. - */ -Blockly.geras.Colourer.prototype.setColour = function(colour) { - var primary = Blockly.blockRendering.Colourer.parseColour(colour).colour; - this.setColourFromTriplet(primary, null, null); -}; - -Blockly.geras.Colourer.prototype.setFromStyle = function(blockStyle) { - var primary = - Blockly.blockRendering.Colourer.parseColour(blockStyle['colourPrimary']) - .colour; - this.setColourFromTriplet(primary, - blockStyle['colourSecondary'], - blockStyle['colourTertiary']); -}; - diff --git a/core/renderers/geras/path_object.js b/core/renderers/geras/path_object.js index 416cb54a9..5fafb4ff3 100644 --- a/core/renderers/geras/path_object.js +++ b/core/renderers/geras/path_object.js @@ -34,6 +34,7 @@ goog.require('Blockly.utils.dom'); * @param {!SVGElement} root The root SVG element. * @constructor * @implements {Blockly.blockRendering.IPathObject} + * @extends {Blockly.blockRendering.PathObject} * @package */ Blockly.geras.PathObject = function(root) { @@ -66,7 +67,16 @@ Blockly.geras.PathObject = function(root) { */ this.svgPathLight = Blockly.utils.dom.createSvgElement('path', {'class': 'blocklyPathLight'}, this.svgRoot); + + + this.hue_; + this.primaryColour; + this.secondaryColour; + this.tertiaryColour; + this.darkColour; }; +Blockly.utils.object.inherits(Blockly.geras.PathObject, + Blockly.blockRendering.PathObject); /** * Set each of the paths generated by the renderer onto the respective SVG element. @@ -90,3 +100,36 @@ Blockly.geras.PathObject.prototype.flipRTL = function() { this.svgPathLight.setAttribute('transform', 'scale(-1 1)'); this.svgPathDark.setAttribute('transform', 'translate(1,1) scale(-1 1)'); }; + +/** + * Apply the stored colours to the block's path, taking into account whether + * the paths belong to a shadow block. + * @param {boolean} isShadow True if the block is a shadow block. + * @package + */ +Blockly.geras.PathObject.prototype.applyColour = function(isShadow) { + if (isShadow) { + this.svgPathLight.style.display = 'none'; + this.svgPathDark.setAttribute('fill', this.secondaryColour); + this.svgPath.setAttribute('stroke', 'none'); + this.svgPath.setAttribute('fill', this.secondaryColour); + } else { + this.svgPathLight.style.display = ''; + this.svgPathDark.style.display = ''; + this.svgPath.setAttribute('stroke', 'none'); + this.svgPathLight.setAttribute('stroke', this.tertiaryColour); + this.svgPathDark.setAttribute('fill', this.darkColour); + this.svgPath.setAttribute('fill', this.primaryColour); + } +}; + +/** + * @override + */ +Blockly.geras.PathObject.prototype.setColourFromTriplet = function(primary, + secondary, tertiary) { + Blockly.geras.PathObject.superClass_.setColourFromTriplet.call(this, primary, + secondary, tertiary); + this.darkColour = tertiary || + Blockly.utils.colour.blend('#000', primary, 0.2); +}; diff --git a/core/renderers/geras/renderer.js b/core/renderers/geras/renderer.js index f5745e361..b01597ffd 100644 --- a/core/renderers/geras/renderer.js +++ b/core/renderers/geras/renderer.js @@ -26,7 +26,6 @@ goog.provide('Blockly.geras.Renderer'); goog.require('Blockly.blockRendering'); goog.require('Blockly.blockRendering.Renderer'); goog.require('Blockly.geras.ConstantProvider'); -goog.require('Blockly.geras.Colourer'); goog.require('Blockly.geras.Drawer'); goog.require('Blockly.geras.HighlightConstantProvider'); goog.require('Blockly.geras.PathObject'); @@ -107,17 +106,6 @@ Blockly.geras.Renderer.prototype.makePathObject = function(root) { return new Blockly.geras.PathObject(root); }; -/** - * Create a new instance of a renderer path object. - * @param {!Blockly.BlockSvg} block The root SVG element. - * @param {!Blockly.geras.IPathObject} pathObject [description] - * @return {!Blockly.geras.Colourer} The renderer path object. - * @package - */ -Blockly.geras.Renderer.prototype.makeColourer = function(block, - pathObject) { - return new Blockly.geras.Colourer(block, pathObject); -}; /** * Create a new instance of the renderer's highlight constant provider. diff --git a/core/utils/colour.js b/core/utils/colour.js index 928fc935b..08aeae89b 100644 --- a/core/utils/colour.js +++ b/core/utils/colour.js @@ -211,3 +211,39 @@ Blockly.utils.colour.names = { 'white': '#ffffff', 'yellow': '#ffff00' }; + +/** + * Parse a block colour from a number or string, as provided in a block + * definition. + * @param {number|string} colour HSV hue value (0 to 360), #RRGGBB string, + * or a message reference string pointing to one of those two values. + * @return {{hue: ?number, colour: string}} An object containing the colour as + * a #RRGGBB string, and the hue if the input was an HSV hue value. + */ +Blockly.utils.colour.parseBlockColour = function(colour) { + var dereferenced = (typeof colour == 'string') ? + Blockly.utils.replaceMessageReferences(colour) : colour; + + var hue = Number(dereferenced); + if (!isNaN(hue) && 0 <= hue && hue <= 360) { + return { + hue: hue, + hex: Blockly.hueToHex(hue) + }; + } else { + var hex = Blockly.utils.colour.parse(dereferenced); + if (hex) { + // Only store hue if colour is set as a hue. + return { + hue: null, + hex: hex + }; + } else { + var errorMsg = 'Invalid colour: "' + dereferenced + '"'; + if (colour != dereferenced) { + errorMsg += ' (from "' + colour + '")'; + } + throw Error(errorMsg); + } + } +}; diff --git a/tests/blocks/test_blocks.js b/tests/blocks/test_blocks.js index 6f9e0af33..bf79c2f9d 100644 --- a/tests/blocks/test_blocks.js +++ b/tests/blocks/test_blocks.js @@ -29,7 +29,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT "message0": "stack block", "previousStatement": null, "nextStatement": null, - "style": "math_blocks" + "colour": "120" }, { "type": "test_basic_dummy",