From b7b262efd5241da294f7ed0326e77b37be0d663c Mon Sep 17 00:00:00 2001 From: alschmiedt Date: Fri, 8 Nov 2019 13:51:22 -0800 Subject: [PATCH] Action update (#3389) Make it easier for users to create new shortcuts --- core/block_svg.js | 6 +- core/flyout_base.js | 12 +++ core/keyboard_nav/cursor.js | 36 ++++++++ core/keyboard_nav/flyout_cursor.js | 20 ++++ core/keyboard_nav/navigation.js | 142 +++++++++++++---------------- 5 files changed, 138 insertions(+), 78 deletions(-) diff --git a/core/block_svg.js b/core/block_svg.js index 98ea1c63e..057f0d60c 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -660,8 +660,12 @@ Blockly.BlockSvg.prototype.setCollapsed = function(collapsed) { Blockly.BlockSvg.prototype.tab = function(start, forward) { var tabCursor = new Blockly.TabNavigateCursor(); tabCursor.setCurNode(Blockly.ASTNode.createFieldNode(start)); + var action = forward ? + Blockly.navigation.ACTION_NEXT : Blockly.navigation.ACTION_PREVIOUS; - var nextNode = forward ? tabCursor.next() : tabCursor.prev(); + tabCursor.onBlocklyAction(action); + + var nextNode = tabCursor.getCurNode(); if (nextNode) { var nextField = /** @type {!Blockly.Field} */ (nextNode.getLocation()); nextField.showEditor(); diff --git a/core/flyout_base.js b/core/flyout_base.js index c5e9e7236..99d22fbc8 100644 --- a/core/flyout_base.js +++ b/core/flyout_base.js @@ -847,3 +847,15 @@ Blockly.Flyout.prototype.placeNewBlock_ = function(oldBlock) { block.moveBy(finalOffset.x, finalOffset.y); return block; }; + +/** + * Handles the given action. + * This is only triggered when keyboard accessibility mode is enabled. + * @param {!Blockly.Action} action The action to be handled. + * @return {boolean} True if the flyout handled the action, false otherwise. + * @package + */ +Blockly.Flyout.prototype.onBlocklyAction = function(action) { + var cursor = this.workspace_.getCursor(); + return cursor.onBlocklyAction(action); +}; diff --git a/core/keyboard_nav/cursor.js b/core/keyboard_nav/cursor.js index f7550273e..8847df3fa 100644 --- a/core/keyboard_nav/cursor.js +++ b/core/keyboard_nav/cursor.js @@ -24,6 +24,7 @@ goog.provide('Blockly.Cursor'); +goog.require('Blockly.navigation'); /** * Class for a cursor. @@ -94,10 +95,42 @@ Blockly.Cursor.prototype.hide = function() { } }; +/** + * Handles the given action. + * This is only triggered when keyboard navigation is enabled. + * @param {!Blockly.Action} action The action to be handled. + * @return {boolean} True if the action has been handled, false otherwise. + */ +Blockly.Cursor.prototype.onBlocklyAction = function(action) { + // If we are on a field give it the option to handle the action + if (this.getCurNode() && + this.getCurNode().getType() === Blockly.ASTNode.types.FIELD && + this.getCurNode().getLocation().onBlocklyAction(action)) { + return true; + } + switch (action.name) { + case Blockly.navigation.actionNames.PREVIOUS: + this.prev(); + return true; + case Blockly.navigation.actionNames.OUT: + this.out(); + return true; + case Blockly.navigation.actionNames.NEXT: + this.next(); + return true; + case Blockly.navigation.actionNames.IN: + this.in(); + return true; + default: + return false; + } +}; + /** * Find the next connection, field, or block. * @return {Blockly.ASTNode} The next element, or null if the current node is * not set or there is no next value. + * @protected */ Blockly.Cursor.prototype.next = function() { var curNode = this.getCurNode(); @@ -122,6 +155,7 @@ Blockly.Cursor.prototype.next = function() { * Find the in connection or field. * @return {Blockly.ASTNode} The in element, or null if the current node is * not set or there is no in value. + * @protected */ Blockly.Cursor.prototype.in = function() { var curNode = this.getCurNode(); @@ -146,6 +180,7 @@ Blockly.Cursor.prototype.in = function() { * Find the previous connection, field, or block. * @return {Blockly.ASTNode} The previous element, or null if the current node * is not set or there is no previous value. + * @protected */ Blockly.Cursor.prototype.prev = function() { var curNode = this.getCurNode(); @@ -170,6 +205,7 @@ Blockly.Cursor.prototype.prev = function() { * Find the out connection, field, or block. * @return {Blockly.ASTNode} The out element, or null if the current node is * not set or there is no out value. + * @protected */ Blockly.Cursor.prototype.out = function() { var curNode = this.getCurNode(); diff --git a/core/keyboard_nav/flyout_cursor.js b/core/keyboard_nav/flyout_cursor.js index 7c296f422..4fa6c4299 100644 --- a/core/keyboard_nav/flyout_cursor.js +++ b/core/keyboard_nav/flyout_cursor.js @@ -39,6 +39,26 @@ Blockly.FlyoutCursor = function() { }; Blockly.utils.object.inherits(Blockly.FlyoutCursor, Blockly.Cursor); +/** + * Handles the given action. + * This is only triggered when keyboard navigation is enabled. + * @param {!Blockly.Action} action The action to be handled. + * @return {boolean} True if the action has been handled, false otherwise. + * @override + */ +Blockly.FlyoutCursor.prototype.onBlocklyAction = function(action) { + switch (action.name) { + case Blockly.navigation.actionNames.PREVIOUS: + this.prev(); + return true; + case Blockly.navigation.actionNames.NEXT: + this.next(); + return true; + default: + return false; + } +}; + /** * Find the next connection, field, or block. * @return {Blockly.ASTNode} The next element, or null if the current node is diff --git a/core/keyboard_nav/navigation.js b/core/keyboard_nav/navigation.js index b5d4aaa08..0a076b9da 100644 --- a/core/keyboard_nav/navigation.js +++ b/core/keyboard_nav/navigation.js @@ -749,8 +749,8 @@ Blockly.navigation.onKeyPress = function(e) { }; /** - * Execute any actions on the flyout, workspace, or toolbox that correspond to - * the given action. + * Decides which actions to handle depending on keyboard navigation and readonly + * states. * @param {!Blockly.Action} action The current action. * @return {boolean} True if the action has been handled, false otherwise. */ @@ -775,92 +775,44 @@ Blockly.navigation.onBlocklyAction = function(action) { /** * Handles the action or dispatches to the appropriate action handler. - * @param {!Blockly.Action} action The current action + * @param {!Blockly.Action} action The action to handle. * @return {boolean} True if the action has been handled, false otherwise. * @private */ Blockly.navigation.handleActions_ = function(action) { - var workspace = Blockly.getMainWorkspace(); - if (action.name === Blockly.navigation.actionNames.TOGGLE_KEYBOARD_NAV) { + if (action.name == Blockly.navigation.actionNames.TOOLBOX || + Blockly.navigation.currentState_ == Blockly.navigation.STATE_TOOLBOX) { + return Blockly.navigation.toolboxOnAction_(action); + } else if (action.name == Blockly.navigation.actionNames.TOGGLE_KEYBOARD_NAV) { Blockly.navigation.disableKeyboardAccessibility(); return true; - } else if (action.name === Blockly.navigation.actionNames.TOOLBOX) { - if (!workspace.getToolbox()) { - Blockly.navigation.focusFlyout_(); - } else { - Blockly.navigation.focusToolbox_(); - } - return true; - } else if (Blockly.navigation.currentState_ === Blockly.navigation.STATE_WS) { - var curNode = workspace.getCursor().getCurNode(); - var actionHandled = false; - if (curNode && curNode.getType() === Blockly.ASTNode.types.FIELD) { - actionHandled = curNode.getLocation().onBlocklyAction(action); - } - if (!actionHandled) { - actionHandled = Blockly.navigation.workspaceOnAction_(action); - } - return actionHandled; - } else if (Blockly.navigation.currentState_ === Blockly.navigation.STATE_FLYOUT) { + } if (Blockly.navigation.currentState_ == Blockly.navigation.STATE_WS) { + return Blockly.navigation.workspaceOnAction_(action); + } else if (Blockly.navigation.currentState_ == Blockly.navigation.STATE_FLYOUT) { return Blockly.navigation.flyoutOnAction_(action); - } else if (Blockly.navigation.currentState_ === Blockly.navigation.STATE_TOOLBOX) { - return Blockly.navigation.toolboxOnAction_(action); } return false; }; /** - * Handle all actions performed on the workspace. - * @param {!Blockly.Action} action The action to handle. - * @return {boolean} True if the action has been handled, false otherwise. - * @private - */ -Blockly.navigation.workspaceOnAction_ = function(action) { - var workspace = Blockly.getMainWorkspace(); - switch (action.name) { - case Blockly.navigation.actionNames.PREVIOUS: - workspace.getCursor().prev(); - return true; - case Blockly.navigation.actionNames.OUT: - workspace.getCursor().out(); - return true; - case Blockly.navigation.actionNames.NEXT: - workspace.getCursor().next(); - return true; - case Blockly.navigation.actionNames.IN: - workspace.getCursor().in(); - return true; - case Blockly.navigation.actionNames.INSERT: - Blockly.navigation.modify_(); - return true; - case Blockly.navigation.actionNames.MARK: - Blockly.navigation.handleEnterForWS_(); - return true; - case Blockly.navigation.actionNames.DISCONNECT: - Blockly.navigation.disconnectBlocks_(); - return true; - default: - return false; - } -}; - -/** - * Handle all actions performed on the flyout. + * Handles the given action for the flyout. * @param {!Blockly.Action} action The action to handle. * @return {boolean} True if the action has been handled, false otherwise. * @private */ Blockly.navigation.flyoutOnAction_ = function(action) { + var workspace = Blockly.getMainWorkspace(); + var toolbox = workspace.getToolbox(); + var flyout = toolbox ? toolbox.flyout_ : workspace.getFlyout(); + + if (flyout && flyout.onBlocklyAction(action)) { + return true; + } + switch (action.name) { - case Blockly.navigation.actionNames.PREVIOUS: - Blockly.navigation.getFlyoutCursor_().prev(); - return true; case Blockly.navigation.actionNames.OUT: Blockly.navigation.focusToolbox_(); return true; - case Blockly.navigation.actionNames.NEXT: - Blockly.navigation.getFlyoutCursor_().next(); - return true; case Blockly.navigation.actionNames.MARK: Blockly.navigation.insertFromFlyout(); return true; @@ -873,24 +825,60 @@ Blockly.navigation.flyoutOnAction_ = function(action) { }; /** - * Handle all actions performeed on the toolbox. + * Handles the given action for the toolbox. * @param {!Blockly.Action} action The action to handle. * @return {boolean} True if the action has been handled, false otherwise. * @private */ Blockly.navigation.toolboxOnAction_ = function(action) { - if (action.name === Blockly.navigation.actionNames.EXIT) { - Blockly.navigation.focusWorkspace_(); - return true; - } - var toolbox = Blockly.getMainWorkspace().getToolbox(); - var handled = toolbox.onBlocklyAction(action); - if (!handled && action.name === Blockly.navigation.actionNames.IN) { - Blockly.navigation.focusFlyout_(); + var workspace = Blockly.getMainWorkspace(); + var toolbox = workspace.getToolbox(); + var handled = toolbox ? toolbox.onBlocklyAction(action) : false; + + if (handled) { return true; } - return handled; + if (action.name === Blockly.navigation.actionNames.TOOLBOX) { + if (!workspace.getToolbox()) { + Blockly.navigation.focusFlyout_(); + } else { + Blockly.navigation.focusToolbox_(); + } + return true; + } else if (action.name === Blockly.navigation.actionNames.IN) { + Blockly.navigation.focusFlyout_(); + return true; + } else if (action.name === Blockly.navigation.actionNames.EXIT) { + Blockly.navigation.focusWorkspace_(); + return true; + } + return false; +}; + +/** + * Handles the given action for the workspace. + * @param {!Blockly.Action} action The action to handle. + * @return {boolean} True if the action has been handled, false otherwise. + * @private + */ +Blockly.navigation.workspaceOnAction_ = function(action) { + if (Blockly.getMainWorkspace().getCursor().onBlocklyAction(action)) { + return true; + } + switch (action.name) { + case Blockly.navigation.actionNames.INSERT: + Blockly.navigation.modify_(); + return true; + case Blockly.navigation.actionNames.MARK: + Blockly.navigation.handleEnterForWS_(); + return true; + case Blockly.navigation.actionNames.DISCONNECT: + Blockly.navigation.disconnectBlocks_(); + return true; + default: + return false; + } }; /**