diff --git a/blockly_uncompressed.js b/blockly_uncompressed.js index c3b5961e8..ab92a378d 100644 --- a/blockly_uncompressed.js +++ b/blockly_uncompressed.js @@ -89,12 +89,14 @@ goog.addDependency("../../core/options.js", ['Blockly.Options'], ['Blockly.utils goog.addDependency("../../core/procedures.js", ['Blockly.Procedures'], ['Blockly.Blocks', 'Blockly.constants', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Field', 'Blockly.Msg', 'Blockly.Names', 'Blockly.utils.xml', 'Blockly.Workspace', 'Blockly.Xml']); goog.addDependency("../../core/rendered_connection.js", ['Blockly.RenderedConnection'], ['Blockly.Connection', 'Blockly.Events', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom', 'Blockly.utils.object']); goog.addDependency("../../core/renderers/common/block_rendering.js", ['Blockly.blockRendering'], ['Blockly.utils.object']); +goog.addDependency("../../core/renderers/common/colourer.js", ['Blockly.blockRendering.Colourer', 'Blockly.blockRendering.IColourer'], ['Blockly.blockRendering.IPathObject']); goog.addDependency("../../core/renderers/common/constants.js", ['Blockly.blockRendering.ConstantProvider'], ['Blockly.utils.svgPaths']); goog.addDependency("../../core/renderers/common/debugger.js", ['Blockly.blockRendering.Debug'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.Types']); goog.addDependency("../../core/renderers/common/drawer.js", ['Blockly.blockRendering.Drawer'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.Types', 'Blockly.utils.svgPaths']); goog.addDependency("../../core/renderers/common/info.js", ['Blockly.blockRendering.RenderInfo'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.ExternalValueInput', 'Blockly.blockRendering.Hat', 'Blockly.blockRendering.InlineInput', 'Blockly.blockRendering.InRowSpacer', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.RoundCorner', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.StatementInput', 'Blockly.blockRendering.SquareCorner', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.Types']); goog.addDependency("../../core/renderers/common/path_object.js", ['Blockly.blockRendering.IPathObject', 'Blockly.blockRendering.PathObject'], ['Blockly.utils.dom']); -goog.addDependency("../../core/renderers/common/renderer.js", ['Blockly.blockRendering.Renderer'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.blockRendering.Drawer', 'Blockly.blockRendering.IPathObject', 'Blockly.blockRendering.PathObject', 'Blockly.blockRendering.RenderInfo', 'Blockly.CursorSvg']); +goog.addDependency("../../core/renderers/common/renderer.js", ['Blockly.blockRendering.Renderer'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.blockRendering.Drawer', 'Blockly.blockRendering.IPathObject', 'Blockly.blockRendering.PathObject', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.IColourer', 'Blockly.CursorSvg']); +goog.addDependency("../../core/renderers/geras/colourer.js", ['Blockly.geras.Colourer'], ['Blockly.geras.PathObject']); goog.addDependency("../../core/renderers/geras/constants.js", ['Blockly.geras.ConstantProvider'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.utils.object']); goog.addDependency("../../core/renderers/geras/drawer.js", ['Blockly.geras.Drawer'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.blockRendering.Drawer', 'Blockly.geras.Highlighter', 'Blockly.geras.PathObject', 'Blockly.geras.RenderInfo', 'Blockly.utils.object', 'Blockly.utils.svgPaths']); goog.addDependency("../../core/renderers/geras/highlight_constants.js", ['Blockly.geras.HighlightConstantProvider'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.utils.svgPaths']); @@ -102,7 +104,7 @@ goog.addDependency("../../core/renderers/geras/highlighter.js", ['Blockly.geras. goog.addDependency("../../core/renderers/geras/info.js", ['Blockly.geras', 'Blockly.geras.RenderInfo'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.Types', 'Blockly.blockRendering.ExternalValueInput', 'Blockly.geras.InlineInput', 'Blockly.geras.StatementInput', 'Blockly.utils.object']); goog.addDependency("../../core/renderers/geras/measurables/inputs.js", ['Blockly.geras.InlineInput', 'Blockly.geras.StatementInput'], ['Blockly.utils.object']); goog.addDependency("../../core/renderers/geras/path_object.js", ['Blockly.geras.PathObject'], ['Blockly.blockRendering.IPathObject', 'Blockly.utils.dom']); -goog.addDependency("../../core/renderers/geras/renderer.js", ['Blockly.geras.Renderer'], ['Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.geras.ConstantProvider', 'Blockly.geras.Drawer', 'Blockly.geras.HighlightConstantProvider', 'Blockly.geras.PathObject', 'Blockly.geras.RenderInfo', 'Blockly.utils.object']); +goog.addDependency("../../core/renderers/geras/renderer.js", ['Blockly.geras.Renderer'], ['Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.geras.ConstantProvider', 'Blockly.geras.Colourer', 'Blockly.geras.Drawer', 'Blockly.geras.HighlightConstantProvider', 'Blockly.geras.PathObject', 'Blockly.geras.RenderInfo', 'Blockly.utils.object']); goog.addDependency("../../core/renderers/measurables/base.js", ['Blockly.blockRendering.Measurable'], ['Blockly.blockRendering.Types']); goog.addDependency("../../core/renderers/measurables/connections.js", ['Blockly.blockRendering.Connection', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.PreviousConnection'], ['Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Types', 'Blockly.utils.object']); goog.addDependency("../../core/renderers/measurables/inputs.js", ['Blockly.blockRendering.ExternalValueInput', 'Blockly.blockRendering.InlineInput', 'Blockly.blockRendering.InputConnection', 'Blockly.blockRendering.StatementInput'], ['Blockly.blockRendering.Connection', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Types', 'Blockly.utils.object']); diff --git a/core/block.js b/core/block.js index c61bc1280..2b0ce8d34 100644 --- a/core/block.js +++ b/core/block.js @@ -905,63 +905,10 @@ Blockly.Block.prototype.setTooltip = function(newTip) { * @return {string} #RRGGBB string. */ Blockly.Block.prototype.getColour = function() { + // TODO: Can we remove this? return this.colour_; }; -/** - * Get the secondary colour of a block. - * @return {?string} #RRGGBB string. - */ -Blockly.Block.prototype.getColourSecondary = function() { - return this.colourSecondary_; -}; - -/** - * Get the tertiary colour of a block. - * @return {?string} #RRGGBB string. - */ -Blockly.Block.prototype.getColourTertiary = function() { - return this.colourTertiary_; -}; - -/** - * Get the shadow colour of a block. - * @return {?string} #RRGGBB string. - */ -Blockly.Block.prototype.getColourShadow = function() { - var colourSecondary = this.getColourSecondary(); - if (colourSecondary) { - return colourSecondary; - } - return Blockly.utils.colour.blend('#fff', this.getColour(), 0.6); -}; - -/** - * Get the border colour(s) of a block. - * @return {{colourDark, colourLight, colourBorder}} An object containing - * colour values for the border(s) of the block. If the block is using a - * style the colourBorder will be defined and equal to the tertiary colour - * of the style (#RRGGBB string). Otherwise the colourDark and colourLight - * attributes will be defined (#RRGGBB strings). - * @package - */ -Blockly.Block.prototype.getColourBorder = function() { - var colourTertiary = this.getColourTertiary(); - if (colourTertiary) { - return { - colourBorder: colourTertiary, - colourLight: null, - colourDark: null - }; - } - var colour = this.getColour(); - return { - colourBorder: null, - colourLight: Blockly.utils.colour.blend('#fff', colour, 0.3), - colourDark: Blockly.utils.colour.blend('#000', colour, 0.2) - }; -}; - /** * Get the name of the block style. * @return {?string} Name of the block style. @@ -984,48 +931,20 @@ Blockly.Block.prototype.getHue = function() { * or a message reference string pointing to one of those two values. */ Blockly.Block.prototype.setColour = function(colour) { - var dereferenced = (typeof colour == 'string') ? - Blockly.utils.replaceMessageReferences(colour) : colour; - - var hue = Number(dereferenced); - if (!isNaN(hue) && 0 <= hue && hue <= 360) { - this.hue_ = hue; - this.colour_ = Blockly.hueToHex(hue); - } else { - var hex = Blockly.utils.colour.parse(dereferenced); - if (hex) { - this.colour_ = hex; - // Only store hue if colour is set as a hue. - this.hue_ = null; - } else { - var errorMsg = 'Invalid colour: "' + dereferenced + '"'; - if (colour != dereferenced) { - errorMsg += ' (from "' + colour + '")'; - } - throw Error(errorMsg); - } - } + // 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); + this.hue_ = parsed.hue; + this.colour_ = parsed.colour; }; /** * Set the style and colour values of a block. * @param {string} blockStyleName Name of the block style - * @throws {Error} if the block style does not exist. */ Blockly.Block.prototype.setStyle = function(blockStyleName) { - var theme = this.workspace.getTheme(); - var blockStyle = theme.getBlockStyle(blockStyleName); this.styleName_ = blockStyleName; - - if (blockStyle) { - this.colourSecondary_ = blockStyle['colourSecondary']; - this.colourTertiary_ = blockStyle['colourTertiary']; - this.hat = blockStyle.hat; - // Set colour will trigger an updateColour() on a block_svg - this.setColour(blockStyle['colourPrimary']); - } else { - throw Error('Invalid style name: ' + blockStyleName); - } }; /** diff --git a/core/block_svg.js b/core/block_svg.js index b404e27fb..8af8b9e66 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -71,6 +71,14 @@ Blockly.BlockSvg = function(workspace, prototypeName, opt_id) { 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. @@ -267,7 +275,7 @@ Blockly.BlockSvg.prototype.initSvg = function() { for (var i = 0; i < icons.length; i++) { icons[i].createIcon(); } - this.updateColour(); + this.applyColour(); this.updateMovable(); var svg = this.getSvgRoot(); if (!this.workspace.options.readOnly && !this.eventsInit_ && svg) { @@ -939,7 +947,7 @@ Blockly.BlockSvg.prototype.setEditable = function(editable) { */ Blockly.BlockSvg.prototype.setShadow = function(shadow) { Blockly.BlockSvg.superClass_.setShadow.call(this, shadow); - this.updateColour(); + this.applyColour(); }; /** @@ -1033,69 +1041,30 @@ Blockly.BlockSvg.prototype.dispose = function(healStack, animate) { /** * Change the colour of a block. + * @package */ -Blockly.BlockSvg.prototype.updateColour = function() { +Blockly.BlockSvg.prototype.applyColour = function() { if (!this.isEnabled()) { // Disabled blocks don't have colour. return; } - if (this.isShadow()) { - this.setShadowColour_(); - } else { - this.setBorderColour_(); - this.svgPath_.setAttribute('fill', this.getColour()); - } + this.colourer.applyColour(this.isShadow()); var icons = this.getIcons(); for (var i = 0; i < icons.length; i++) { - icons[i].updateColour(); + // TODO: Decide whether to make icon and field applyColour take in a + // colourer object. + icons[i].applyColour(); } for (var x = 0, input; input = this.inputList[x]; x++) { for (var y = 0, field; field = input.fieldRow[y]; y++) { - field.updateColour(); + field.applyColour(); } } }; -/** - * Sets the colour of the border. - * Removes the light and dark paths if a border colour is defined. - * @private - */ -Blockly.BlockSvg.prototype.setBorderColour_ = function() { - var borderColours = this.getColourBorder(); - if (borderColours.colourBorder) { - this.svgPathLight_.style.display = 'none'; - this.svgPathDark_.style.display = 'none'; - - this.svgPath_.setAttribute('stroke', borderColours.colourBorder); - } else { - this.svgPathLight_.style.display = ''; - this.svgPathDark_.style.display = ''; - this.svgPath_.setAttribute('stroke', 'none'); - - this.svgPathLight_.setAttribute('stroke', borderColours.colourLight); - this.svgPathDark_.setAttribute('fill', borderColours.colourDark); - } -}; - -/** - * Sets the colour of shadow blocks. - * @return {?string} The background colour of the block. - * @private - */ -Blockly.BlockSvg.prototype.setShadowColour_ = function() { - var shadowColour = this.getColourShadow() || ''; - - this.svgPathLight_.style.display = 'none'; - this.svgPathDark_.setAttribute('fill', shadowColour); - this.svgPath_.setAttribute('stroke', 'none'); - this.svgPath_.setAttribute('fill', shadowColour); - return shadowColour; -}; - /** * Enable or disable a block. */ @@ -1111,7 +1080,7 @@ Blockly.BlockSvg.prototype.updateDisabled = function() { var removed = Blockly.utils.dom.removeClass( /** @type {!Element} */ (this.svgGroup_), 'blocklyDisabled'); if (removed) { - this.updateColour(); + this.applyColour(); } } var children = this.getChildren(false); @@ -1340,15 +1309,40 @@ Blockly.BlockSvg.prototype.setDeleteStyle = function(enable) { // Overrides of functions on Blockly.Block that take into account whether the // block has been rendered. +/** + * Get the colour of a block. + * @return {string} #RRGGBB string. + */ +Blockly.BlockSvg.prototype.getColour = function() { + return this.colourer.getColour(); +}; + /** * Change the colour of a block. * @param {number|string} colour HSV hue value, or #RRGGBB string. */ Blockly.BlockSvg.prototype.setColour = function(colour) { Blockly.BlockSvg.superClass_.setColour.call(this, colour); + this.colourer.setColour(colour); + this.applyColour(); +}; - if (this.rendered) { - this.updateColour(); +/** + * Set the style and colour values of a block. + * @param {string} blockStyleName Name of the block style + * @throws {Error} if the block style does not exist. + */ +Blockly.BlockSvg.prototype.setStyle = function(blockStyleName) { + var theme = this.workspace.getTheme(); + var blockStyle = theme.getBlockStyle(blockStyleName); + this.styleName_ = blockStyleName; + + if (blockStyle) { + this.hat = blockStyle.hat; + this.colourer.setFromStyle(blockStyle); + this.applyColour(); + } else { + throw Error('Invalid style name: ' + blockStyleName); } }; diff --git a/core/comment.js b/core/comment.js index 31f3942bc..680b6ca95 100644 --- a/core/comment.js +++ b/core/comment.js @@ -247,7 +247,7 @@ Blockly.Comment.prototype.createEditableBubble_ = function() { // Expose this comment's block's ID on its top-level SVG group. this.bubble_.setSvgId(this.block_.id); this.bubble_.registerResizeEvent(this.onBubbleResize_.bind(this)); - this.updateColour(); + this.applyColour(); }; /** diff --git a/core/field.js b/core/field.js index ee7cc7d17..0b13b7bdb 100644 --- a/core/field.js +++ b/core/field.js @@ -603,10 +603,10 @@ Blockly.Field.prototype.getSvgRoot = function() { /** * Updates the field to match the colour/style of the block. Should only be - * called by BlockSvg.updateColour(). + * called by BlockSvg.applyColour(). * @package */ -Blockly.Field.prototype.updateColour = function() { +Blockly.Field.prototype.applyColour = function() { // Non-abstract sub-classes may wish to implement this. See FieldDropdown. }; diff --git a/core/field_angle.js b/core/field_angle.js index eb26c4597..1025824e6 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 border = this.sourceBlock_.getColourBorder(); - border = border.colourBorder || border.colourLight; - Blockly.DropDownDiv.setColour(this.sourceBlock_.getColour(), border); + var colourer = this.sourceBlock_.colourer; + Blockly.DropDownDiv.setColour(colourer.getColour(), + colourer.getBorderColour()); Blockly.DropDownDiv.showPositionedByField( this, this.dropdownDispose_.bind(this)); diff --git a/core/field_date.js b/core/field_date.js index ebbfbc562..9ed4f68bd 100644 --- a/core/field_date.js +++ b/core/field_date.js @@ -129,9 +129,9 @@ Blockly.FieldDate.prototype.render_ = function() { * Updates the field's colours to match those of the block. * @package */ -Blockly.FieldDate.prototype.updateColour = function() { - this.todayColour_ = this.sourceBlock_.getColour(); - this.selectedColour_ = this.sourceBlock_.getColourShadow(); +Blockly.FieldDate.prototype.applyColour = function() { + this.todayColour_ = this.sourceBlock_.colourer.getColour(); + this.selectedColour_ = this.sourceBlock_.colourer.getShadowColour(); this.updateEditor_(); }; diff --git a/core/field_dropdown.js b/core/field_dropdown.js index 3a67f6be4..9c59069f3 100644 --- a/core/field_dropdown.js +++ b/core/field_dropdown.js @@ -468,13 +468,13 @@ Blockly.FieldDropdown.prototype.doValueUpdate_ = function(newValue) { * Updates the dropdown arrow to match the colour/style of the block. * @package */ -Blockly.FieldDropdown.prototype.updateColour = function() { +Blockly.FieldDropdown.prototype.applyColour = function() { // Update arrow's colour. if (this.sourceBlock_ && this.arrow_) { if (this.sourceBlock_.isShadow()) { - this.arrow_.style.fill = this.sourceBlock_.getColourShadow(); + this.arrow_.style.fill = this.sourceBlock_.colourer.getColourShadow(); } else { - this.arrow_.style.fill = this.sourceBlock_.getColour(); + this.arrow_.style.fill = this.sourceBlock_.colourer.getColour(); } } }; diff --git a/core/icon.js b/core/icon.js index bf2c9043a..5b3a28a23 100644 --- a/core/icon.js +++ b/core/icon.js @@ -139,9 +139,9 @@ Blockly.Icon.prototype.iconClick_ = function(e) { /** * Change the colour of the associated bubble to match its block. */ -Blockly.Icon.prototype.updateColour = function() { +Blockly.Icon.prototype.applyColour = function() { if (this.isVisible()) { - this.bubble_.setColour(this.block_.getColour()); + this.bubble_.setColour(this.block_.colourer.getColour()); } }; diff --git a/core/mutator.js b/core/mutator.js index 46ed7070d..061420dbc 100644 --- a/core/mutator.js +++ b/core/mutator.js @@ -322,7 +322,7 @@ Blockly.Mutator.prototype.setVisible = function(visible) { this.resizeBubble_(); // When the mutator's workspace changes, update the source block. this.workspace_.addChangeListener(this.workspaceChanged_.bind(this)); - this.updateColour(); + this.applyColour(); } else { // Dispose of the bubble. this.svgDialog_ = null; diff --git a/core/renderers/common/colourer.js b/core/renderers/common/colourer.js new file mode 100644 index 000000000..563c0fec3 --- /dev/null +++ b/core/renderers/common/colourer.js @@ -0,0 +1,131 @@ +/** + * @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/renderer.js b/core/renderers/common/renderer.js index 49a83943a..c9d19902e 100644 --- a/core/renderers/common/renderer.js +++ b/core/renderers/common/renderer.js @@ -28,6 +28,8 @@ 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'); @@ -125,6 +127,18 @@ 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 new file mode 100644 index 000000000..eb8548cd7 --- /dev/null +++ b/core/renderers/geras/colourer.js @@ -0,0 +1,119 @@ +/** + * @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/renderer.js b/core/renderers/geras/renderer.js index fcd5e516d..f5745e361 100644 --- a/core/renderers/geras/renderer.js +++ b/core/renderers/geras/renderer.js @@ -26,6 +26,7 @@ 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'); @@ -106,6 +107,18 @@ 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. * @return {!Blockly.geras.HighlightConstantProvider} The highlight constant diff --git a/core/warning.js b/core/warning.js index fe9f8c4f7..fde0bd50c 100644 --- a/core/warning.js +++ b/core/warning.js @@ -149,7 +149,7 @@ Blockly.Warning.prototype.createBubble = function() { textElement.setAttribute('x', maxWidth + Blockly.Bubble.BORDER_WIDTH); } } - this.updateColour(); + this.applyColour(); }; /**