From 70c24cf94e4383407afe5065405ae2eb123b48ef Mon Sep 17 00:00:00 2001 From: Sam El-Husseini Date: Mon, 18 Nov 2019 16:14:19 -0800 Subject: [PATCH] CSS in renderers (#3446) * Have each renderer declare some CSS that is specific to the renderer. --- core/css.js | 29 +--------- core/renderers/common/block_rendering.js | 7 ++- core/renderers/common/constants.js | 3 - core/renderers/common/renderer.js | 71 +++++++++++++++++++++++- core/renderers/geras/renderer.js | 5 +- core/renderers/thrasos/renderer.js | 5 +- core/renderers/zelos/constants.js | 10 ++++ core/renderers/zelos/renderer.js | 28 +++++++++- tests/playgrounds/screenshot.js | 10 +++- 9 files changed, 124 insertions(+), 44 deletions(-) diff --git a/core/css.js b/core/css.js index f2af021bf..20778f3d5 100644 --- a/core/css.js +++ b/core/css.js @@ -77,6 +77,7 @@ Blockly.Css.inject = function(hasCss, pathToMedia) { // Inject CSS tag at start of head. var cssNode = document.createElement('style'); + cssNode.id = 'blockly-common-style'; var cssTextNode = document.createTextNode(text); cssNode.appendChild(cssTextNode); document.head.insertBefore(cssNode, document.head.firstChild); @@ -254,11 +255,6 @@ Blockly.Css.CONTENT = [ 'stroke-width: 1;', '}', - '.injectionDiv:not(.zelos-renderer) .blocklySelected>.blocklyPath {', - 'stroke: #fc3;', - 'stroke-width: 3px;', - '}', - '.blocklySelected>.blocklyPathLight {', 'display: none;', '}', @@ -334,13 +330,6 @@ Blockly.Css.CONTENT = [ 'display: none;', '}', - '.blocklyText {', - 'cursor: default;', - 'fill: #fff;', - 'font-family: sans-serif;', - 'font-size: 11pt;', - '}', - '.blocklyMultilineText {', 'font-family: monospace;', '}', @@ -349,22 +338,6 @@ Blockly.Css.CONTENT = [ 'pointer-events: none;', '}', - '.blocklyNonEditableText>rect,', - '.blocklyEditableText>rect {', - 'fill: #fff;', - 'fill-opacity: .6;', - '}', - - '.blocklyNonEditableText>text,', - '.blocklyEditableText>text {', - 'fill: #000;', - '}', - - '.blocklyEditableText:not(.editing):hover>rect {', - 'stroke: #fff;', - 'stroke-width: 2;', - '}', - '.blocklyBubbleText {', 'fill: #000;', '}', diff --git a/core/renderers/common/block_rendering.js b/core/renderers/common/block_rendering.js index 794a83717..da3a71f6a 100644 --- a/core/renderers/common/block_rendering.js +++ b/core/renderers/common/block_rendering.js @@ -101,15 +101,16 @@ Blockly.blockRendering.init = function(name) { /** * Wrap the renderer constructor into a temporary constructor * function so the closure compiler treats it as a constructor. + * @param {string} name The renderer name. * @constructor * @extends {Blockly.blockRendering.Renderer} */ - var rendererCtor = function() { - rendererCtor.superClass_.constructor.call(this); + var rendererCtor = function(name) { + rendererCtor.superClass_.constructor.call(this, name); }; Blockly.utils.object.inherits(rendererCtor, Blockly.blockRendering.rendererMap_[name]); - var renderer = new rendererCtor(); + var renderer = new rendererCtor(name); renderer.init(); return renderer; }; diff --git a/core/renderers/common/constants.js b/core/renderers/common/constants.js index 940a80564..68caf6f31 100644 --- a/core/renderers/common/constants.js +++ b/core/renderers/common/constants.js @@ -161,7 +161,6 @@ Blockly.blockRendering.ConstantProvider = function() { /** * Point size of text. Should match blocklyText's font-size in CSS. * @type {number} - * @const */ this.FIELD_TEXT_FONTSIZE = 11; @@ -174,14 +173,12 @@ Blockly.blockRendering.ConstantProvider = function() { /** * Text font weight. Should match blocklyText's font-weight in CSS. * @type {string} - * @const */ this.FIELD_TEXT_FONTWEIGHT = 'normal'; /** * Text font family. Should match blocklyText's font-family in CSS. * @type {string} - * @const */ this.FIELD_TEXT_FONTFAMILY = 'sans-serif'; diff --git a/core/renderers/common/renderer.js b/core/renderers/common/renderer.js index b8386f1ac..22e36f4a9 100644 --- a/core/renderers/common/renderer.js +++ b/core/renderers/common/renderer.js @@ -35,10 +35,18 @@ goog.requireType('Blockly.blockRendering.Debug'); /** * The base class for a block renderer. + * @param {string} name The renderer name. * @package * @constructor */ -Blockly.blockRendering.Renderer = function() { +Blockly.blockRendering.Renderer = function(name) { + + /** + * The renderer name. + * @type {string} + * @protected + */ + this.name_ = name; /** * The renderer's constant provider. @@ -55,6 +63,7 @@ Blockly.blockRendering.Renderer = function() { Blockly.blockRendering.Renderer.prototype.init = function() { this.constants_ = this.makeConstants_(); this.constants_.init(); + this.injectCSS_(this.getCSS_()); }; /** @@ -139,6 +148,66 @@ Blockly.blockRendering.Renderer.prototype.getConstants = function() { (this.constants_)); }; +/** + * Get any renderer specific CSS to inject when the renderer is initialized. + * @return {!Array.} Array of CSS strings. + * @protected + */ +Blockly.blockRendering.Renderer.prototype.getCSS_ = function() { + var selector = '.' + this.name_ + '-renderer'; + var constants = this.getConstants(); + return [ + /* eslint-disable indent */ + selector + ' .blocklyText {', + 'cursor: default;', + 'fill: #fff;', + 'font-family: ' + constants.FIELD_TEXT_FONTFAMILY + ';', + 'font-size: ' + constants.FIELD_TEXT_FONTSIZE + 'pt;', + 'font-weight: ' + constants.FIELD_TEXT_FONTWEIGHT + ';', + '}', + selector + ' .blocklyNonEditableText>rect,', + selector + ' .blocklyEditableText>rect {', + 'fill: #fff;', + 'fill-opacity: .6;', + '}', + + selector + ' .blocklyNonEditableText>text,', + selector + ' .blocklyEditableText>text {', + 'fill: #000;', + '}', + + selector + ' .blocklyEditableText:not(.editing):hover>rect {', + 'stroke: #fff;', + 'stroke-width: 2;', + '}', + selector + ' .blocklySelected>.blocklyPath {', + 'stroke: #fc3;', + 'stroke-width: 3px;', + '}', + /* eslint-enable indent */ + ]; +}; + +/** + * Get any renderer specific CSS to inject when the renderer is initialized. + * @param {!Array.} cssArray Array of CSS strings. + * @private + */ +Blockly.blockRendering.Renderer.prototype.injectCSS_ = function(cssArray) { + var cssNodeId = 'blockly-renderer-style-' + this.name_; + if (document.getElementById(cssNodeId)) { + // Already injected. + return; + } + var text = cssArray.join('\n'); + // Inject CSS tag at start of head. + var cssNode = document.createElement('style'); + cssNode.id = cssNodeId; + var cssTextNode = document.createTextNode(text); + cssNode.appendChild(cssTextNode); + document.head.insertBefore(cssNode, document.head.firstChild); +}; + /** * Determine whether or not to highlight a connection. * @param {Blockly.Connection} _conn The connection to determine whether or not diff --git a/core/renderers/geras/renderer.js b/core/renderers/geras/renderer.js index 0a18191bf..769ff3f34 100644 --- a/core/renderers/geras/renderer.js +++ b/core/renderers/geras/renderer.js @@ -35,12 +35,13 @@ goog.require('Blockly.utils.object'); /** * The geras renderer. + * @param {string} name The renderer name. * @package * @constructor * @extends {Blockly.blockRendering.Renderer} */ -Blockly.geras.Renderer = function() { - Blockly.geras.Renderer.superClass_.constructor.call(this); +Blockly.geras.Renderer = function(name) { + Blockly.geras.Renderer.superClass_.constructor.call(this, name); /** * The renderer's highlight constant provider. diff --git a/core/renderers/thrasos/renderer.js b/core/renderers/thrasos/renderer.js index 048cf2a5e..2e49886a3 100644 --- a/core/renderers/thrasos/renderer.js +++ b/core/renderers/thrasos/renderer.js @@ -30,12 +30,13 @@ goog.require('Blockly.utils.object'); /** * The thrasos renderer. + * @param {string} name The renderer name. * @package * @constructor * @extends {Blockly.blockRendering.Renderer} */ -Blockly.thrasos.Renderer = function() { - Blockly.thrasos.Renderer.superClass_.constructor.call(this); +Blockly.thrasos.Renderer = function(name) { + Blockly.thrasos.Renderer.superClass_.constructor.call(this, name); }; Blockly.utils.object.inherits(Blockly.thrasos.Renderer, Blockly.blockRendering.Renderer); diff --git a/core/renderers/zelos/constants.js b/core/renderers/zelos/constants.js index ea12bad23..04f461f66 100644 --- a/core/renderers/zelos/constants.js +++ b/core/renderers/zelos/constants.js @@ -159,6 +159,16 @@ Blockly.zelos.ConstantProvider = function() { */ this.FULL_BLOCK_FIELDS = true; + /** + * @override + */ + this.FIELD_TEXT_FONTSIZE = 12; + + /** + * @override + */ + this.FIELD_TEXT_FONTWEIGHT = 'bold'; + /** * @override */ diff --git a/core/renderers/zelos/renderer.js b/core/renderers/zelos/renderer.js index f5ecdda5b..751ce8a07 100644 --- a/core/renderers/zelos/renderer.js +++ b/core/renderers/zelos/renderer.js @@ -35,12 +35,13 @@ goog.require('Blockly.zelos.CursorSvg'); /** * The zelos renderer. + * @param {string} name The renderer name. * @package * @constructor * @extends {Blockly.blockRendering.Renderer} */ -Blockly.zelos.Renderer = function() { - Blockly.zelos.Renderer.superClass_.constructor.call(this); +Blockly.zelos.Renderer = function(name) { + Blockly.zelos.Renderer.superClass_.constructor.call(this, name); }; Blockly.utils.object.inherits(Blockly.zelos.Renderer, Blockly.blockRendering.Renderer); @@ -122,4 +123,27 @@ Blockly.zelos.Renderer.prototype.shouldInsertDraggedBlock = function(_block, return false; }; +/** + * @override + */ +Blockly.zelos.Renderer.prototype.getCSS_ = function() { + var selector = '.' + this.name_ + '-renderer'; + var constants = this.getConstants(); + return [ + /* eslint-disable indent */ + selector + ' .blocklyText {', + 'cursor: default;', + 'fill: #fff;', + 'font-family: ' + constants.FIELD_TEXT_FONTFAMILY + ';', + 'font-size: ' + constants.FIELD_TEXT_FONTSIZE + 'pt;', + 'font-weight: ' + constants.FIELD_TEXT_FONTWEIGHT + ';', + '}', + + selector + ' .blocklyDropdownText {', + 'fill: #fff !important;', + '}', + /* eslint-enable indent */ + ]; +}; + Blockly.blockRendering.register('zelos', Blockly.zelos.Renderer); diff --git a/tests/playgrounds/screenshot.js b/tests/playgrounds/screenshot.js index 70e347729..c0a9663a2 100644 --- a/tests/playgrounds/screenshot.js +++ b/tests/playgrounds/screenshot.js @@ -80,15 +80,19 @@ function workspaceToSvg_(workspace, callback, customCss) { svg.setAttribute('viewBox', x + ' ' + y + ' ' + width + ' ' + height); - svg.setAttribute('class', 'blocklySvg'); + svg.setAttribute('class', 'blocklySvg ' + + (workspace.options.renderer || 'geras') + '-renderer ' + + (workspace.getTheme ? workspace.getTheme().name + '-theme' : '')); svg.setAttribute('width', width); svg.setAttribute('height', height); svg.setAttribute("style", 'background-color: transparent'); var css = [].slice.call(document.head.querySelectorAll('style')) - .filter(function(el) { return /\.blocklySvg/.test(el.innerText); })[0]; + .filter(function(el) { return /\.blocklySvg/.test(el.innerText) || + (el.id.indexOf('blockly-') === 0); }).map(function(el) { + return el.innerText; }).join('\n'); var style = document.createElement('style'); - style.innerHTML = css.innerText + '\n' + customCss; + style.innerHTML = css + '\n' + customCss; svg.insertBefore(style, svg.firstChild); var svgAsXML = (new XMLSerializer).serializeToString(svg);