From e620b1fde6fd8db692ff834ce63808b11e47b739 Mon Sep 17 00:00:00 2001 From: alschmiedt Date: Tue, 3 Sep 2019 16:11:06 -0700 Subject: [PATCH] Action list (#2959) * Support keyboard navigation in read only mode --- core/blockly.js | 13 ++++++- core/keyboard_nav/ast_node.js | 12 +++--- core/keyboard_nav/navigation.js | 24 ++++++++++-- tests/mocha/navigation_test.js | 67 +++++++++++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 12 deletions(-) diff --git a/core/blockly.js b/core/blockly.js index 0ca6d1be4..b075c751e 100644 --- a/core/blockly.js +++ b/core/blockly.js @@ -205,14 +205,23 @@ Blockly.svgResize = function(workspace) { // are multiple workspaces and non-main workspaces are able to accept input. Blockly.onKeyDown_ = function(e) { var mainWorkspace = Blockly.mainWorkspace; - if (mainWorkspace.options.readOnly || Blockly.utils.isTargetInput(e) || + + if (Blockly.utils.isTargetInput(e) || (mainWorkspace.rendered && !mainWorkspace.isVisible())) { - // No key actions on readonly workspaces. // When focused on an HTML text input widget, don't trap any keys. // Ignore keypresses on rendered workspaces that have been explicitly // hidden. return; } + + if (mainWorkspace.options.readOnly) { + // When in read only mode handle key actions for keyboard navigation. + if (Blockly.keyboardAccessibilityMode) { + Blockly.navigation.onKeyPress(e); + } + return; + } + var deleteBlock = false; if (e.keyCode == Blockly.utils.KeyCodes.ESC) { // Pressing esc closes the context menu. diff --git a/core/keyboard_nav/ast_node.js b/core/keyboard_nav/ast_node.js index 76e95d0ef..ff6ff1423 100644 --- a/core/keyboard_nav/ast_node.js +++ b/core/keyboard_nav/ast_node.js @@ -288,7 +288,7 @@ Blockly.ASTNode.prototype.findPreviousEditableField_ = function(location, var previousField = null; var startIdx = opt_last ? fieldRow.length - 1 : fieldIdx - 1; for (var i = startIdx, field; field = fieldRow[i]; i--) { - if (field.isCurrentlyEditable()) { + if (field.EDITABLE) { previousField = field; return Blockly.ASTNode.createFieldNode(previousField); } @@ -312,7 +312,7 @@ Blockly.ASTNode.prototype.findNextForInput_ = function() { for (var i = curIdx + 1, input; input = block.inputList[i]; i++) { var fieldRow = input.fieldRow; for (var j = 0, field; field = fieldRow[j]; j++) { - if (field.isCurrentlyEditable()) { + if (field.EDITABLE) { return Blockly.ASTNode.createFieldNode(field); } } @@ -340,7 +340,7 @@ Blockly.ASTNode.prototype.findNextForField_ = function() { for (var i = curIdx, input; input = block.inputList[i]; i++) { var fieldRow = input.fieldRow; while (fieldIdx < fieldRow.length) { - if (fieldRow[fieldIdx].isCurrentlyEditable()) { + if (fieldRow[fieldIdx].EDITABLE) { return Blockly.ASTNode.createFieldNode(fieldRow[fieldIdx]); } fieldIdx++; @@ -371,7 +371,7 @@ Blockly.ASTNode.prototype.findPrevForInput_ = function() { } var fieldRow = input.fieldRow; for (var j = fieldRow.length - 1, field; field = fieldRow[j]; j--) { - if (field.isCurrentlyEditable()) { + if (field.EDITABLE) { return Blockly.ASTNode.createFieldNode(field); } } @@ -397,7 +397,7 @@ Blockly.ASTNode.prototype.findPrevForField_ = function() { } var fieldRow = input.fieldRow; while (fieldIdx > -1) { - if (fieldRow[fieldIdx].isCurrentlyEditable()) { + if (fieldRow[fieldIdx].EDITABLE) { return Blockly.ASTNode.createFieldNode(fieldRow[fieldIdx]); } fieldIdx--; @@ -501,7 +501,7 @@ Blockly.ASTNode.prototype.findFirstFieldOrInput_ = function(block) { for (var i = 0, input; input = inputs[i]; i++) { var fieldRow = input.fieldRow; for (var j = 0, field; field = fieldRow[j]; j++) { - if (field.isCurrentlyEditable()) { + if (field.EDITABLE) { return Blockly.ASTNode.createFieldNode(field); } } diff --git a/core/keyboard_nav/navigation.js b/core/keyboard_nav/navigation.js index 6b1795c65..c3247aa78 100644 --- a/core/keyboard_nav/navigation.js +++ b/core/keyboard_nav/navigation.js @@ -837,13 +837,18 @@ Blockly.navigation.onKeyPress = function(e) { var key = Blockly.user.keyMap.serializeKeyEvent(e); var action = Blockly.user.keyMap.getActionByKeyCode(key); var curNode = Blockly.navigation.cursor_.getCurNode(); + var readOnly = Blockly.getMainWorkspace().options.readOnly; var actionHandled = false; if (action) { - if (curNode && curNode.getType() === Blockly.ASTNode.types.FIELD) { - actionHandled = curNode.getLocation().onBlocklyAction(action); - } - if (!actionHandled) { + if (!readOnly) { + if (curNode && curNode.getType() === Blockly.ASTNode.types.FIELD) { + actionHandled = curNode.getLocation().onBlocklyAction(action); + } + if (!actionHandled) { + actionHandled = Blockly.navigation.onBlocklyAction(action); + } + } else if (Blockly.navigation.READONLY_ACTION_LIST.indexOf(action) > -1) { actionHandled = Blockly.navigation.onBlocklyAction(action); } } @@ -1085,3 +1090,14 @@ Blockly.navigation.ACTION_TOOLBOX = new Blockly.Action( */ Blockly.navigation.ACTION_EXIT = new Blockly.Action( Blockly.navigation.actionNames.EXIT, 'Close the current modal, such as a toolbox or field editor.'); + +/** + * List of actions that can be performed in read only mode. + * @type {!Array} + */ +Blockly.navigation.READONLY_ACTION_LIST = [ + Blockly.navigation.ACTION_PREVIOUS, + Blockly.navigation.ACTION_OUT, + Blockly.navigation.ACTION_IN, + Blockly.navigation.ACTION_NEXT +]; diff --git a/tests/mocha/navigation_test.js b/tests/mocha/navigation_test.js index 4e9c0c6df..654eb9d33 100644 --- a/tests/mocha/navigation_test.js +++ b/tests/mocha/navigation_test.js @@ -370,6 +370,73 @@ suite('Navigation', function() { field.onBlocklyAction.restore(); Blockly.navigation.onBlocklyAction.restore(); }); + suite('Test key press in read only mode', function() { + setup(function() { + Blockly.defineBlocksWithJsonArray([{ + "type": "field_block", + "message0": "%1 %2", + "args0": [ + { + "type": "field_dropdown", + "name": "NAME", + "options": [ + [ + "a", + "optionA" + ] + ] + }, + { + "type": "input_value", + "name": "NAME" + } + ], + "previousStatement": null, + "nextStatement": null, + "colour": 230, + "tooltip": "", + "helpUrl": "" + }]); + this.workspace = new Blockly.Workspace({readOnly: true}); + Blockly.navigation.setCursor(this.workspace.cursor); + Blockly.mainWorkspace = this.workspace; + this.fieldBlock1 = this.workspace.newBlock('field_block'); + Blockly.keyboardAccessibilityMode = true; + this.mockEvent = { + getModifierState: function() { + return false; + } + }; + }); + + teardown(function() { + delete Blockly.Blocks['field_block']; + Blockly.mainWorkspace = null; + this.workspace.dispose(); + }); + + test('Perform valid action for read only', function() { + var astNode = Blockly.ASTNode.createBlockNode(this.fieldBlock1); + this.workspace.cursor.setLocation(astNode); + this.mockEvent.keyCode = Blockly.utils.KeyCodes.S; + chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent)); + }); + + test('Perform invalid action for read only', function() { + var astNode = Blockly.ASTNode.createBlockNode(this.fieldBlock1); + this.workspace.cursor.setLocation(astNode); + this.mockEvent.keyCode = Blockly.utils.KeyCodes.I; + chai.assert.isFalse(Blockly.navigation.onKeyPress(this.mockEvent)); + }); + + test('Try to perform action on a field', function() { + var field = this.fieldBlock1.inputList[0].fieldRow[0]; + var astNode = Blockly.ASTNode.createFieldNode(field); + this.workspace.cursor.setLocation(astNode); + this.mockEvent.keyCode = Blockly.utils.KeyCodes.ENTER; + chai.assert.isFalse(Blockly.navigation.onKeyPress(this.mockEvent)); + }); + }); }); suite('Insert Functions', function() {