mirror of
https://github.com/google/blockly.git
synced 2026-01-05 08:00:09 +01:00
Add keyboard navigation support for multiple workspaces (#3352)
* Add keyboard navigation support for multiple workspaces
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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())) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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."
|
||||
}
|
||||
|
||||
@@ -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."
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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');
|
||||
|
||||
Reference in New Issue
Block a user