From 13ac939b91e76212f61c8bfce36723b497cfcf83 Mon Sep 17 00:00:00 2001 From: Sam El-Husseini Date: Tue, 10 Mar 2020 18:36:29 -0700 Subject: [PATCH] Support passing renderer overrides in options (#3734) * Support passing in renderer options through workspace options. --- core/block_svg.js | 3 + core/field.js | 31 +++++--- core/field_checkbox.js | 2 +- core/field_colour.js | 8 +- core/field_dropdown.js | 46 ++++++------ core/field_multilineinput.js | 28 +++---- core/field_textinput.js | 10 +-- core/field_variable.js | 2 +- core/mutator.js | 3 +- core/options.js | 1 + core/renderers/common/block_rendering.js | 5 +- core/renderers/common/constants.js | 96 +++++++----------------- core/renderers/common/path_object.js | 8 +- core/renderers/common/renderer.js | 31 ++++++-- core/renderers/geras/path_object.js | 4 +- core/renderers/geras/renderer.js | 6 +- core/renderers/zelos/constants.js | 34 +++++---- core/renderers/zelos/path_object.js | 8 +- core/theme_manager.js | 4 - core/toolbox.js | 3 +- core/trashcan.js | 3 +- core/workspace_svg.js | 10 ++- 22 files changed, 176 insertions(+), 170 deletions(-) diff --git a/core/block_svg.js b/core/block_svg.js index 911712058..a5a263083 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -586,6 +586,9 @@ Blockly.BlockSvg.prototype.getBoundingRectangle = function() { * A dirty field is a field that needs to be re-rendererd. */ Blockly.BlockSvg.prototype.markDirty = function() { + this.pathObject.constants = + (/** @type {!Blockly.WorkspaceSvg} */ (this.workspace)) + .getRenderer().getConstants(); for (var i = 0, input; (input = this.inputList[i]); i++) { input.markDirty(); } diff --git a/core/field.js b/core/field.js index c51ec65e8..6a48a3050 100644 --- a/core/field.js +++ b/core/field.js @@ -249,9 +249,19 @@ Blockly.Field.prototype.setSourceBlock = function(block) { throw Error('Field already bound to a block.'); } this.sourceBlock_ = block; - if (block.workspace.rendered) { - this.constants_ = block.workspace.getRenderer().getConstants(); +}; + +/** + * Get the renderer constant provider. + * @return {?Blockly.blockRendering.ConstantProvider} The renderer constant + * provider. + */ +Blockly.Field.prototype.getConstants = function() { + if (!this.constants_ && this.sourceBlock_ && this.sourceBlock_.workspace && + this.sourceBlock_.workspace.rendered) { + this.constants_ = this.sourceBlock_.workspace.getRenderer().getConstants(); } + return this.constants_; }; /** @@ -313,8 +323,8 @@ Blockly.Field.prototype.createBorderRect_ = function() { this.borderRect_ = /** @type {!SVGRectElement} **/ (Blockly.utils.dom.createSvgElement('rect', { - 'rx': this.constants_.FIELD_BORDER_RECT_RADIUS, - 'ry': this.constants_.FIELD_BORDER_RECT_RADIUS, + 'rx': this.getConstants().FIELD_BORDER_RECT_RADIUS, + 'ry': this.getConstants().FIELD_BORDER_RECT_RADIUS, 'x': 0, 'y': 0, 'height': this.size_.height, @@ -335,7 +345,7 @@ Blockly.Field.prototype.createTextElement_ = function() { { 'class': 'blocklyText', }, this.fieldGroup_)); - if (this.constants_.FIELD_TEXT_BASELINE_CENTER) { + if (this.getConstants().FIELD_TEXT_BASELINE_CENTER) { this.textElement_.setAttribute('dominant-baseline', 'central'); } this.textContent_ = document.createTextNode(''); @@ -607,9 +617,9 @@ Blockly.Field.prototype.updateWidth = function() { * @protected */ Blockly.Field.prototype.updateSize_ = function(opt_margin) { - var constants = this.constants_; + var constants = this.getConstants(); var xOffset = opt_margin != undefined ? opt_margin : - (this.borderRect_ ? this.constants_.FIELD_BORDER_RECT_X_PADDING : 0); + (this.borderRect_ ? this.getConstants().FIELD_BORDER_RECT_X_PADDING : 0); var totalWidth = xOffset * 2; var totalHeight = constants.FIELD_TEXT_HEIGHT; @@ -643,7 +653,7 @@ Blockly.Field.prototype.positionTextElement_ = function(xOffset, contentWidth) { if (!this.textElement_) { return; } - var constants = this.constants_; + var constants = this.getConstants(); var halfHeight = this.size_.height / 2; this.textElement_.setAttribute('x', this.sourceBlock_.RTL ? @@ -664,9 +674,9 @@ Blockly.Field.prototype.positionBorderRect_ = function() { this.borderRect_.setAttribute('width', this.size_.width); this.borderRect_.setAttribute('height', this.size_.height); this.borderRect_.setAttribute('rx', - this.constants_.FIELD_BORDER_RECT_RADIUS); + this.getConstants().FIELD_BORDER_RECT_RADIUS); this.borderRect_.setAttribute('ry', - this.constants_.FIELD_BORDER_RECT_RADIUS); + this.getConstants().FIELD_BORDER_RECT_RADIUS); }; @@ -796,6 +806,7 @@ Blockly.Field.prototype.setText = function(_newText) { */ Blockly.Field.prototype.markDirty = function() { this.isDirty_ = true; + this.constants_ = null; }; /** diff --git a/core/field_checkbox.js b/core/field_checkbox.js index 14736be32..e4818ebe8 100644 --- a/core/field_checkbox.js +++ b/core/field_checkbox.js @@ -113,7 +113,7 @@ Blockly.FieldCheckbox.prototype.render_ = function() { if (this.textContent_) { this.textContent_.nodeValue = this.getDisplayText_(); } - this.updateSize_(this.constants_.FIELD_CHECKBOX_X_OFFSET); + this.updateSize_(this.getConstants().FIELD_CHECKBOX_X_OFFSET); }; /** diff --git a/core/field_colour.js b/core/field_colour.js index 362082d73..6cf82d089 100644 --- a/core/field_colour.js +++ b/core/field_colour.js @@ -173,9 +173,9 @@ Blockly.FieldColour.prototype.configure_ = function(config) { */ Blockly.FieldColour.prototype.initView = function() { this.size_ = new Blockly.utils.Size( - this.constants_.FIELD_COLOUR_DEFAULT_WIDTH, - this.constants_.FIELD_COLOUR_DEFAULT_HEIGHT); - if (!this.constants_.FIELD_COLOUR_FULL_BLOCK) { + this.getConstants().FIELD_COLOUR_DEFAULT_WIDTH, + this.getConstants().FIELD_COLOUR_DEFAULT_HEIGHT); + if (!this.getConstants().FIELD_COLOUR_FULL_BLOCK) { this.createBorderRect_(); this.borderRect_.style['fillOpacity'] = '1'; } else { @@ -187,7 +187,7 @@ Blockly.FieldColour.prototype.initView = function() { * @override */ Blockly.FieldColour.prototype.applyColour = function() { - if (!this.constants_.FIELD_COLOUR_FULL_BLOCK) { + if (!this.getConstants().FIELD_COLOUR_FULL_BLOCK) { if (this.borderRect_) { this.borderRect_.style.fill = this.getValue(); } diff --git a/core/field_dropdown.js b/core/field_dropdown.js index b50340d60..87062c5a4 100644 --- a/core/field_dropdown.js +++ b/core/field_dropdown.js @@ -201,7 +201,7 @@ Blockly.FieldDropdown.prototype.initView = function() { this.imageElement_ = /** @type {!SVGImageElement} */ (Blockly.utils.dom.createSvgElement('image', {}, this.fieldGroup_)); - if (this.constants_.FIELD_DROPDOWN_SVG_ARROW) { + if (this.getConstants().FIELD_DROPDOWN_SVG_ARROW) { this.createSVGArrow_(); } else { this.createTextArrow_(); @@ -218,8 +218,8 @@ Blockly.FieldDropdown.prototype.initView = function() { * @protected */ Blockly.FieldDropdown.prototype.shouldAddBorderRect_ = function() { - return !this.constants_.FIELD_DROPDOWN_NO_BORDER_RECT_SHADOW || - (this.constants_.FIELD_DROPDOWN_NO_BORDER_RECT_SHADOW && + return !this.getConstants().FIELD_DROPDOWN_NO_BORDER_RECT_SHADOW || + (this.getConstants().FIELD_DROPDOWN_NO_BORDER_RECT_SHADOW && !this.sourceBlock_.isShadow()); }; @@ -247,11 +247,11 @@ Blockly.FieldDropdown.prototype.createTextArrow_ = function() { */ Blockly.FieldDropdown.prototype.createSVGArrow_ = function() { this.svgArrow_ = Blockly.utils.dom.createSvgElement('image', { - 'height': this.constants_.FIELD_DROPDOWN_SVG_ARROW_SIZE + 'px', - 'width': this.constants_.FIELD_DROPDOWN_SVG_ARROW_SIZE + 'px' + 'height': this.getConstants().FIELD_DROPDOWN_SVG_ARROW_SIZE + 'px', + 'width': this.getConstants().FIELD_DROPDOWN_SVG_ARROW_SIZE + 'px' }, this.fieldGroup_); this.svgArrow_.setAttributeNS(Blockly.utils.dom.XLINK_NS, 'xlink:href', - this.constants_.FIELD_DROPDOWN_SVG_ARROW_DATAURI); + this.getConstants().FIELD_DROPDOWN_SVG_ARROW_DATAURI); }; /** @@ -273,7 +273,7 @@ Blockly.FieldDropdown.prototype.showEditor_ = function(opt_e) { Blockly.utils.dom.addClass( /** @type {!Element} */ (this.menu_.getElement()), 'blocklyDropdownMenu'); - if (this.constants_.FIELD_DROPDOWN_COLOURED_DIV) { + if (this.getConstants().FIELD_DROPDOWN_COLOURED_DIV) { var primaryColour = (this.sourceBlock_.isShadow()) ? this.sourceBlock_.getParent().getColour() : this.sourceBlock_.getColour(); @@ -587,19 +587,19 @@ Blockly.FieldDropdown.prototype.renderSelectedImage_ = function(imageJson) { // Height and width include the border rect. var hasBorder = !!this.borderRect_; var height = Math.max( - hasBorder ? this.constants_.FIELD_DROPDOWN_BORDER_RECT_HEIGHT : 0, + hasBorder ? this.getConstants().FIELD_DROPDOWN_BORDER_RECT_HEIGHT : 0, imageHeight + Blockly.FieldDropdown.IMAGE_Y_PADDING); - var xPadding = hasBorder ? this.constants_.FIELD_BORDER_RECT_X_PADDING : 0; + var xPadding = hasBorder ? this.getConstants().FIELD_BORDER_RECT_X_PADDING : 0; var arrowWidth = 0; if (this.svgArrow_) { arrowWidth = this.positionSVGArrow_(imageWidth + xPadding, height / 2 - - this.constants_.FIELD_DROPDOWN_SVG_ARROW_SIZE / 2); + this.getConstants().FIELD_DROPDOWN_SVG_ARROW_SIZE / 2); } else { arrowWidth = Blockly.utils.dom.getFastTextWidth( /** @type {!SVGTSpanElement} */ (this.arrow_), - this.constants_.FIELD_TEXT_FONTSIZE, - this.constants_.FIELD_TEXT_FONTWEIGHT, - this.constants_.FIELD_TEXT_FONTFAMILY); + this.getConstants().FIELD_TEXT_FONTSIZE, + this.getConstants().FIELD_TEXT_FONTWEIGHT, + this.getConstants().FIELD_TEXT_FONTFAMILY); } this.size_.width = imageWidth + arrowWidth + xPadding * 2; this.size_.height = height; @@ -632,17 +632,17 @@ Blockly.FieldDropdown.prototype.renderSelectedText_ = function() { // Height and width include the border rect. var hasBorder = !!this.borderRect_; var height = Math.max( - hasBorder ? this.constants_.FIELD_DROPDOWN_BORDER_RECT_HEIGHT : 0, - this.constants_.FIELD_TEXT_HEIGHT); + hasBorder ? this.getConstants().FIELD_DROPDOWN_BORDER_RECT_HEIGHT : 0, + this.getConstants().FIELD_TEXT_HEIGHT); var textWidth = Blockly.utils.dom.getFastTextWidth(this.textElement_, - this.constants_.FIELD_TEXT_FONTSIZE, - this.constants_.FIELD_TEXT_FONTWEIGHT, - this.constants_.FIELD_TEXT_FONTFAMILY); - var xPadding = hasBorder ? this.constants_.FIELD_BORDER_RECT_X_PADDING : 0; + this.getConstants().FIELD_TEXT_FONTSIZE, + this.getConstants().FIELD_TEXT_FONTWEIGHT, + this.getConstants().FIELD_TEXT_FONTFAMILY); + var xPadding = hasBorder ? this.getConstants().FIELD_BORDER_RECT_X_PADDING : 0; var arrowWidth = 0; if (this.svgArrow_) { arrowWidth = this.positionSVGArrow_(textWidth + xPadding, height / 2 - - this.constants_.FIELD_DROPDOWN_SVG_ARROW_SIZE / 2); + this.getConstants().FIELD_DROPDOWN_SVG_ARROW_SIZE / 2); } this.size_.width = textWidth + arrowWidth + xPadding * 2; this.size_.height = height; @@ -662,9 +662,9 @@ Blockly.FieldDropdown.prototype.positionSVGArrow_ = function(x, y) { return 0; } var hasBorder = !!this.borderRect_; - var xPadding = hasBorder ? this.constants_.FIELD_BORDER_RECT_X_PADDING : 0; - var textPadding = this.constants_.FIELD_DROPDOWN_SVG_ARROW_PADDING; - var svgArrowSize = this.constants_.FIELD_DROPDOWN_SVG_ARROW_SIZE; + var xPadding = hasBorder ? this.getConstants().FIELD_BORDER_RECT_X_PADDING : 0; + var textPadding = this.getConstants().FIELD_DROPDOWN_SVG_ARROW_PADDING; + var svgArrowSize = this.getConstants().FIELD_DROPDOWN_SVG_ARROW_SIZE; var arrowX = this.sourceBlock_.RTL ? xPadding : x + textPadding; this.svgArrow_.setAttribute('transform', 'translate(' + arrowX + ',' + y + ')'); diff --git a/core/field_multilineinput.js b/core/field_multilineinput.js index 5cb9b8e2b..4e2f7dfc2 100644 --- a/core/field_multilineinput.js +++ b/core/field_multilineinput.js @@ -136,13 +136,13 @@ Blockly.FieldMultilineInput.prototype.render_ = function() { var lines = this.getDisplayText_().split('\n'); var y = 0; for (var i = 0; i < lines.length; i++) { - var lineHeight = this.constants_.FIELD_TEXT_HEIGHT + - this.constants_.FIELD_BORDER_RECT_Y_PADDING; + var lineHeight = this.getConstants().FIELD_TEXT_HEIGHT + + this.getConstants().FIELD_BORDER_RECT_Y_PADDING; var span = Blockly.utils.dom.createSvgElement('text', { 'class': 'blocklyText blocklyMultilineText', - x: this.constants_.FIELD_BORDER_RECT_X_PADDING, - y: y + this.constants_.FIELD_BORDER_RECT_Y_PADDING, - dy: this.constants_.FIELD_TEXT_BASELINE + x: this.getConstants().FIELD_BORDER_RECT_X_PADDING, + y: y + this.getConstants().FIELD_BORDER_RECT_Y_PADDING, + dy: this.getConstants().FIELD_TEXT_BASELINE }, this.textGroup_); span.appendChild(document.createTextNode(lines[i])); y += lineHeight; @@ -186,12 +186,12 @@ Blockly.FieldMultilineInput.prototype.updateSize_ = function() { if (textWidth > totalWidth) { totalWidth = textWidth; } - totalHeight += this.constants_.FIELD_TEXT_HEIGHT + - (i > 0 ? this.constants_.FIELD_BORDER_RECT_Y_PADDING : 0); + totalHeight += this.getConstants().FIELD_TEXT_HEIGHT + + (i > 0 ? this.getConstants().FIELD_BORDER_RECT_Y_PADDING : 0); } if (this.borderRect_) { - totalHeight += this.constants_.FIELD_BORDER_RECT_Y_PADDING * 2; - totalWidth += this.constants_.FIELD_BORDER_RECT_X_PADDING * 2; + totalHeight += this.getConstants().FIELD_BORDER_RECT_Y_PADDING * 2; + totalWidth += this.getConstants().FIELD_BORDER_RECT_X_PADDING * 2; this.borderRect_.setAttribute('width', totalWidth); this.borderRect_.setAttribute('height', totalHeight); } @@ -214,17 +214,17 @@ Blockly.FieldMultilineInput.prototype.widgetCreate_ = function() { /** @type {HTMLTextAreaElement} */ (document.createElement('textarea')); htmlInput.className = 'blocklyHtmlInput blocklyHtmlTextAreaInput'; htmlInput.setAttribute('spellcheck', this.spellcheck_); - var fontSize = (this.constants_.FIELD_TEXT_FONTSIZE * scale) + 'pt'; + var fontSize = (this.getConstants().FIELD_TEXT_FONTSIZE * scale) + 'pt'; div.style.fontSize = fontSize; htmlInput.style.fontSize = fontSize; var borderRadius = (Blockly.FieldTextInput.BORDERRADIUS * scale) + 'px'; htmlInput.style.borderRadius = borderRadius; - var paddingX = this.constants_.FIELD_BORDER_RECT_X_PADDING * scale; - var paddingY = this.constants_.FIELD_BORDER_RECT_Y_PADDING * scale / 2; + var paddingX = this.getConstants().FIELD_BORDER_RECT_X_PADDING * scale; + var paddingY = this.getConstants().FIELD_BORDER_RECT_Y_PADDING * scale / 2; htmlInput.style.padding = paddingY + 'px ' + paddingX + 'px ' + paddingY + 'px ' + paddingX + 'px'; - var lineHeight = this.constants_.FIELD_TEXT_HEIGHT + - this.constants_.FIELD_BORDER_RECT_Y_PADDING; + var lineHeight = this.getConstants().FIELD_TEXT_HEIGHT + + this.getConstants().FIELD_BORDER_RECT_Y_PADDING; htmlInput.style.lineHeight = (lineHeight * scale) + 'px'; div.appendChild(htmlInput); diff --git a/core/field_textinput.js b/core/field_textinput.js index 4bf4b8406..b2c4d7ce6 100644 --- a/core/field_textinput.js +++ b/core/field_textinput.js @@ -128,7 +128,7 @@ Blockly.FieldTextInput.prototype.configure_ = function(config) { * @override */ Blockly.FieldTextInput.prototype.initView = function() { - if (this.constants_.FULL_BLOCK_FIELDS) { + if (this.getConstants().FULL_BLOCK_FIELDS) { // Step one: figure out if this is the only field on this block. // Rendering is quite different in that case. var nFields = 0; @@ -216,13 +216,13 @@ Blockly.FieldTextInput.prototype.doValueUpdate_ = function(newValue) { * @package */ Blockly.FieldTextInput.prototype.applyColour = function() { - if (this.sourceBlock_ && this.constants_.FULL_BLOCK_FIELDS) { + if (this.sourceBlock_ && this.getConstants().FULL_BLOCK_FIELDS) { if (this.borderRect_) { this.borderRect_.setAttribute('stroke', this.sourceBlock_.style.colourTertiary); } else { this.sourceBlock_.pathObject.svgPath.setAttribute('fill', - this.constants_.FIELD_BORDER_RECT_COLOUR); + this.getConstants().FIELD_BORDER_RECT_COLOUR); } } }; @@ -332,7 +332,7 @@ Blockly.FieldTextInput.prototype.widgetCreate_ = function() { htmlInput.setAttribute('spellcheck', this.spellcheck_); var scale = this.workspace_.scale; var fontSize = - (this.constants_.FIELD_TEXT_FONTSIZE * scale) + 'pt'; + (this.getConstants().FIELD_TEXT_FONTSIZE * scale) + 'pt'; div.style.fontSize = fontSize; htmlInput.style.fontSize = fontSize; var borderRadius = @@ -350,7 +350,7 @@ Blockly.FieldTextInput.prototype.widgetCreate_ = function() { htmlInput.style.border = (1 * scale) + 'px solid ' + strokeColour; div.style.borderRadius = borderRadius; div.style.transition = 'box-shadow 0.25s ease 0s'; - if (this.constants_.FIELD_TEXTINPUT_BOX_SHADOW) { + if (this.getConstants().FIELD_TEXTINPUT_BOX_SHADOW) { div.style.boxShadow = 'rgba(255, 255, 255, 0.3) 0px 0px 0px ' + 4 * scale + 'px'; } diff --git a/core/field_variable.js b/core/field_variable.js index 2410cf6c1..7d7801384 100644 --- a/core/field_variable.js +++ b/core/field_variable.js @@ -145,7 +145,7 @@ Blockly.FieldVariable.prototype.initModel = function() { */ Blockly.FieldVariable.prototype.shouldAddBorderRect_ = function() { return Blockly.FieldVariable.superClass_.shouldAddBorderRect_.call(this) && - (!this.constants_.FIELD_DROPDOWN_NO_BORDER_RECT_SHADOW || + (!this.getConstants().FIELD_DROPDOWN_NO_BORDER_RECT_SHADOW || this.sourceBlock_.type != 'variables_get'); }; diff --git a/core/mutator.js b/core/mutator.js index 6deed6b52..00e996a84 100644 --- a/core/mutator.js +++ b/core/mutator.js @@ -160,7 +160,8 @@ Blockly.Mutator.prototype.createEditor_ = function() { 'media': this.block_.workspace.options.pathToMedia, 'rtl': this.block_.RTL, 'horizontalLayout': false, - 'renderer': this.block_.workspace.options.renderer + 'renderer': this.block_.workspace.options.renderer, + 'rendererOverrides': this.block_.workspace.options.rendererOverrides })); workspaceOptions.toolboxPosition = this.block_.RTL ? Blockly.TOOLBOX_AT_RIGHT : Blockly.TOOLBOX_AT_LEFT; diff --git a/core/options.js b/core/options.js index 307129915..fd97f8d94 100644 --- a/core/options.js +++ b/core/options.js @@ -131,6 +131,7 @@ Blockly.Options = function(options) { this.theme = Blockly.Options.parseThemeOptions_(options); this.keyMap = keyMap; this.renderer = renderer; + this.rendererOverrides = options['rendererOverrides']; /** * The SVG element for the grid pattern. diff --git a/core/renderers/common/block_rendering.js b/core/renderers/common/block_rendering.js index 5c2a091f0..8a7757cd1 100644 --- a/core/renderers/common/block_rendering.js +++ b/core/renderers/common/block_rendering.js @@ -80,16 +80,17 @@ Blockly.blockRendering.stopDebugger = function() { * Initialize anything needed for rendering (constants, etc). * @param {!string} name Name of the renderer to initialize. * @param {!Blockly.Theme} theme The workspace theme object. + * @param {Object=} opt_rendererOverrides Rendering constant overrides. * @return {!Blockly.blockRendering.Renderer} The new instance of a renderer. * Already initialized. * @package */ -Blockly.blockRendering.init = function(name, theme) { +Blockly.blockRendering.init = function(name, theme, opt_rendererOverrides) { if (!Blockly.blockRendering.rendererMap_[name]) { throw Error('Renderer not registered: ', name); } var renderer = (/** @type {!Blockly.blockRendering.Renderer} */ ( new Blockly.blockRendering.rendererMap_[name](name))); - renderer.init(theme); + renderer.init(theme, opt_rendererOverrides); return renderer; }; diff --git a/core/renderers/common/constants.js b/core/renderers/common/constants.js index 5de6be11c..8de863042 100644 --- a/core/renderers/common/constants.js +++ b/core/renderers/common/constants.js @@ -28,27 +28,6 @@ goog.requireType('Blockly.blockRendering.Debug'); */ Blockly.blockRendering.ConstantProvider = function() { - /** - * A placeholder value for number constants that are dynamically set. - * @type {number} - * @protected - */ - this.DYNAMICALLY_SET_ = -1; - - /** - * A placeholder value for string constants that are dynamically set. - * @type {string} - * @protected - */ - this.DYNAMICALLY_SET_STRING_ = ''; - - /** - * A placeholder value for boolean constants that are dynamically set. - * @type {boolean} - * @protected - */ - this.DYNAMICALLY_SET_BOOLEAN_ = false; - /** * The size of an empty spacer. * @type {number} @@ -202,7 +181,7 @@ Blockly.blockRendering.ConstantProvider = function() { * connections. Can be overridden by 'hat' property on Theme.BlockStyle. * @type {boolean} */ - this.ADD_START_HATS = this.DYNAMICALLY_SET_BOOLEAN_; + this.ADD_START_HATS = false; /** * Height of the top hat. @@ -254,39 +233,36 @@ Blockly.blockRendering.ConstantProvider = function() { this.JAGGED_TEETH_WIDTH = 6; /** - * Point size of text. This constant is dynamically set in - * ``setFontConstants_`` to the size of the font used by the renderer/theme. + * Point size of text. * @type {number} */ - this.FIELD_TEXT_FONTSIZE = this.DYNAMICALLY_SET_; + this.FIELD_TEXT_FONTSIZE = 11; + + /** + * Text font weight. + * @type {string} + */ + this.FIELD_TEXT_FONTWEIGHT = 'normal'; + + /** + * Text font family. + * @type {string} + */ + this.FIELD_TEXT_FONTFAMILY = 'sans-serif'; /** * Height of text. This constant is dynamically set in ``setFontConstants_`` * to be the height of the text based on the font used. * @type {number} */ - this.FIELD_TEXT_HEIGHT = this.DYNAMICALLY_SET_; - + this.FIELD_TEXT_HEIGHT = -1; // Dynamically set + /** * Text baseline. This constant is dynamically set in ``setFontConstants_`` * to be the baseline of the text based on the font used. * @type {number} */ - this.FIELD_TEXT_BASELINE = this.DYNAMICALLY_SET_; - - /** - * Text font weight. This constant is dynamically set in - * ``setFontConstants_`` to the weight of the font used by the renderer/theme. - * @type {string} - */ - this.FIELD_TEXT_FONTWEIGHT = this.DYNAMICALLY_SET_STRING_; - - /** - * Text font family. This constant is dynamically set in - * ``setFontConstants_`` to the family of the font used by the renderer/theme. - * @type {string} - */ - this.FIELD_TEXT_FONTFAMILY = this.DYNAMICALLY_SET_STRING_; + this.FIELD_TEXT_BASELINE = -1; // Dynamically set /** * A field's border rect corner radius. @@ -417,9 +393,9 @@ Blockly.blockRendering.ConstantProvider = function() { * A random identifier used to ensure a unique ID is used for each * filter/pattern for the case of multiple Blockly instances on a page. * @type {string} - * @protected + * @package */ - this.randomIdentifier_ = String(Math.random()).substring(2); + this.randomIdentifier = String(Math.random()).substring(2); /** * The ID of the emboss filter, or the empty string if no filter is set. @@ -613,22 +589,8 @@ Blockly.blockRendering.ConstantProvider.prototype.setDynamicProperties_ = /* eslint-disable indent */ this.setFontConstants_(theme); - this.ADD_START_HATS = theme.startHats != null ? theme.startHats : false; -}; /* eslint-enable indent */ - -/** - * Get an object representing the default font styles specified by the renderer. - * @return {!Blockly.Theme.FontStyle} A theme font style. - * @protected - */ -Blockly.blockRendering.ConstantProvider.prototype.getDefaultFontStyle_ = - function() { - /* eslint-disable indent */ - return { - 'weight': 'normal', - 'size': 11, - 'family': 'sans-serif' - }; + this.ADD_START_HATS = theme.startHats != null ? theme.startHats : + this.ADD_START_HATS; }; /* eslint-enable indent */ /** @@ -638,17 +600,15 @@ Blockly.blockRendering.ConstantProvider.prototype.getDefaultFontStyle_ = */ Blockly.blockRendering.ConstantProvider.prototype.setFontConstants_ = function( theme) { - var defaultFontStyle = this.getDefaultFontStyle_(); - this.FIELD_TEXT_FONTFAMILY = theme.fontStyle && theme.fontStyle['family'] != undefined ? - theme.fontStyle['family'] : defaultFontStyle['family']; + theme.fontStyle['family'] : this.FIELD_TEXT_FONTFAMILY; this.FIELD_TEXT_FONTWEIGHT = theme.fontStyle && theme.fontStyle['weight'] != undefined ? - theme.fontStyle['weight'] : defaultFontStyle['weight']; + theme.fontStyle['weight'] : this.FIELD_TEXT_FONTWEIGHT; this.FIELD_TEXT_FONTSIZE = theme.fontStyle && theme.fontStyle['size'] != undefined ? - theme.fontStyle['size'] : defaultFontStyle['size']; + theme.fontStyle['size'] : this.FIELD_TEXT_FONTSIZE; var fontMetrics = Blockly.utils.dom.measureFontMetrics('Hg', this.FIELD_TEXT_FONTSIZE + 'pt', @@ -1039,7 +999,7 @@ Blockly.blockRendering.ConstantProvider.prototype.createDom = function(svg, */ var embossFilter = Blockly.utils.dom.createSvgElement('filter', - {'id': 'blocklyEmbossFilter' + this.randomIdentifier_}, defs); + {'id': 'blocklyEmbossFilter' + this.randomIdentifier}, defs); Blockly.utils.dom.createSvgElement('feGaussianBlur', {'in': 'SourceAlpha', 'stdDeviation': 1, 'result': 'blur'}, embossFilter); var feSpecularLighting = Blockly.utils.dom.createSvgElement('feSpecularLighting', @@ -1083,7 +1043,7 @@ Blockly.blockRendering.ConstantProvider.prototype.createDom = function(svg, */ var disabledPattern = Blockly.utils.dom.createSvgElement('pattern', { - 'id': 'blocklyDisabledPattern' + this.randomIdentifier_, + 'id': 'blocklyDisabledPattern' + this.randomIdentifier, 'patternUnits': 'userSpaceOnUse', 'width': 10, 'height': 10 @@ -1098,7 +1058,7 @@ Blockly.blockRendering.ConstantProvider.prototype.createDom = function(svg, if (Blockly.blockRendering.Debug) { var debugFilter = Blockly.utils.dom.createSvgElement('filter', { - 'id': 'blocklyDebugFilter' + this.randomIdentifier_, + 'id': 'blocklyDebugFilter' + this.randomIdentifier, 'height': '160%', 'width': '180%', y: '-30%', diff --git a/core/renderers/common/path_object.js b/core/renderers/common/path_object.js index 3e9ba2086..3cfd0a32e 100644 --- a/core/renderers/common/path_object.js +++ b/core/renderers/common/path_object.js @@ -35,9 +35,9 @@ Blockly.blockRendering.PathObject = function(root, style, constants) { /** * The renderer's constant provider. * @type {!Blockly.blockRendering.ConstantProvider} - * @protected + * @package */ - this.constants_ = constants; + this.constants = constants; this.svgRoot = root; @@ -178,7 +178,7 @@ Blockly.blockRendering.PathObject.prototype.updateHighlighted = function( enable) { if (enable) { this.svgPath.setAttribute('filter', - 'url(#' + this.constants_.embossFilterId + ')'); + 'url(#' + this.constants.embossFilterId + ')'); } else { this.svgPath.setAttribute('filter', 'none'); } @@ -206,7 +206,7 @@ Blockly.blockRendering.PathObject.prototype.updateDisabled_ = function( this.setClass_('blocklyDisabled', disabled); if (disabled) { this.svgPath.setAttribute('fill', - 'url(#' + this.constants_.disabledPatternId + ')'); + 'url(#' + this.constants.disabledPatternId + ')'); } }; diff --git a/core/renderers/common/renderer.js b/core/renderers/common/renderer.js index 82a46ea2e..0413a57e5 100644 --- a/core/renderers/common/renderer.js +++ b/core/renderers/common/renderer.js @@ -44,15 +44,28 @@ Blockly.blockRendering.Renderer = function(name) { * @private */ this.constants_ = null; + + /** + * Rendering constant overrides, passed in through options. + * @type {?Object} + * @package + */ + this.overrides = null; }; /** * Initialize the renderer. * @param {!Blockly.Theme} theme The workspace theme object. + * @param {Object=} opt_rendererOverrides Rendering constant overrides. * @package */ -Blockly.blockRendering.Renderer.prototype.init = function(theme) { +Blockly.blockRendering.Renderer.prototype.init = function(theme, + opt_rendererOverrides) { this.constants_ = this.makeConstants_(); + if (opt_rendererOverrides) { + this.overrides = opt_rendererOverrides; + Blockly.utils.object.mixin(this.constants_, opt_rendererOverrides); + } this.constants_.setTheme(theme); this.constants_.init(); }; @@ -64,11 +77,17 @@ Blockly.blockRendering.Renderer.prototype.init = function(theme) { * @package */ Blockly.blockRendering.Renderer.prototype.refresh = function(svg, theme) { - var constants = this.getConstants(); - constants.dispose(); - constants.setTheme(theme); - constants.init(); - constants.createDom(svg, this.name); + var previousConstants = this.getConstants(); + previousConstants.dispose(); + this.constants_ = this.makeConstants_(); + if (this.overrides) { + Blockly.utils.object.mixin(this.constants_, this.overrides); + } + // Ensure the constant provider's random identifier does not change. + this.constants_.randomIdentifier = previousConstants.randomIdentifier; + this.constants_.setTheme(theme); + this.constants_.init(); + this.getConstants().createDom(svg, this.name); }; /** diff --git a/core/renderers/geras/path_object.js b/core/renderers/geras/path_object.js index 5db60d23b..76884c9bc 100644 --- a/core/renderers/geras/path_object.js +++ b/core/renderers/geras/path_object.js @@ -36,7 +36,7 @@ Blockly.geras.PathObject = function(root, style, constants) { * The renderer's constant provider. * @type {!Blockly.geras.ConstantProvider} */ - this.constants_ = constants; + this.constants = constants; this.svgRoot = root; @@ -142,7 +142,7 @@ Blockly.geras.PathObject.prototype.setStyle = function(blockStyle) { Blockly.geras.PathObject.prototype.updateHighlighted = function(highlighted) { if (highlighted) { this.svgPath.setAttribute('filter', - 'url(#' + this.constants_.embossFilterId + ')'); + 'url(#' + this.constants.embossFilterId + ')'); this.svgPathLight.style.display = 'none'; } else { this.svgPath.setAttribute('filter', 'none'); diff --git a/core/renderers/geras/renderer.js b/core/renderers/geras/renderer.js index 66ffa186c..65f6094f7 100644 --- a/core/renderers/geras/renderer.js +++ b/core/renderers/geras/renderer.js @@ -48,8 +48,10 @@ Blockly.utils.object.inherits(Blockly.geras.Renderer, * @package * @override */ -Blockly.geras.Renderer.prototype.init = function(theme) { - Blockly.geras.Renderer.superClass_.init.call(this, theme); +Blockly.geras.Renderer.prototype.init = function(theme, + opt_rendererOverrides) { + Blockly.geras.Renderer.superClass_.init.call(this, theme, + opt_rendererOverrides); this.highlightConstants_ = this.makeHighlightConstants_(); this.highlightConstants_.init(); }; diff --git a/core/renderers/zelos/constants.js b/core/renderers/zelos/constants.js index 738bc7e06..103cfebdb 100644 --- a/core/renderers/zelos/constants.js +++ b/core/renderers/zelos/constants.js @@ -234,6 +234,22 @@ Blockly.zelos.ConstantProvider = function() { */ this.FULL_BLOCK_FIELDS = true; + /** + * @override + */ + this.FIELD_TEXT_FONTSIZE = 3 * this.GRID_UNIT; + + /** + * @override + */ + this.FIELD_TEXT_FONTWEIGHT = 'bold'; + + /** + * @override + */ + this.FIELD_TEXT_FONTFAMILY = + '"Helvetica Neue", "Segoe UI", Helvetica, sans-serif'; + /** * @override */ @@ -367,18 +383,6 @@ Blockly.zelos.ConstantProvider = function() { Blockly.utils.object.inherits(Blockly.zelos.ConstantProvider, Blockly.blockRendering.ConstantProvider); -/** - * @override - */ -Blockly.zelos.ConstantProvider.prototype.getDefaultFontStyle_ = function() { - var fontStyle = - Blockly.zelos.ConstantProvider.superClass_.getDefaultFontStyle_.call(this); - fontStyle['weight'] = 'bold'; - fontStyle['size'] = 3 * this.GRID_UNIT; - fontStyle['family'] = '"Helvetica Neue", "Segoe UI", Helvetica, sans-serif'; - return fontStyle; -}; - /** * @override */ @@ -807,7 +811,7 @@ Blockly.zelos.ConstantProvider.prototype.createDom = function(svg, // Instead use a gaussian blur, and then set all alpha to 1 with a transfer. var selectedGlowFilter = Blockly.utils.dom.createSvgElement('filter', { - 'id': 'blocklySelectedGlowFilter' + this.randomIdentifier_, + 'id': 'blocklySelectedGlowFilter' + this.randomIdentifier, 'height': '160%', 'width': '180%', y: '-30%', @@ -849,7 +853,7 @@ Blockly.zelos.ConstantProvider.prototype.createDom = function(svg, // Instead use a gaussian blur, and then set all alpha to 1 with a transfer. var replacementGlowFilter = Blockly.utils.dom.createSvgElement('filter', { - 'id': 'blocklyReplacementGlowFilter' + this.randomIdentifier_, + 'id': 'blocklyReplacementGlowFilter' + this.randomIdentifier, 'height': '160%', 'width': '180%', y: '-30%', @@ -960,7 +964,7 @@ Blockly.zelos.ConstantProvider.prototype.getCSS_ = function(name) { // Disabled outline paths. selector + ' .blocklyDisabled > .blocklyOutlinePath {', - 'fill: url(#blocklyDisabledPattern' + this.randomIdentifier_ + ')', + 'fill: url(#blocklyDisabledPattern' + this.randomIdentifier + ')', '}', /* eslint-enable indent */ ]; diff --git a/core/renderers/zelos/path_object.js b/core/renderers/zelos/path_object.js index 5b305b35e..3bca20b0f 100644 --- a/core/renderers/zelos/path_object.js +++ b/core/renderers/zelos/path_object.js @@ -38,7 +38,7 @@ Blockly.zelos.PathObject = function(root, style, constants) { * The renderer's constant provider. * @type {!Blockly.zelos.ConstantProvider} */ - this.constants_ = constants; + this.constants = constants; /** * The selected path of the block. @@ -124,7 +124,7 @@ Blockly.zelos.PathObject.prototype.updateSelected = function(enable) { /** @type {!SVGElement} */ (this.svgPath.cloneNode(true)); this.svgPathSelected_.setAttribute('fill', 'none'); this.svgPathSelected_.setAttribute('filter', - 'url(#' + this.constants_.selectedGlowFilterId + ')'); + 'url(#' + this.constants.selectedGlowFilterId + ')'); this.svgRoot.appendChild(this.svgPathSelected_); } } else { @@ -143,7 +143,7 @@ Blockly.zelos.PathObject.prototype.updateReplacementFade = function( this.setClass_('blocklyReplaceable', enable); if (enable) { this.svgPath.setAttribute('filter', - 'url(#' + this.constants_.replacementGlowFilterId + ')'); + 'url(#' + this.constants.replacementGlowFilterId + ')'); } else { this.svgPath.removeAttribute('filter'); } @@ -161,7 +161,7 @@ Blockly.zelos.PathObject.prototype.updateShapeForInputHighlight = function( } if (enable) { outlinePath.setAttribute('filter', - 'url(#' + this.constants_.replacementGlowFilterId + ')'); + 'url(#' + this.constants.replacementGlowFilterId + ')'); } else { outlinePath.removeAttribute('filter'); } diff --git a/core/theme_manager.js b/core/theme_manager.js index 3d2f51273..4741c8b8b 100644 --- a/core/theme_manager.js +++ b/core/theme_manager.js @@ -94,10 +94,6 @@ Blockly.ThemeManager.prototype.setTheme = function(theme) { // Refresh all subscribed workspaces. for (var i = 0, workspace; (workspace = this.subscribedWorkspaces_[i]); i++) { workspace.refreshTheme(); - // Re-render if workspace is visible - if (workspace.isVisible()) { - workspace.setVisible(true); - } } // Refresh all registered Blockly UI components. diff --git a/core/toolbox.js b/core/toolbox.js index 1060dd2a3..28bc7b676 100644 --- a/core/toolbox.js +++ b/core/toolbox.js @@ -175,7 +175,8 @@ Blockly.Toolbox.prototype.init = function() { 'rtl': workspace.RTL, 'oneBasedIndex': workspace.options.oneBasedIndex, 'horizontalLayout': workspace.horizontalLayout, - 'renderer': workspace.options.renderer + 'renderer': workspace.options.renderer, + 'rendererOverrides': workspace.options.rendererOverrides })); workspaceOptions.toolboxPosition = workspace.options.toolboxPosition; diff --git a/core/trashcan.js b/core/trashcan.js index 58ce8a61a..5c45cf82b 100644 --- a/core/trashcan.js +++ b/core/trashcan.js @@ -56,7 +56,8 @@ Blockly.Trashcan = function(workspace) { 'parentWorkspace': this.workspace_, 'rtl': this.workspace_.RTL, 'oneBasedIndex': this.workspace_.options.oneBasedIndex, - 'renderer': this.workspace_.options.renderer + 'renderer': this.workspace_.options.renderer, + 'rendererOverrides': this.workspace_.options.rendererOverrides })); // Create vertical or horizontal flyout. if (this.workspace_.horizontalLayout) { diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 3bf3d9dbc..d23aa5887 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -136,7 +136,7 @@ Blockly.WorkspaceSvg = function(options, * @private */ this.renderer_ = Blockly.blockRendering.init(this.options.renderer || 'geras', - this.getTheme()); + this.getTheme(), this.options.rendererOverrides); /** * Cached parent SVG. @@ -532,6 +532,11 @@ Blockly.WorkspaceSvg.prototype.refreshTheme = function() { this.toolbox_.updateColourFromTheme(); } + // Re-render if workspace is visible + if (this.isVisible()) { + this.setVisible(true); + } + var event = new Blockly.Events.Ui(null, 'theme', null, null); event.workspaceId = this.id; Blockly.Events.fire(event); @@ -888,7 +893,8 @@ Blockly.WorkspaceSvg.prototype.addFlyout = function(tagName) { 'rtl': this.RTL, 'oneBasedIndex': this.options.oneBasedIndex, 'horizontalLayout': this.horizontalLayout, - 'renderer': this.options.renderer + 'renderer': this.options.renderer, + 'rendererOverrides': this.options.rendererOverrides })); workspaceOptions.toolboxPosition = this.options.toolboxPosition; if (this.horizontalLayout) {