diff --git a/src/containers/blocks.jsx b/src/containers/blocks.jsx index 471dc6cd08643341c290f66227b723ef28dbffb1..f3e46dfaf2bd9f88496cbaed2a29e9ce1ae62061 100644 --- a/src/containers/blocks.jsx +++ b/src/containers/blocks.jsx @@ -172,6 +172,11 @@ class Blocks extends React.Component { this.workspace.reportValue(data.id, data.value); } onWorkspaceUpdate (data) { + // When we change sprites, update the toolbox to have the new sprite's blocks + if (this.props.vm.editingTarget) { + this.props.updateToolboxState(makeToolboxXML(this.props.vm.editingTarget.id)); + } + if (this.props.vm.editingTarget && !this.state.workspaceMetrics[this.props.vm.editingTarget.id]) { this.onWorkspaceMetricsChange(); } @@ -179,6 +184,8 @@ class Blocks extends React.Component { // Remove and reattach the workspace listener (but allow flyout events) this.workspace.removeChangeListener(this.props.vm.blockListener); const dom = this.ScratchBlocks.Xml.textToDom(data.xml); + // @todo This line rerenders toolbox, and the change in the toolbox XML also rerenders the toolbox. + // We should only rerender the toolbox once. See https://github.com/LLK/scratch-gui/issues/901 this.ScratchBlocks.Xml.clearWorkspaceAndLoadFromXml(dom, this.workspace); this.workspace.addChangeListener(this.props.vm.blockListener); @@ -193,8 +200,8 @@ class Blocks extends React.Component { handleExtensionAdded (blocksInfo) { this.ScratchBlocks.defineBlocksWithJsonArray(blocksInfo.map(blockInfo => blockInfo.json)); const dynamicBlocksXML = this.props.vm.runtime.getBlocksXML(); - const toolboxXML = makeToolboxXML(dynamicBlocksXML); - this.props.onExtensionAdded(toolboxXML); + const toolboxXML = makeToolboxXML(this.props.vm.editingTarget.id, dynamicBlocksXML); + this.props.updateToolboxState(toolboxXML); } handleCategorySelected (categoryName) { this.workspace.toolbox_.setSelectedCategoryByName(categoryName); @@ -220,7 +227,7 @@ class Blocks extends React.Component { vm, isVisible, onActivateColorPicker, - onExtensionAdded, + updateToolboxState, onRequestCloseExtensionLibrary, toolboxXML, ...props @@ -257,7 +264,6 @@ Blocks.propTypes = { extensionLibraryVisible: PropTypes.bool, isVisible: PropTypes.bool, onActivateColorPicker: PropTypes.func, - onExtensionAdded: PropTypes.func, onRequestCloseExtensionLibrary: PropTypes.func, options: PropTypes.shape({ media: PropTypes.string, @@ -281,6 +287,7 @@ Blocks.propTypes = { comments: PropTypes.bool }), toolboxXML: PropTypes.string, + updateToolboxState: PropTypes.func, vm: PropTypes.instanceOf(VM).isRequired }; @@ -322,11 +329,11 @@ const mapStateToProps = state => ({ const mapDispatchToProps = dispatch => ({ onActivateColorPicker: callback => dispatch(activateColorPicker(callback)), - onExtensionAdded: toolboxXML => { - dispatch(updateToolbox(toolboxXML)); - }, onRequestCloseExtensionLibrary: () => { dispatch(closeExtensionLibrary()); + }, + updateToolboxState: toolboxXML => { + dispatch(updateToolbox(toolboxXML)); } }); diff --git a/src/lib/make-toolbox-xml.js b/src/lib/make-toolbox-xml.js index 315e1388c51656e87be18924c559fb506322dc6c..608cf92b3c307efd54e34ae6aa2d26a19e747f6c 100644 --- a/src/lib/make-toolbox-xml.js +++ b/src/lib/make-toolbox-xml.js @@ -2,7 +2,8 @@ const categorySeparator = '<sep gap="36"/>'; const blockSeparator = '<sep gap="36"/>'; // At default scale, about 28px -const motion = ` +const motion = function (targetId) { + return ` <category name="Motion" colour="#4C97FF" secondaryColour="#3373CC"> <block type="motion_movesteps"> <value name="STEPS"> @@ -120,14 +121,16 @@ const motion = ` ${blockSeparator} <block type="motion_setrotationstyle"/> ${blockSeparator} - <block id="xposition" type="motion_xposition"/> - <block id="yposition" type="motion_yposition"/> - <block id="direction" type="motion_direction"/> + <block id="${targetId}_xposition" type="motion_xposition"/> + <block id="${targetId}_yposition" type="motion_yposition"/> + <block id="${targetId}_direction" type="motion_direction"/> ${categorySeparator} </category> -`; + `; +}; -const looks = ` +const looks = function (targetId) { + return ` <category name="Looks" colour="#9966FF" secondaryColour="#774DCB"> <block type="looks_sayforsecs"> <value name="MESSAGE"> @@ -229,15 +232,17 @@ const looks = ` </value> </block> ${blockSeparator} - <block id="costumeorder" type="looks_costumeorder"/> + <block id="${targetId}_costumeorder" type="looks_costumeorder"/> <block id="backdroporder" type="looks_backdroporder"/> <block id="backdropname" type="looks_backdropname"/> - <block id="size" type="looks_size"/> + <block id="${targetId}_size" type="looks_size"/> ${categorySeparator} </category> -`; + `; +}; -const sound = ` +const sound = function () { + return ` <category name="Sound" colour="#D65CD6" secondaryColour="#BD42BD"> <block type="sound_play"> <value name="SOUND_MENU"> @@ -284,9 +289,11 @@ const sound = ` <block id="volume" type="sound_volume"/> ${categorySeparator} </category> -`; + `; +}; -const events = ` +const events = function () { + return ` <category name="Events" colour="#FFD500" secondaryColour="#CC9900"> <block type="event_whenflagclicked"/> <block type="event_whenkeypressed"> @@ -317,9 +324,11 @@ const events = ` </block> ${categorySeparator} </category> -`; + `; +}; -const control = ` +const control = function () { + return ` <category name="Control" colour="#FFAB19" secondaryColour="#CF8B17"> <block type="control_wait"> <value name="DURATION"> @@ -354,9 +363,11 @@ const control = ` <block type="control_delete_this_clone"/> ${categorySeparator} </category> -`; + `; +}; -const sensing = ` +const sensing = function () { + return ` <category name="Sensing" colour="#4CBFE6" secondaryColour="#2E8EB8"> <block type="sensing_touchingobject"> <value name="TOUCHINGOBJECTMENU"> @@ -422,9 +433,11 @@ const sensing = ` <block type="sensing_dayssince2000"/> ${categorySeparator} </category> -`; + `; +}; -const operators = ` +const operators = function () { + return ` <category name="Operators" colour="#40BF4A" secondaryColour="#389438"> <block type="operator_add"> <value name="NUM1"> @@ -602,33 +615,37 @@ const operators = ` </block> ${categorySeparator} </category> -`; + `; +}; -const data = ` +const data = function () { + return ` <category name="Data" colour="#FF8C1A" secondaryColour="#DB6E00" custom="VARIABLE"> </category> -`; + `; +}; const xmlOpen = '<xml style="display: none">'; const xmlClose = '</xml>'; /** + * @param {!string} targetId - The current editing target * @param {string?} categoriesXML - null for default toolbox, or an XML string with <category> elements. * @returns {string} - a ScratchBlocks-style XML document for the contents of the toolbox. */ -const makeToolboxXML = function (categoriesXML) { +const makeToolboxXML = function (targetId, categoriesXML) { const gap = [categorySeparator]; const everything = [ xmlOpen, - motion, gap, - looks, gap, - sound, gap, - events, gap, - control, gap, - sensing, gap, - operators, gap, - data + motion(targetId), gap, + looks(targetId), gap, + sound(targetId), gap, + events(targetId), gap, + control(targetId), gap, + sensing(targetId), gap, + operators(targetId), gap, + data(targetId) ]; if (categoriesXML) { diff --git a/src/reducers/toolbox.js b/src/reducers/toolbox.js index 4b06a807cac5e10ebb0d643ab06eea39e5b56b77..3f559884f0771545acc67eacf867adcc0274f994 100644 --- a/src/reducers/toolbox.js +++ b/src/reducers/toolbox.js @@ -1,9 +1,9 @@ const UPDATE_TOOLBOX = 'scratch-gui/toolbox/UPDATE_TOOLBOX'; - import makeToolboxXML from '../lib/make-toolbox-xml'; const initialState = { - toolboxXML: makeToolboxXML() + // @todo switch this to make the stage's XML + toolboxXML: makeToolboxXML('') }; const reducer = function (state, action) {