From 019082b795cc63cc9efa8d96e9be663607de0c30 Mon Sep 17 00:00:00 2001 From: Sean Lip Date: Thu, 16 Jun 2016 14:52:38 -0700 Subject: [PATCH] Fix tree focus issues. --- accessible/app.component.js | 7 +-- accessible/tree.service.js | 80 +++++++++++++++----------- accessible/workspace-tree.component.js | 38 +++++++----- accessible/workspace.component.js | 12 ++-- 4 files changed, 78 insertions(+), 59 deletions(-) diff --git a/accessible/app.component.js b/accessible/app.component.js index f81481fc4..5de03e575 100644 --- a/accessible/app.component.js +++ b/accessible/app.component.js @@ -63,10 +63,5 @@ blocklyApp.AppView = ng.core blocklyApp.UtilsService] }) .Class({ - constructor: [blocklyApp.TreeService, function(_treeService) { - this.treeService = _treeService; - }], - ngAfterViewInit: function() { - this.treeService.initTreeRegistry(); - } + constructor: [function() {}] }); diff --git a/accessible/tree.service.js b/accessible/tree.service.js index 3e54e054f..0b98687af 100644 --- a/accessible/tree.service.js +++ b/accessible/tree.service.js @@ -27,8 +27,6 @@ blocklyApp.TreeService = ng.core .Class({ constructor: function() { - // A list of all trees in the application. - this.treeRegistry = []; // Keeping track of the last key pressed. If the user presses // enter (to edit a text input or press a button), the keyboard // focus shifts to that element. In the next keystroke, if the user @@ -36,52 +34,66 @@ blocklyApp.TreeService = ng.core // to shift focus back to the tree as a whole. this.previousKey_ = null; }, - initTreeRegistry: function() { - this.treeRegistry = []; - this.treeRegistry.push(document.getElementById('blockly-toolbox-tree')); - // TODO(sll): Extend this to handle injected toolbar buttons. - this.treeRegistry.push(document.getElementById('clear-workspace')); - - // Focus on the toolbox tree. - this.treeRegistry[0].focus(); + getToolboxTreeNode_: function() { + return document.getElementById('blockly-toolbox-tree'); }, - isTreeInRegistry: function(treeId) { - return this.treeRegistry.some(function(tree) { + getWorkspaceTreeNodes_: function() { + // Returns a list of all the top-level workspace tree nodes on the page. + return Array.from(document.querySelectorAll('ol.blocklyWorkspaceTree')); + }, + getAllTreeNodes_: function() { + // Returns a list of all top-level tree nodes on the page. + var trees = []; + + trees.push(document.getElementById('blockly-toolbox-tree')); + // TODO(sll): Extend this to handle injected toolbar buttons. + if (blocklyApp.workspace.topBlocks_.length > 0) { + trees.push(document.getElementById('clear-workspace')); + } + + trees = trees.concat(this.getWorkspaceTreeNodes_()); + return trees; + }, + focusOnToolbox: function() { + this.getToolboxTreeNode_().focus(); + }, + isTopLevelWorkspaceTree: function(treeId) { + return this.getWorkspaceTreeNodes_().some(function(tree) { return tree.id == treeId; }); }, - addTreeToRegistry: function(tree) { - this.treeRegistry.push(tree); - }, - deleteTreeFromRegistry: function(treeId) { - // Shift focus to the next tree (if it exists), otherwise shift focus - // to the previous tree. - var movedToNextTree = this.goToNextTree(treeId); - if (!movedToNextTree) { - this.goToPreviousTree(treeId); - } - - // Delete the tree from the tree registry. - for (var i = 0; i < this.treeRegistry.length; i++) { - if (this.treeRegistry[i].id == treeId) { - this.treeRegistry.splice(i, 1); - break; + getNodeToFocusOnWhenTreeIsDeleted: function(deletedTreeId) { + // This returns the node to focus on after the deletion happens. + // We shift focus to the next tree (if it exists), otherwise we shift + // focus to the previous tree. + var workspaceTrees = this.getWorkspaceTreeNodes_(); + for (var i = 0; i < workspaceTrees.length; i++) { + if (workspaceTrees[i].id == deletedTreeId) { + if (i + 1 < workspaceTrees.length) { + return workspaceTrees[i + 1]; + } else if (i > 0) { + return workspaceTrees[i - 1]; + } } } + + return this.getToolboxTreeNode_(); }, goToNextTree: function(treeId) { - for (var i = 0; i < this.treeRegistry.length - 1; i++) { - if (this.treeRegistry[i].id == treeId) { - this.treeRegistry[i + 1].focus(); + var trees = this.getAllTreeNodes_(); + for (var i = 0; i < trees.length - 1; i++) { + if (trees[i].id == treeId) { + trees[i + 1].focus(); return true; } } return false; }, goToPreviousTree: function(treeId) { - for (var i = this.treeRegistry.length - 1; i > 0; i--) { - if (this.treeRegistry[i].id == treeId) { - this.treeRegistry[i - 1].focus(); + var trees = this.getAllTreeNodes_(); + for (var i = trees.length - 1; i > 0; i--) { + if (trees[i].id == treeId) { + trees[i - 1].focus(); return true; } } diff --git a/accessible/workspace-tree.component.js b/accessible/workspace-tree.component.js index a58b6e35e..4793f74b6 100644 --- a/accessible/workspace-tree.component.js +++ b/accessible/workspace-tree.component.js @@ -161,12 +161,12 @@ blocklyApp.WorkspaceTreeComponent = ng.core this.idMap['parentList'] = this.utilsService.generateUniqueId(); }, ngAfterViewInit: function() { - // If this is a top-level tree in the workspace, add it to the tree - // registry and set its active descendant. + // If this is a top-level tree in the workspace, set its active + // descendant. if (this.tree && - (!this.tree.id || !this.treeService.isTreeInRegistry(this.tree.id))) { + (!this.tree.id || + !this.treeService.isTopLevelWorkspaceTree(this.tree.id))) { this.tree.id = this.utilsService.generateUniqueId(); - this.treeService.addTreeToRegistry(this.tree); this.treeService.setActiveDesc( document.getElementById(this.idMap['parentList']), this.tree); @@ -183,18 +183,25 @@ blocklyApp.WorkspaceTreeComponent = ng.core }, cutToClipboard: function(block) { if (this.isTopLevelBlock(block)) { - this.treeService.deleteTreeFromRegistry(this.tree.id); + nextNodeToFocusOn = this.treeService.getNodeToFocusOnWhenTreeIsDeleted( + this.tree.id); + this.clipboardService.cut(block); + nextNodeToFocusOn.focus(); + } else { + // TODO(sll): Change the active descendant of the tree. + this.clipboardService.cut(block); } - - this.clipboardService.cut(block); }, deleteBlock: function(block, cutToClipboard) { if (this.isTopLevelBlock(block)) { - this.treeService.deleteTreeFromRegistry(this.tree.id); + nextNodeToFocusOn = this.treeService.getNodeToFocusOnWhenTreeIsDeleted( + this.tree.id); + block.dispose(true); + nextNodeToFocusOn.focus(); + } else { + // TODO(sll): Change the active descendant of the tree. + block.dispose(true); } - - // TODO(sll): Change the active descendant of the tree. - block.dispose(true); }, generateAriaLabelledByAttr: function(mainLabel, secondLabel, isDisabled) { return this.utilsService.generateAriaLabelledByAttr( @@ -210,9 +217,14 @@ blocklyApp.WorkspaceTreeComponent = ng.core this.clipboardService.pasteToMarkedConnection(block, false); if (this.isTopLevelBlock(block)) { - this.treeService.deleteTreeFromRegistry(this.tree.id); + nextNodeToFocusOn = this.treeService.getNodeToFocusOnWhenTreeIsDeleted( + this.tree.id); + block.dispose(true); + nextNodeToFocusOn.focus(); + } else { + // TODO(sll): Change the active descendant of the tree. + block.dispose(true); } - block.dispose(true); alert('Block moved to marked spot: ' + block.toString()); } diff --git a/accessible/workspace.component.js b/accessible/workspace.component.js index bbe308283..527ea2787 100644 --- a/accessible/workspace.component.js +++ b/accessible/workspace.component.js @@ -45,7 +45,7 @@ blocklyApp.WorkspaceComponent = ng.core
    @@ -71,14 +71,14 @@ blocklyApp.WorkspaceComponent = ng.core }], clearWorkspace: function() { this.workspace.clear(); - this.treeService.initTreeRegistry(); + this.treeService.focusOnToolbox(); }, - onWorkspaceToolbarKeypress: function(event) { + onWorkspaceToolbarKeypress: function(e) { this.treeService.onWorkspaceToolbarKeypress( - event, document.activeElement.id); + e, document.activeElement.id); }, - onKeypress: function(event, tree){ - this.treeService.onKeypress(event, tree); + onKeypress: function(e, tree) { + this.treeService.onKeypress(e, tree); }, isWorkspaceEmpty: function() { return !this.workspace.topBlocks_.length;