diff --git a/blockly_uncompressed.js b/blockly_uncompressed.js index e64b8c588..e035da4c1 100644 --- a/blockly_uncompressed.js +++ b/blockly_uncompressed.js @@ -85,6 +85,7 @@ goog.addDependency("../../../" + dir + "/core/options.js", ['Blockly.Options'], goog.addDependency("../../../" + dir + "/core/procedures.js", ['Blockly.Procedures'], ['Blockly.Blocks', 'Blockly.constants', 'Blockly.Events.BlockChange', 'Blockly.Field', 'Blockly.Names', 'Blockly.Workspace', 'Blockly.Xml', 'Blockly.Xml.utils']); goog.addDependency("../../../" + dir + "/core/rendered_connection.js", ['Blockly.RenderedConnection'], ['Blockly.Connection', 'Blockly.utils', 'goog.math.Coordinate']); goog.addDependency("../../../" + dir + "/core/scrollbar.js", ['Blockly.Scrollbar', 'Blockly.ScrollbarPair'], ['Blockly.utils', 'goog.events.BrowserFeature', 'goog.math.Coordinate']); +goog.addDependency("../../../" + dir + "/core/theme.js", ['Blockly.Theme'], []); goog.addDependency("../../../" + dir + "/core/toolbox.js", ['Blockly.Toolbox'], ['Blockly.Events.Ui', 'Blockly.Flyout', 'Blockly.HorizontalFlyout', 'Blockly.Touch', 'Blockly.utils', 'Blockly.VerticalFlyout', 'goog.events', 'goog.events.BrowserFeature', 'goog.html.SafeHtml', 'goog.html.SafeStyle', 'goog.math.Rect', 'goog.style', 'goog.ui.tree.TreeControl', 'goog.ui.tree.TreeNode']); goog.addDependency("../../../" + dir + "/core/tooltip.js", ['Blockly.Tooltip'], ['Blockly.utils', 'goog.dom']); goog.addDependency("../../../" + dir + "/core/touch.js", ['Blockly.Touch'], ['Blockly.utils', 'goog.events.BrowserFeature']); @@ -1796,6 +1797,7 @@ goog.require('Blockly.Procedures'); goog.require('Blockly.RenderedConnection'); goog.require('Blockly.Scrollbar'); goog.require('Blockly.ScrollbarPair'); +goog.require('Blockly.Theme'); goog.require('Blockly.Toolbox'); goog.require('Blockly.Tooltip'); goog.require('Blockly.Touch'); diff --git a/blocks/colour.js b/blocks/colour.js index 22898f3cb..3b76ee5be 100644 --- a/blocks/colour.js +++ b/blocks/colour.js @@ -55,8 +55,8 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT } ], "output": "Colour", - "colour": "%{BKY_COLOUR_HUE}", "helpUrl": "%{BKY_COLOUR_PICKER_HELPURL}", + "style": "colour_blocks", "tooltip": "%{BKY_COLOUR_PICKER_TOOLTIP}", "extensions": ["parent_tooltip_when_inline"] }, @@ -66,8 +66,8 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT "type": "colour_random", "message0": "%{BKY_COLOUR_RANDOM_TITLE}", "output": "Colour", - "colour": "%{BKY_COLOUR_HUE}", "helpUrl": "%{BKY_COLOUR_RANDOM_HELPURL}", + "style": "colour_blocks", "tooltip": "%{BKY_COLOUR_RANDOM_TOOLTIP}" }, @@ -96,8 +96,8 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT } ], "output": "Colour", - "colour": "%{BKY_COLOUR_HUE}", "helpUrl": "%{BKY_COLOUR_RGB_HELPURL}", + "style": "colour_blocks", "tooltip": "%{BKY_COLOUR_RGB_TOOLTIP}" }, @@ -127,8 +127,8 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT } ], "output": "Colour", - "colour": "%{BKY_COLOUR_HUE}", "helpUrl": "%{BKY_COLOUR_BLEND_HELPURL}", + "style": "colour_blocks", "tooltip": "%{BKY_COLOUR_BLEND_TOOLTIP}" } ]); // END JSON EXTRACT (Do not delete this comment.) diff --git a/blocks/lists.js b/blocks/lists.js index 79cae676f..80a551768 100644 --- a/blocks/lists.js +++ b/blocks/lists.js @@ -51,7 +51,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT "type": "lists_create_empty", "message0": "%{BKY_LISTS_CREATE_EMPTY_TITLE}", "output": "Array", - "colour": "%{BKY_LISTS_HUE}", + "style": "list_blocks", "tooltip": "%{BKY_LISTS_CREATE_EMPTY_TOOLTIP}", "helpUrl": "%{BKY_LISTS_CREATE_EMPTY_HELPURL}" }, @@ -71,7 +71,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT } ], "output": "Array", - "colour": "%{BKY_LISTS_HUE}", + "style": "list_blocks", "tooltip": "%{BKY_LISTS_REPEAT_TOOLTIP}", "helpUrl": "%{BKY_LISTS_REPEAT_HELPURL}" }, @@ -88,7 +88,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT ], "output": "Array", "inputsInline": true, - "colour": "%{BKY_LISTS_HUE}", + "style": "list_blocks", "tooltip": "%{BKY_LISTS_REVERSE_TOOLTIP}", "helpUrl": "%{BKY_LISTS_REVERSE_HELPURL}" }, @@ -104,7 +104,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT } ], "output": "Boolean", - "colour": "%{BKY_LISTS_HUE}", + "style": "list_blocks", "tooltip": "%{BKY_LISTS_ISEMPTY_TOOLTIP}", "helpUrl": "%{BKY_LISTS_ISEMPTY_HELPURL}" }, @@ -120,7 +120,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT } ], "output": "Number", - "colour": "%{BKY_LISTS_HUE}", + "style": "list_blocks", "tooltip": "%{BKY_LISTS_LENGTH_TOOLTIP}", "helpUrl": "%{BKY_LISTS_LENGTH_HELPURL}" } @@ -133,7 +133,7 @@ Blockly.Blocks['lists_create_with'] = { */ init: function() { this.setHelpUrl(Blockly.Msg['LISTS_CREATE_WITH_HELPURL']); - this.setColour(Blockly.Msg['LISTS_HUE']); + this.setStyle('list_blocks'); this.itemCount_ = 3; this.updateShape_(); this.setOutput(true, 'Array'); @@ -256,7 +256,7 @@ Blockly.Blocks['lists_create_with_container'] = { * @this Blockly.Block */ init: function() { - this.setColour(Blockly.Msg['LISTS_HUE']); + this.setStyle('list_blocks'); this.appendDummyInput() .appendField(Blockly.Msg['LISTS_CREATE_WITH_CONTAINER_TITLE_ADD']); this.appendStatementInput('STACK'); @@ -271,7 +271,7 @@ Blockly.Blocks['lists_create_with_item'] = { * @this Blockly.Block */ init: function() { - this.setColour(Blockly.Msg['LISTS_HUE']); + this.setStyle('list_blocks'); this.appendDummyInput() .appendField(Blockly.Msg['LISTS_CREATE_WITH_ITEM_TITLE']); this.setPreviousStatement(true); @@ -293,7 +293,7 @@ Blockly.Blocks['lists_indexOf'] = { [Blockly.Msg['LISTS_INDEX_OF_LAST'], 'LAST'] ]; this.setHelpUrl(Blockly.Msg['LISTS_INDEX_OF_HELPURL']); - this.setColour(Blockly.Msg['LISTS_HUE']); + this.setStyle('list_blocks'); this.setOutput(true, 'Number'); this.appendValueInput('VALUE') .setCheck('Array') @@ -331,7 +331,7 @@ Blockly.Blocks['lists_getIndex'] = { [Blockly.Msg['LISTS_GET_INDEX_RANDOM'], 'RANDOM'] ]; this.setHelpUrl(Blockly.Msg['LISTS_GET_INDEX_HELPURL']); - this.setColour(Blockly.Msg['LISTS_HUE']); + this.setStyle('list_blocks'); var modeMenu = new Blockly.FieldDropdown(MODE, function(value) { var isStatement = (value == 'REMOVE'); this.sourceBlock_.updateStatement_(isStatement); @@ -515,7 +515,7 @@ Blockly.Blocks['lists_setIndex'] = { [Blockly.Msg['LISTS_GET_INDEX_RANDOM'], 'RANDOM'] ]; this.setHelpUrl(Blockly.Msg['LISTS_SET_INDEX_HELPURL']); - this.setColour(Blockly.Msg['LISTS_HUE']); + this.setStyle('list_blocks'); this.appendValueInput('LIST') .setCheck('Array') .appendField(Blockly.Msg['LISTS_SET_INDEX_INPUT_IN_LIST']); @@ -654,7 +654,7 @@ Blockly.Blocks['lists_getSublist'] = { [Blockly.Msg['LISTS_GET_SUBLIST_END_LAST'], 'LAST'] ]; this.setHelpUrl(Blockly.Msg['LISTS_GET_SUBLIST_HELPURL']); - this.setColour(Blockly.Msg['LISTS_HUE']); + this.setStyle('list_blocks'); this.appendValueInput('LIST') .setCheck('Array') .appendField(Blockly.Msg['LISTS_GET_SUBLIST_INPUT_IN_LIST']); @@ -779,7 +779,7 @@ Blockly.Blocks['lists_sort'] = { } ], "output": "Array", - "colour": Blockly.Msg['LISTS_HUE'], + "style": "list_blocks", "tooltip": Blockly.Msg['LISTS_SORT_TOOLTIP'], "helpUrl": Blockly.Msg['LISTS_SORT_HELPURL'] }); @@ -803,7 +803,7 @@ Blockly.Blocks['lists_split'] = { thisBlock.updateType_(newMode); }); this.setHelpUrl(Blockly.Msg['LISTS_SPLIT_HELPURL']); - this.setColour(Blockly.Msg['LISTS_HUE']); + this.setStyle('list_blocks'); this.appendValueInput('INPUT') .setCheck('String') .appendField(dropdown, 'MODE'); diff --git a/blocks/logic.js b/blocks/logic.js index e904085c6..9fcecbfce 100644 --- a/blocks/logic.js +++ b/blocks/logic.js @@ -57,7 +57,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT } ], "output": "Boolean", - "colour": "%{BKY_LOGIC_HUE}", + "style": "logic_blocks", "tooltip": "%{BKY_LOGIC_BOOLEAN_TOOLTIP}", "helpUrl": "%{BKY_LOGIC_BOOLEAN_HELPURL}" }, @@ -81,7 +81,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT ], "previousStatement": null, "nextStatement": null, - "colour": "%{BKY_LOGIC_HUE}", + "style": "logic_blocks", "helpUrl": "%{BKY_CONTROLS_IF_HELPURL}", "mutator": "controls_if_mutator", "extensions": ["controls_if_tooltip"] @@ -113,7 +113,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT ], "previousStatement": null, "nextStatement": null, - "colour": "%{BKY_LOGIC_HUE}", + "style": "logic_blocks", "tooltip": "%{BKYCONTROLS_IF_TOOLTIP_2}", "helpUrl": "%{BKY_CONTROLS_IF_HELPURL}", "extensions": ["controls_if_tooltip"] @@ -146,7 +146,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT ], "inputsInline": true, "output": "Boolean", - "colour": "%{BKY_LOGIC_HUE}", + "style": "logic_blocks", "helpUrl": "%{BKY_LOGIC_COMPARE_HELPURL}", "extensions": ["logic_compare", "logic_op_tooltip"] }, @@ -176,7 +176,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT ], "inputsInline": true, "output": "Boolean", - "colour": "%{BKY_LOGIC_HUE}", + "style": "logic_blocks", "helpUrl": "%{BKY_LOGIC_OPERATION_HELPURL}", "extensions": ["logic_op_tooltip"] }, @@ -192,7 +192,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT } ], "output": "Boolean", - "colour": "%{BKY_LOGIC_HUE}", + "style": "logic_blocks", "tooltip": "%{BKY_LOGIC_NEGATE_TOOLTIP}", "helpUrl": "%{BKY_LOGIC_NEGATE_HELPURL}" }, @@ -201,7 +201,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT "type": "logic_null", "message0": "%{BKY_LOGIC_NULL}", "output": null, - "colour": "%{BKY_LOGIC_HUE}", + "style": "logic_blocks", "tooltip": "%{BKY_LOGIC_NULL_TOOLTIP}", "helpUrl": "%{BKY_LOGIC_NULL_HELPURL}" }, @@ -231,7 +231,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT } ], "output": null, - "colour": "%{BKY_LOGIC_HUE}", + "style": "logic_blocks", "tooltip": "%{BKY_LOGIC_TERNARY_TOOLTIP}", "helpUrl": "%{BKY_LOGIC_TERNARY_HELPURL}", "extensions": ["logic_ternary"] @@ -245,7 +245,7 @@ Blockly.defineBlocksWithJsonArray([ // Mutator blocks. Do not extract. "message0": "%{BKY_CONTROLS_IF_IF_TITLE_IF}", "nextStatement": null, "enableContextMenu": false, - "colour": "%{BKY_LOGIC_HUE}", + "style": "logic_blocks", "tooltip": "%{BKY_CONTROLS_IF_IF_TOOLTIP}" }, // Block representing the else-if statement in the controls_if mutator. @@ -255,7 +255,7 @@ Blockly.defineBlocksWithJsonArray([ // Mutator blocks. Do not extract. "previousStatement": null, "nextStatement": null, "enableContextMenu": false, - "colour": "%{BKY_LOGIC_HUE}", + "style": "logic_blocks", "tooltip": "%{BKY_CONTROLS_IF_ELSEIF_TOOLTIP}" }, // Block representing the else statement in the controls_if mutator. @@ -264,7 +264,7 @@ Blockly.defineBlocksWithJsonArray([ // Mutator blocks. Do not extract. "message0": "%{BKY_CONTROLS_IF_ELSE_TITLE_ELSE}", "previousStatement": null, "enableContextMenu": false, - "colour": "%{BKY_LOGIC_HUE}", + "style": "logic_blocks", "tooltip": "%{BKY_CONTROLS_IF_ELSE_TOOLTIP}" } ]); diff --git a/blocks/loops.js b/blocks/loops.js index 23ef5824b..801780839 100644 --- a/blocks/loops.js +++ b/blocks/loops.js @@ -59,7 +59,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT }], "previousStatement": null, "nextStatement": null, - "colour": "%{BKY_LOOPS_HUE}", + "style": "loop_blocks", "tooltip": "%{BKY_CONTROLS_REPEAT_TOOLTIP}", "helpUrl": "%{BKY_CONTROLS_REPEAT_HELPURL}" }, @@ -82,7 +82,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT }], "previousStatement": null, "nextStatement": null, - "colour": "%{BKY_LOOPS_HUE}", + "style": "loop_blocks", "tooltip": "%{BKY_CONTROLS_REPEAT_TOOLTIP}", "helpUrl": "%{BKY_CONTROLS_REPEAT_HELPURL}" }, @@ -112,7 +112,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT }], "previousStatement": null, "nextStatement": null, - "colour": "%{BKY_LOOPS_HUE}", + "style": "loop_blocks", "helpUrl": "%{BKY_CONTROLS_WHILEUNTIL_HELPURL}", "extensions": ["controls_whileUntil_tooltip"] }, @@ -153,7 +153,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT "inputsInline": true, "previousStatement": null, "nextStatement": null, - "colour": "%{BKY_LOOPS_HUE}", + "style": "loop_blocks", "helpUrl": "%{BKY_CONTROLS_FOR_HELPURL}", "extensions": [ "contextMenu_newGetVariableBlock", @@ -183,7 +183,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT }], "previousStatement": null, "nextStatement": null, - "colour": "%{BKY_LOOPS_HUE}", + "style": "loop_blocks", "helpUrl": "%{BKY_CONTROLS_FOREACH_HELPURL}", "extensions": [ "contextMenu_newGetVariableBlock", @@ -203,7 +203,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT ] }], "previousStatement": null, - "colour": "%{BKY_LOOPS_HUE}", + "style": "loop_blocks", "helpUrl": "%{BKY_CONTROLS_FLOW_STATEMENTS_HELPURL}", "extensions": [ "controls_flow_tooltip", diff --git a/blocks/math.js b/blocks/math.js index 18f664368..3d4f337ae 100644 --- a/blocks/math.js +++ b/blocks/math.js @@ -53,8 +53,8 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT "value": 0 }], "output": "Number", - "colour": "%{BKY_MATH_HUE}", "helpUrl": "%{BKY_MATH_NUMBER_HELPURL}", + "style": "math_blocks", "tooltip": "%{BKY_MATH_NUMBER_TOOLTIP}", "extensions": ["parent_tooltip_when_inline"] }, @@ -88,7 +88,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT ], "inputsInline": true, "output": "Number", - "colour": "%{BKY_MATH_HUE}", + "style": "math_blocks", "helpUrl": "%{BKY_MATH_ARITHMETIC_HELPURL}", "extensions": ["math_op_tooltip"] }, @@ -118,7 +118,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT } ], "output": "Number", - "colour": "%{BKY_MATH_HUE}", + "style": "math_blocks", "helpUrl": "%{BKY_MATH_SINGLE_HELPURL}", "extensions": ["math_op_tooltip"] }, @@ -147,7 +147,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT } ], "output": "Number", - "colour": "%{BKY_MATH_HUE}", + "style": "math_blocks", "helpUrl": "%{BKY_MATH_TRIG_HELPURL}", "extensions": ["math_op_tooltip"] }, @@ -171,7 +171,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT } ], "output": "Number", - "colour": "%{BKY_MATH_HUE}", + "style": "math_blocks", "tooltip": "%{BKY_MATH_CONSTANT_TOOLTIP}", "helpUrl": "%{BKY_MATH_CONSTANT_HELPURL}" }, @@ -203,7 +203,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT ], "inputsInline": true, "output": "Boolean", - "colour": "%{BKY_MATH_HUE}", + "style": "math_blocks", "tooltip": "%{BKY_MATH_IS_TOOLTIP}", "mutator": "math_is_divisibleby_mutator" }, @@ -226,7 +226,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT ], "previousStatement": null, "nextStatement": null, - "colour": "%{BKY_VARIABLES_HUE}", + "style": "variable_blocks", "helpUrl": "%{BKY_MATH_CHANGE_HELPURL}", "extensions": ["math_change_tooltip"] }, @@ -252,7 +252,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT } ], "output": "Number", - "colour": "%{BKY_MATH_HUE}", + "style": "math_blocks", "helpUrl": "%{BKY_MATH_ROUND_HELPURL}", "tooltip": "%{BKY_MATH_ROUND_TOOLTIP}" }, @@ -284,7 +284,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT } ], "output": "Number", - "colour": "%{BKY_MATH_HUE}", + "style": "math_blocks", "helpUrl": "%{BKY_MATH_ONLIST_HELPURL}", "mutator": "math_modes_of_list_mutator", "extensions": ["math_op_tooltip"] @@ -308,7 +308,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT ], "inputsInline": true, "output": "Number", - "colour": "%{BKY_MATH_HUE}", + "style": "math_blocks", "tooltip": "%{BKY_MATH_MODULO_TOOLTIP}", "helpUrl": "%{BKY_MATH_MODULO_HELPURL}" }, @@ -336,7 +336,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT ], "inputsInline": true, "output": "Number", - "colour": "%{BKY_MATH_HUE}", + "style": "math_blocks", "tooltip": "%{BKY_MATH_CONSTRAIN_TOOLTIP}", "helpUrl": "%{BKY_MATH_CONSTRAIN_HELPURL}" }, @@ -359,7 +359,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT ], "inputsInline": true, "output": "Number", - "colour": "%{BKY_MATH_HUE}", + "style": "math_blocks", "tooltip": "%{BKY_MATH_RANDOM_INT_TOOLTIP}", "helpUrl": "%{BKY_MATH_RANDOM_INT_HELPURL}" }, @@ -369,7 +369,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT "type": "math_random_float", "message0": "%{BKY_MATH_RANDOM_FLOAT_TITLE_RANDOM}", "output": "Number", - "colour": "%{BKY_MATH_HUE}", + "style": "math_blocks", "tooltip": "%{BKY_MATH_RANDOM_FLOAT_TOOLTIP}", "helpUrl": "%{BKY_MATH_RANDOM_FLOAT_HELPURL}" }, @@ -392,7 +392,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT ], "inputsInline": true, "output": "Number", - "colour": "%{BKY_MATH_HUE}", + "style": "math_blocks", "tooltip": "%{BKY_MATH_ATAN2_TOOLTIP}", "helpUrl": "%{BKY_MATH_ATAN2_HELPURL}" } diff --git a/blocks/procedures.js b/blocks/procedures.js index a8b15604b..c19ec0618 100644 --- a/blocks/procedures.js +++ b/blocks/procedures.js @@ -50,7 +50,7 @@ Blockly.Blocks['procedures_defnoreturn'] = { Blockly.Msg['PROCEDURES_DEFNORETURN_COMMENT']) { this.setCommentText(Blockly.Msg['PROCEDURES_DEFNORETURN_COMMENT']); } - this.setColour(Blockly.Msg['PROCEDURES_HUE']); + this.setStyle('procedure_blocks'); this.setTooltip(Blockly.Msg['PROCEDURES_DEFNORETURN_TOOLTIP']); this.setHelpUrl(Blockly.Msg['PROCEDURES_DEFNORETURN_HELPURL']); this.arguments_ = []; @@ -410,7 +410,7 @@ Blockly.Blocks['procedures_defreturn'] = { Blockly.Msg['PROCEDURES_DEFRETURN_COMMENT']) { this.setCommentText(Blockly.Msg['PROCEDURES_DEFRETURN_COMMENT']); } - this.setColour(Blockly.Msg['PROCEDURES_HUE']); + this.setStyle('procedure_blocks'); this.setTooltip(Blockly.Msg['PROCEDURES_DEFRETURN_TOOLTIP']); this.setHelpUrl(Blockly.Msg['PROCEDURES_DEFRETURN_HELPURL']); this.arguments_ = []; @@ -456,7 +456,7 @@ Blockly.Blocks['procedures_mutatorcontainer'] = { this.appendDummyInput('STATEMENT_INPUT') .appendField(Blockly.Msg['PROCEDURES_ALLOW_STATEMENTS']) .appendField(new Blockly.FieldCheckbox('TRUE'), 'STATEMENTS'); - this.setColour(Blockly.Msg['PROCEDURES_HUE']); + this.setStyle('procedure_blocks'); this.setTooltip(Blockly.Msg['PROCEDURES_MUTATORCONTAINER_TOOLTIP']); this.contextMenu = false; }, @@ -546,7 +546,7 @@ Blockly.Blocks['procedures_mutatorarg'] = { .appendField(field, 'NAME'); this.setPreviousStatement(true); this.setNextStatement(true); - this.setColour(Blockly.Msg['PROCEDURES_HUE']); + this.setStyle('procedure_blocks'); this.setTooltip(Blockly.Msg['PROCEDURES_MUTATORARG_TOOLTIP']); this.contextMenu = false; @@ -630,7 +630,7 @@ Blockly.Blocks['procedures_callnoreturn'] = { .appendField(this.id, 'NAME'); this.setPreviousStatement(true); this.setNextStatement(true); - this.setColour(Blockly.Msg['PROCEDURES_HUE']); + this.setStyle('procedure_blocks'); // Tooltip is set in renameProcedure. this.setHelpUrl(Blockly.Msg['PROCEDURES_CALLNORETURN_HELPURL']); this.arguments_ = []; @@ -971,7 +971,7 @@ Blockly.Blocks['procedures_callreturn'] = { this.appendDummyInput('TOPROW') .appendField('', 'NAME'); this.setOutput(true); - this.setColour(Blockly.Msg['PROCEDURES_HUE']); + this.setStyle('procedure_blocks'); // Tooltip is set in domToMutation. this.setHelpUrl(Blockly.Msg['PROCEDURES_CALLRETURN_HELPURL']); this.arguments_ = []; @@ -1008,7 +1008,7 @@ Blockly.Blocks['procedures_ifreturn'] = { this.setInputsInline(true); this.setPreviousStatement(true); this.setNextStatement(true); - this.setColour(Blockly.Msg['PROCEDURES_HUE']); + this.setStyle('procedure_blocks'); this.setTooltip(Blockly.Msg['PROCEDURES_IFRETURN_TOOLTIP']); this.setHelpUrl(Blockly.Msg['PROCEDURES_IFRETURN_HELPURL']); this.hasReturnValue_ = true; diff --git a/blocks/text.js b/blocks/text.js index ea79d43cd..9d491bdc6 100644 --- a/blocks/text.js +++ b/blocks/text.js @@ -48,7 +48,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT "text": "" }], "output": "String", - "colour": "%{BKY_TEXTS_HUE}", + "style": "text_blocks", "helpUrl": "%{BKY_TEXT_TEXT_HELPURL}", "tooltip": "%{BKY_TEXT_TEXT_TOOLTIP}", "extensions": [ @@ -60,7 +60,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT "type": "text_join", "message0": "", "output": "String", - "colour": "%{BKY_TEXTS_HUE}", + "style": "text_blocks", "helpUrl": "%{BKY_TEXT_JOIN_HELPURL}", "tooltip": "%{BKY_TEXT_JOIN_TOOLTIP}", "mutator": "text_join_mutator" @@ -76,7 +76,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT "type": "input_statement", "name": "STACK" }], - "colour": "%{BKY_TEXTS_HUE}", + "style": "text_blocks", "tooltip": "%{BKY_TEXT_CREATE_JOIN_TOOLTIP}", "enableContextMenu": false }, @@ -85,7 +85,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT "message0": "%{BKY_TEXT_CREATE_JOIN_ITEM_TITLE_ITEM}", "previousStatement": null, "nextStatement": null, - "colour": "%{BKY_TEXTS_HUE}", + "style": "text_blocks", "tooltip": "%{BKY_TEXT_CREATE_JOIN_ITEM_TOOLTIP}", "enableContextMenu": false }, @@ -103,7 +103,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT }], "previousStatement": null, "nextStatement": null, - "colour": "%{BKY_TEXTS_HUE}", + "style": "text_blocks", "extensions": [ "text_append_tooltip" ] @@ -119,7 +119,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT } ], "output": 'Number', - "colour": "%{BKY_TEXTS_HUE}", + "style": "text_blocks", "tooltip": "%{BKY_TEXT_LENGTH_TOOLTIP}", "helpUrl": "%{BKY_TEXT_LENGTH_HELPURL}" }, @@ -134,7 +134,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT } ], "output": 'Boolean', - "colour": "%{BKY_TEXTS_HUE}", + "style": "text_blocks", "tooltip": "%{BKY_TEXT_ISEMPTY_TOOLTIP}", "helpUrl": "%{BKY_TEXT_ISEMPTY_HELPURL}" }, @@ -168,7 +168,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT } ], "output": "Number", - "colour": "%{BKY_TEXTS_HUE}", + "style": "text_blocks", "helpUrl": "%{BKY_TEXT_INDEXOF_HELPURL}", "inputsInline": true, "extensions": [ @@ -197,7 +197,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT } ], "output": "String", - "colour": "%{BKY_TEXTS_HUE}", + "style": "text_blocks", "helpUrl": "%{BKY_TEXT_CHARAT_HELPURL}", "inputsInline": true, "mutator": "text_charAt_mutator" @@ -221,7 +221,7 @@ Blockly.Blocks['text_getSubstring'] = { [Blockly.Msg['TEXT_GET_SUBSTRING_END_LAST'], 'LAST'] ]; this.setHelpUrl(Blockly.Msg['TEXT_GET_SUBSTRING_HELPURL']); - this.setColour(Blockly.Msg['TEXTS_HUE']); + this.setStyle('text_blocks'); this.appendValueInput('STRING') .setCheck('String') .appendField(Blockly.Msg['TEXT_GET_SUBSTRING_INPUT_IN_TEXT']); @@ -329,7 +329,7 @@ Blockly.Blocks['text_changeCase'] = { [Blockly.Msg['TEXT_CHANGECASE_OPERATOR_TITLECASE'], 'TITLECASE'] ]; this.setHelpUrl(Blockly.Msg['TEXT_CHANGECASE_HELPURL']); - this.setColour(Blockly.Msg['TEXTS_HUE']); + this.setStyle('text_blocks'); this.appendValueInput('TEXT') .setCheck('String') .appendField(new Blockly.FieldDropdown(OPERATORS), 'CASE'); @@ -350,7 +350,7 @@ Blockly.Blocks['text_trim'] = { [Blockly.Msg['TEXT_TRIM_OPERATOR_RIGHT'], 'RIGHT'] ]; this.setHelpUrl(Blockly.Msg['TEXT_TRIM_HELPURL']); - this.setColour(Blockly.Msg['TEXTS_HUE']); + this.setStyle('text_blocks'); this.appendValueInput('TEXT') .setCheck('String') .appendField(new Blockly.FieldDropdown(OPERATORS), 'MODE'); @@ -375,7 +375,7 @@ Blockly.Blocks['text_print'] = { ], "previousStatement": null, "nextStatement": null, - "colour": Blockly.Msg['TEXTS_HUE'], + "style": "text_blocks", "tooltip": Blockly.Msg['TEXT_PRINT_TOOLTIP'], "helpUrl": Blockly.Msg['TEXT_PRINT_HELPURL'] }); @@ -393,7 +393,7 @@ Blockly.Blocks['text_prompt_ext'] = { [Blockly.Msg['TEXT_PROMPT_TYPE_NUMBER'], 'NUMBER'] ]; this.setHelpUrl(Blockly.Msg['TEXT_PROMPT_HELPURL']); - this.setColour(Blockly.Msg['TEXTS_HUE']); + this.setStyle('text_blocks'); // Assign 'this' to a variable for use in the closures below. var thisBlock = this; var dropdown = new Blockly.FieldDropdown(TYPES, function(newOp) { @@ -453,7 +453,7 @@ Blockly.Blocks['text_prompt'] = { // Assign 'this' to a variable for use in the closures below. var thisBlock = this; this.setHelpUrl(Blockly.Msg['TEXT_PROMPT_HELPURL']); - this.setColour(Blockly.Msg['TEXTS_HUE']); + this.setStyle('text_blocks'); var dropdown = new Blockly.FieldDropdown(TYPES, function(newOp) { thisBlock.updateType_(newOp); }); @@ -496,7 +496,7 @@ Blockly.Blocks['text_count'] = { ], "output": "Number", "inputsInline": true, - "colour": Blockly.Msg['TEXTS_HUE'], + "style": "text_blocks", "tooltip": Blockly.Msg['TEXT_COUNT_TOOLTIP'], "helpUrl": Blockly.Msg['TEXT_COUNT_HELPURL'] }); @@ -530,7 +530,7 @@ Blockly.Blocks['text_replace'] = { ], "output": "String", "inputsInline": true, - "colour": Blockly.Msg['TEXTS_HUE'], + "style": "text_blocks", "tooltip": Blockly.Msg['TEXT_REPLACE_TOOLTIP'], "helpUrl": Blockly.Msg['TEXT_REPLACE_HELPURL'] }); @@ -554,7 +554,7 @@ Blockly.Blocks['text_reverse'] = { ], "output": "String", "inputsInline": true, - "colour": Blockly.Msg['TEXTS_HUE'], + "style": "text_blocks", "tooltip": Blockly.Msg['TEXT_REVERSE_TOOLTIP'], "helpUrl": Blockly.Msg['TEXT_REVERSE_HELPURL'] }); diff --git a/blocks/variables.js b/blocks/variables.js index 0d8e0d950..4ea1d1b07 100644 --- a/blocks/variables.js +++ b/blocks/variables.js @@ -55,7 +55,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT } ], "output": null, - "colour": "%{BKY_VARIABLES_HUE}", + "style": "variable_blocks", "helpUrl": "%{BKY_VARIABLES_GET_HELPURL}", "tooltip": "%{BKY_VARIABLES_GET_TOOLTIP}", "extensions": ["contextMenu_variableSetterGetter"] @@ -77,7 +77,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT ], "previousStatement": null, "nextStatement": null, - "colour": "%{BKY_VARIABLES_HUE}", + "style": "variable_blocks", "tooltip": "%{BKY_VARIABLES_SET_TOOLTIP}", "helpUrl": "%{BKY_VARIABLES_SET_HELPURL}", "extensions": ["contextMenu_variableSetterGetter"] diff --git a/blocks/variables_dynamic.js b/blocks/variables_dynamic.js index fe30b8f22..03b34c2cb 100644 --- a/blocks/variables_dynamic.js +++ b/blocks/variables_dynamic.js @@ -52,7 +52,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT "variable": "%{BKY_VARIABLES_DEFAULT_NAME}" }], "output": null, - "colour": "%{BKY_VARIABLES_DYNAMIC_HUE}", + "style": "variable_dynamic_blocks", "helpUrl": "%{BKY_VARIABLES_GET_HELPURL}", "tooltip": "%{BKY_VARIABLES_GET_TOOLTIP}", "extensions": ["contextMenu_variableDynamicSetterGetter"] @@ -73,7 +73,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT ], "previousStatement": null, "nextStatement": null, - "colour": "%{BKY_VARIABLES_DYNAMIC_HUE}", + "style": "variable_dynamic_blocks", "tooltip": "%{BKY_VARIABLES_SET_TOOLTIP}", "helpUrl": "%{BKY_VARIABLES_SET_HELPURL}", "extensions": ["contextMenu_variableDynamicSetterGetter"] diff --git a/core/block.js b/core/block.js index c5cea853b..a672248eb 100644 --- a/core/block.js +++ b/core/block.js @@ -152,6 +152,12 @@ Blockly.Block = function(workspace, prototypeName, opt_id) { */ this.isInsertionMarker_ = false; + /** + * Name of the type of hat. + * @type {string|undefined} + */ + this.hat = undefined; + // Copy the type-specific functions and data from the prototype. if (prototypeName) { /** @type {string} */ @@ -216,6 +222,14 @@ Blockly.Block.obtain = function(workspace, prototypeName) { */ Blockly.Block.prototype.data = null; +/** + * Colour of the block as HSV hue value (0-360) + * This may be null if the block colour was not set via a hue number. + * @type {?number} + * @private + */ +Blockly.Block.prototype.hue_ = null; + /** * Colour of the block in '#RRGGBB' format. * @type {string} @@ -224,11 +238,28 @@ Blockly.Block.prototype.data = null; Blockly.Block.prototype.colour_ = '#000000'; /** - * Colour of the block as HSV hue value (0-360) - * @type {?number} + * Secondary colour of the block. + * Colour of shadow blocks. + * @type {?string} * @private - */ -Blockly.Block.prototype.hue_ = null; + */ +Blockly.Block.prototype.secondaryColour_ = null; + +/** + * Tertiary colour of the block. + * Colour of the block's border. + * @type {?string} + * @private + */ +Blockly.Block.prototype.tertiaryColour_ = null; + +/** + * Name of the block style. + * @type {?string} + * @private + */ +Blockly.Block.prototype.styleName_ = null; + /** * Dispose of this block. @@ -824,6 +855,30 @@ Blockly.Block.prototype.getColour = function() { return this.colour_; }; +/** + * Get the secondary colour of a block. + * @return {?string} #RRGGBB string. + */ +Blockly.Block.prototype.getSecondaryColour = function() { + return this.secondaryColour_; +}; + +/** + * Get the tertiary colour of a block. + * @return {?string} #RRGGBB string. + */ +Blockly.Block.prototype.getTertiaryColour = function() { + return this.tertiaryColour_; +}; + +/** + * Get the name of the block style. + * @return {?string} Name of the block style. + */ +Blockly.Block.prototype.getStyleName = function() { + return this.styleName_; +}; + /** * Get the HSV hue value of a block. Null if hue not set. * @return {?number} Hue value (0-360) @@ -859,6 +914,32 @@ Blockly.Block.prototype.setColour = function(colour) { } }; +/** + * Set the style and colour values of a block. + * @param {string} blockStyleName Name of the block style + * @throws {Error} if the block style does not exist. + */ +Blockly.Block.prototype.setStyle = function(blockStyleName) { + var theme = Blockly.getTheme(); + if (!theme) { + throw Error('Trying to set block style to ' + blockStyleName + + ' before theme was defined via Blockly.setTheme().'); + } + var blockStyle = theme.getBlockStyle(blockStyleName); + this.styleName_ = blockStyleName; + + if (blockStyle) { + this.secondaryColour_ = blockStyle.secondaryColour; + this.tertiaryColour_ = blockStyle.tertiaryColour; + this.hat = blockStyle.hat; + // Set colour will trigger an updateColour() on a block_svg + this.setColour(blockStyle.primaryColour); + } + else { + throw Error('Invalid style name: ' + blockStyleName); + } +}; + /** * Sets a callback function to use whenever the block's parent workspace * changes, replacing any prior onchange handler. This is usually only called @@ -1264,7 +1345,20 @@ Blockly.Block.prototype.jsonInit = function(json) { } // Set basic properties of block. - this.jsonInitColour_(json, warningPrefix); + // Makes styles backward compatible with old way of defining hat style. + if (json['style'] && json['style'].hat) { + this.hat = json['style'].hat; + //Must set to null so it doesn't error when checking for style and colour. + json['style'] = null; + } + + if (json['style'] && json['colour']) { + throw Error(warningPrefix + 'Must not have both a colour and a style.'); + } else if (json['style']) { + this.jsonInitStyle_(json, warningPrefix); + } else { + this.jsonInitColour_(json, warningPrefix); + } // Interpolate the message blocks. var i = 0; @@ -1344,6 +1438,21 @@ Blockly.Block.prototype.jsonInitColour_ = function(json, warningPrefix) { } }; +/** + * Initialize the style of this block from the JSON description. + * @param {!Object} json Structured data describing the block. + * @param {string} warningPrefix Warning prefix string identifying block. + * @private + */ +Blockly.Block.prototype.jsonInitStyle_ = function(json, warningPrefix) { + var blockStyleName = json['style']; + try { + this.setStyle(blockStyleName); + } catch (styleError) { + console.warn(warningPrefix + 'Style does not exist: ', blockStyleName); + } +}; + /** * Add key/values from mixinObj to this block object. By default, this method * will check that the keys in mixinObj will not overwrite existing values in diff --git a/core/block_render_svg.js b/core/block_render_svg.js index be058b91a..6275d1aec 100644 --- a/core/block_render_svg.js +++ b/core/block_render_svg.js @@ -567,6 +567,9 @@ Blockly.BlockSvg.prototype.renderDraw_ = function(iconWidth, inputRows) { this.squareTopLeftCorner_ = true; this.squareBottomLeftCorner_ = true; } else { + var renderCap = typeof this.hat !== undefined ? this.hat === 'cap' : + Blockly.BlockSvg.START_HAT; + this.squareTopLeftCorner_ = false; this.squareBottomLeftCorner_ = false; // If this block is in the middle of a stack, square the corners. @@ -575,7 +578,7 @@ Blockly.BlockSvg.prototype.renderDraw_ = function(iconWidth, inputRows) { if (prevBlock && prevBlock.getNextBlock() == this) { this.squareTopLeftCorner_ = true; } - } else if (Blockly.BlockSvg.START_HAT) { + } else if (renderCap) { // No output or previous connection. this.squareTopLeftCorner_ = true; this.startHat_ = true; diff --git a/core/block_svg.js b/core/block_svg.js index 7a2c7dfa5..8df2dbf03 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -935,18 +935,14 @@ Blockly.BlockSvg.prototype.updateColour = function() { return; } var hexColour = this.getColour(); + var secondaryColour = this.getSecondaryColour(); + var tertiaryColour = this.getTertiaryColour(); var rgb = goog.color.hexToRgb(hexColour); + if (this.isShadow()) { - rgb = goog.color.lighten(rgb, 0.6); - hexColour = goog.color.rgbArrayToHex(rgb); - this.svgPathLight_.style.display = 'none'; - this.svgPathDark_.setAttribute('fill', hexColour); + hexColour = this.setShadowColour_(rgb, secondaryColour); } else { - this.svgPathLight_.style.display = ''; - var hexLight = goog.color.rgbArrayToHex(goog.color.lighten(rgb, 0.3)); - var hexDark = goog.color.rgbArrayToHex(goog.color.darken(rgb, 0.2)); - this.svgPathLight_.setAttribute('stroke', hexLight); - this.svgPathDark_.setAttribute('fill', hexDark); + this.setBorderColour_(rgb, tertiaryColour); } this.svgPath_.setAttribute('fill', hexColour); @@ -964,6 +960,51 @@ Blockly.BlockSvg.prototype.updateColour = function() { } }; +/** + * Sets the colour of the border. + * Removes the light and dark paths if a tertiary colour is defined. + * @param {!string} rgb Colour of the block. + * @param {?string} tertiaryColour Colour of the border. + */ +Blockly.BlockSvg.prototype.setBorderColour_ = function(rgb, tertiaryColour) { + if (tertiaryColour) { + this.svgPathLight_.setAttribute('stroke', 'none'); + this.svgPathDark_.setAttribute('fill', 'none'); + this.svgPath_.setAttribute('stroke', tertiaryColour); + } else { + this.svgPathLight_.style.display = ''; + var hexLight = goog.color.rgbArrayToHex(goog.color.lighten(rgb, 0.3)); + var hexDark = goog.color.rgbArrayToHex(goog.color.darken(rgb, 0.2)); + this.svgPathLight_.setAttribute('stroke', hexLight); + this.svgPathDark_.setAttribute('fill', hexDark); + this.svgPath_.setAttribute('stroke', 'none'); + + } +}; + +/** + * Sets the colour of shadow blocks. + * @param {!string} rgb Primary colour of the block. + * @param {?string} secondaryColour Colour for shadow block. + * @return {!string} hexColour The background color of the block. + */ +Blockly.BlockSvg.prototype.setShadowColour_ = function( + rgb, secondaryColour) { + var hexColour; + if (secondaryColour) { + this.svgPathLight_.style.display = 'none'; + this.svgPathDark_.style.display = 'none'; + this.svgPath_.setAttribute('fill', secondaryColour); + hexColour = secondaryColour; + } else { + rgb = goog.color.lighten(rgb, 0.6); + hexColour = goog.color.rgbArrayToHex(rgb); + this.svgPathLight_.style.display = 'none'; + this.svgPathDark_.setAttribute('fill', hexColour); + } + return hexColour; +}; + /** * Enable or disable a block. */ diff --git a/core/blockly.js b/core/blockly.js index f17be4d61..5f2a71908 100644 --- a/core/blockly.js +++ b/core/blockly.js @@ -112,6 +112,13 @@ Blockly.clipboardTypeCounts_ = null; */ Blockly.cache3dSupported_ = null; +/** + * Holds all Blockly style attributes. + * @type {?Blockly.Theme} + * @private + */ +Blockly.theme_ = null; + /** * Convert a hue (HSV model) into an RGB hex triplet. * @param {number} hue Hue on a colour wheel (0-360). @@ -673,6 +680,64 @@ Blockly.checkBlockColourConstant_ = function( } }; + +/** + * Sets the theme for blockly and refreshes all blocks in the toolbox and workspace. + * @param {Blockly.Theme} theme Theme for blockly. + */ +Blockly.setTheme = function(theme) { + this.theme_ = theme; + var ws = Blockly.getMainWorkspace(); + + //update all blocks in workspace that have a style name + this.updateBlockStyles_(ws.getAllBlocks().filter( + function(block){ + return block.getStyleName() !== undefined; + } + )); + + //update blocks in the flyout + if (!ws.toolbox_ && ws.flyout_ && ws.flyout_.workspace_) { + this.updateBlockStyles_(ws.flyout_.workspace_.getAllBlocks()); + } else { + ws.refreshToolboxSelection(); + } + + //update colours on the categories + if (ws.toolbox_) { + ws.toolbox_.updateColourFromTheme(); + } + + var event = new Blockly.Events.Ui(null, 'themeChanged'); + event.workspaceId = ws.id; + Blockly.Events.fire(event); +}; + +/** + * Updates all the blocks with new style. + * @param {!Array.} blocks List of blocks to update the style on. + * @private + */ +Blockly.updateBlockStyles_ = function(blocks) { + for (var i = 0; i < blocks.length; i++) { + var block = blocks[i]; + var blockStyleName = block.getStyleName(); + + block.setStyle(blockStyleName); + if (block.mutator) { + block.mutator.updateBlockStyle(blockStyleName); + } + } +}; + +/** + * Gets the theme. + * @return {?Blockly.Theme} theme Theme for blockly. + */ +Blockly.getTheme = function() { + return this.theme_; +}; + // IE9 does not have a console. Create a stub to stop errors. if (!goog.global['console']) { goog.global['console'] = { diff --git a/core/inject.js b/core/inject.js index 921a15753..2be8fff63 100644 --- a/core/inject.js +++ b/core/inject.js @@ -69,10 +69,13 @@ Blockly.inject = function(container, opt_options) { var workspace = Blockly.createMainWorkspace_(svg, options, blockDragSurface, workspaceDragSurface); + Blockly.setTheme(options.theme); + Blockly.init_(workspace); Blockly.mainWorkspace = workspace; Blockly.svgResize(workspace); + return workspace; }; diff --git a/core/mutator.js b/core/mutator.js index b1367749c..004be6996 100644 --- a/core/mutator.js +++ b/core/mutator.js @@ -397,6 +397,28 @@ Blockly.Mutator.prototype.dispose = function() { Blockly.Icon.prototype.dispose.call(this); }; +/** + * Update the styles on all blocks in the mutator. + * @public + */ +Blockly.Mutator.prototype.updateBlockStyle = function() { + var ws = this.workspace_; + + if (ws && ws.getAllBlocks()){ + var workspaceBlocks = ws.getAllBlocks(); + for (var i = 0; i < workspaceBlocks.length; i++) { + var block = workspaceBlocks[i]; + block.setStyle(block.getStyleName()); + } + + var flyoutBlocks = ws.flyout_.workspace_.getAllBlocks(); + for (var i = 0; i < flyoutBlocks.length; i++) { + var block = flyoutBlocks[i]; + block.setStyle(block.getStyleName()); + } + } +}; + /** * Reconnect an block to a mutated input. * @param {Blockly.Connection} connectionChild Connection on child block. diff --git a/core/options.js b/core/options.js index 0bbf36889..9dd209a02 100644 --- a/core/options.js +++ b/core/options.js @@ -119,6 +119,10 @@ Blockly.Options = function(options) { } else { var oneBasedIndex = !!options['oneBasedIndex']; } + var theme = options['theme']; + if (theme === undefined) { + theme = Blockly.Themes.Classic; + } this.RTL = rtl; this.oneBasedIndex = oneBasedIndex; @@ -140,6 +144,7 @@ Blockly.Options = function(options) { this.gridOptions = Blockly.Options.parseGridOptions_(options); this.zoomOptions = Blockly.Options.parseZoomOptions_(options); this.toolboxPosition = toolboxPosition; + this.theme = theme; }; /** diff --git a/core/theme.js b/core/theme.js new file mode 100644 index 000000000..993e59bc4 --- /dev/null +++ b/core/theme.js @@ -0,0 +1,93 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2018 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview The class representing a theme. + */ +'use strict'; + +goog.provide('Blockly.Theme'); +/** + * Class for a theme. + * @param {?Object.} blockStyles A map from style + * names (strings) to objects with style attributes relating to blocks. + * @param {?Object.} categoryStyles A map from style + * names (strings) to objects with style attributes relating to categories. + * @constructor + */ +Blockly.Theme = function(blockStyles, categoryStyles) { + this.blockStyles_ = blockStyles; + this.categoryStyles_ = categoryStyles; +}; + +/** + * Overrides or adds all values from blockStyles to blockStyles_ + * @param {Object.} blockStyles List of + * block styles. + */ +Blockly.Theme.prototype.setAllBlockStyles = function(blockStyles) { + for (var key in blockStyles) { + this.setBlockStyle(key, blockStyles[key]); + } +}; + +/** + * Gets a list of all the block style names. + * @return{Array.} styleName List of blockstyle names. + */ +Blockly.Theme.prototype.getAllBlockStyles = function() { + return this.blockStyles_; +}; + +/** + * Gets the BlockStyle for the given block style name. + * @param{String} blockStyleName The name of the block style. + * @return {Blockly.BlockStyle} The style with the block style name. + */ +Blockly.Theme.prototype.getBlockStyle = function(blockStyleName) { + return this.blockStyles_[blockStyleName]; +}; + +/** + * Overrides or adds a style to the blockStyles map. + * @param{String} blockStyleName The name of the block style. + * @param{Blockly.BlockStyle} blockStyle The block style +*/ +Blockly.Theme.prototype.setBlockStyle = function(blockStyleName, blockStyle) { + this.blockStyles_[blockStyleName] = blockStyle; +}; + +/** + * Gets the CategoryStyle for the given category style name. + * @param{String} categoryStyleName The name of the block style. + * @return {Blockly.CategoryStyle} The style with the block style name. + */ +Blockly.Theme.prototype.getCategoryStyle = function(categoryStyleName) { + return this.categoryStyles_[categoryStyleName]; +}; + +/** + * Overrides or adds a style to the categoryStyles map. + * @param{String} categoryStyleName The name of the category style. + * @param{Blockly.CategoryStyle} categoryStyle The category style +*/ +Blockly.Theme.prototype.setCategoryStyle = function(categoryStyleName, categoryStyle) { + this.categoryStyles_[categoryStyleName] = categoryStyle; +}; diff --git a/core/toolbox.js b/core/toolbox.js index 685b9be0e..8c49c360c 100644 --- a/core/toolbox.js +++ b/core/toolbox.js @@ -332,25 +332,22 @@ Blockly.Toolbox.prototype.syncTrees_ = function(treeIn, treeOut, pathToMedia) { openNode = newOpenNode; } } - // Decode the colour for any potential message references - // (eg. `%{BKY_MATH_HUE}`). - var colour = Blockly.utils.replaceMessageReferences( - childIn.getAttribute('colour')); - if (colour === null || colour === '') { - // No attribute. No colour. - childOut.hexColour = ''; - } else if (/^#[0-9a-fA-F]{6}$/.test(colour)) { - childOut.hexColour = colour; - this.hasColours_ = true; - } else if (typeof colour === 'number' || - (typeof colour === 'string' && !isNaN(Number(colour)))) { - childOut.hexColour = Blockly.hueToRgb(Number(colour)); - this.hasColours_ = true; - } else { + + var styleName = childIn.getAttribute('style'); + var colour = childIn.getAttribute('colour'); + + if (colour && styleName) { childOut.hexColour = ''; console.warn('Toolbox category "' + categoryName + - '" has unrecognized colour attribute: ' + colour); + '" can not have both a style and a colour'); } + else if (styleName) { + this.setColourFromStyle_(styleName, childOut, categoryName); + } + else { + this.setColour_(colour, childOut, categoryName); + } + if (childIn.getAttribute('expanded') == 'true') { if (childOut.blocks.length) { // This is a category that directly contains blocks. @@ -395,6 +392,103 @@ Blockly.Toolbox.prototype.syncTrees_ = function(treeIn, treeOut, pathToMedia) { return openNode; }; +/** + * Sets the colour on the category. + * @param {number|string} colourValue HSV hue value (0 to 360), #RRGGBB string, + * or a message reference string pointing to one of those two values. + * @param {Blockly.Toolbox.TreeNode} childOut The child to set the hexColour on. + * @param {string} categoryName Name of the toolbox category. + * @private + */ +Blockly.Toolbox.prototype.setColour_ = function(colourValue, childOut, categoryName){ + // Decode the colour for any potential message references + // (eg. `%{BKY_MATH_HUE}`). + var colour = Blockly.utils.replaceMessageReferences(colourValue); + if (colour === null || colour === '') { + // No attribute. No colour. + childOut.hexColour = ''; + } else if (/^#[0-9a-fA-F]{6}$/.test(colour)) { + childOut.hexColour = colour; + this.hasColours_ = true; + } else if (typeof colour === 'number' || + (typeof colour === 'string' && !isNaN(Number(colour)))) { + childOut.hexColour = Blockly.hueToRgb(Number(colour)); + this.hasColours_ = true; + } else { + childOut.hexColour = ''; + console.warn('Toolbox category "' + categoryName + + '" has unrecognized colour attribute: ' + colour); + } +}; + +/** + * Retrieves and sets the colour for the category using the style name. + * The category colour is set from the colour style attribute. + * @param {string} styleName Name of the style. + * @param {!Blockly.Toolbox.TreeNode} childOut The child to set the hexColour on. + * @param {string} categoryName Name of the toolbox category. + */ +Blockly.Toolbox.prototype.setColourFromStyle_ = function( + styleName, childOut, categoryName){ + childOut.styleName = styleName; + if (styleName && Blockly.getTheme()) { + var style = Blockly.getTheme().getCategoryStyle(styleName); + if (style && style.colour) { + this.setColour_(style.colour, childOut, categoryName); + } + else { + console.warn('Style "' + styleName + '" must exist and contain a colour value'); + } + } +}; + +/** + * Recursively updates all the category colours using the category style name. + * @param {Blockly.Toolbox.TreeNode=} opt_tree Starting point of tree. + * Defaults to the root node. + * @private + */ +Blockly.Toolbox.prototype.updateColourFromTheme_ = function(opt_tree) { + var tree = opt_tree || this.tree_; + if (tree) { + var children = tree.getChildren(false); + for (var i = 0, child; child = children[i]; i++) { + if (child.styleName) { + this.setColourFromStyle_(child.styleName, child, ''); + this.addColour_(); + } + this.updateColourFromTheme_(child); + } + } +}; + +/** + * Updates the category colours and background colour of selected categories. + */ +Blockly.Toolbox.prototype.updateColourFromTheme = function() { + var tree = this.tree_; + if (tree) { + this.updateColourFromTheme_(tree); + this.updateSelectedItemColour_(tree); + } +}; + +/** + * Updates the background colour of the selected category. + * @param {!Blockly.Toolbox.TreeNode} tree Starting point of tree. + * Defaults to the root node. + * @private + */ +Blockly.Toolbox.prototype.updateSelectedItemColour_ = function(tree) { + var selectedItem = tree.selectedItem_; + if (selectedItem) { + var hexColour = selectedItem.hexColour || '#57e'; + selectedItem.getRowElement().style.backgroundColor = hexColour; + tree.toolbox_.addColour_(selectedItem); + } +}; + + /** * Recursively add colours to this toolbox. * @param {Blockly.Toolbox.TreeNode=} opt_tree Starting point of tree. diff --git a/tests/blocks/test_blocks.js b/tests/blocks/test_blocks.js index 092f2d3a6..d92193452 100644 --- a/tests/blocks/test_blocks.js +++ b/tests/blocks/test_blocks.js @@ -30,7 +30,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT "message0": "value to stack", "nextStatement": null, "output": null, - "colour": 230 + "style": "math_blocks" }, { "type": "value_to_statement", @@ -42,7 +42,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT } ], "output": null, - "colour": 230 + "style": "math_blocks" }, { "type": "limit_instances", @@ -58,7 +58,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT ], "previousStatement": null, "nextStatement": null, - "colour": 230, + "style": "math_blocks", }, { "type": "example_dropdown_long", @@ -199,7 +199,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT "text": "0" } ], - "colour": 230, + "style": "math_blocks", "output": "Number", "tooltip": "A number." }, @@ -214,7 +214,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT "text": "0" } ], - "colour": 230, + "style": "math_blocks", "output": "Number", "tooltip": "An integer." }, @@ -229,7 +229,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT "text": "0" } ], - "colour": 230, + "style": "math_blocks", "output": "Number", "tooltip": "A dollar amount." }, @@ -246,7 +246,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT "text": "0" } ], - "colour": 230, + "style": "math_blocks", "output": "Note", "tooltip": "A midi note." }, @@ -262,7 +262,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT "alt": "*" } ], - "colour": 160 + "style": "text_blocks" }, { "type": "image_small", @@ -276,7 +276,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT "alt": "*" } ], - "colour": 160 + "style": "text_blocks" }, { "type": "image_large", @@ -290,7 +290,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT "alt": "*" } ], - "colour": 160 + "style": "text_blocks" }, { "type": "image_fliprtl", @@ -319,7 +319,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT "alt": "*" } ], - "colour": 160 + "style": "text_blocks" }, { "type": "test_with_lots_of_network_icons", @@ -444,16 +444,13 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT "alt": "H" } ], - "colour": 160 + "style": "text_blocks" }, { "type": "styled_event_cap", "message0": "Hat block (event)", "nextStatement": null, - "colour": 330, - "style": { - "hat": "cap" - } + "style": "hat_blocks" }, { "type": "block_colour_hex1", diff --git a/tests/generators/index.html b/tests/generators/index.html index dbff605dc..a3bb602f7 100644 --- a/tests/generators/index.html +++ b/tests/generators/index.html @@ -59,6 +59,8 @@ + + diff --git a/tests/jsunit/block_test.js b/tests/jsunit/block_test.js index d7efed435..28d85281c 100644 --- a/tests/jsunit/block_test.js +++ b/tests/jsunit/block_test.js @@ -22,9 +22,14 @@ * @fileoverview Tests for Blockly.Block * @author fenichel@google.com (Rachel Fenichel) */ + +goog.require('goog.testing'); +goog.require('goog.testing.MockControl'); + 'use strict'; var workspace; +var mockControl_; function defineTestBlocks() { Blockly.defineBlocksWithJsonArray([{ @@ -53,6 +58,7 @@ function undefineTestBlocks() { function blockTest_setUp() { defineTestBlocks(); + mockControl_ = new goog.testing.MockControl(); workspace = new Blockly.Workspace(); } @@ -252,3 +258,43 @@ function test_block_row_unplug_multi_inputs_child() { blockTest_tearDown(); } } + +function test_set_style() { + blockTest_setUp(); + var styleStub = { + getBlockStyle: function() { + return{ + "primaryColour": "#FFFFFF", + "secondaryColour":"#AABBCC", + "tertiaryColour":"#DDEEFF" + } + } + }; + setUpMockMethod(mockControl_, Blockly, 'getTheme', null, [styleStub]); + var blockA = workspace.newBlock('row_block'); + blockA.setStyle('styleOne'); + + assertEquals(blockA.colour_, '#FFFFFF'); + assertEquals(blockA.secondaryColour_, '#AABBCC'); + assertEquals(blockA.tertiaryColour_, '#DDEEFF'); + + blockTest_tearDown(); +} + +function test_set_style_throw_exception() { + blockTest_setUp(); + var styleStub = { + getBlockStyle: function() { + return null; + } + }; + setUpMockMethod(mockControl_, Blockly, 'getTheme', null, [styleStub]); + var blockA = workspace.newBlock('row_block'); + try { + blockA.setStyle('styleOne'); + }catch(error) { + assertEquals(error.message, "Invalid style name: styleOne"); + }finally { + blockTest_tearDown(); + } +} diff --git a/tests/jsunit/index.html b/tests/jsunit/index.html index c84375f88..18ee34d32 100644 --- a/tests/jsunit/index.html +++ b/tests/jsunit/index.html @@ -29,6 +29,7 @@ + diff --git a/tests/jsunit/theme_test.js b/tests/jsunit/theme_test.js new file mode 100644 index 000000000..782b8be90 --- /dev/null +++ b/tests/jsunit/theme_test.js @@ -0,0 +1,154 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2018 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + /** + * @fileoverview Tests for Blockly.Style + * @author aschmiedt@google.com (Abby Schmiedt) + */ +'use strict'; + +function defineThemeTestBlocks() { + Blockly.defineBlocksWithJsonArray([{ + "type": "stack_block", + "message0": "", + "previousStatement": null, + "nextStatement": null + }, + { + "type": "row_block", + "message0": "%1", + "args0": [ + { + "type": "input_value", + "name": "INPUT" + } + ], + "output": null + }]); +}; + +function undefineThemeTestBlocks() { + delete Blockly.Blocks['stack_block']; + delete Blockly.Blocks['row_block']; +} + + +function createBlockStyles() { + return { + "styleOne": { + "primaryColour": "colour1", + "secondaryColour":"colour2", + "tertiaryColour":"colour3" + } + }; +} + +function createMultipleBlockStyles() { + return { + "styleOne": { + "primaryColour": "colour1", + "secondaryColour":"colour2", + "tertiaryColour":"colour3" + }, + "styleTwo": { + "primaryColour": "colour1", + "secondaryColour":"colour2", + "tertiaryColour":"colour3" + } + }; +} + +function test_setAllBlockStyles() { + var theme = new Blockly.Theme(createBlockStyles()); + stringifyAndCompare(createBlockStyles(), theme.blockStyles_); + theme.setAllBlockStyles(createMultipleBlockStyles()); + stringifyAndCompare(createMultipleBlockStyles(), theme.blockStyles_); +} + +function test_getAllBlockStyles() { + var theme = new Blockly.Theme(createMultipleBlockStyles()); + var allBlocks = theme.getAllBlockStyles(); + stringifyAndCompare(createMultipleBlockStyles(), allBlocks); + +} + +function test_getBlockStyles() { + var theme = new Blockly.Theme(createBlockStyles()); + var blockStyle = theme.getBlockStyle('styleOne'); + + stringifyAndCompare(blockStyle, createBlockStyles().styleOne); +} + +function test_setBlockStyleUpdate() { + var theme = new Blockly.Theme(createBlockStyles()); + var blockStyle = createBlockStyles(); + blockStyle.styleOne.primaryColour = 'somethingElse'; + + theme.setBlockStyle('styleOne', blockStyle.styleOne); + + stringifyAndCompare(theme.blockStyles_, blockStyle); +} + +function test_setBlockStyleAdd() { + var theme = new Blockly.Theme(createBlockStyles()); + var blockStyle = createMultipleBlockStyles(); + + theme.setBlockStyle('styleTwo', blockStyle.styleTwo); + + stringifyAndCompare(theme.blockStyles_, blockStyle); +} + +function test_setTheme() { + defineThemeTestBlocks(); + var blockStyles = createBlockStyles(); + var workspace = new Blockly.WorkspaceSvg({}); + var blockA = workspace.newBlock('stack_block'); + var blocks = [blockA]; + + blockA.setStyle = function() {this.styleName_ = 'styleTwo'}; + var callCount = 1; + workspace.refreshToolboxSelection = function() { + return ++callCount; + }; + blockA.styleName_ = 'styleOne'; + + setUpMockMethod(mockControl_, Blockly, 'getMainWorkspace', null, [workspace]); + + Blockly.setTheme(blockStyles); + + //Checks that the theme was set correctly on Blockly namespace + stringifyAndCompare(Blockly.getTheme(), blockStyles); + + //Checks that the setTheme function was called on the block + assertEquals(blockA.getStyleName(), 'styleTwo'); + + //check that the toolbox refreshed method was called + assertEquals(workspace.refreshToolboxSelection(), 3); + + assertEquals(Blockly.Events.FIRE_QUEUE_.pop().element, 'themeChanged'); + + undefineThemeTestBlocks(); +} + +function stringifyAndCompare(val1, val2) { + var stringVal1 = JSON.stringify(val1); + var stringVal2 = JSON.stringify(val2); + assertEquals(stringVal1, stringVal2); +} diff --git a/tests/multi_playground.html b/tests/multi_playground.html index 8177a87db..f6a74f589 100644 --- a/tests/multi_playground.html +++ b/tests/multi_playground.html @@ -13,6 +13,7 @@ + + + + +