Support extending themes with a base theme property. (#3731)

* Support extending themes with a base theme property.
This commit is contained in:
Sam El-Husseini
2020-03-10 10:30:13 -07:00
committed by GitHub
parent f96d1c9768
commit a9e10807ca
6 changed files with 100 additions and 179 deletions

View File

@@ -282,8 +282,7 @@ Blockly.Options.parseThemeOptions_ = function(options) {
if (theme instanceof Blockly.Theme) {
return /** @type {!Blockly.Theme} */ (theme);
}
return new Blockly.Theme('builtin',
theme['blockStyles'], theme['categoryStyles'], theme['componentStyles']);
return Blockly.Theme.defineTheme('builtin', theme);
};
/**

View File

@@ -13,27 +13,27 @@ goog.provide('Blockly.Theme');
goog.require('Blockly.utils');
goog.require('Blockly.utils.colour');
goog.require('Blockly.utils.object');
/**
* Class for a theme.
* @param {string} name Theme name.
* @param {!Object.<string, Blockly.Theme.BlockStyle>} blockStyles A map from
* style names (strings) to objects with style attributes for blocks.
* @param {!Object.<string, Blockly.Theme.CategoryStyle>} categoryStyles A map
* from style names (strings) to objects with style attributes for
* @param {!Object.<string, Blockly.Theme.BlockStyle>=} opt_blockStyles A map
* from style names (strings) to objects with style attributes for blocks.
* @param {!Object.<string, Blockly.Theme.CategoryStyle>=} opt_categoryStyles A
* map from style names (strings) to objects with style attributes for
* categories.
* @param {!Object.<string, *>=} opt_componentStyles A map of Blockly component
* names to style value.
* @constructor
*/
Blockly.Theme = function(name, blockStyles, categoryStyles,
Blockly.Theme = function(name, opt_blockStyles, opt_categoryStyles,
opt_componentStyles) {
/**
* The theme name. This can be used to reference a specific theme in CSS.
* @type {string}
* @package
*/
this.name = name;
@@ -42,36 +42,36 @@ Blockly.Theme = function(name, blockStyles, categoryStyles,
* @type {!Object.<string, !Blockly.Theme.BlockStyle>}
* @package
*/
this.blockStyles = blockStyles;
this.blockStyles = opt_blockStyles || Object.create(null);
/**
* The category styles map.
* @type {!Object.<string, Blockly.Theme.CategoryStyle>}
* @package
*/
this.categoryStyles = categoryStyles;
this.categoryStyles = opt_categoryStyles || Object.create(null);
/**
* The UI components styles map.
* @type {!Object.<string, *>}
* @private
* @package
*/
this.componentStyles_ = opt_componentStyles || Object.create(null);
this.componentStyles = opt_componentStyles || Object.create(null);
/**
* The font style.
* @type {?Blockly.Theme.FontStyle}
* @type {Blockly.Theme.FontStyle}
* @package
*/
this.fontStyle = null;
this.fontStyle = /** @type {Blockly.Theme.FontStyle} */ (Object.create(null));
/**
* Whether or not to add a 'hat' on top of all blocks with no previous or
* output connections.
* @type {?boolean}
* @type {boolean}
* @package
*/
this.startHats = null;
this.startHats = false;
};
/**
@@ -122,6 +122,31 @@ Blockly.Theme.prototype.setCategoryStyle = function(categoryStyleName,
this.categoryStyles[categoryStyleName] = categoryStyle;
};
/**
* Gets the style for a given Blockly UI component. If the style value is a
* string, we attempt to find the value of any named references.
* @param {string} componentName The name of the component.
* @return {?string} The style value.
*/
Blockly.Theme.prototype.getComponentStyle = function(componentName) {
var style = this.componentStyles[componentName];
if (style && typeof propertyValue == 'string' &&
this.getComponentStyle(/** @type {string} */ (style))) {
return this.getComponentStyle(/** @type {string} */ (style));
}
return style ? String(style) : null;
};
/**
* Configure a specific Blockly UI component with a style value.
* @param {string} componentName The name of the component.
* @param {*} styleValue The style value.
*/
Blockly.Theme.prototype.setComponentStyle = function(componentName,
styleValue) {
this.componentStyles[componentName] = styleValue;
};
/**
* Configure a theme's font style.
* @param {Blockly.Theme.FontStyle} fontStyle The font style.
@@ -140,26 +165,28 @@ Blockly.Theme.prototype.setStartHats = function(startHats) {
};
/**
* Gets the style for a given Blockly UI component. If the style value is a
* string, we attempt to find the value of any named references.
* @param {string} componentName The name of the component.
* @return {?string} The style value.
*/
Blockly.Theme.prototype.getComponentStyle = function(componentName) {
var style = this.componentStyles_[componentName];
if (style && typeof propertyValue == 'string' &&
this.getComponentStyle(/** @type {string} */ (style))) {
return this.getComponentStyle(/** @type {string} */ (style));
}
return style ? String(style) : null;
};
/**
* Configure a specific Blockly UI component with a style value.
* @param {string} componentName The name of the component.
* @param {*} styleValue The style value.
* Define a new Blockly theme.
* @param {string} name The name of the theme.
* @param {!Object} themeObj An object containing theme properties.
* @return {!Blockly.Theme} A new Blockly theme.
*/
Blockly.Theme.prototype.setComponentStyle = function(componentName,
styleValue) {
this.componentStyles_[componentName] = styleValue;
Blockly.Theme.defineTheme = function(name, themeObj) {
var theme = new Blockly.Theme(name);
var base = themeObj['base'];
if (base && base instanceof Blockly.Theme) {
Blockly.utils.object.deepMerge(theme, base);
}
Blockly.utils.object.deepMerge(theme.blockStyles,
themeObj['blockStyles']);
Blockly.utils.object.deepMerge(theme.categoryStyles,
themeObj['categoryStyles']);
Blockly.utils.object.deepMerge(theme.componentStyles,
themeObj['componentStyles']);
Blockly.utils.object.deepMerge(theme.fontStyle,
themeObj['fontStyle']);
if (themeObj['startHats'] != null) {
theme.startHats = themeObj['startHats'];
}
return theme;
};

View File

@@ -12,143 +12,18 @@
goog.provide('Blockly.Themes.Dark');
goog.require('Blockly.Css');
goog.require('Blockly.Theme');
// Temporary holding object.
Blockly.Themes.Dark = {};
Blockly.Themes.Dark.defaultBlockStyles = {
"colour_blocks": {
"colourPrimary": "#a5745b",
"colourSecondary": "#dbc7bd",
"colourTertiary": "#845d49"
},
"list_blocks": {
"colourPrimary": "#745ba5",
"colourSecondary": "#c7bddb",
"colourTertiary": "#5d4984"
},
"logic_blocks": {
"colourPrimary": "#5b80a5",
"colourSecondary": "#bdccdb",
"colourTertiary": "#496684"
},
"loop_blocks": {
"colourPrimary": "#5ba55b",
"colourSecondary": "#bddbbd",
"colourTertiary": "#498449"
},
"math_blocks": {
"colourPrimary": "#5b67a5",
"colourSecondary": "#bdc2db",
"colourTertiary": "#495284"
},
"procedure_blocks": {
"colourPrimary": "#995ba5",
"colourSecondary": "#d6bddb",
"colourTertiary": "#7a4984"
},
"text_blocks": {
"colourPrimary": "#5ba58c",
"colourSecondary": "#bddbd1",
"colourTertiary": "#498470"
},
"variable_blocks": {
"colourPrimary": "#a55b99",
"colourSecondary": "#dbbdd6",
"colourTertiary": "#84497a"
},
"variable_dynamic_blocks": {
"colourPrimary": "#a55b99",
"colourSecondary": "#dbbdd6",
"colourTertiary": "#84497a"
},
"hat_blocks": {
"colourPrimary": "#a55b99",
"colourSecondary": "#dbbdd6",
"colourTertiary": "#84497a",
"hat": "cap"
Blockly.Themes.Dark = Blockly.Theme.defineTheme('dark', {
'base': Blockly.Themes.Classic,
'componentStyles': {
'workspaceBackgroundColour': '#1e1e1e',
'toolboxBackgroundColour': '#333',
'toolboxForegroundColour': '#fff',
'flyoutBackgroundColour': '#252526',
'flyoutForegroundColour': '#ccc',
'flyoutOpacity': 1,
'scrollbarColour': '#797979',
'scrollbarOpacity': 0.4
}
};
Blockly.Themes.Dark.categoryStyles = {
"colour_category": {
"colour": "#a5745b"
},
"list_category": {
"colour": "#745ba5"
},
"logic_category": {
"colour": "#5b80a5"
},
"loop_category": {
"colour": "#5ba55b"
},
"math_category": {
"colour": "#5b67a5"
},
"procedure_category": {
"colour": "#995ba5"
},
"text_category": {
"colour": "#5ba58c"
},
"variable_category": {
"colour": "#a55b99"
},
"variable_dynamic_category": {
"colour": "#a55b99"
}
};
// This style is still being fleshed out and may change.
Blockly.Themes.Dark =
new Blockly.Theme('dark', Blockly.Themes.Dark.defaultBlockStyles,
Blockly.Themes.Dark.categoryStyles);
Blockly.Themes.Dark.setComponentStyle('workspaceBackgroundColour', '#1e1e1e');
Blockly.Themes.Dark.setComponentStyle('toolboxBackgroundColour', '#333');
Blockly.Themes.Dark.setComponentStyle('toolboxForegroundColour', '#fff');
Blockly.Themes.Dark.setComponentStyle('flyoutBackgroundColour', '#252526');
Blockly.Themes.Dark.setComponentStyle('flyoutForegroundColour', '#ccc');
Blockly.Themes.Dark.setComponentStyle('flyoutOpacity', 1);
Blockly.Themes.Dark.setComponentStyle('scrollbarColour', '#797979');
Blockly.Themes.Dark.setComponentStyle('scrollbarOpacity', 0.4);
/**
* CSS for the dark theme.
* This registers CSS that is specific to this theme. It does so by prepending a
* ``.dark-theme`` selector before every CSS rule that we wish to override by
* this theme.
*/
(function() {
var selector = '.dark-theme';
Blockly.Css.register([
/* eslint-disable indent */
// Toolbox hover
selector + ' .blocklyTreeRow:not(.blocklyTreeSelected):hover {',
'background-color: #2a2d2e;',
'}',
// Dropdown and Widget div.
selector + '.blocklyWidgetDiv .goog-menu, ',
selector + '.blocklyDropDownDiv {',
'background-color: #3c3c3c;',
'}',
selector + '.blocklyDropDownDiv {',
'border-color: #565656;',
'}',
selector + '.blocklyWidgetDiv .goog-menuitem-content, ',
selector + '.blocklyDropDownDiv .goog-menuitem-content {',
'color: #f0f0f0;',
'}',
selector + '.blocklyWidgetDiv .goog-menuitem-disabled',
' .goog-menuitem-content,',
selector + '.blocklyDropDownDiv .goog-menuitem-disabled',
' .goog-menuitem-content {',
'color: #8a8a8a !important;',
'}',
/* eslint-enable indent */
]);
})();
});

View File

@@ -780,7 +780,7 @@ Blockly.Css.register([
'}',
'.blocklyTreeRow:not(.blocklyTreeSelected):hover {',
'background-color: #e4e4e4;',
'background-color: rgba(255, 255, 255, 0.2);',
'}',
'.blocklyTreeSeparator {',

View File

@@ -37,6 +37,24 @@ Blockly.utils.object.mixin = function(target, source) {
}
};
/**
* Complete a deep merge of all members of a source object with a target object.
* @param {!Object} target Target.
* @param {!Object} source Source.
* @return {!Object} The resulting object.
*/
Blockly.utils.object.deepMerge = function(target, source) {
for (var x in source) {
if (typeof source[x] === 'object') {
target[x] = Blockly.utils.object.deepMerge(
target[x] || Object.create(null), source[x]);
} else {
target[x] = source[x];
}
}
return target;
};
/**
* Returns an array of a given object's own enumerable property values.
* @param {!Object} obj Object containing values.

View File

@@ -11,15 +11,17 @@ goog.provide('Blockly.TestThemes');
/**
* A theme with classic colours but enables start hats.
*/
Blockly.Themes.TestHats = new Blockly.Theme('testhats',
Blockly.Themes.Classic.blockStyles, Blockly.Themes.Classic.categoryStyles);
Blockly.Themes.TestHats = Blockly.Theme.defineTheme('testhats', {
'base': Blockly.Themes.Classic
});
Blockly.Themes.TestHats.setStartHats(true);
/**
* A theme with classic colours but a different font.
*/
Blockly.Themes.TestFont = new Blockly.Theme('testfont',
Blockly.Themes.Classic.blockStyles, Blockly.Themes.Classic.categoryStyles);
Blockly.Themes.TestFont = Blockly.Theme.defineTheme('testfont', {
'base': Blockly.Themes.Classic
});
Blockly.Themes.TestFont.setFontStyle({
'family': '"Times New Roman", Times, serif',
'weight': null, // Use default font-weight