From 85c4db5a10cf74c63aabd7467fd5f41e0737522a Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Fri, 25 Oct 2019 15:28:00 -0700 Subject: [PATCH 01/12] First pass at colourers --- blockly_uncompressed.js | 6 +- core/block.js | 95 ++-------------------- core/block_svg.js | 96 ++++++++++------------ core/comment.js | 2 +- core/field.js | 4 +- core/field_angle.js | 6 +- core/field_date.js | 6 +- core/field_dropdown.js | 6 +- core/icon.js | 4 +- core/mutator.js | 2 +- core/renderers/common/colourer.js | 131 ++++++++++++++++++++++++++++++ core/renderers/common/renderer.js | 14 ++++ core/renderers/geras/colourer.js | 119 +++++++++++++++++++++++++++ core/renderers/geras/renderer.js | 13 +++ core/warning.js | 2 +- 15 files changed, 349 insertions(+), 157 deletions(-) create mode 100644 core/renderers/common/colourer.js create mode 100644 core/renderers/geras/colourer.js 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(); }; /** From 6f520335ea8f9ac8a445f4b222704f64db5ab31f Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Mon, 28 Oct 2019 14:57:36 -0700 Subject: [PATCH 02/12] Cleanup of colouring code, and move it into path_object. --- core/block.js | 4 +- core/block_svg.js | 29 ++---- core/field_angle.js | 6 +- core/field_date.js | 4 +- core/field_dropdown.js | 4 +- core/icon.js | 2 +- core/renderers/common/colourer.js | 131 --------------------------- core/renderers/common/path_object.js | 70 ++++++++++++++ core/renderers/common/renderer.js | 14 --- core/renderers/geras/colourer.js | 119 ------------------------ core/renderers/geras/path_object.js | 43 +++++++++ core/renderers/geras/renderer.js | 12 --- core/utils/colour.js | 36 ++++++++ tests/blocks/test_blocks.js | 2 +- 14 files changed, 166 insertions(+), 310 deletions(-) delete mode 100644 core/renderers/common/colourer.js delete mode 100644 core/renderers/geras/colourer.js 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", From ecef3467fdd310c46f33f41d2b1377f025958e87 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Mon, 28 Oct 2019 15:16:59 -0700 Subject: [PATCH 03/12] Colourer/path_object cleanup --- core/block_svg.js | 2 +- core/renderers/common/i_path_object.js | 63 ++++++++++++++++++++++++++ core/renderers/common/path_object.js | 49 ++++++++++---------- core/renderers/geras/path_object.js | 38 +++++++++++++--- 4 files changed, 121 insertions(+), 31 deletions(-) create mode 100644 core/renderers/common/i_path_object.js diff --git a/core/block_svg.js b/core/block_svg.js index 98228c024..364e8808b 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -1322,7 +1322,7 @@ Blockly.BlockSvg.prototype.setStyle = function(blockStyleName) { if (blockStyle) { this.hat = blockStyle.hat; - this.pathObject.setFromStyle(blockStyle); + this.pathObject.setColourFromStyle(blockStyle); this.applyColour(); } else { throw Error('Invalid style name: ' + blockStyleName); diff --git a/core/renderers/common/i_path_object.js b/core/renderers/common/i_path_object.js new file mode 100644 index 000000000..4cbd940ac --- /dev/null +++ b/core/renderers/common/i_path_object.js @@ -0,0 +1,63 @@ +/** + * @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 The interface for an object that owns a block's rendering SVG + * elements. + * @author fenichel@google.com (Rachel Fenichel) + */ + +'use strict'; + +goog.provide('Blockly.blockRendering.IPathObject'); + + +/** + * An interface for a block's path object. + * @param {!SVGElement} _root The root SVG element. + * @interface + */ +Blockly.blockRendering.IPathObject = function(_root) {}; + +/** + * 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.IPathObject.prototype.applyColour; + +/** + * 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. + * @package + */ +Blockly.blockRendering.IPathObject.prototype.setColour; + +/** + * Update colour properties based on a block style. + * @param {!Blockly.Theme.BlockStyle} blockStyle The block style to use. + * @package + */ +Blockly.blockRendering.IPathObject.prototype.setColourFromStyle; + +/** + * Flip the SVG paths in RTL. + * @package + */ +Blockly.geras.PathObject.prototype.flipRTL; diff --git a/core/renderers/common/path_object.js b/core/renderers/common/path_object.js index 34d65733b..015e2af52 100644 --- a/core/renderers/common/path_object.js +++ b/core/renderers/common/path_object.js @@ -22,19 +22,12 @@ 'use strict'; -goog.provide('Blockly.blockRendering.IPathObject'); goog.provide('Blockly.blockRendering.PathObject'); +goog.require('Blockly.blockRendering.IPathObject'); goog.require('Blockly.utils.dom'); -/** - * An interface for a block's path object. - * @param {!SVGElement} _root The root SVG element. - * @interface - */ -Blockly.blockRendering.IPathObject = function(_root) {}; - /** * An object that handles creating and setting each of the SVG elements * used by the renderer. @@ -75,11 +68,26 @@ Blockly.blockRendering.PathObject = function(root) { {'class': 'blocklyPathDark', 'transform': 'translate(1,1)'}, this.svgRoot); + /** + * Primary colour of the block in '#RRGGBB' format. + * @type {string} + * @package + */ + this.primaryColour = '#000000'; - this.hue_; - this.primaryColour; - this.secondaryColour; - this.tertiaryColour; + /** + * Secondary colour of the block in '#RRGGBB' format. + * @type {string} + * @package + */ + this.secondaryColour = '#000000'; + + /** + * Tertiary colour of the block in '#RRGGBB' format. + * @type {string} + * @package + */ + this.tertiaryColour = '#000000'; }; /** @@ -125,9 +133,9 @@ Blockly.blockRendering.PathObject.prototype.applyColour = function(isShadow) { * generate it. * @param {string} tertiary The tertiary colour, or null to have the colourer * generate it. - * @package + * @protected */ -Blockly.blockRendering.PathObject.prototype.setColourFromTriplet = function( +Blockly.blockRendering.PathObject.prototype.setColourFromTriplet_ = function( primary, secondary, tertiary) { this.primaryColour = primary; this.secondaryColour = secondary || @@ -140,13 +148,11 @@ Blockly.blockRendering.PathObject.prototype.setColourFromTriplet = function( * 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. + * @package */ 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); + this.setColourFromTriplet_(parsed.hex, null, null); }; /** @@ -154,14 +160,11 @@ Blockly.blockRendering.PathObject.prototype.setColour = function(colour) { * @param {!Blockly.Theme.BlockStyle} blockStyle The block style to use. * @package */ -Blockly.blockRendering.PathObject.prototype.setFromStyle = function( +Blockly.blockRendering.PathObject.prototype.setColourFromStyle = function( blockStyle) { var parsed = Blockly.utils.colour.parseBlockColour(blockStyle['colourPrimary']); - if (parsed.hue) { - this.hue_ = parsed.hue; - } - this.setColourFromTriplet(parsed.hex, + this.setColourFromTriplet_(parsed.hex, blockStyle['colourSecondary'], blockStyle['colourTertiary']); }; diff --git a/core/renderers/geras/path_object.js b/core/renderers/geras/path_object.js index 5fafb4ff3..e1e90b51f 100644 --- a/core/renderers/geras/path_object.js +++ b/core/renderers/geras/path_object.js @@ -69,11 +69,35 @@ Blockly.geras.PathObject = function(root) { {'class': 'blocklyPathLight'}, this.svgRoot); - this.hue_; - this.primaryColour; - this.secondaryColour; - this.tertiaryColour; - this.darkColour; + /** + * Primary colour of the block in '#RRGGBB' format. + * @type {string} + * @package + */ + this.primaryColour = '#000000'; + + /** + * Secondary colour of the block in '#RRGGBB' format. + * Used for the body of a shadow block in Geras. + * @type {string} + * @package + */ + this.secondaryColour = '#000000'; + + /** + * Tertiary colour of the block in '#RRGGBB' format. + * Used for the light path (highlight) in Geras. + * @type {string} + * @package + */ + this.tertiaryColour = '#000000'; + + /** + * The colour of the dark path on the block in '#RRGGBB' format. + * @type {string} + * @package + */ + this.darkColour = '#000000'; }; Blockly.utils.object.inherits(Blockly.geras.PathObject, Blockly.blockRendering.PathObject); @@ -126,9 +150,9 @@ Blockly.geras.PathObject.prototype.applyColour = function(isShadow) { /** * @override */ -Blockly.geras.PathObject.prototype.setColourFromTriplet = function(primary, +Blockly.geras.PathObject.prototype.setColourFromTriplet_ = function(primary, secondary, tertiary) { - Blockly.geras.PathObject.superClass_.setColourFromTriplet.call(this, primary, + Blockly.geras.PathObject.superClass_.setColourFromTriplet_.call(this, primary, secondary, tertiary); this.darkColour = tertiary || Blockly.utils.colour.blend('#000', primary, 0.2); From 7e84dcd3c475d9cdb639afeaa286a45c1ee01e9a Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Mon, 28 Oct 2019 15:18:32 -0700 Subject: [PATCH 04/12] Remove colourSecondary/tertiary from block. --- core/block.js | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/core/block.js b/core/block.js index f462c2f29..b896ac84b 100644 --- a/core/block.js +++ b/core/block.js @@ -256,22 +256,6 @@ Blockly.Block.prototype.hue_ = null; */ Blockly.Block.prototype.colour_ = '#000000'; -/** - * Secondary colour of the block. - * Colour of shadow blocks. - * @type {?string} - * @private - */ -Blockly.Block.prototype.colourSecondary_ = null; - -/** - * Tertiary colour of the block. - * Colour of the block's border. - * @type {?string} - * @private - */ -Blockly.Block.prototype.colourTertiary_ = null; - /** * Name of the block style. * @type {?string} From f4bbeb0688d77bdd55e36c00c588023b846cac1d Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Mon, 28 Oct 2019 15:27:01 -0700 Subject: [PATCH 05/12] Fix bad function definition. --- core/renderers/common/i_path_object.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/renderers/common/i_path_object.js b/core/renderers/common/i_path_object.js index 4cbd940ac..e23faa262 100644 --- a/core/renderers/common/i_path_object.js +++ b/core/renderers/common/i_path_object.js @@ -60,4 +60,4 @@ Blockly.blockRendering.IPathObject.prototype.setColourFromStyle; * Flip the SVG paths in RTL. * @package */ -Blockly.geras.PathObject.prototype.flipRTL; +Blockly.blockRendering.IPathObject.prototype.flipRTL; From ca1e49bf8ead42976ecc3e392c526828a6dc9e68 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Mon, 28 Oct 2019 17:34:15 -0700 Subject: [PATCH 06/12] Fields now go through blockSvg for colours. --- core/block.js | 4 ---- core/block_svg.js | 18 +++++++++++++++++- core/field_angle.js | 5 ++--- core/field_date.js | 4 ++-- core/field_dropdown.js | 4 ++-- core/renderers/common/path_object.js | 4 ++-- core/renderers/geras/path_object.js | 1 + core/renderers/geras/renderer.js | 1 - core/utils/colour.js | 3 ++- 9 files changed, 28 insertions(+), 16 deletions(-) diff --git a/core/block.js b/core/block.js index b896ac84b..65e054b31 100644 --- a/core/block.js +++ b/core/block.js @@ -889,7 +889,6 @@ Blockly.Block.prototype.setTooltip = function(newTip) { * @return {string} #RRGGBB string. */ Blockly.Block.prototype.getColour = function() { - // TODO: Can we remove this? return this.colour_; }; @@ -915,9 +914,6 @@ Blockly.Block.prototype.getHue = function() { * or a message reference string pointing to one of those two values. */ 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.utils.colour.parseBlockColour(colour); this.hue_ = parsed.hue; this.colour_ = parsed.hex; diff --git a/core/block_svg.js b/core/block_svg.js index 364e8808b..523f771d2 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -1289,9 +1289,9 @@ 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. @@ -1300,6 +1300,22 @@ Blockly.BlockSvg.prototype.getColour = function() { return this.pathObject.primaryColour; }; +/** + * Get the secondary colour of a block. + * @return {string} #RRGGBB string. + */ +Blockly.BlockSvg.prototype.getSecondaryColour = function() { + return this.pathObject.primaryColour; +}; + +/** + * Get the tertiary colour of a block. + * @return {string} #RRGGBB string. + */ +Blockly.BlockSvg.prototype.getTertiaryColour = function() { + return this.pathObject.primaryColour; +}; + /** * Change the colour of a block. * @param {number|string} colour HSV hue value, or #RRGGBB string. diff --git a/core/field_angle.js b/core/field_angle.js index dafed3810..7076d5760 100644 --- a/core/field_angle.js +++ b/core/field_angle.js @@ -269,9 +269,8 @@ Blockly.FieldAngle.prototype.showEditor_ = function() { var editor = this.dropdownCreate_(); Blockly.DropDownDiv.getContentDiv().appendChild(editor); - var pathObject = this.sourceBlock_.pathObject; - Blockly.DropDownDiv.setColour(pathObject.primaryColour, - pathObject.tertiaryColour); + Blockly.DropDownDiv.setColour(this.sourceBlock_.getColour(), + this.sourceBlock_.getTertiaryColour()); Blockly.DropDownDiv.showPositionedByField( this, this.dropdownDispose_.bind(this)); diff --git a/core/field_date.js b/core/field_date.js index b6fff8f2c..e22c9a6f0 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_.pathObject.primaryColour; - this.selectedColour_ = this.sourceBlock_.pathObject.secondaryColour; + this.todayColour_ = this.sourceBlock_.getColour(); + this.selectedColour_ = this.sourceBlock_.getSecondaryColour(); this.updateEditor_(); }; diff --git a/core/field_dropdown.js b/core/field_dropdown.js index 179f98e23..fe5866de1 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_.pathObject.secondaryColour; + this.arrow_.style.fill = this.sourceBlock_.getSecondaryColour(); } else { - this.arrow_.style.fill = this.sourceBlock_.pathObject.primaryColour; + this.arrow_.style.fill = this.sourceBlock_.getColour(); } } }; diff --git a/core/renderers/common/path_object.js b/core/renderers/common/path_object.js index 015e2af52..64eede105 100644 --- a/core/renderers/common/path_object.js +++ b/core/renderers/common/path_object.js @@ -129,9 +129,9 @@ Blockly.blockRendering.PathObject.prototype.applyColour = function(isShadow) { /** * 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 + * @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 + * @param {?string} tertiary The tertiary colour, or null to have the colourer * generate it. * @protected */ diff --git a/core/renderers/geras/path_object.js b/core/renderers/geras/path_object.js index e1e90b51f..134b52999 100644 --- a/core/renderers/geras/path_object.js +++ b/core/renderers/geras/path_object.js @@ -26,6 +26,7 @@ goog.provide('Blockly.geras.PathObject'); goog.require('Blockly.blockRendering.IPathObject'); goog.require('Blockly.utils.dom'); +goog.require('Blockly.utils.object'); /** diff --git a/core/renderers/geras/renderer.js b/core/renderers/geras/renderer.js index b01597ffd..fcd5e516d 100644 --- a/core/renderers/geras/renderer.js +++ b/core/renderers/geras/renderer.js @@ -106,7 +106,6 @@ Blockly.geras.Renderer.prototype.makePathObject = function(root) { return new Blockly.geras.PathObject(root); }; - /** * Create a new instance of the renderer's highlight constant provider. * @return {!Blockly.geras.HighlightConstantProvider} The highlight constant diff --git a/core/utils/colour.js b/core/utils/colour.js index 08aeae89b..1a0f50dd0 100644 --- a/core/utils/colour.js +++ b/core/utils/colour.js @@ -217,8 +217,9 @@ Blockly.utils.colour.names = { * 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 + * @return {{hue: ?number, hex: string}} An object containing the colour as * a #RRGGBB string, and the hue if the input was an HSV hue value. + * @throws {Error} If If the colour cannot be parsed. */ Blockly.utils.colour.parseBlockColour = function(colour) { var dereferenced = (typeof colour == 'string') ? From 5ef08fc0d4d2d2622495c0bc898d8a44207da116 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 30 Oct 2019 14:22:35 -0700 Subject: [PATCH 07/12] Switch to using the style object in the path object. --- core/block.js | 4 +- core/block_svg.js | 41 +++++++------ core/field_angle.js | 4 +- core/field_date.js | 4 +- core/field_dropdown.js | 4 +- core/renderers/common/i_path_object.js | 14 ++--- core/renderers/common/path_object.js | 79 ++++--------------------- core/renderers/geras/path_object.js | 58 ++++++------------ core/theme.js | 82 ++++++++++++++++++++++++-- core/utils/colour.js | 3 +- 10 files changed, 141 insertions(+), 152 deletions(-) diff --git a/core/block.js b/core/block.js index 65e054b31..9c1cdb38f 100644 --- a/core/block.js +++ b/core/block.js @@ -252,14 +252,14 @@ Blockly.Block.prototype.hue_ = null; /** * Colour of the block in '#RRGGBB' format. * @type {string} - * @private + * @protected */ Blockly.Block.prototype.colour_ = '#000000'; /** * Name of the block style. * @type {?string} - * @private + * @protected */ Blockly.Block.prototype.styleName_ = null; diff --git a/core/block_svg.js b/core/block_svg.js index 523f771d2..510220a89 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -63,6 +63,13 @@ Blockly.BlockSvg = function(workspace, prototypeName, opt_id) { this.svgGroup_ = Blockly.utils.dom.createSvgElement('g', {}, null); this.svgGroup_.translate_ = ''; + /** + * A block style object. + * @type {!Blockly.Theme.BlockStyle} + * @public + */ + this.style = workspace.getTheme().getBlockStyle(null); + /** * The renderer's path object. * @type {Blockly.blockRendering.IPathObject} @@ -1297,23 +1304,7 @@ Blockly.BlockSvg.prototype.setDeleteStyle = function(enable) { * @return {string} #RRGGBB string. */ Blockly.BlockSvg.prototype.getColour = function() { - return this.pathObject.primaryColour; -}; - -/** - * Get the secondary colour of a block. - * @return {string} #RRGGBB string. - */ -Blockly.BlockSvg.prototype.getSecondaryColour = function() { - return this.pathObject.primaryColour; -}; - -/** - * Get the tertiary colour of a block. - * @return {string} #RRGGBB string. - */ -Blockly.BlockSvg.prototype.getTertiaryColour = function() { - return this.pathObject.primaryColour; + return this.style.colourPrimary; }; /** @@ -1322,7 +1313,12 @@ Blockly.BlockSvg.prototype.getTertiaryColour = function() { */ Blockly.BlockSvg.prototype.setColour = function(colour) { Blockly.BlockSvg.superClass_.setColour.call(this, colour); - this.pathObject.setColour(colour); + var styleObj = this.workspace.getTheme().getBlockStyleForColour(this.colour_); + + this.pathObject.setStyle(styleObj.style); + this.style = styleObj.style; + this.styleName_ = styleObj.name; + this.applyColour(); }; @@ -1332,13 +1328,16 @@ Blockly.BlockSvg.prototype.setColour = function(colour) { * @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); + var blockStyle = this.workspace.getTheme().getBlockStyle(blockStyleName); this.styleName_ = blockStyleName; if (blockStyle) { this.hat = blockStyle.hat; - this.pathObject.setColourFromStyle(blockStyle); + this.pathObject.setStyle(blockStyle); + // Set colour to match Block. + this.colour_ = blockStyle.colourPrimary; + this.style = blockStyle; + this.applyColour(); } else { throw Error('Invalid style name: ' + blockStyleName); diff --git a/core/field_angle.js b/core/field_angle.js index 7076d5760..3a550bcbb 100644 --- a/core/field_angle.js +++ b/core/field_angle.js @@ -269,8 +269,8 @@ Blockly.FieldAngle.prototype.showEditor_ = function() { var editor = this.dropdownCreate_(); Blockly.DropDownDiv.getContentDiv().appendChild(editor); - Blockly.DropDownDiv.setColour(this.sourceBlock_.getColour(), - this.sourceBlock_.getTertiaryColour()); + Blockly.DropDownDiv.setColour(this.sourceBlock_.style.colourPrimary, + this.sourceBlock_.style.colourTertiary); Blockly.DropDownDiv.showPositionedByField( this, this.dropdownDispose_.bind(this)); diff --git a/core/field_date.js b/core/field_date.js index e22c9a6f0..a39ca849e 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_.getColour(); - this.selectedColour_ = this.sourceBlock_.getSecondaryColour(); + this.todayColour_ = this.sourceBlock_.style.colourPrimary; + this.selectedColour_ = this.sourceBlock_.style.colourSecondary; this.updateEditor_(); }; diff --git a/core/field_dropdown.js b/core/field_dropdown.js index fe5866de1..5693d107c 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_.getSecondaryColour(); + this.arrow_.style.fill = this.sourceBlock_.style.colourSecondary; } else { - this.arrow_.style.fill = this.sourceBlock_.getColour(); + this.arrow_.style.fill = this.sourceBlock_.style.colourPrimary; } } }; diff --git a/core/renderers/common/i_path_object.js b/core/renderers/common/i_path_object.js index e23faa262..fd81f8e03 100644 --- a/core/renderers/common/i_path_object.js +++ b/core/renderers/common/i_path_object.js @@ -25,6 +25,8 @@ goog.provide('Blockly.blockRendering.IPathObject'); +goog.requireType('Blockly.Theme'); + /** * An interface for a block's path object. @@ -42,19 +44,11 @@ Blockly.blockRendering.IPathObject = function(_root) {}; Blockly.blockRendering.IPathObject.prototype.applyColour; /** - * 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. - * @package - */ -Blockly.blockRendering.IPathObject.prototype.setColour; - -/** - * Update colour properties based on a block style. + * Update the style. * @param {!Blockly.Theme.BlockStyle} blockStyle The block style to use. * @package */ -Blockly.blockRendering.IPathObject.prototype.setColourFromStyle; +Blockly.blockRendering.IPathObject.prototype.setStyle; /** * Flip the SVG paths in RTL. diff --git a/core/renderers/common/path_object.js b/core/renderers/common/path_object.js index 64eede105..e24f60a14 100644 --- a/core/renderers/common/path_object.js +++ b/core/renderers/common/path_object.js @@ -25,6 +25,7 @@ goog.provide('Blockly.blockRendering.PathObject'); goog.require('Blockly.blockRendering.IPathObject'); +goog.require('Blockly.Theme'); goog.require('Blockly.utils.dom'); @@ -60,34 +61,11 @@ Blockly.blockRendering.PathObject = function(root) { {'class': 'blocklyPathLight'}, this.svgRoot); /** - * The dark path of the block. - * @type {SVGElement} - * @package + * The style object to use when colouring block paths. + * @type {!Blockly.Theme.BlockStyle} + * @public */ - this.svgPathDark = Blockly.utils.dom.createSvgElement('path', - {'class': 'blocklyPathDark', 'transform': 'translate(1,1)'}, - this.svgRoot); - - /** - * Primary colour of the block in '#RRGGBB' format. - * @type {string} - * @package - */ - this.primaryColour = '#000000'; - - /** - * Secondary colour of the block in '#RRGGBB' format. - * @type {string} - * @package - */ - this.secondaryColour = '#000000'; - - /** - * Tertiary colour of the block in '#RRGGBB' format. - * @type {string} - * @package - */ - this.tertiaryColour = '#000000'; + this.style = Blockly.Theme.createBlockStyle('#0000000'); }; /** @@ -98,7 +76,6 @@ Blockly.blockRendering.PathObject = function(root) { Blockly.blockRendering.PathObject.prototype.setPaths = function(pathString) { this.svgPath.setAttribute('d', pathString); this.svgPathLight.style.display = 'none'; - this.svgPathDark.style.display = 'none'; }; /** @@ -119,52 +96,18 @@ Blockly.blockRendering.PathObject.prototype.flipRTL = function() { Blockly.blockRendering.PathObject.prototype.applyColour = function(isShadow) { if (isShadow) { this.svgPath.setAttribute('stroke', 'none'); - this.svgPath.setAttribute('fill', this.secondaryColour); + this.svgPath.setAttribute('fill', this.style.colourSecondary); } else { - this.svgPath.setAttribute('stroke', this.tertiaryColour); - this.svgPath.setAttribute('fill', this.primaryColour); + this.svgPath.setAttribute('stroke', this.style.colourTertiary); + this.svgPath.setAttribute('fill', this.style.colourPrimary); } }; /** - * 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. - * @protected - */ -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. - * @package - */ -Blockly.blockRendering.PathObject.prototype.setColour = function(colour) { - var parsed = Blockly.utils.colour.parseBlockColour(colour); - this.setColourFromTriplet_(parsed.hex, null, null); -}; - -/** - * Update colour properties based on a block style. + * Set the style. * @param {!Blockly.Theme.BlockStyle} blockStyle The block style to use. * @package */ -Blockly.blockRendering.PathObject.prototype.setColourFromStyle = function( - blockStyle) { - var parsed = - Blockly.utils.colour.parseBlockColour(blockStyle['colourPrimary']); - this.setColourFromTriplet_(parsed.hex, - blockStyle['colourSecondary'], - blockStyle['colourTertiary']); +Blockly.blockRendering.PathObject.prototype.setStyle = function(blockStyle) { + this.style = blockStyle; }; diff --git a/core/renderers/geras/path_object.js b/core/renderers/geras/path_object.js index 134b52999..8c63b4eb0 100644 --- a/core/renderers/geras/path_object.js +++ b/core/renderers/geras/path_object.js @@ -35,7 +35,6 @@ goog.require('Blockly.utils.object'); * @param {!SVGElement} root The root SVG element. * @constructor * @implements {Blockly.blockRendering.IPathObject} - * @extends {Blockly.blockRendering.PathObject} * @package */ Blockly.geras.PathObject = function(root) { @@ -69,39 +68,20 @@ Blockly.geras.PathObject = function(root) { this.svgPathLight = Blockly.utils.dom.createSvgElement('path', {'class': 'blocklyPathLight'}, this.svgRoot); - - /** - * Primary colour of the block in '#RRGGBB' format. - * @type {string} - * @package - */ - this.primaryColour = '#000000'; - - /** - * Secondary colour of the block in '#RRGGBB' format. - * Used for the body of a shadow block in Geras. - * @type {string} - * @package - */ - this.secondaryColour = '#000000'; - - /** - * Tertiary colour of the block in '#RRGGBB' format. - * Used for the light path (highlight) in Geras. - * @type {string} - * @package - */ - this.tertiaryColour = '#000000'; - /** * The colour of the dark path on the block in '#RRGGBB' format. * @type {string} * @package */ this.darkColour = '#000000'; + + /** + * The style object to use when colouring block paths. + * @type {!Blockly.Theme.BlockStyle} + * @public + */ + this.style = Blockly.Theme.createBlockStyle('#0000000'); }; -Blockly.utils.object.inherits(Blockly.geras.PathObject, - Blockly.blockRendering.PathObject); /** * Set each of the paths generated by the renderer onto the respective SVG element. @@ -135,26 +115,26 @@ Blockly.geras.PathObject.prototype.flipRTL = function() { Blockly.geras.PathObject.prototype.applyColour = function(isShadow) { if (isShadow) { this.svgPathLight.style.display = 'none'; - this.svgPathDark.setAttribute('fill', this.secondaryColour); + this.svgPathDark.setAttribute('fill', this.style.colourSecondary); this.svgPath.setAttribute('stroke', 'none'); - this.svgPath.setAttribute('fill', this.secondaryColour); + this.svgPath.setAttribute('fill', this.style.colourSecondary); } 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); + this.svgPathLight.setAttribute('stroke', this.style.colourTertiary); + this.svgPathDark.setAttribute('fill', this.colourDark); + this.svgPath.setAttribute('fill', this.style.colourPrimary); } }; /** - * @override + * Set the style. + * @param {!Blockly.Theme.BlockStyle} blockStyle The block style to use. + * @package */ -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); +Blockly.geras.PathObject.prototype.setStyle = function(blockStyle) { + this.style = blockStyle; + this.colourDark = + Blockly.utils.colour.blend('#000', this.style.colourPrimary, 0.2); }; diff --git a/core/theme.js b/core/theme.js index 58adea0e2..dbae4f453 100644 --- a/core/theme.js +++ b/core/theme.js @@ -22,6 +22,7 @@ goog.provide('Blockly.Theme'); +goog.require('Blockly.utils.colour'); /** * Class for a theme. @@ -40,7 +41,10 @@ Blockly.Theme = function(blockStyles, categoryStyles, opt_componentStyles) { * @type {!Object.} * @private */ - this.blockStyles_ = blockStyles; + this.blockStyles_ = {}; + + // Make sure all styles are valid before insterting them into the map. + this.setAllBlockStyles(blockStyles); /** * The category styles map. @@ -97,22 +101,90 @@ Blockly.Theme.prototype.getAllBlockStyles = function() { /** * Gets the BlockStyle for the given block style name. - * @param {string} blockStyleName The name of the block style. - * @return {Blockly.Theme.BlockStyle|undefined} The named block style. + * @param {?string} blockStyleName The name of the block style. + * @return {!Blockly.Theme.BlockStyle} The named block style, or a default style + * if no style with the given name was found. */ Blockly.Theme.prototype.getBlockStyle = function(blockStyleName) { - return this.blockStyles_[blockStyleName]; + var defaultStyle = Blockly.Theme.createBlockStyle('#000000'); + if (blockStyleName == null) { + return defaultStyle; + } + return this.blockStyles_[blockStyleName] || defaultStyle; }; /** * Overrides or adds a style to the blockStyles map. * @param {string} blockStyleName The name of the block style. * @param {Blockly.Theme.BlockStyle} blockStyle The block style. -*/ + * @package + */ Blockly.Theme.prototype.setBlockStyle = function(blockStyleName, blockStyle) { + blockStyle = Blockly.Theme.validatedBlockStyle(blockStyle); this.blockStyles_[blockStyleName] = blockStyle; }; +/** + * Get or create a block style based on a single colour value. Generate a name + * for the style based on the colour. + * @param {string} colour #RRGGBB colour string. + * @return {{style: !Blockly.Theme.BlockStyle, name: string}} An object + * containing the style and an autogenerated name for that style. + * @package + */ +Blockly.Theme.prototype.getBlockStyleForColour = function(colour) { + var name = 'auto_' + colour; + if (!this.blockStyles_[name]) { + this.blockStyles_[name] = Blockly.Theme.createBlockStyle(colour); + } + return {style: this.blockStyles_[name], name: name}; +}; + +/** + * Create a block style object based on the given colour. + * @param {string} colour #RRGGBB colour string. + * @return {!Blockly.Theme.BlockStyle} A populated block style based on the + * given colour. + * @package + */ +Blockly.Theme.createBlockStyle = function(colour) { + return { + colourPrimary: colour, + colourSecondary: Blockly.utils.colour.blend('#fff', colour, 0.6) || colour, + colourTertiary: Blockly.utils.colour.blend('#fff', colour, 0.3) || colour, + hat: '' + }; +}; + +/** + * Get a full block style object based on the input style object. Populate + * any missing values. + * @param {!Blockly.Theme.BlockStyle} blockStyle A full or partial block + * style object. + * @return {!Blockly.Theme.BlockStyle} A full block style object, with all + * required properties populated. + * @package + */ +Blockly.Theme.validatedBlockStyle = function(blockStyle) { + // Make a new object with all of the same properties. + var valid = {}; + Blockly.utils.object.mixin(valid, blockStyle); + + // Validate required properties. + var parsedColour = Blockly.utils.colour.parseBlockColour( + valid.colourPrimary || '#000000'); + valid.colourPrimary = parsedColour.hex; + valid.colourSecondary = valid.colourSecondary || + Blockly.utils.colour.blend('#fff', valid.colourPrimary, 0.6) || + valid.colourPrimary; + valid.colourTertiary = valid.colourTertiary || + Blockly.utils.colour.blend('#fff', valid.colourPrimary, 0.3) || + valid.colourPrimary; + + valid.hat = valid.hat || ''; + return valid; +}; + /** * Gets the CategoryStyle for the given category style name. * @param {string} categoryStyleName The name of the category style. diff --git a/core/utils/colour.js b/core/utils/colour.js index 1a0f50dd0..48f56576f 100644 --- a/core/utils/colour.js +++ b/core/utils/colour.js @@ -229,7 +229,8 @@ Blockly.utils.colour.parseBlockColour = function(colour) { if (!isNaN(hue) && 0 <= hue && hue <= 360) { return { hue: hue, - hex: Blockly.hueToHex(hue) + hex: Blockly.utils.colour.hsvToHex(hue, Blockly.HSV_SATURATION, + Blockly.HSV_VALUE * 255) }; } else { var hex = Blockly.utils.colour.parse(dereferenced); From e91304ede1ccdd797785c293e071db9554cec40f Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 30 Oct 2019 14:25:48 -0700 Subject: [PATCH 08/12] Fix bad reference --- core/icon.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/icon.js b/core/icon.js index f703beea2..5fb7cf9c7 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_.pathObject.primaryColour); + this.bubble_.setColour(this.block_.style.colourPrimary); } }; From dd33d772f79276a1c7e92c39ba137482d6a6064e Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 30 Oct 2019 14:48:02 -0700 Subject: [PATCH 09/12] Rebuild --- blockly_uncompressed.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/blockly_uncompressed.js b/blockly_uncompressed.js index ab92a378d..5426d79fa 100644 --- a/blockly_uncompressed.js +++ b/blockly_uncompressed.js @@ -74,7 +74,7 @@ goog.addDependency("../../core/inject.js", ['Blockly.inject'], ['Blockly.BlockDr goog.addDependency("../../core/input.js", ['Blockly.Input'], ['Blockly.Connection', 'Blockly.FieldLabel']); goog.addDependency("../../core/insertion_marker_manager.js", ['Blockly.InsertionMarkerManager'], ['Blockly.blockAnimations', 'Blockly.Events']); goog.addDependency("../../core/keyboard_nav/action.js", ['Blockly.Action'], []); -goog.addDependency("../../core/keyboard_nav/ast_node.js", ['Blockly.ASTNode'], []); +goog.addDependency("../../core/keyboard_nav/ast_node.js", ['Blockly.ASTNode'], ['Blockly.utils.Coordinate']); goog.addDependency("../../core/keyboard_nav/cursor.js", ['Blockly.Cursor'], []); goog.addDependency("../../core/keyboard_nav/cursor_svg.js", ['Blockly.CursorSvg'], ['Blockly.Cursor', 'Blockly.utils.object']); goog.addDependency("../../core/keyboard_nav/flyout_cursor.js", ['Blockly.FlyoutCursor'], ['Blockly.Cursor', 'Blockly.utils.object']); @@ -89,22 +89,21 @@ 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/i_path_object.js", ['Blockly.blockRendering.IPathObject'], []); 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.blockRendering.IColourer', 'Blockly.CursorSvg']); -goog.addDependency("../../core/renderers/geras/colourer.js", ['Blockly.geras.Colourer'], ['Blockly.geras.PathObject']); +goog.addDependency("../../core/renderers/common/path_object.js", ['Blockly.blockRendering.PathObject'], ['Blockly.blockRendering.IPathObject', 'Blockly.Theme', '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/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']); goog.addDependency("../../core/renderers/geras/highlighter.js", ['Blockly.geras.Highlighter'], ['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/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.Colourer', 'Blockly.geras.Drawer', 'Blockly.geras.HighlightConstantProvider', 'Blockly.geras.PathObject', 'Blockly.geras.RenderInfo', 'Blockly.utils.object']); +goog.addDependency("../../core/renderers/geras/path_object.js", ['Blockly.geras.PathObject'], ['Blockly.blockRendering.IPathObject', 'Blockly.utils.dom', 'Blockly.utils.object']); +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/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']); @@ -124,7 +123,7 @@ goog.addDependency("../../core/renderers/zelos/measurables/rows.js", ['Blockly.z goog.addDependency("../../core/renderers/zelos/renderer.js", ['Blockly.zelos.Renderer'], ['Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.utils.object', 'Blockly.zelos.ConstantProvider', 'Blockly.zelos.Drawer', 'Blockly.zelos.RenderInfo']); goog.addDependency("../../core/requires.js", ['Blockly.requires'], ['Blockly', 'Blockly.Comment', 'Blockly.HorizontalFlyout', 'Blockly.VerticalFlyout', 'Blockly.FlyoutButton', 'Blockly.Generator', 'Blockly.Toolbox', 'Blockly.Trashcan', 'Blockly.VariablesDynamic', 'Blockly.ZoomControls', 'Blockly.Mutator', 'Blockly.FieldAngle', 'Blockly.FieldCheckbox', 'Blockly.FieldColour', 'Blockly.FieldDropdown', 'Blockly.FieldLabelSerializable', 'Blockly.FieldImage', 'Blockly.FieldTextInput', 'Blockly.FieldMultilineInput', 'Blockly.FieldNumber', 'Blockly.FieldVariable', 'Blockly.geras.Renderer', 'Blockly.Themes.Classic', 'Blockly.Themes.Dark', 'Blockly.Themes.Deuteranopia', 'Blockly.Themes.HighContrast', 'Blockly.Themes.Tritanopia']); goog.addDependency("../../core/scrollbar.js", ['Blockly.Scrollbar', 'Blockly.ScrollbarPair'], ['Blockly.Touch', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom']); -goog.addDependency("../../core/theme.js", ['Blockly.Theme'], []); +goog.addDependency("../../core/theme.js", ['Blockly.Theme'], ['Blockly.utils.colour']); goog.addDependency("../../core/theme/classic.js", ['Blockly.Themes.Classic'], ['Blockly.Theme']); goog.addDependency("../../core/theme/dark.js", ['Blockly.Themes.Dark'], ['Blockly.Theme']); goog.addDependency("../../core/theme/deuteranopia.js", ['Blockly.Themes.Deuteranopia'], ['Blockly.Theme']); From 46fad66ab183799d19bf81ab2aa29593a149ebba Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 30 Oct 2019 15:58:53 -0700 Subject: [PATCH 10/12] Review feedback. --- core/block_svg.js | 2 -- core/renderers/common/path_object.js | 4 ++-- core/renderers/geras/path_object.js | 7 ++++--- core/theme.js | 20 +++++++++----------- core/utils/colour.js | 2 +- 5 files changed, 16 insertions(+), 19 deletions(-) diff --git a/core/block_svg.js b/core/block_svg.js index 510220a89..fe2b0b705 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -1043,8 +1043,6 @@ Blockly.BlockSvg.prototype.applyColour = function() { var icons = this.getIcons(); for (var i = 0; i < icons.length; i++) { - // TODO: Decide whether to make icon and field applyColour take in a - // colourer object. icons[i].applyColour(); } diff --git a/core/renderers/common/path_object.js b/core/renderers/common/path_object.js index e24f60a14..493245289 100644 --- a/core/renderers/common/path_object.js +++ b/core/renderers/common/path_object.js @@ -63,9 +63,9 @@ Blockly.blockRendering.PathObject = function(root) { /** * The style object to use when colouring block paths. * @type {!Blockly.Theme.BlockStyle} - * @public + * @package */ - this.style = Blockly.Theme.createBlockStyle('#0000000'); + this.style = Blockly.Theme.createBlockStyle('#000000'); }; /** diff --git a/core/renderers/geras/path_object.js b/core/renderers/geras/path_object.js index 8c63b4eb0..42e995b85 100644 --- a/core/renderers/geras/path_object.js +++ b/core/renderers/geras/path_object.js @@ -25,6 +25,7 @@ goog.provide('Blockly.geras.PathObject'); goog.require('Blockly.blockRendering.IPathObject'); +goog.require('Blockly.Theme'); goog.require('Blockly.utils.dom'); goog.require('Blockly.utils.object'); @@ -73,14 +74,14 @@ Blockly.geras.PathObject = function(root) { * @type {string} * @package */ - this.darkColour = '#000000'; + this.colourDark = '#000000'; /** * The style object to use when colouring block paths. * @type {!Blockly.Theme.BlockStyle} - * @public + * @package */ - this.style = Blockly.Theme.createBlockStyle('#0000000'); + this.style = Blockly.Theme.createBlockStyle('#000000'); }; /** diff --git a/core/theme.js b/core/theme.js index dbae4f453..a46f1af15 100644 --- a/core/theme.js +++ b/core/theme.js @@ -38,12 +38,12 @@ goog.require('Blockly.utils.colour'); Blockly.Theme = function(blockStyles, categoryStyles, opt_componentStyles) { /** * The block styles map. - * @type {!Object.} + * @type {!Object.} * @private */ this.blockStyles_ = {}; - // Make sure all styles are valid before insterting them into the map. + // Make sure all styles are valid before inserting them into the map. this.setAllBlockStyles(blockStyles); /** @@ -106,18 +106,14 @@ Blockly.Theme.prototype.getAllBlockStyles = function() { * if no style with the given name was found. */ Blockly.Theme.prototype.getBlockStyle = function(blockStyleName) { - var defaultStyle = Blockly.Theme.createBlockStyle('#000000'); - if (blockStyleName == null) { - return defaultStyle; - } - return this.blockStyles_[blockStyleName] || defaultStyle; + return this.blockStyles_[blockStyleName || ''] || + Blockly.Theme.createBlockStyle('#000000'); }; /** * Overrides or adds a style to the blockStyles map. * @param {string} blockStyleName The name of the block style. * @param {Blockly.Theme.BlockStyle} blockStyle The block style. - * @package */ Blockly.Theme.prototype.setBlockStyle = function(blockStyleName, blockStyle) { blockStyle = Blockly.Theme.validatedBlockStyle(blockStyle); @@ -159,7 +155,7 @@ Blockly.Theme.createBlockStyle = function(colour) { /** * Get a full block style object based on the input style object. Populate * any missing values. - * @param {!Blockly.Theme.BlockStyle} blockStyle A full or partial block + * @param {Blockly.Theme.BlockStyle} blockStyle A full or partial block * style object. * @return {!Blockly.Theme.BlockStyle} A full block style object, with all * required properties populated. @@ -168,11 +164,13 @@ Blockly.Theme.createBlockStyle = function(colour) { Blockly.Theme.validatedBlockStyle = function(blockStyle) { // Make a new object with all of the same properties. var valid = {}; - Blockly.utils.object.mixin(valid, blockStyle); + if (blockStyle) { + Blockly.utils.object.mixin(valid, blockStyle); + } // Validate required properties. var parsedColour = Blockly.utils.colour.parseBlockColour( - valid.colourPrimary || '#000000'); + valid.colourPrimary || '#000'); valid.colourPrimary = parsedColour.hex; valid.colourSecondary = valid.colourSecondary || Blockly.utils.colour.blend('#fff', valid.colourPrimary, 0.6) || diff --git a/core/utils/colour.js b/core/utils/colour.js index 48f56576f..810bd5637 100644 --- a/core/utils/colour.js +++ b/core/utils/colour.js @@ -219,7 +219,7 @@ Blockly.utils.colour.names = { * or a message reference string pointing to one of those two values. * @return {{hue: ?number, hex: string}} An object containing the colour as * a #RRGGBB string, and the hue if the input was an HSV hue value. - * @throws {Error} If If the colour cannot be parsed. + * @throws {Error} If the colour cannot be parsed. */ Blockly.utils.colour.parseBlockColour = function(colour) { var dereferenced = (typeof colour == 'string') ? From 5634ff5de0134f8b9734dc734a30d8a626143c5f Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 30 Oct 2019 16:07:41 -0700 Subject: [PATCH 11/12] Remove extra annotation --- core/block_svg.js | 1 - 1 file changed, 1 deletion(-) diff --git a/core/block_svg.js b/core/block_svg.js index fe2b0b705..2a2a83c9c 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -66,7 +66,6 @@ Blockly.BlockSvg = function(workspace, prototypeName, opt_id) { /** * A block style object. * @type {!Blockly.Theme.BlockStyle} - * @public */ this.style = workspace.getTheme().getBlockStyle(null); From 207e0ebe7b11d195f9c4b63eafe14d44ce30afe8 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 30 Oct 2019 17:24:03 -0700 Subject: [PATCH 12/12] Add tests and better validation for block styles --- core/theme.js | 12 +-- core/theme/deuteranopia.js | 2 +- tests/jsunit/block_test.js | 22 ------ tests/jsunit/theme_test.js | 153 ------------------------------------- tests/mocha/block_test.js | 62 +++++++++++++++ tests/mocha/theme_test.js | 139 ++++++++++++++++++++++++++++++--- 6 files changed, 198 insertions(+), 192 deletions(-) delete mode 100644 tests/jsunit/theme_test.js diff --git a/core/theme.js b/core/theme.js index a46f1af15..c086d3714 100644 --- a/core/theme.js +++ b/core/theme.js @@ -172,12 +172,12 @@ Blockly.Theme.validatedBlockStyle = function(blockStyle) { var parsedColour = Blockly.utils.colour.parseBlockColour( valid.colourPrimary || '#000'); valid.colourPrimary = parsedColour.hex; - valid.colourSecondary = valid.colourSecondary || - Blockly.utils.colour.blend('#fff', valid.colourPrimary, 0.6) || - valid.colourPrimary; - valid.colourTertiary = valid.colourTertiary || - Blockly.utils.colour.blend('#fff', valid.colourPrimary, 0.3) || - valid.colourPrimary; + valid.colourSecondary = valid.colourSecondary ? + Blockly.utils.colour.parseBlockColour(valid.colourSecondary).hex : + Blockly.utils.colour.blend('#fff', valid.colourPrimary, 0.6); + valid.colourTertiary = valid.colourTertiary ? + Blockly.utils.colour.parseBlockColour(valid.colourTertiary).hex : + Blockly.utils.colour.blend('#fff', valid.colourPrimary, 0.3); valid.hat = valid.hat || ''; return valid; diff --git a/core/theme/deuteranopia.js b/core/theme/deuteranopia.js index a2c18ab84..05b4c9664 100644 --- a/core/theme/deuteranopia.js +++ b/core/theme/deuteranopia.js @@ -34,7 +34,7 @@ Blockly.Themes.Deuteranopia = {}; Blockly.Themes.Deuteranopia.defaultBlockStyles = { "colour_blocks": { "colourPrimary": "#f2a72c", - "colourSecondary": "#f1c17", + "colourSecondary": "#f1c170", "colourTertiary": "#da921c" }, "list_blocks": { diff --git a/tests/jsunit/block_test.js b/tests/jsunit/block_test.js index f8fba5bed..a6588c353 100644 --- a/tests/jsunit/block_test.js +++ b/tests/jsunit/block_test.js @@ -253,28 +253,6 @@ function test_block_row_unplug_multi_inputs_child() { } } -function test_set_style() { - blockTest_setUp(); - var styleStub = { - getBlockStyle: function() { - return { - "colourPrimary": "#ffffff", - "colourSecondary": "#aabbcc", - "colourTertiary": "#ddeeff" - }; - } - }; - mockControl_ = setUpMockMethod(workspace, 'getTheme', null, [styleStub]); - var blockA = workspace.newBlock('row_block'); - blockA.setStyle('styleOne'); - - assertEquals('#ffffff', blockA.colour_); - assertEquals('#aabbcc', blockA.colourSecondary_); - assertEquals('#ddeeff', blockA.colourTertiary_); - - blockTest_tearDown(); -} - function test_set_style_throw_exception() { blockTest_setUp(); var styleStub = { diff --git a/tests/jsunit/theme_test.js b/tests/jsunit/theme_test.js deleted file mode 100644 index 22ba32c2e..000000000 --- a/tests/jsunit/theme_test.js +++ /dev/null @@ -1,153 +0,0 @@ -/** - * @license - * Copyright 2018 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 Tests for Blockly.Style - * @author aschmiedt@google.com (Abby Schmiedt) - */ -'use strict'; - -function defineThemeTestBlocks() { - Blockly.defineBlocksWithJsonArray([{ - "type": "stack_block", - "message0": "", - "previousStatement": null, - "nextStatement": null - }, - { - "type": "row_block", - "message0": "%1", - "args0": [ - { - "type": "input_value", - "name": "INPUT" - } - ], - "output": null - }]); -}; - -function undefineThemeTestBlocks() { - delete Blockly.Blocks['stack_block']; - delete Blockly.Blocks['row_block']; -} - - -function createBlockStyles() { - return { - "styleOne": { - "colourPrimary": "colour1", - "colourSecondary":"colour2", - "colourTertiary":"colour3" - } - }; -} - -function createMultipleBlockStyles() { - return { - "styleOne": { - "colourPrimary": "colour1", - "colourSecondary":"colour2", - "colourTertiary":"colour3" - }, - "styleTwo": { - "colourPrimary": "colour1", - "colourSecondary":"colour2", - "colourTertiary":"colour3" - } - }; -} - -function test_setAllBlockStyles() { - var theme = new Blockly.Theme(createBlockStyles()); - stringifyAndCompare(createBlockStyles(), theme.blockStyles_); - theme.setAllBlockStyles(createMultipleBlockStyles()); - stringifyAndCompare(createMultipleBlockStyles(), theme.blockStyles_); -} - -function test_getAllBlockStyles() { - var theme = new Blockly.Theme(createMultipleBlockStyles()); - var allBlocks = theme.getAllBlockStyles(); - stringifyAndCompare(createMultipleBlockStyles(), allBlocks); - -} - -function test_getBlockStyles() { - var theme = new Blockly.Theme(createBlockStyles()); - var blockStyle = theme.getBlockStyle('styleOne'); - - stringifyAndCompare(blockStyle, createBlockStyles().styleOne); -} - -function test_setBlockStyleUpdate() { - var theme = new Blockly.Theme(createBlockStyles()); - var blockStyle = createBlockStyles(); - blockStyle.styleOne.colourPrimary = 'somethingElse'; - - theme.setBlockStyle('styleOne', blockStyle.styleOne); - - stringifyAndCompare(theme.blockStyles_, blockStyle); -} - -function test_setBlockStyleAdd() { - var theme = new Blockly.Theme(createBlockStyles()); - var blockStyle = createMultipleBlockStyles(); - - theme.setBlockStyle('styleTwo', blockStyle.styleTwo); - - stringifyAndCompare(theme.blockStyles_, blockStyle); -} - -function test_setTheme() { - defineThemeTestBlocks(); - var blockStyles = createBlockStyles(); - var workspace = new Blockly.WorkspaceSvg({}); - var blockA = workspace.newBlock('stack_block'); - var blocks = [blockA]; - - blockA.setStyle = function() {this.styleName_ = 'styleTwo'}; - var callCount = 1; - workspace.refreshToolboxSelection = function() { - return ++callCount; - }; - blockA.styleName_ = 'styleOne'; - - var mockControl_ = setUpMockMethod(Blockly, 'getMainWorkspace', null, [workspace]); - - workspace.setTheme(blockStyles); - - //Checks that the theme was set correctly on Blockly namespace - stringifyAndCompare(workspace.getTheme(), blockStyles); - - //Checks that the setTheme function was called on the block - assertEquals(blockA.getStyleName(), 'styleTwo'); - - //check that the toolbox refreshed method was called - assertEquals(workspace.refreshToolboxSelection(), 3); - - assertEquals(Blockly.Events.FIRE_QUEUE_.pop().element, 'theme'); - - undefineThemeTestBlocks(); - - mockControl_.restore(); -} - -function stringifyAndCompare(val1, val2) { - var stringVal1 = JSON.stringify(val1); - var stringVal2 = JSON.stringify(val2); - assertEquals(stringVal1, stringVal2); -} diff --git a/tests/mocha/block_test.js b/tests/mocha/block_test.js index 27b496fcb..b01e96989 100644 --- a/tests/mocha/block_test.js +++ b/tests/mocha/block_test.js @@ -1249,4 +1249,66 @@ suite('Blocks', function() { }); }); }); + + suite('Style', function() { + suite('Headless', function() { + setup(function() { + this.block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( + '' + ), this.workspace); + }); + test('Set colour', function() { + this.block.setColour('20'); + assertEquals(this.block.getColour(), '#a5745b'); + assertEquals(this.block.colour_, this.block.getColour()); + assertEquals(this.block.hue_, '20'); + }); + test('Set style', function() { + this.block.setStyle('styleOne'); + assertEquals(this.block.getStyleName(), 'styleOne'); + assertEquals(this.block.hue_, null); + // Calling setStyle does not update the colour on a headless block. + assertEquals(this.block.getColour(), '#000000'); + }); + }); + suite('Rendered', function() { + setup(function() { + this.workspace = Blockly.inject('blocklyDiv', {}); + this.block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( + '' + ), this.workspace); + this.workspace.setTheme(new Blockly.Theme({ + "styleOne" : { + "colourPrimary": "#000000", + "colourSecondary": "#999999", + "colourTertiary": "#4d4d4d", + "hat": '' + } + }), {}); + }); + teardown(function() { + this.workspace.dispose(); + }); + test('Set colour hue', function() { + this.block.setColour('20'); + assertEquals(this.block.getStyleName(), 'auto_#a5745b'); + assertEquals(this.block.getColour(), '#a5745b'); + assertEquals(this.block.colour_, this.block.getColour()); + assertEquals(this.block.hue_, '20'); + }); + test('Set colour hex', function() { + this.block.setColour('#000000'); + assertEquals(this.block.getStyleName(), 'auto_#000000'); + assertEquals(this.block.getColour(), '#000000'); + assertEquals(this.block.colour_, this.block.getColour()); + assertEquals(this.block.hue_, null); + }); + test('Set style', function() { + this.block.setStyle('styleOne'); + assertEquals(this.block.getStyleName(), 'styleOne'); + assertEquals(this.block.getColour(), '#000000'); + assertEquals(this.block.colour_, this.block.getColour()); + }); + }); + }); }); diff --git a/tests/mocha/theme_test.js b/tests/mocha/theme_test.js index 4f17b730b..e6d5f7592 100644 --- a/tests/mocha/theme_test.js +++ b/tests/mocha/theme_test.js @@ -52,9 +52,10 @@ suite('Theme', function() { function createBlockStyles() { return { "styleOne": { - "colourPrimary": "colour1", - "colourSecondary":"colour2", - "colourTertiary":"colour3" + "colourPrimary": "#aaaaaa", + "colourSecondary": "#bbbbbb", + "colourTertiary": "#cccccc", + "hat": 'cap' } }; } @@ -62,14 +63,16 @@ suite('Theme', function() { function createMultipleBlockStyles() { return { "styleOne": { - "colourPrimary": "colour1", - "colourSecondary":"colour2", - "colourTertiary":"colour3" + "colourPrimary": "#aaaaaa", + "colourSecondary": "#bbbbbb", + "colourTertiary": "#cccccc", + "hat": 'cap' }, "styleTwo": { - "colourPrimary": "colour1", - "colourSecondary":"colour2", - "colourTertiary":"colour3" + "colourPrimary": "#000000", + "colourSecondary": "#999999", + "colourTertiary": "#4d4d4d", + "hat": '' } }; } @@ -103,7 +106,7 @@ suite('Theme', function() { test('Set BlockStyle Update', function() { var theme = new Blockly.Theme(createBlockStyles()); var blockStyle = createBlockStyles(); - blockStyle.styleOne.colourPrimary = 'somethingElse'; + blockStyle.styleOne.colourPrimary = '#00ff00'; theme.setBlockStyle('styleOne', blockStyle.styleOne); @@ -152,4 +155,120 @@ suite('Theme', function() { stub.restore(); }); + suite('Validate block styles', function() { + test('Null', function() { + var inputStyle = null; + var expectedOutput = { + "colourPrimary": "#000000", + "colourSecondary": "#999999", + "colourTertiary": "#4d4d4d", + "hat": '' + }; + stringifyAndCompare( + Blockly.Theme.validatedBlockStyle(inputStyle), expectedOutput); + }); + + test('Empty', function() { + var inputStyle = {}; + var expectedOutput = { + "colourPrimary": "#000000", + "colourSecondary": "#999999", + "colourTertiary": "#4d4d4d", + "hat": '' + }; + stringifyAndCompare( + Blockly.Theme.validatedBlockStyle(inputStyle), expectedOutput); + }); + + test('Incomplete hex', function() { + var inputStyle = { + "colourPrimary": "#012345" + }; + var expectedOutput = { + "colourPrimary": "#012345", + "colourSecondary": "#99a7b5", + "colourTertiary": "#4d657d", + "hat": '' + }; + stringifyAndCompare( + Blockly.Theme.validatedBlockStyle(inputStyle), expectedOutput); + }); + + test('Complete hex', function() { + var inputStyle = { + "colourPrimary": "#aaaaaa", + "colourSecondary": "#bbbbbb", + "colourTertiary": "#cccccc", + "hat": 'cap' + }; + var expectedOutput = { + "colourPrimary": "#aaaaaa", + "colourSecondary": "#bbbbbb", + "colourTertiary": "#cccccc", + "hat": 'cap' + }; + stringifyAndCompare( + Blockly.Theme.validatedBlockStyle(inputStyle), expectedOutput); + }); + + test('Complete hue', function() { + var inputStyle = { + "colourPrimary": "20", + "colourSecondary": "40", + "colourTertiary": "60", + }; + var expectedOutput = { + "colourPrimary": "#a5745b", + "colourSecondary": "#a58c5b", + "colourTertiary": "#a5a55b", + "hat": '' + }; + stringifyAndCompare( + Blockly.Theme.validatedBlockStyle(inputStyle), expectedOutput); + }); + + test('Incomplete hue', function() { + var inputStyle = { + "colourPrimary": "20", + }; + var expectedOutput = { + "colourPrimary": "#a5745b", + "colourSecondary": "#dbc7bd", + "colourTertiary": "#c09e8c", + "hat": '' + }; + stringifyAndCompare( + Blockly.Theme.validatedBlockStyle(inputStyle), expectedOutput); + }); + + test('Complete css colour name', function() { + var inputStyle = { + "colourPrimary": "red", + "colourSecondary": "white", + "colourTertiary": "blue" + }; + var expectedOutput = { + "colourPrimary": "#ff0000", + "colourSecondary": "#ffffff", + "colourTertiary": "#0000ff", + "hat": '' + }; + stringifyAndCompare( + Blockly.Theme.validatedBlockStyle(inputStyle), expectedOutput); + }); + + test('Incomplete css colour name', function() { + var inputStyle = { + "colourPrimary": "black", + }; + var expectedOutput = { + "colourPrimary": "#000000", + "colourSecondary": "#999999", + "colourTertiary": "#4d4d4d", + "hat": '' + }; + stringifyAndCompare( + Blockly.Theme.validatedBlockStyle(inputStyle), expectedOutput); + }); + }); });