diff --git a/src/editor/editor.ts b/src/editor/editor.ts index 7d0c21fc..d634a986 100644 --- a/src/editor/editor.ts +++ b/src/editor/editor.ts @@ -31,9 +31,9 @@ import * as eventHandler from '../blocks/mrc_event_handler'; import * as classMethodDef from '../blocks/mrc_class_method_def'; import * as mechanismComponentHolder from '../blocks/mrc_mechanism_component_holder'; //import { testAllBlocksInToolbox } from '../toolbox/toolbox_tests'; -import { getToolboxJSON } from '../toolbox/toolbox'; +import { applyExpandedCategories, getToolboxJSON } from '../toolbox/toolbox'; -const EMPTY_TOOLBOX: Blockly.utils.toolbox.ToolboxDefinition = { +const EMPTY_TOOLBOX: Blockly.utils.toolbox.ToolboxInfo = { kind: 'categoryToolbox', contents: [], }; @@ -58,7 +58,7 @@ export class Editor { private mechanismClassNameToModuleContent: {[mechanismClassName: string]: storageModuleContent.ModuleContent} = {}; private bindedOnChange: any = null; private shownPythonToolboxCategories: Set | null = null; - private toolbox: Blockly.utils.toolbox.ToolboxDefinition = EMPTY_TOOLBOX; + private toolbox: Blockly.utils.toolbox.ToolboxInfo = EMPTY_TOOLBOX; constructor( blocklyWorkspace: Blockly.WorkspaceSvg, @@ -199,6 +199,10 @@ export class Editor { return; } const toolbox = getToolboxJSON(this.shownPythonToolboxCategories, this); + const previousToolbox = this.blocklyWorkspace.getToolbox(); + if (previousToolbox) { + applyExpandedCategories(previousToolbox, toolbox); + } if (toolbox != this.toolbox) { this.toolbox = toolbox; this.blocklyWorkspace.updateToolbox(toolbox); diff --git a/src/toolbox/toolbox.ts b/src/toolbox/toolbox.ts index 998d6bfa..a3a539af 100644 --- a/src/toolbox/toolbox.ts +++ b/src/toolbox/toolbox.ts @@ -8,9 +8,9 @@ import { getCategory as getEventCategory } from './event_category'; export function getToolboxJSON( shownPythonToolboxCategories: Set | null, - editor: Editor): Blockly.utils.toolbox.ToolboxDefinition { + editor: Editor): Blockly.utils.toolbox.ToolboxInfo { - const toolbox: Blockly.utils.toolbox.ToolboxDefinition = { + const toolbox: Blockly.utils.toolbox.ToolboxInfo = { kind: 'categoryToolbox', contents: [] }; @@ -34,3 +34,70 @@ export function getToolboxJSON( // This trouble is prevented by stringifying and parsing. return JSON.parse(JSON.stringify(toolbox)); } + +/** + * Collects the expanded/collapsed state of categories in the previous toolbox and applies them + * to the new toolbox. + */ +export function applyExpandedCategories( + previousToolbox: Blockly.IToolbox, + newToolbox: Blockly.utils.toolbox.ToolboxInfo) { + const expanded: {[categoryPath: string]: boolean} = {}; + recursivelyCollectExpandedCategories((previousToolbox as any).getToolboxItems(), expanded, '', null); + recursivelyApplyExpandedCategories(newToolbox.contents, expanded, ''); +} + +/** + * Collects the expanded/collapsed state for all collapsible items. + */ +function recursivelyCollectExpandedCategories( + items: Blockly.IToolboxItem[], + expanded: {[categoryPath: string]: boolean}, + parentPath: string, + parent: Blockly.IToolboxItem | null) { + items + .filter(item => item.getParent() == parent) + .filter(item => item.isCollapsible()) + .forEach(item => { + const collapsibleItem = item as Blockly.ICollapsibleToolboxItem; + const path = makePath(parentPath, collapsibleItem.getName()); + expanded[path] = collapsibleItem.isExpanded(); + recursivelyCollectExpandedCategories( + collapsibleItem.getChildToolboxItems(), expanded, path, item); + }); +} + +/** + * Applies previously collected expanded/collapsed state to the given toolbox categories. + */ +function recursivelyApplyExpandedCategories( + contents: toolboxItems.ContentsType[], + expanded: {[categoryPath: string]: boolean}, + parentPath: string) { + contents + .filter(item => item.kind === 'category') + .forEach(item => { + const category = item as toolboxItems.Category; + const path = makePath(parentPath, category.name); + if (path in expanded) { + if (expanded[path]) { + category.expanded = true; + } else { + delete category.expanded; + } + } + if (category.contents) { + recursivelyApplyExpandedCategories(category.contents, expanded, path); + } + }); +} + +/** + * Returns a string that combines the given parentPath with the given child. + */ +function makePath(parentPath: string, child: string) { + if (parentPath) { + return parentPath + '\u2705' + child; + } + return child; +} diff --git a/src/toolbox/toolbox_tests.ts b/src/toolbox/toolbox_tests.ts index 586676e7..9b0d7d1d 100644 --- a/src/toolbox/toolbox_tests.ts +++ b/src/toolbox/toolbox_tests.ts @@ -25,8 +25,8 @@ import * as toolboxItems from './items'; // Tests -export function testAllBlocksInToolbox(toolbox : Blockly.utils.toolbox.ToolboxDefinition) { - const contents = (toolbox as toolboxItems.Category).contents; +export function testAllBlocksInToolbox(toolbox : Blockly.utils.toolbox.ToolboxInfo) { + const contents = toolbox.contents; alert('Press OK to run tests on all blocks from the toolbox.'); const toolboxTestData = new ToolboxTestData(contents, () => { alert('Completed tests on all blocks in the toolbox. See console for any errors.');