From 6c05ece593094c002a23fa697871de63d18ba706 Mon Sep 17 00:00:00 2001 From: Sean Lip Date: Thu, 8 Sep 2016 18:22:02 -0700 Subject: [PATCH 01/11] Minor wording change. --- accessible/messages.js | 2 +- accessible/workspace-tree.component.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/accessible/messages.js b/accessible/messages.js index 681b5d535..af3a871ac 100644 --- a/accessible/messages.js +++ b/accessible/messages.js @@ -70,4 +70,4 @@ Blockly.Msg.BLOCK_MOVED_TO_MARKED_SPOT_MSB = 'Block moved to marked spot: '; Blockly.Msg.TOOLBOX_BLOCK = 'toolbox block'; Blockly.Msg.WORKSPACE_BLOCK = 'workspace block'; Blockly.Msg.SUBMENU_INDICATOR = 'move right to view submenu'; -Blockly.Msg.MORE_OPTIONS = 'More options'; +Blockly.Msg.BLOCK_OPTIONS = 'Block options'; diff --git a/accessible/workspace-tree.component.js b/accessible/workspace-tree.component.js index 04ec09b12..8aa2e65eb 100644 --- a/accessible/workspace-tree.component.js +++ b/accessible/workspace-tree.component.js @@ -71,7 +71,7 @@ blocklyApp.WorkspaceTreeComponent = ng.core
  • - +
    1. Date: Thu, 15 Sep 2016 17:43:56 -0700 Subject: [PATCH 02/11] Simplify the handling of the active desc for a toolbox without categories. --- accessible/toolbox-tree.component.js | 21 +++++++++++++++------ accessible/toolbox.component.js | 9 ++++----- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/accessible/toolbox-tree.component.js b/accessible/toolbox-tree.component.js index e5bbb75be..826a44edd 100644 --- a/accessible/toolbox-tree.component.js +++ b/accessible/toolbox-tree.component.js @@ -29,7 +29,7 @@ blocklyApp.ToolboxTreeComponent = ng.core selector: 'blockly-toolbox-tree', template: `
    2. @@ -69,7 +69,7 @@ blocklyApp.ToolboxTreeComponent = ng.core return blocklyApp.ToolboxTreeComponent; })], inputs: [ - 'block', 'displayBlockMenu', 'level', 'index', 'tree', 'noCategories', 'isTopLevel'], + 'block', 'displayBlockMenu', 'level', 'tree', 'isFirstToolboxTree'], pipes: [blocklyApp.TranslatePipe] }) .Class({ @@ -92,10 +92,19 @@ blocklyApp.ToolboxTreeComponent = ng.core 'blockCopyButton', 'sendToSelected', 'sendToSelectedButton']); } this.idMap = this.utilsService.generateIds(elementsNeedingIds); - if (this.isTopLevel) { - this.idMap['parentList'] = 'blockly-toolbox-tree-node0'; - } else { - this.idMap['parentList'] = this.utilsService.generateUniqueId(); + this.idMap['parentList'] = this.utilsService.generateUniqueId(); + }, + ngAfterViewInit: function() { + // If this is the first tree in the category-less toolbox, set its active + // descendant after the ids have been computed. + // Note that a timeout is needed here in order to trigger Angular + // change detection. + if (this.isFirstToolboxTree) { + var that = this; + setTimeout(function() { + that.treeService.setActiveDesc( + that.idMap['parentList'], 'blockly-toolbox-tree'); + }); } }, getBlockDescription: function() { diff --git a/accessible/toolbox.component.js b/accessible/toolbox.component.js index a7d5acd93..d43bb1532 100644 --- a/accessible/toolbox.component.js +++ b/accessible/toolbox.component.js @@ -55,13 +55,12 @@ blocklyApp.ToolboxComponent = ng.core
    3. +
      + role="treeitem" [level]="1" [block]="block" + [tree]="tree" [displayBlockMenu]="true" + [isFirstToolboxTree]="i === 0">
    From 72b00130e471b0fc45c3270f320a9d3478d6a26c Mon Sep 17 00:00:00 2001 From: Sean Lip Date: Thu, 15 Sep 2016 18:09:36 -0700 Subject: [PATCH 03/11] Remove tables from the HTML. Move the toolbar buttons to the end so that they don't impede switching between the toolbox and workspace. --- accessible/app.component.js | 15 +++---- accessible/media/accessible.css | 15 ++++++- accessible/toolbox.component.js | 74 ++++++++++++++++--------------- accessible/tree.service.js | 10 ++--- accessible/workspace.component.js | 48 ++++++++++---------- 5 files changed, 86 insertions(+), 76 deletions(-) diff --git a/accessible/app.component.js b/accessible/app.component.js index e1c4329d3..42d04608e 100644 --- a/accessible/app.component.js +++ b/accessible/app.component.js @@ -32,16 +32,11 @@ blocklyApp.AppView = ng.core - - - - - -
    - {{'TOOLBOX_LOAD'|translate}} - - {{'WORKSPACE_LOAD'|translate}} -
    + +
    + {{'TOOLBOX_LOAD'|translate}} + {{'WORKSPACE_LOAD'|translate}} +
    diff --git a/accessible/media/accessible.css b/accessible/media/accessible.css index 5a68527ca..c1a514cb5 100644 --- a/accessible/media/accessible.css +++ b/accessible/media/accessible.css @@ -1,6 +1,17 @@ -.blocklyTable { - vertical-align: top; +.blocklyToolboxColumn { + float: left; + width: 350px; } +.blocklyWorkspaceColumn { + float: left; + width: 300px; +} +.blocklyToolbarColumn { + float: left; + margin-top: 20px; + width: 200px; +} + .blocklyTree .blocklyActiveDescendant > label, .blocklyTree .blocklyActiveDescendant > div > label, .blocklyActiveDescendant > button, diff --git a/accessible/toolbox.component.js b/accessible/toolbox.component.js index d43bb1532..6b7e14af3 100644 --- a/accessible/toolbox.component.js +++ b/accessible/toolbox.component.js @@ -27,43 +27,45 @@ blocklyApp.ToolboxComponent = ng.core .Component({ selector: 'blockly-toolbox', template: ` -

    Toolbox

    -
      - +
      +

      Toolbox

      +
        + -
        - - -
        -
      +
      + + +
      +
    + `, directives: [blocklyApp.ToolboxTreeComponent] }) diff --git a/accessible/tree.service.js b/accessible/tree.service.js index ecf5fd8cc..2ce53dbe2 100644 --- a/accessible/tree.service.js +++ b/accessible/tree.service.js @@ -35,19 +35,19 @@ blocklyApp.TreeService = ng.core getToolboxTreeNode_: function() { return document.getElementById('blockly-toolbox-tree'); }, - getWorkspaceToolbarButtonNodes_: function() { - return Array.from(document.querySelectorAll( - 'button.blocklyWorkspaceToolbarButton')); - }, // Returns a list of all top-level workspace tree nodes on the page. getWorkspaceTreeNodes_: function() { return Array.from(document.querySelectorAll('ol.blocklyWorkspaceTree')); }, + getWorkspaceToolbarButtonNodes_: function() { + return Array.from(document.querySelectorAll( + 'button.blocklyWorkspaceToolbarButton')); + }, // Returns a list of all top-level tree nodes on the page. getAllTreeNodes_: function() { var treeNodes = [this.getToolboxTreeNode_()]; - treeNodes = treeNodes.concat(this.getWorkspaceToolbarButtonNodes_()); treeNodes = treeNodes.concat(this.getWorkspaceTreeNodes_()); + treeNodes = treeNodes.concat(this.getWorkspaceToolbarButtonNodes_()); return treeNodes; }, isTopLevelWorkspaceTree: function(treeId) { diff --git a/accessible/workspace.component.js b/accessible/workspace.component.js index 255735020..d69ec7ea8 100644 --- a/accessible/workspace.component.js +++ b/accessible/workspace.component.js @@ -27,33 +27,35 @@ blocklyApp.WorkspaceComponent = ng.core .Component({ selector: 'blockly-workspace', template: ` -
  • @@ -107,6 +107,9 @@ blocklyApp.ToolboxTreeComponent = ng.core }); } }, + isWorkspaceEmpty: function() { + return this.utilsService.isWorkspaceEmpty(); + }, getBlockDescription: function() { return this.utilsService.getBlockDescription(this.block); }, From ee403c6a95f9d706400e31ad8ef0a4e23bdf2ab8 Mon Sep 17 00:00:00 2001 From: Sean Lip Date: Mon, 19 Sep 2016 15:45:46 -0700 Subject: [PATCH 07/11] Add keyboard shortcuts for cut, copy and paste operations. --- accessible/toolbox-tree.component.js | 17 +-- accessible/toolbox.component.js | 21 +--- accessible/tree.service.js | 165 ++++++++++++++++++++++++- accessible/utils.service.js | 8 ++ accessible/workspace-tree.component.js | 39 +----- 5 files changed, 186 insertions(+), 64 deletions(-) diff --git a/accessible/toolbox-tree.component.js b/accessible/toolbox-tree.component.js index 15a0df07c..be14f33ff 100644 --- a/accessible/toolbox-tree.component.js +++ b/accessible/toolbox-tree.component.js @@ -28,7 +28,7 @@ blocklyApp.ToolboxTreeComponent = ng.core .Component({ selector: 'blockly-toolbox-tree', template: ` -
  • @@ -85,14 +85,17 @@ blocklyApp.ToolboxTreeComponent = ng.core this.utilsService = _utilsService; }], ngOnInit: function() { - var elementsNeedingIds = ['blockSummaryLabel']; + var idKeys = ['toolboxBlockRoot', 'blockSummaryLabel']; if (this.displayBlockMenu) { - elementsNeedingIds = elementsNeedingIds.concat(['blockSummarylabel', + idKeys = idKeys.concat([ 'workspaceCopy', 'workspaceCopyButton', 'blockCopy', 'blockCopyButton', 'sendToSelected', 'sendToSelectedButton']); } - this.idMap = this.utilsService.generateIds(elementsNeedingIds); - this.idMap['parentList'] = this.utilsService.generateUniqueId(); + + this.idMap = {}; + for (var i = 0; i < idKeys.length; i++) { + this.idMap[idKeys[i]] = this.block.id + idKeys[i]; + } }, ngAfterViewInit: function() { // If this is the first tree in the category-less toolbox, set its active @@ -103,7 +106,7 @@ blocklyApp.ToolboxTreeComponent = ng.core var that = this; setTimeout(function() { that.treeService.setActiveDesc( - that.idMap['parentList'], 'blockly-toolbox-tree'); + that.idMap['toolboxBlockRoot'], 'blockly-toolbox-tree'); }); } }, @@ -129,7 +132,7 @@ blocklyApp.ToolboxTreeComponent = ng.core setTimeout(function() { that.treeService.focusOnBlock(newBlockId); that.notificationsService.setStatusMessage( - blockDescription + ' copied to workspace. ' + + blockDescription + ' copied to new workspace group. ' + 'Now on copied block in workspace.'); }); }, diff --git a/accessible/toolbox.component.js b/accessible/toolbox.component.js index 6b7e14af3..fd81d0f43 100644 --- a/accessible/toolbox.component.js +++ b/accessible/toolbox.component.js @@ -74,7 +74,6 @@ blocklyApp.ToolboxComponent = ng.core blocklyApp.TreeService, blocklyApp.UtilsService, function(_treeService, _utilsService) { this.toolboxCategories = []; - this.toolboxWorkspaces = Object.create(null); this.treeService = _treeService; this.utilsService = _utilsService; @@ -126,24 +125,6 @@ blocklyApp.ToolboxComponent = ng.core 'Move right to access ' + numBlocks + ' blocks in this category.'; }, getToolboxWorkspace: function(categoryNode) { - if (categoryNode.attributes && categoryNode.attributes.name) { - var categoryName = categoryNode.attributes.name.value; - } else { - var categoryName = 'no-category'; - } - if (this.toolboxWorkspaces[categoryName]) { - return this.toolboxWorkspaces[categoryName]; - } else { - var categoryWorkspace = new Blockly.Workspace(); - if (categoryName == 'no-category') { - for (var i = 0; i < categoryNode.length; i++) { - Blockly.Xml.domToBlock(categoryWorkspace, categoryNode[i]); - } - } else { - Blockly.Xml.domToWorkspace(categoryNode, categoryWorkspace); - } - this.toolboxWorkspaces[categoryName] = categoryWorkspace; - return this.toolboxWorkspaces[categoryName]; - } + return this.treeService.getToolboxWorkspace(categoryNode); } }); diff --git a/accessible/tree.service.js b/accessible/tree.service.js index 2ce53dbe2..d303591b7 100644 --- a/accessible/tree.service.js +++ b/accessible/tree.service.js @@ -27,10 +27,15 @@ blocklyApp.TreeService = ng.core .Class({ constructor: [ - blocklyApp.NotificationsService, function(_notificationsService) { + blocklyApp.NotificationsService, blocklyApp.UtilsService, + blocklyApp.ClipboardService, + function(_notificationsService, _utilsService, _clipboardService) { // Stores active descendant ids for each tree in the page. this.activeDescendantIds_ = {}; this.notificationsService = _notificationsService; + this.utilsService = _utilsService; + this.clipboardService = _clipboardService; + this.toolboxWorkspaces = {}; }], getToolboxTreeNode_: function() { return document.getElementById('blockly-toolbox-tree'); @@ -43,6 +48,39 @@ blocklyApp.TreeService = ng.core return Array.from(document.querySelectorAll( 'button.blocklyWorkspaceToolbarButton')); }, + getToolboxWorkspace: function(categoryNode) { + if (categoryNode.attributes && categoryNode.attributes.name) { + var categoryName = categoryNode.attributes.name.value; + } else { + var categoryName = 'no-category'; + } + + if (this.toolboxWorkspaces.hasOwnProperty(categoryName)) { + return this.toolboxWorkspaces[categoryName]; + } else { + var categoryWorkspace = new Blockly.Workspace(); + if (categoryName == 'no-category') { + for (var i = 0; i < categoryNode.length; i++) { + Blockly.Xml.domToBlock(categoryWorkspace, categoryNode[i]); + } + } else { + Blockly.Xml.domToWorkspace(categoryNode, categoryWorkspace); + } + + this.toolboxWorkspaces[categoryName] = categoryWorkspace; + return this.toolboxWorkspaces[categoryName]; + } + }, + getToolboxBlockById: function(blockId) { + for (var categoryName in this.toolboxWorkspaces) { + var putativeBlock = this.utilsService.getBlockByIdFromWorkspace( + blockId, this.toolboxWorkspaces[categoryName]); + if (putativeBlock) { + return putativeBlock; + } + } + return null; + }, // Returns a list of all top-level tree nodes on the page. getAllTreeNodes_: function() { var treeNodes = [this.getToolboxTreeNode_()]; @@ -239,6 +277,77 @@ blocklyApp.TreeService = ng.core } } }, + isIsolatedTopLevelBlock_: function(block) { + // Returns whether the given block is at the top level, and has no + // siblings. + var blockIsAtTopLevel = !block.getParent(); + var blockHasNoSiblings = ( + (!block.nextConnection || + !block.nextConnection.targetConnection) && + (!block.previousConnection || + !block.previousConnection.targetConnection)); + return blockIsAtTopLevel && blockHasNoSiblings; + }, + removeBlockAndSetFocus: function(block, blockRootNode, deleteBlockFunc) { + // This method runs the given deletion 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. + var treeId = this.getTreeIdForBlock(block.id); + if (this.isIsolatedTopLevelBlock_(block)) { + var nextNodeToFocusOn = this.getNodeToFocusOnWhenTreeIsDeleted(treeId); + + this.clearActiveDesc(treeId); + deleteBlockFunc(); + // Invoke a digest cycle, so that the DOM settles. + setTimeout(function() { + nextNodeToFocusOn.focus(); + }); + } else { + var nextActiveDesc = this.getNextActiveDescWhenBlockIsDeleted( + blockRootNode); + this.runWhilePreservingFocus( + deleteBlockFunc, treeId, nextActiveDesc.id); + } + }, + cutBlock_: function(block, blockRootNode) { + var blockDescription = this.utilsService.getBlockDescription(block); + + var that = this; + this.removeBlockAndSetFocus(block, blockRootNode, function() { + that.clipboardService.cut(block); + }); + + setTimeout(function() { + if (that.utilsService.isWorkspaceEmpty()) { + that.notificationsService.setStatusMessage( + blockDescription + ' cut. Workspace is empty.'); + } else { + that.notificationsService.setStatusMessage( + blockDescription + ' cut. Now on workspace.'); + } + }); + }, + copyBlock_: function(block) { + var blockDescription = this.utilsService.getBlockDescription(block); + this.clipboardService.copy(block); + this.notificationsService.setStatusMessage( + blockDescription + ' ' + Blockly.Msg.COPIED_BLOCK_MSG); + }, + pasteToConnection_: function(block, connection) { + var destinationTreeId = this.getTreeIdForBlock( + connection.getSourceBlock().id); + this.clearActiveDesc(destinationTreeId); + + var newBlockId = this.clipboardService.pasteFromClipboard(connection); + + // Invoke a digest cycle, so that the DOM settles. + var that = this; + setTimeout(function() { + that.focusOnBlock(newBlockId); + }); + }, onKeypress: function(e, tree) { var treeId = tree.id; var activeDesc = document.getElementById(this.getActiveDescId(treeId)); @@ -248,9 +357,61 @@ blocklyApp.TreeService = ng.core return; } - if (e.altKey || e.ctrlKey) { + if (e.altKey) { // Do not intercept combinations such as Alt+Home. return; + } + + if (e.ctrlKey) { + var activeDesc = document.getElementById(this.getActiveDescId(treeId)); + + // Scout up the tree to see whether we're in the toolbox or workspace. + var scoutNode = activeDesc; + var TARGET_TAG_NAMES = ['BLOCKLY-TOOLBOX', 'BLOCKLY-WORKSPACE']; + while (TARGET_TAG_NAMES.indexOf(scoutNode.tagName) === -1) { + scoutNode = scoutNode.parentNode; + } + var inToolbox = (scoutNode.tagName == 'BLOCKLY-TOOLBOX'); + + // Disallow cutting and pasting in the toolbox. + if (inToolbox && e.keyCode != 67) { + if (e.keyCode == 86) { + this.notificationsService.setStatusMessage( + 'Cannot paste block in toolbox.'); + } else if (e.keyCode == 88) { + this.notificationsService.setStatusMessage( + 'Cannot cut block in toolbox. Try copying instead.'); + } + } + + // Starting from the activeDesc, walk up the tree until we find the + // root of the current block. + var blockRootSuffix = inToolbox ? 'toolboxBlockRoot' : 'blockRoot'; + var putativeBlockRootNode = activeDesc; + while (putativeBlockRootNode.id.indexOf(blockRootSuffix) === -1) { + putativeBlockRootNode = putativeBlockRootNode.parentNode; + } + var blockRootNode = putativeBlockRootNode; + + var blockId = blockRootNode.id.substring( + 0, blockRootNode.id.length - blockRootSuffix.length); + var block = inToolbox ? + this.getToolboxBlockById(blockId) : + this.utilsService.getBlockById(blockId); + + if (e.keyCode == 88) { + // Cut block. + this.cutBlock_(block, blockRootNode); + } else if (e.keyCode == 67) { + // Copy block. Note that, in this case, we might be in the workspace + // or toolbox. + this.copyBlock_(block); + } else if (e.keyCode == 86) { + // Paste block, if possible. + var targetConnection = + e.shiftKey ? block.previousConnection : block.nextConnection; + this.pasteToConnection_(block, targetConnection); + } } else if (document.activeElement.tagName == 'INPUT') { // For input fields, only Esc and Tab keystrokes are handled specially. if (e.keyCode == 27 || e.keyCode == 9) { diff --git a/accessible/utils.service.js b/accessible/utils.service.js index 3a7eef797..160f90ad6 100644 --- a/accessible/utils.service.js +++ b/accessible/utils.service.js @@ -70,5 +70,13 @@ blocklyApp.UtilsService = ng.core }, isWorkspaceEmpty: function() { return !blocklyApp.workspace.topBlocks_.length; + }, + getBlockById: function(blockId) { + return this.getBlockByIdFromWorkspace(blockId, blocklyApp.workspace); + }, + getBlockByIdFromWorkspace: function(blockId, workspace) { + // This is used for non-default workspaces, such as those comprising the + // toolbox. + return workspace.getBlockById(blockId); } }); diff --git a/accessible/workspace-tree.component.js b/accessible/workspace-tree.component.js index 8aa2e65eb..eb8361ff1 100644 --- a/accessible/workspace-tree.component.js +++ b/accessible/workspace-tree.component.js @@ -48,8 +48,7 @@ blocklyApp.WorkspaceTreeComponent = ng.core
  • + [attr.aria-level]="level + 1"> @@ -115,40 +114,10 @@ blocklyApp.WorkspaceTreeComponent = ng.core getBlockDescription: function() { return this.utilsService.getBlockDescription(this.block); }, - isIsolatedTopLevelBlock_: function(block) { - // Returns whether the given block is at the top level, and has no - // siblings. - var blockIsAtTopLevel = !block.getParent(); - var blockHasNoSiblings = ( - (!block.nextConnection || - !block.nextConnection.targetConnection) && - (!block.previousConnection || - !block.previousConnection.targetConnection)); - return blockIsAtTopLevel && blockHasNoSiblings; - }, 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); - - this.treeService.clearActiveDesc(this.tree.id); - deleteBlockFunc(); - // Invoke a digest cycle, so that the DOM settles. - setTimeout(function() { - nextNodeToFocusOn.focus(); - }); - } else { - var blockRootNode = document.getElementById(this.idMap['blockRoot']); - var nextActiveDesc = - this.treeService.getNextActiveDescWhenBlockIsDeleted( - blockRootNode); - this.treeService.runWhilePreservingFocus( - deleteBlockFunc, this.tree.id, nextActiveDesc.id); - } + this.treeService.removeBlockAndSetFocus( + block, document.getElementById(this.idMap['blockRoot']), + deleteBlockFunc); }, cutBlock_: function() { var blockDescription = this.getBlockDescription(); From 284f71c46ad04be26ee31e041a7009e32d59f2c2 Mon Sep 17 00:00:00 2001 From: Sean Lip Date: Mon, 19 Sep 2016 16:04:08 -0700 Subject: [PATCH 08/11] Remove clipboard buttons to simplify UI. --- accessible/clipboard.service.js | 3 + accessible/toolbox-tree.component.js | 16 +----- accessible/tree.service.js | 9 ++- accessible/workspace-tree.component.js | 76 +------------------------- 4 files changed, 12 insertions(+), 92 deletions(-) diff --git a/accessible/clipboard.service.js b/accessible/clipboard.service.js index 98764d2b6..9a7e7451a 100644 --- a/accessible/clipboard.service.js +++ b/accessible/clipboard.service.js @@ -111,6 +111,9 @@ blocklyApp.ClipboardService = ng.core this.clipboardBlockNextConnection_ = block.nextConnection; this.clipboardBlockOutputConnection_ = block.outputConnection; }, + isClipboardEmpty: function() { + return !this.clipboardBlockXml_; + }, pasteFromClipboard: function(inputConnection) { var connection = inputConnection; // If the connection is a 'previousConnection' and that connection is diff --git a/accessible/toolbox-tree.component.js b/accessible/toolbox-tree.component.js index be14f33ff..ee60a4d22 100644 --- a/accessible/toolbox-tree.component.js +++ b/accessible/toolbox-tree.component.js @@ -34,13 +34,6 @@ blocklyApp.ToolboxTreeComponent = ng.core [attr.aria-level]="level">
      -
    1. - -
    2. @@ -88,8 +81,8 @@ blocklyApp.ToolboxTreeComponent = ng.core var idKeys = ['toolboxBlockRoot', 'blockSummaryLabel']; if (this.displayBlockMenu) { idKeys = idKeys.concat([ - 'workspaceCopy', 'workspaceCopyButton', 'blockCopy', - 'blockCopyButton', 'sendToSelected', 'sendToSelectedButton']); + 'workspaceCopy', 'workspaceCopyButton', 'sendToSelected', + 'sendToSelectedButton']); } this.idMap = {}; @@ -136,11 +129,6 @@ blocklyApp.ToolboxTreeComponent = ng.core 'Now on copied block in workspace.'); }); }, - copyToClipboard: function() { - this.clipboardService.copy(this.block); - this.notificationsService.setStatusMessage( - this.getBlockDescription() + ' ' + Blockly.Msg.COPIED_BLOCK_MSG); - }, copyToMarkedSpot: function() { var blockDescription = this.getBlockDescription(); // Clean up the active desc for the destination tree. diff --git a/accessible/tree.service.js b/accessible/tree.service.js index d303591b7..6635b7319 100644 --- a/accessible/tree.service.js +++ b/accessible/tree.service.js @@ -335,13 +335,16 @@ blocklyApp.TreeService = ng.core this.notificationsService.setStatusMessage( blockDescription + ' ' + Blockly.Msg.COPIED_BLOCK_MSG); }, - pasteToConnection_: function(block, connection) { + pasteToConnection: function(block, connection) { + if (this.clipboardService.isClipboardEmpty()) { + return; + } + var destinationTreeId = this.getTreeIdForBlock( connection.getSourceBlock().id); this.clearActiveDesc(destinationTreeId); var newBlockId = this.clipboardService.pasteFromClipboard(connection); - // Invoke a digest cycle, so that the DOM settles. var that = this; setTimeout(function() { @@ -410,7 +413,7 @@ blocklyApp.TreeService = ng.core // Paste block, if possible. var targetConnection = e.shiftKey ? block.previousConnection : block.nextConnection; - this.pasteToConnection_(block, targetConnection); + this.pasteToConnection(block, targetConnection); } } else if (document.activeElement.tagName == 'INPUT') { // For input fields, only Esc and Tab keystrokes are handled specially. diff --git a/accessible/workspace-tree.component.js b/accessible/workspace-tree.component.js index eb8361ff1..b7763fbc9 100644 --- a/accessible/workspace-tree.component.js +++ b/accessible/workspace-tree.component.js @@ -119,24 +119,6 @@ blocklyApp.WorkspaceTreeComponent = ng.core block, document.getElementById(this.idMap['blockRoot']), deleteBlockFunc); }, - cutBlock_: function() { - var blockDescription = this.getBlockDescription(); - - var that = this; - this.removeBlockAndSetFocus_(this.block, function() { - that.clipboardService.cut(that.block); - }); - - setTimeout(function() { - if (that.utilsService.isWorkspaceEmpty()) { - that.notificationsService.setStatusMessage( - blockDescription + ' cut. Workspace is empty.'); - } else { - that.notificationsService.setStatusMessage( - blockDescription + ' cut. Now on workspace.'); - } - }); - }, deleteBlock_: function() { var blockDescription = this.getBlockDescription(); @@ -156,19 +138,6 @@ blocklyApp.WorkspaceTreeComponent = ng.core } }); }, - pasteToConnection_: function(connection) { - var destinationTreeId = this.treeService.getTreeIdForBlock( - connection.getSourceBlock().id); - this.treeService.clearActiveDesc(destinationTreeId); - - var newBlockId = this.clipboardService.pasteFromClipboard(connection); - - // Invoke a digest cycle, so that the DOM settles. - var that = this; - setTimeout(function() { - that.treeService.focusOnBlock(newBlockId); - }); - }, moveToMarkedSpot_: function() { var blockDescription = this.getBlockDescription(); var oldDestinationTreeId = this.treeService.getTreeIdForBlock( @@ -203,60 +172,17 @@ blocklyApp.WorkspaceTreeComponent = ng.core '. Now on moved block in workspace.'); }); }, - copyBlock_: function() { - this.clipboardService.copy(this.block); - this.notificationsService.setStatusMessage( - this.getBlockDescription() + ' ' + Blockly.Msg.COPIED_BLOCK_MSG); - }, markSpotBefore_: function() { this.clipboardService.markConnection(this.block.previousConnection); }, markSpotAfter_: function() { this.clipboardService.markConnection(this.block.nextConnection); }, - pasteToNextConnection_: function() { - this.pasteToConnection_(this.block.nextConnection); - }, - pasteToPreviousConnection_: function() { - this.pasteToConnection_(this.block.previousConnection); - }, ngOnInit: function() { var that = this; // Generate a list of action buttons. this.actionButtonsInfo = [{ - baseIdKey: 'cut', - translationIdForText: 'CUT_BLOCK', - action: that.cutBlock_.bind(that), - isDisabled: function() { - return false; - } - }, { - baseIdKey: 'copy', - translationIdForText: 'COPY_BLOCK', - action: that.copyBlock_.bind(that), - isDisabled: function() { - return false; - } - }, { - baseIdKey: 'pasteBefore', - translationIdForText: 'PASTE_BEFORE', - action: that.pasteToPreviousConnection_.bind(that), - isDisabled: function() { - return Boolean( - !that.block.previousConnection || - !that.isCompatibleWithClipboard(that.block.previousConnection)); - } - }, { - baseIdKey: 'pasteAfter', - translationIdForText: 'PASTE_AFTER', - action: that.pasteToNextConnection_.bind(that), - isDisabled: function() { - return Boolean( - !that.block.nextConnection || - !that.isCompatibleWithClipboard(that.block.nextConnection)); - } - }, { baseIdKey: 'markBefore', translationIdForText: 'MARK_SPOT_BEFORE', action: that.markSpotBefore_.bind(that), @@ -301,7 +227,7 @@ blocklyApp.WorkspaceTreeComponent = ng.core baseIdKey: 'paste', translationIdForText: 'PASTE', action: function(connection) { - that.pasteToConnection_(connection); + that.treeService.pasteToConnection(that.block, connection); }, isDisabled: function(connection) { return !that.isCompatibleWithClipboard(connection); From f4e316e8a9f25cb30cd4ffcdbe0a688fee110bb1 Mon Sep 17 00:00:00 2001 From: Sean Lip Date: Mon, 19 Sep 2016 16:59:24 -0700 Subject: [PATCH 09/11] Prevent setting a number value to NaN. Select the field value on entry to an input field. State the contents of the input field when describing the field. --- accessible/field.component.js | 10 +++++++--- accessible/tree.service.js | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/accessible/field.component.js b/accessible/field.component.js index e10323db7..c2a8f9fe1 100644 --- a/accessible/field.component.js +++ b/accessible/field.component.js @@ -30,12 +30,12 @@ blocklyApp.FieldComponent = ng.core template: `
      @@ -78,6 +78,10 @@ blocklyApp.FieldComponent = ng.core // this.generateElementNames() are unique. this.idMap = this.utilsService.generateIds(elementsNeedingIds); }, + setNumberValue: function(newValue) { + // Do not permit a residual value of NaN after a backspace event. + this.field.setValue(newValue || 0); + }, generateAriaLabelledByAttr: function(mainLabel, secondLabel) { return mainLabel + ' ' + secondLabel; }, diff --git a/accessible/tree.service.js b/accessible/tree.service.js index 6635b7319..071bc95ea 100644 --- a/accessible/tree.service.js +++ b/accessible/tree.service.js @@ -454,6 +454,7 @@ blocklyApp.TreeService = ng.core break; } else if (currentNode.tagName == 'INPUT') { currentNode.focus(); + currentNode.select(); this.notificationsService.setStatusMessage( 'Type a value, then press Escape to exit'); break; From 270ed961bf34db3ffff59b2c333158668f0b248f Mon Sep 17 00:00:00 2001 From: Sean Lip Date: Mon, 19 Sep 2016 19:33:35 -0700 Subject: [PATCH 10/11] Add aria-level and aria-selected attrs to dropdowns. --- accessible/field.component.js | 5 +++-- accessible/workspace-tree.component.js | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/accessible/field.component.js b/accessible/field.component.js index c2a8f9fe1..5a5302960 100644 --- a/accessible/field.component.js +++ b/accessible/field.component.js @@ -44,7 +44,8 @@ blocklyApp.FieldComponent = ng.core
      1. + [attr.aria-labelledBy]="generateAriaLabelledByAttr(idMap[optionValue + 'Button'], 'blockly-button')" + [attr.aria-level]="level" [attr.aria-selected]="field.getValue() == optionValue">