Add keyboard navigation support for multiple workspaces (#3352)

* Add keyboard navigation support for multiple workspaces
This commit is contained in:
alschmiedt
2019-10-28 12:53:51 -07:00
committed by GitHub
parent cbc79444f6
commit cbf867f441
15 changed files with 51 additions and 36 deletions

View File

@@ -325,7 +325,7 @@ Blockly.Block.prototype.dispose = function(healStack) {
this.workspace.removeChangeListener(this.onchangeWrapper_);
}
if (Blockly.keyboardAccessibilityMode) {
if (this.workspace.keyboardAccessibilityMode) {
// No-op if this is called from the block_svg class.
Blockly.navigation.moveCursorOnBlockDelete(this);
}

View File

@@ -1031,7 +1031,7 @@ Blockly.BlockSvg.prototype.dispose = function(healStack, animate) {
Blockly.ContextMenu.hide();
}
if (Blockly.keyboardAccessibilityMode) {
if (this.workspace.keyboardAccessibilityMode) {
Blockly.navigation.moveCursorOnBlockDelete(this);
}

View File

@@ -72,12 +72,6 @@ Blockly.selected = null;
*/
Blockly.cursor = null;
/**
* Whether or not we're currently in keyboard accessibility mode.
* @type {boolean}
*/
Blockly.keyboardAccessibilityMode = false;
/**
* All of the connections on blocks that are currently being dragged.
* @type {!Array.<!Blockly.Connection>}
@@ -183,6 +177,9 @@ 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) {
return;
}
if (Blockly.utils.isTargetInput(e) ||
(mainWorkspace.rendered && !mainWorkspace.isVisible())) {

View File

@@ -657,7 +657,7 @@ Blockly.Gesture.prototype.handleWsStart = function(e, ws) {
this.setStartWorkspace_(ws);
this.mostRecentEvent_ = e;
this.doStart(e);
if (Blockly.keyboardAccessibilityMode) {
if (this.startWorkspace_.keyboardAccessibilityMode) {
Blockly.navigation.setState(Blockly.navigation.STATE_WS);
}
};

View File

@@ -61,6 +61,10 @@ Blockly.inject = function(container, opt_options) {
(/** @type {!Blockly.BlocklyOptions} */ ({})));
var subContainer = document.createElement('div');
subContainer.className = 'injectionDiv';
subContainer.tabIndex = 0;
Blockly.utils.aria.setState(subContainer,
Blockly.utils.aria.State.LABEL, Blockly.Msg['WORKSPACE_ARIA_LABEL']);
container.appendChild(subContainer);
var svg = Blockly.createDom_(subContainer, options);
@@ -78,6 +82,14 @@ Blockly.inject = function(container, opt_options) {
Blockly.svgResize(workspace);
subContainer.addEventListener('focus', function() {
Blockly.mainWorkspace = workspace;
});
subContainer.addEventListener('blur', function() {
Blockly.mainWorkspace = null;
});
return workspace;
};

View File

@@ -666,8 +666,8 @@ Blockly.navigation.moveCursorOnBlockMutation = function(mutatedBlock) {
* Enable accessibility mode.
*/
Blockly.navigation.enableKeyboardAccessibility = function() {
if (!Blockly.keyboardAccessibilityMode) {
Blockly.keyboardAccessibilityMode = true;
if (!Blockly.getMainWorkspace().keyboardAccessibilityMode) {
Blockly.getMainWorkspace().keyboardAccessibilityMode = true;
Blockly.navigation.focusWorkspace_();
}
};
@@ -676,9 +676,9 @@ Blockly.navigation.enableKeyboardAccessibility = function() {
* Disable accessibility mode.
*/
Blockly.navigation.disableKeyboardAccessibility = function() {
if (Blockly.keyboardAccessibilityMode) {
if (Blockly.getMainWorkspace().keyboardAccessibilityMode) {
var workspace = Blockly.getMainWorkspace();
Blockly.keyboardAccessibilityMode = false;
Blockly.getMainWorkspace().keyboardAccessibilityMode = false;
workspace.getCursor().hide();
workspace.getMarker().hide();
if (Blockly.navigation.getFlyoutCursor_()) {
@@ -758,7 +758,7 @@ Blockly.navigation.onBlocklyAction = function(action) {
var readOnly = Blockly.getMainWorkspace().options.readOnly;
var actionHandled = false;
if (Blockly.keyboardAccessibilityMode) {
if (Blockly.getMainWorkspace().keyboardAccessibilityMode) {
if (!readOnly) {
actionHandled = Blockly.navigation.handleActions_(action);
// If in readonly mode only handle valid actions.

View File

@@ -378,7 +378,8 @@ Blockly.Mutator.prototype.workspaceChanged_ = function(e) {
block.render();
}
if (oldMutation != newMutation && Blockly.keyboardAccessibilityMode) {
if (oldMutation != newMutation &&
this.workspace_.keyboardAccessibilityMode) {
Blockly.navigation.moveCursorOnBlockMutation(block);
}
// Don't update the bubble until the drag has ended, to avoid moving blocks

View File

@@ -296,13 +296,13 @@ Blockly.Toolbox.prototype.handleAfterTreeSelected_ = function(
if (this.lastCategory_ != newNode) {
this.flyout_.scrollToStart();
}
if (Blockly.keyboardAccessibilityMode) {
if (this.workspace_.keyboardAccessibilityMode) {
Blockly.navigation.setState(Blockly.navigation.STATE_TOOLBOX);
}
} else {
// Hide the flyout.
this.flyout_.hide();
if (Blockly.keyboardAccessibilityMode &&
if (this.workspace_.keyboardAccessibilityMode &&
!(newNode instanceof Blockly.Toolbox.TreeSeparator)) {
Blockly.navigation.setState(Blockly.navigation.STATE_WS);
}

View File

@@ -138,6 +138,13 @@ Blockly.Workspace = function(opt_options) {
new Blockly.ThemeManager(this.options.theme || Blockly.Themes.Classic);
this.themeManager_.subscribeWorkspace(this);
/**
* True if keyboard accessibility mode is on, false otherwise.
* @type {boolean}
* @package
*/
this.keyboardAccessibilityMode = false;
};
/**

View File

@@ -1198,7 +1198,7 @@ Blockly.WorkspaceSvg.prototype.pasteBlock_ = function(xmlBlock) {
// Handle paste for keyboard navigation
var markedNode = this.getMarker().getCurNode();
if (Blockly.keyboardAccessibilityMode && markedNode &&
if (this.keyboardAccessibilityMode && markedNode &&
markedNode.isConnection()) {
var markedLocation =
/** @type {!Blockly.Connection} */ (markedNode.getLocation());

View File

@@ -1,7 +1,7 @@
{
"@metadata": {
"author": "Ellen Spertus <ellen.spertus@gmail.com>",
"lastupdated": "2019-10-04 13:16:14.900805",
"lastupdated": "2019-10-28 11:09:19.411955",
"locale": "en",
"messagedocumentation" : "qqq"
},
@@ -403,5 +403,6 @@
"PROCEDURES_IFRETURN_HELPURL": "http://c2.com/cgi/wiki?GuardClause",
"PROCEDURES_IFRETURN_WARNING": "Warning: This block may be used only within a function definition.",
"WORKSPACE_COMMENT_DEFAULT_TEXT": "Say something...",
"WORKSPACE_ARIA_LABEL": "Blockly Workspace",
"COLLAPSED_WARNINGS_WARNING": "Collapsed blocks contain warnings."
}

View File

@@ -1,13 +1,4 @@
{
"@metadata": {
"authors": [
"Espertus",
"Liuxinyu970226",
"Metalhead64",
"Robby",
"Shirayuki"
]
},
"VARIABLES_DEFAULT_NAME": "default name - A simple, general default name for a variable, preferably short. For more context, see [[Translating:Blockly#infrequent_message_types]].\n{{Identical|Item}}",
"UNNAMED_KEY": "default name - A simple, default name for an unnamed function or variable. Preferably indicates that the item is unnamed.",
"TODAY": "button text - Button that sets a calendar to today's date.\n{{Identical|Today}}",
@@ -406,5 +397,6 @@
"PROCEDURES_IFRETURN_HELPURL": "{{Optional}} url - Information about guard clauses.",
"PROCEDURES_IFRETURN_WARNING": "warning - This appears if the user tries to use this block outside of a function definition.",
"WORKSPACE_COMMENT_DEFAULT_TEXT": "comment text - This text appears in a new workspace comment, to hint that the user can type here.",
"WORKSPACE_ARIA_LABEL": "workspace - This text is read out when a user navigates to the workspace while using a screen reader.",
"COLLAPSED_WARNINGS_WARNING": "warning - This appears if the user collapses a block, and blocks inside that block have warnings attached to them. It should inform the user that the block they collapsed contains blocks that have warnings."
}

View File

@@ -1629,6 +1629,11 @@ Blockly.Msg.PROCEDURES_IFRETURN_WARNING = 'Warning: This block may be used only
/// the user can type here.
Blockly.Msg.WORKSPACE_COMMENT_DEFAULT_TEXT = 'Say something...';
/** @type {string} */
/// workspace - This text is read out when a user navigates to the workspace while
/// using a screen reader.
Blockly.Msg.WORKSPACE_ARIA_LABEL = 'Blockly Workspace';
/** @type {string} */
/// warning - This appears if the user collapses a block, and blocks inside
/// that block have warnings attached to them. It should inform the user that the

View File

@@ -90,8 +90,8 @@ suite('Gesture', function() {
};
var ws = Blockly.inject('blocklyDiv', {});
var gesture = new Blockly.Gesture(this.e, ws);
assertFalse(Blockly.keyboardAccessibilityMode);
assertFalse(Blockly.getMainWorkspace().keyboardAccessibilityMode);
gesture.doWorkspaceClick_(event);
assertTrue(Blockly.keyboardAccessibilityMode);
assertTrue(Blockly.getMainWorkspace().keyboardAccessibilityMode);
});
});

View File

@@ -349,7 +349,7 @@ suite('Navigation', function() {
this.workspace = new Blockly.Workspace({readOnly: false});
Blockly.user.keyMap.setKeyMap(Blockly.user.keyMap.createDefaultKeyMap());
Blockly.mainWorkspace = this.workspace;
Blockly.keyboardAccessibilityMode = true;
Blockly.getMainWorkspace().keyboardAccessibilityMode = true;
Blockly.navigation.currentState_ = Blockly.navigation.STATE_WS;
this.mockEvent = {
@@ -407,24 +407,24 @@ suite('Navigation', function() {
test('Toggle Action Off', function() {
this.mockEvent.keyCode = 'Control75';
sinon.spy(Blockly.navigation, 'onBlocklyAction');
Blockly.keyboardAccessibilityMode = true;
Blockly.getMainWorkspace().keyboardAccessibilityMode = true;
var isHandled = Blockly.navigation.onKeyPress(this.mockEvent);
chai.assert.isTrue(isHandled);
chai.assert.isTrue(Blockly.navigation.onBlocklyAction.calledOnce);
chai.assert.isFalse(Blockly.keyboardAccessibilityMode);
chai.assert.isFalse(Blockly.getMainWorkspace().keyboardAccessibilityMode);
Blockly.navigation.onBlocklyAction.restore();
});
test('Toggle Action On', function() {
this.mockEvent.keyCode = 'Control75';
sinon.stub(Blockly.navigation, 'focusWorkspace_');
Blockly.keyboardAccessibilityMode = false;
Blockly.getMainWorkspace().keyboardAccessibilityMode = false;
var isHandled = Blockly.navigation.onKeyPress(this.mockEvent);
chai.assert.isTrue(isHandled);
chai.assert.isTrue(Blockly.navigation.focusWorkspace_.calledOnce);
chai.assert.isTrue(Blockly.keyboardAccessibilityMode);
chai.assert.isTrue(Blockly.getMainWorkspace().keyboardAccessibilityMode);
Blockly.navigation.focusWorkspace_.restore();
this.workspace.dispose();
});
@@ -459,7 +459,7 @@ suite('Navigation', function() {
this.workspace = new Blockly.Workspace({readOnly: true});
this.workspace.setCursor(new Blockly.Cursor());
Blockly.mainWorkspace = this.workspace;
Blockly.keyboardAccessibilityMode = true;
Blockly.getMainWorkspace().keyboardAccessibilityMode = true;
Blockly.navigation.currentState_ = Blockly.navigation.STATE_WS;
this.fieldBlock1 = this.workspace.newBlock('field_block');