From 5ef08fc0d4d2d2622495c0bc898d8a44207da116 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 30 Oct 2019 14:22:35 -0700 Subject: [PATCH] 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);