diff --git a/accessible/clipboard.service.js b/accessible/clipboard.service.js index eb8c0896d..035939104 100644 --- a/accessible/clipboard.service.js +++ b/accessible/clipboard.service.js @@ -111,5 +111,7 @@ blocklyApp.ClipboardService = ng.core Blockly.Msg.PASTED_BLOCK_TO_MARKED_SPOT_MSG + reconstitutedBlock.toString()); } + + this.markedConnection_ = null; } }); diff --git a/accessible/tree.service.js b/accessible/tree.service.js index d9c0784fa..2387de9ae 100644 --- a/accessible/tree.service.js +++ b/accessible/tree.service.js @@ -117,9 +117,10 @@ blocklyApp.TreeService = ng.core }, // Runs the given function while preserving the focus and active descendant // for the given tree. - runWhilePreservingFocus: function(func, treeId) { - var activeDescId = this.getActiveDescId(treeId); - this.unmarkActiveDesc_(activeDescId); + runWhilePreservingFocus: function(func, treeId, optionalNewActiveDescId) { + var oldDescId = this.getActiveDescId(treeId); + var newDescId = optionalNewActiveDescId || oldDescId; + this.unmarkActiveDesc_(oldDescId); func(); // The timeout is needed in order to give the DOM time to stabilize @@ -127,8 +128,8 @@ blocklyApp.TreeService = ng.core // pasteAbove(). var that = this; setTimeout(function() { - that.markActiveDesc_(activeDescId); - that.activeDescendantIds_[treeId] = activeDescId; + that.markActiveDesc_(newDescId); + that.activeDescendantIds_[treeId] = newDescId; document.getElementById(treeId).focus(); }, 0); }, @@ -153,11 +154,43 @@ blocklyApp.TreeService = ng.core isButtonOrFieldNode_: function(node) { return ['BUTTON', 'INPUT'].indexOf(node.tagName) != -1; }, + getNextActiveDescWhenBlockIsDeleted: function(blockRootNode) { + // Go up a level, if possible. + var nextNode = blockRootNode.parentNode; + while (nextNode && nextNode.tagName != 'LI') { + nextNode = nextNode.parentNode; + } + if (nextNode) { + return nextNode; + } + + // Otherwise, go to the next sibling. + var nextSibling = this.getNextSibling(blockRootNode); + if (nextSibling) { + return nextSibling; + } + + // Otherwise, go to the previous sibling. + var previousSibling = this.getPreviousSibling(blockRootNode); + if (previousSibling) { + return previousSibling; + } + + // Otherwise, this is a top-level isolated block, which means that + // something's gone wrong and this function should not have been called + // in the first place. + console.error('Could not handle deletion of block.' + blockRootNode); + }, onKeypress: function(e, tree) { var treeId = tree.id; var activeDesc = document.getElementById(this.getActiveDescId(treeId)); if (!activeDesc) { - console.log('ERROR: no active descendant for current tree.'); + console.error('ERROR: no active descendant for current tree.'); + // TODO(sll): This just gives the focus somewhere to go in the event + // of an error, but we need to generalize this to other trees (both + // within and outside the workspace). + this.setActiveDesc( + blocklyApp.workspace.topBlocks_[0].id + 'blockSummary', treeId); return; } diff --git a/accessible/workspace-tree.component.js b/accessible/workspace-tree.component.js index 48ecd2c14..98ef4d775 100644 --- a/accessible/workspace-tree.component.js +++ b/accessible/workspace-tree.component.js @@ -168,7 +168,7 @@ blocklyApp.WorkspaceTreeComponent = ng.core var elementsNeedingIds = this.getElementsNeedingIds_(); this.idMap = {} - this.idMap['parentList'] = this.utilsService.generateUniqueId(); + this.idMap['parentList'] = this.block.id + 'parent'; for (var i = 0; i < elementsNeedingIds.length; i++) { this.idMap[elementsNeedingIds[i]] = this.block.id + elementsNeedingIds[i]; @@ -198,10 +198,15 @@ blocklyApp.WorkspaceTreeComponent = ng.core isCompatibleWithClipboard: function(connection) { return this.clipboardService.isCompatibleWithClipboard(connection); }, - isTopLevelBlock: function(block) { - return blocklyApp.workspace.topBlocks_.some(function(topBlock) { - return topBlock.id == block.id; - }); + isIsolatedTopLevelBlock: function(block) { + // Returns whether the given block is at the top level, and has no + // siblings. + return Boolean( + !block.nextConnection.targetConnection && + !block.previousConnection.targetConnection && + blocklyApp.workspace.topBlocks_.some(function(topBlock) { + return topBlock.id == block.id; + })); }, generateAriaLabelledByAttr: function(mainLabel, secondLabel, isDisabled) { return this.utilsService.generateAriaLabelledByAttr( @@ -219,40 +224,45 @@ blocklyApp.WorkspaceTreeComponent = ng.core that.clipboardService.pasteFromClipboard(block.nextConnection); }, this.tree.id); }, - cutToClipboard: function(block) { - if (this.isTopLevelBlock(block)) { - nextNodeToFocusOn = this.treeService.getNodeToFocusOnWhenTreeIsDeleted( - this.tree.id); - this.clipboardService.cut(block); + getRootNode: function() { + // Gets the root HTML node for this component. + return document.getElementById(this.idMap['parentList']); + }, + removeBlockAndSetFocus_: function(block, deleteBlockFunc) { + // This method runs the given function and then does one of two things: + // - If the block is an isolated top-level block, it shifts the tree + // focus. + // - Otherwise, it sets the correct new active desc for the current tree. + if (this.isIsolatedTopLevelBlock(block)) { + var nextNodeToFocusOn = + this.treeService.getNodeToFocusOnWhenTreeIsDeleted(this.tree.id); + deleteBlockFunc(); nextNodeToFocusOn.focus(); } else { - // TODO(sll): Change the active descendant of the tree. - this.clipboardService.cut(block); + var nextActiveDesc = + this.treeService.getNextActiveDescWhenBlockIsDeleted( + this.getRootNode()); + this.treeService.runWhilePreservingFocus( + deleteBlockFunc, this.tree.id, nextActiveDesc.id); } }, + cutToClipboard: function(block) { + var that = this; + this.removeBlockAndSetFocus_(block, function() { + that.clipboardService.cut(block); + }); + }, deleteBlock: function(block) { - if (this.isTopLevelBlock(block)) { - nextNodeToFocusOn = this.treeService.getNodeToFocusOnWhenTreeIsDeleted( - this.tree.id); + this.removeBlockAndSetFocus_(block, function() { block.dispose(true); - nextNodeToFocusOn.focus(); - } else { - // TODO(sll): Change the active descendant of the tree. - block.dispose(true); - } + }); }, sendToMarkedSpot: function(block) { this.clipboardService.pasteToMarkedConnection(block, false); - if (this.isTopLevelBlock(block)) { - nextNodeToFocusOn = this.treeService.getNodeToFocusOnWhenTreeIsDeleted( - this.tree.id); + this.removeBlockAndSetFocus_(block, function() { block.dispose(true); - nextNodeToFocusOn.focus(); - } else { - // TODO(sll): Change the active descendant of the tree. - block.dispose(true); - } + }); alert('Block moved to marked spot: ' + block.toString()); }