diff --git a/core/inject.js b/core/inject.js index be46c684c..b64576bc4 100644 --- a/core/inject.js +++ b/core/inject.js @@ -163,6 +163,10 @@ Blockly.createMainWorkspace_ = function(svg, options, blockDragSurface, mainWorkspace.scale = options.zoomOptions.startScale; svg.appendChild(mainWorkspace.createDom('blocklyMainBackground')); + // Set the theme name on the injection div. + Blockly.utils.dom.addClass(mainWorkspace.getInjectionDiv(), + mainWorkspace.getTheme().name + '-theme'); + if (!options.hasCategories && options.languageTree) { // Add flyout as an that is a sibling of the workspace svg. var flyout = mainWorkspace.addFlyout('svg'); diff --git a/core/options.js b/core/options.js index c84a4264e..d2596c392 100644 --- a/core/options.js +++ b/core/options.js @@ -286,7 +286,7 @@ Blockly.Options.parseThemeOptions_ = function(options) { if (theme instanceof Blockly.Theme) { return /** @type {!Blockly.Theme} */ (theme); } - return new Blockly.Theme( + return new Blockly.Theme('builtin', theme['blockStyles'], theme['categoryStyles'], theme['componentStyles']); }; diff --git a/core/theme.js b/core/theme.js index 52c723322..eab7a856e 100644 --- a/core/theme.js +++ b/core/theme.js @@ -28,6 +28,7 @@ goog.require('Blockly.utils.colour'); /** * Class for a theme. + * @param {string} name Theme name. * @param {!Object.} blockStyles A map from * style names (strings) to objects with style attributes for blocks. * @param {!Object.} categoryStyles A map @@ -37,7 +38,15 @@ goog.require('Blockly.utils.colour'); * names to style value. * @constructor */ -Blockly.Theme = function(blockStyles, categoryStyles, opt_componentStyles) { +Blockly.Theme = function(name, blockStyles, categoryStyles, + opt_componentStyles) { + + /** + * The theme name. This can be used to reference a specific theme in CSS. + * @type {string} + * @package + */ + this.name = name; /** * The block styles map. * @type {!Object.} diff --git a/core/theme/classic.js b/core/theme/classic.js index b84057700..b39836de4 100644 --- a/core/theme/classic.js +++ b/core/theme/classic.js @@ -94,5 +94,5 @@ Blockly.Themes.Classic.categoryStyles = { }; Blockly.Themes.Classic = - new Blockly.Theme(Blockly.Themes.Classic.defaultBlockStyles, + new Blockly.Theme('classic', Blockly.Themes.Classic.defaultBlockStyles, Blockly.Themes.Classic.categoryStyles); diff --git a/core/theme/dark.js b/core/theme/dark.js index 2aff84c02..7f4dad583 100644 --- a/core/theme/dark.js +++ b/core/theme/dark.js @@ -23,6 +23,7 @@ goog.provide('Blockly.Themes.Dark'); +goog.require('Blockly.Css'); goog.require('Blockly.Theme'); @@ -115,7 +116,7 @@ Blockly.Themes.Dark.categoryStyles = { // This style is still being fleshed out and may change. Blockly.Themes.Dark = - new Blockly.Theme(Blockly.Themes.Dark.defaultBlockStyles, + new Blockly.Theme('dark', Blockly.Themes.Dark.defaultBlockStyles, Blockly.Themes.Dark.categoryStyles); Blockly.Themes.Dark.setComponentStyle('workspace', '#1e1e1e'); @@ -126,3 +127,14 @@ Blockly.Themes.Dark.setComponentStyle('flyoutText', '#ccc'); Blockly.Themes.Dark.setComponentStyle('flyoutOpacity', 1); Blockly.Themes.Dark.setComponentStyle('scrollbar', '#797979'); Blockly.Themes.Dark.setComponentStyle('scrollbarOpacity', 0.4); + +/** + * CSS for the dark theme. + */ +Blockly.Css.register([ + /* eslint-disable indent */ + '.dark-theme .blocklyTreeRow:not(.blocklyTreeSelected):hover {', + 'background-color: #2a2d2e;', + '}', + /* eslint-enable indent */ +]); diff --git a/core/theme/deuteranopia.js b/core/theme/deuteranopia.js index dc6d2e21f..dbd1bfbab 100644 --- a/core/theme/deuteranopia.js +++ b/core/theme/deuteranopia.js @@ -110,5 +110,6 @@ Blockly.Themes.Deuteranopia.categoryStyles = { }; Blockly.Themes.Deuteranopia = - new Blockly.Theme(Blockly.Themes.Deuteranopia.defaultBlockStyles, + new Blockly.Theme('deuteranopia', + Blockly.Themes.Deuteranopia.defaultBlockStyles, Blockly.Themes.Deuteranopia.categoryStyles); diff --git a/core/theme/highcontrast.js b/core/theme/highcontrast.js index 449619a6d..3f2c15850 100644 --- a/core/theme/highcontrast.js +++ b/core/theme/highcontrast.js @@ -115,5 +115,6 @@ Blockly.Themes.HighContrast.categoryStyles = { // This style is still being fleshed out and may change. Blockly.Themes.HighContrast = - new Blockly.Theme(Blockly.Themes.HighContrast.defaultBlockStyles, + new Blockly.Theme('highcontrast', + Blockly.Themes.HighContrast.defaultBlockStyles, Blockly.Themes.HighContrast.categoryStyles); diff --git a/core/theme/modern.js b/core/theme/modern.js index 09e892dba..9d6cc6afd 100644 --- a/core/theme/modern.js +++ b/core/theme/modern.js @@ -115,5 +115,5 @@ Blockly.Themes.Modern.categoryStyles = { // This style is still being fleshed out and may change. Blockly.Themes.Modern = - new Blockly.Theme(Blockly.Themes.Modern.defaultBlockStyles, + new Blockly.Theme('modern', Blockly.Themes.Modern.defaultBlockStyles, Blockly.Themes.Modern.categoryStyles); diff --git a/core/theme/tritanopia.js b/core/theme/tritanopia.js index c12fec728..d90d86aa0 100644 --- a/core/theme/tritanopia.js +++ b/core/theme/tritanopia.js @@ -109,5 +109,6 @@ Blockly.Themes.Tritanopia.categoryStyles = { }; Blockly.Themes.Tritanopia = - new Blockly.Theme(Blockly.Themes.Tritanopia.defaultBlockStyles, + new Blockly.Theme('tritanopia', + Blockly.Themes.Tritanopia.defaultBlockStyles, Blockly.Themes.Tritanopia.categoryStyles); diff --git a/core/theme_manager.js b/core/theme_manager.js index 3d6e09d7e..bf012b4d4 100644 --- a/core/theme_manager.js +++ b/core/theme_manager.js @@ -30,11 +30,19 @@ goog.require('Blockly.Theme'); /** * Class for storing and updating a workspace's theme and UI components. + * @param {!Blockly.WorkspaceSvg} workspace The main workspace. * @param {!Blockly.Theme} theme The workspace theme. * @constructor * @package */ -Blockly.ThemeManager = function(theme) { +Blockly.ThemeManager = function(workspace, theme) { + + /** + * The main workspace. + * @type {!Blockly.WorkspaceSvg} + * @private + */ + this.workspace_ = workspace; /** * The Blockly theme to use. @@ -87,8 +95,18 @@ Blockly.ThemeManager.prototype.setTheme = function(theme) { return; } + var prevTheme = this.theme_; this.theme_ = theme; + // Set the theme name onto the injection div. + var injectionDiv = this.workspace_.getInjectionDiv(); + if (injectionDiv) { + if (prevTheme) { + Blockly.utils.dom.removeClass(injectionDiv, prevTheme.name + '-theme'); + } + Blockly.utils.dom.addClass(injectionDiv, this.theme_.name + '-theme'); + } + // Refresh all subscribed workspaces. for (var i = 0, workspace; (workspace = this.subscribedWorkspaces_[i]); i++) { workspace.refreshTheme(); diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 7a8034026..01909a3d0 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -143,7 +143,8 @@ Blockly.WorkspaceSvg = function(options, */ this.themeManager_ = this.options.parentWorkspace ? this.options.parentWorkspace.getThemeManager() : - new Blockly.ThemeManager(this.options.theme || Blockly.Themes.Classic); + new Blockly.ThemeManager(this, + this.options.theme || Blockly.Themes.Classic); /** * The block renderer used for rendering blocks on this workspace. diff --git a/tests/mocha/block_test.js b/tests/mocha/block_test.js index b01e96989..80dca0974 100644 --- a/tests/mocha/block_test.js +++ b/tests/mocha/block_test.js @@ -1277,7 +1277,7 @@ suite('Blocks', function() { this.block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( '' ), this.workspace); - this.workspace.setTheme(new Blockly.Theme({ + this.workspace.setTheme(new Blockly.Theme('test', { "styleOne" : { "colourPrimary": "#000000", "colourSecondary": "#999999", diff --git a/tests/mocha/theme_test.js b/tests/mocha/theme_test.js index e6d5f7592..15f62686a 100644 --- a/tests/mocha/theme_test.js +++ b/tests/mocha/theme_test.js @@ -84,27 +84,27 @@ suite('Theme', function() { } test('Set All BlockStyles', function() { - var theme = new Blockly.Theme(createBlockStyles()); + var theme = new Blockly.Theme('test', createBlockStyles()); stringifyAndCompare(createBlockStyles(), theme.blockStyles_); theme.setAllBlockStyles(createMultipleBlockStyles()); stringifyAndCompare(createMultipleBlockStyles(), theme.blockStyles_); }); test('Get All BlockStyles', function() { - var theme = new Blockly.Theme(createMultipleBlockStyles()); + var theme = new Blockly.Theme('test', createMultipleBlockStyles()); var allBlocks = theme.getAllBlockStyles(); stringifyAndCompare(createMultipleBlockStyles(), allBlocks); }); test('Get BlockStyles', function() { - var theme = new Blockly.Theme(createBlockStyles()); + var theme = new Blockly.Theme('test', createBlockStyles()); var blockStyle = theme.getBlockStyle('styleOne'); stringifyAndCompare(blockStyle, createBlockStyles().styleOne); }); test('Set BlockStyle Update', function() { - var theme = new Blockly.Theme(createBlockStyles()); + var theme = new Blockly.Theme('test', createBlockStyles()); var blockStyle = createBlockStyles(); blockStyle.styleOne.colourPrimary = '#00ff00'; @@ -114,7 +114,7 @@ suite('Theme', function() { }); test('Set BlockStyle Add', function() { - var theme = new Blockly.Theme(createBlockStyles()); + var theme = new Blockly.Theme('test', createBlockStyles()); var blockStyle = createMultipleBlockStyles(); theme.setBlockStyle('styleTwo', blockStyle.styleTwo); diff --git a/tests/mocha/trashcan_test.js b/tests/mocha/trashcan_test.js index 992e2cd91..7e0a0fd39 100644 --- a/tests/mocha/trashcan_test.js +++ b/tests/mocha/trashcan_test.js @@ -16,7 +16,6 @@ */ suite("Trashcan", function() { - var themeManager = new Blockly.ThemeManager(Blockly.Themes.Classic); var workspace = { addChangeListener: function(func) { this.listener = func; @@ -24,13 +23,14 @@ suite("Trashcan", function() { triggerListener: function(event) { this.listener(event); }, - getThemeManager: function() { - return themeManager; - }, options: { maxTrashcanContents: Infinity } }; + var themeManager = new Blockly.ThemeManager(workspace, Blockly.Themes.Classic); + workspace.getThemeManager = function() { + return themeManager; + }; function sendDeleteEvent(xmlString) { var xml = Blockly.Xml.textToDom( '' +