mirror of
https://github.com/google/blockly.git
synced 2026-01-10 02:17:09 +01:00
Merge toolbox navigation (#3054)
* Merge both types of keyboard navigation (with and without accessibility mode).
This commit is contained in:
@@ -40,7 +40,7 @@ goog.require('Blockly.utils.style');
|
||||
*
|
||||
* @param {string} content The content of the node label treated as
|
||||
* plain-text and will be HTML escaped.
|
||||
* @param {Blockly.tree.BaseNode.Config} config The configuration for the tree.
|
||||
* @param {!Blockly.tree.BaseNode.Config} config The configuration for the tree.
|
||||
* @constructor
|
||||
* @extends {Blockly.Component}
|
||||
*/
|
||||
@@ -49,7 +49,7 @@ Blockly.tree.BaseNode = function(content, config) {
|
||||
|
||||
/**
|
||||
* The configuration for the tree.
|
||||
* @type {Blockly.tree.BaseNode.Config}
|
||||
* @type {!Blockly.tree.BaseNode.Config}
|
||||
* @private
|
||||
*/
|
||||
this.config_ = config;
|
||||
@@ -487,6 +487,17 @@ Blockly.tree.BaseNode.prototype.select = function() {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Selects the first node.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.tree.BaseNode.prototype.selectFirst = function() {
|
||||
var tree = this.getTree();
|
||||
if (tree && this.firstChild_) {
|
||||
tree.setSelectedItem(this.firstChild_);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Called from the tree to instruct the node change its selection state.
|
||||
* @param {boolean} selected The new selection state.
|
||||
@@ -885,7 +896,7 @@ Blockly.tree.BaseNode.prototype.getElement = function() {
|
||||
/**
|
||||
* @return {Element} The row is the div that is used to draw the node without
|
||||
* the children.
|
||||
* @protected
|
||||
* @package
|
||||
*/
|
||||
Blockly.tree.BaseNode.prototype.getRowElement = function() {
|
||||
var el = this.getElement();
|
||||
@@ -1116,43 +1127,22 @@ Blockly.tree.BaseNode.prototype.onKeyDown = function(e) {
|
||||
if (e.altKey) {
|
||||
break;
|
||||
}
|
||||
if (this.hasChildren()) {
|
||||
if (!this.getExpanded()) {
|
||||
this.setExpanded(true);
|
||||
} else {
|
||||
this.getFirstChild().select();
|
||||
}
|
||||
}
|
||||
handled = this.selectChild();
|
||||
break;
|
||||
|
||||
case Blockly.utils.KeyCodes.LEFT:
|
||||
if (e.altKey) {
|
||||
break;
|
||||
}
|
||||
if (this.hasChildren() && this.getExpanded() && this.isUserCollapsible_) {
|
||||
this.setExpanded(false);
|
||||
} else {
|
||||
var parent = this.getParent();
|
||||
var tree = this.getTree();
|
||||
// don't go to root if hidden
|
||||
if (parent && (parent != tree)) {
|
||||
parent.select();
|
||||
}
|
||||
}
|
||||
handled = this.selectParent();
|
||||
break;
|
||||
|
||||
case Blockly.utils.KeyCodes.DOWN:
|
||||
var nextNode = this.getNextShownNode();
|
||||
if (nextNode) {
|
||||
nextNode.select();
|
||||
}
|
||||
handled = this.selectNext();
|
||||
break;
|
||||
|
||||
case Blockly.utils.KeyCodes.UP:
|
||||
var previousNode = this.getPreviousShownNode();
|
||||
if (previousNode) {
|
||||
previousNode.select();
|
||||
}
|
||||
handled = this.selectPrevious();
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -1166,6 +1156,70 @@ Blockly.tree.BaseNode.prototype.onKeyDown = function(e) {
|
||||
return handled;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Select the next node.
|
||||
* @return {boolean} True if the action has been handled, false otherwise.
|
||||
* @package
|
||||
*/
|
||||
Blockly.tree.BaseNode.prototype.selectNext = function() {
|
||||
var nextNode = this.getNextShownNode();
|
||||
if (nextNode) {
|
||||
nextNode.select();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Select the previous node.
|
||||
* @return {boolean} True if the action has been handled, false otherwise.
|
||||
* @package
|
||||
*/
|
||||
Blockly.tree.BaseNode.prototype.selectPrevious = function() {
|
||||
var previousNode = this.getPreviousShownNode();
|
||||
if (previousNode) {
|
||||
previousNode.select();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Select the parent node or collapse the current node.
|
||||
* @return {boolean} True if the action has been handled, false otherwise.
|
||||
* @package
|
||||
*/
|
||||
Blockly.tree.BaseNode.prototype.selectParent = function() {
|
||||
if (this.hasChildren() && this.getExpanded() && this.isUserCollapsible_) {
|
||||
this.setExpanded(false);
|
||||
} else {
|
||||
var parent = this.getParent();
|
||||
var tree = this.getTree();
|
||||
// don't go to root if hidden
|
||||
if (parent && (parent != tree)) {
|
||||
parent.select();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Expand the current node if it's not already expanded, or select the
|
||||
* child node.
|
||||
* @return {boolean} True if the action has been handled, false otherwise.
|
||||
* @package
|
||||
*/
|
||||
Blockly.tree.BaseNode.prototype.selectChild = function() {
|
||||
if (this.hasChildren()) {
|
||||
if (!this.getExpanded()) {
|
||||
this.setExpanded(true);
|
||||
} else {
|
||||
this.getFirstChild().select();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {Blockly.tree.BaseNode} The last shown descendant.
|
||||
* @protected
|
||||
@@ -1222,7 +1276,7 @@ Blockly.tree.BaseNode.prototype.getPreviousShownNode = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {Blockly.tree.BaseNode.Config} The configuration for the tree.
|
||||
* @return {!Blockly.tree.BaseNode.Config} The configuration for the tree.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.tree.BaseNode.prototype.getConfig = function() {
|
||||
|
||||
@@ -40,7 +40,7 @@ goog.require('Blockly.utils.style');
|
||||
* Similar to Closure's goog.ui.tree.TreeControl
|
||||
*
|
||||
* @param {Blockly.Toolbox} toolbox The parent toolbox for this tree.
|
||||
* @param {Blockly.tree.BaseNode.Config} config The configuration for the tree.
|
||||
* @param {!Blockly.tree.BaseNode.Config} config The configuration for the tree.
|
||||
* @constructor
|
||||
* @extends {Blockly.tree.BaseNode}
|
||||
*/
|
||||
|
||||
@@ -39,7 +39,7 @@ goog.require('Blockly.utils.KeyCodes');
|
||||
* @param {Blockly.Toolbox} toolbox The parent toolbox for this tree.
|
||||
* @param {string} content The content of the node label treated as
|
||||
* plain-text and will be HTML escaped.
|
||||
* @param {Blockly.tree.BaseNode.Config} config The configuration for the tree.
|
||||
* @param {!Blockly.tree.BaseNode.Config} config The configuration for the tree.
|
||||
* @constructor
|
||||
* @extends {Blockly.tree.BaseNode}
|
||||
*/
|
||||
|
||||
@@ -31,6 +31,7 @@ goog.require('Blockly.Events');
|
||||
goog.require('Blockly.Events.BlockChange');
|
||||
goog.require('Blockly.Field');
|
||||
goog.require('Blockly.fieldRegistry');
|
||||
goog.require('Blockly.navigation');
|
||||
goog.require('Blockly.utils.aria');
|
||||
goog.require('Blockly.utils.colour');
|
||||
goog.require('Blockly.utils.dom');
|
||||
|
||||
@@ -33,14 +33,6 @@ goog.require('Blockly.utils.Coordinate');
|
||||
goog.require('Blockly.user.keyMap');
|
||||
|
||||
|
||||
/**
|
||||
* The current selected category if the toolbox is open or
|
||||
* last selected category if focus is on a different element.
|
||||
* @type {Blockly.tree.BaseNode}
|
||||
* @private
|
||||
*/
|
||||
Blockly.navigation.currentCategory_ = null;
|
||||
|
||||
/**
|
||||
* A function to call to give feedback to the user about logs, warnings, and
|
||||
* errors. You can override this to customize feedback (e.g. warning sounds,
|
||||
@@ -154,94 +146,7 @@ Blockly.navigation.focusToolbox_ = function() {
|
||||
if (!Blockly.getMainWorkspace().getMarker().getCurNode()) {
|
||||
Blockly.navigation.markAtCursor_();
|
||||
}
|
||||
if (workspace && !Blockly.navigation.currentCategory_) {
|
||||
Blockly.navigation.currentCategory_ = toolbox.tree_.firstChild_;
|
||||
}
|
||||
toolbox.tree_.setSelectedItem(Blockly.navigation.currentCategory_);
|
||||
};
|
||||
|
||||
/**
|
||||
* Select the next category.
|
||||
* Taken from closure/goog/ui/tree/basenode.js
|
||||
* @private
|
||||
*/
|
||||
Blockly.navigation.nextCategory_ = function() {
|
||||
if (!Blockly.navigation.currentCategory_) {
|
||||
return;
|
||||
}
|
||||
var curCategory = Blockly.navigation.currentCategory_;
|
||||
var nextNode = curCategory.getNextShownNode();
|
||||
|
||||
if (nextNode) {
|
||||
nextNode.select();
|
||||
Blockly.navigation.currentCategory_ = nextNode;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Select the previous category.
|
||||
* Taken from closure/goog/ui/tree/basenode.js
|
||||
* @private
|
||||
*/
|
||||
Blockly.navigation.previousCategory_ = function() {
|
||||
if (!Blockly.navigation.currentCategory_) {
|
||||
return;
|
||||
}
|
||||
var curCategory = Blockly.navigation.currentCategory_;
|
||||
var previousNode = curCategory.getPreviousShownNode();
|
||||
|
||||
if (previousNode) {
|
||||
previousNode.select();
|
||||
Blockly.navigation.currentCategory_ = previousNode;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Go to child category if there is a nested category.
|
||||
* Taken from closure/goog/ui/tree/basenode.js
|
||||
* @private
|
||||
*/
|
||||
Blockly.navigation.inCategory_ = function() {
|
||||
if (!Blockly.navigation.currentCategory_) {
|
||||
return;
|
||||
}
|
||||
var curCategory = Blockly.navigation.currentCategory_;
|
||||
|
||||
if (curCategory.hasChildren()) {
|
||||
if (!curCategory.getExpanded()) {
|
||||
curCategory.setExpanded(true);
|
||||
} else {
|
||||
curCategory.getFirstChild().select();
|
||||
Blockly.navigation.currentCategory_ = curCategory.getFirstChild();
|
||||
}
|
||||
} else {
|
||||
Blockly.navigation.focusFlyout_();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Go to parent category if we are in a child category.
|
||||
* Taken from closure/goog/ui/tree/basenode.js
|
||||
* @private
|
||||
*/
|
||||
Blockly.navigation.outCategory_ = function() {
|
||||
if (!Blockly.navigation.currentCategory_) {
|
||||
return;
|
||||
}
|
||||
var curCategory = Blockly.navigation.currentCategory_;
|
||||
|
||||
if (curCategory.hasChildren() && curCategory.getExpanded() && curCategory.isUserCollapsible()) {
|
||||
curCategory.setExpanded(false);
|
||||
} else {
|
||||
var parent = curCategory.getParent();
|
||||
var tree = curCategory.getTree();
|
||||
if (parent && parent != tree) {
|
||||
parent.select();
|
||||
|
||||
Blockly.navigation.currentCategory_ = /** @type {Blockly.tree.BaseNode} */
|
||||
(parent);
|
||||
}
|
||||
}
|
||||
toolbox.selectFirstCategory();
|
||||
};
|
||||
|
||||
/***********************/
|
||||
@@ -923,25 +828,18 @@ Blockly.navigation.flyoutOnAction_ = function(action) {
|
||||
* @private
|
||||
*/
|
||||
Blockly.navigation.toolboxOnAction_ = function(action) {
|
||||
switch (action.name) {
|
||||
case Blockly.navigation.actionNames.PREVIOUS:
|
||||
Blockly.navigation.previousCategory_();
|
||||
return true;
|
||||
case Blockly.navigation.actionNames.OUT:
|
||||
Blockly.navigation.outCategory_();
|
||||
return true;
|
||||
case Blockly.navigation.actionNames.NEXT:
|
||||
Blockly.navigation.nextCategory_();
|
||||
return true;
|
||||
case Blockly.navigation.actionNames.IN:
|
||||
Blockly.navigation.inCategory_();
|
||||
return true;
|
||||
case Blockly.navigation.actionNames.EXIT:
|
||||
Blockly.navigation.focusWorkspace_();
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
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_();
|
||||
return true;
|
||||
}
|
||||
|
||||
return handled;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,6 +26,20 @@
|
||||
goog.provide('Blockly.Theme');
|
||||
|
||||
|
||||
/**
|
||||
* Class for a theme.
|
||||
* @param {!Object.<string, Blockly.Theme.BlockStyle>} blockStyles A map from style
|
||||
* names (strings) to objects with style attributes relating to blocks.
|
||||
* @param {!Object.<string, Blockly.Theme.CategoryStyle>} categoryStyles A map from
|
||||
* style names (strings) to objects with style attributes relating to
|
||||
* categories.
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.Theme = function(blockStyles, categoryStyles) {
|
||||
this.blockStyles_ = blockStyles;
|
||||
this.categoryStyles_ = categoryStyles;
|
||||
};
|
||||
|
||||
/**
|
||||
* A block style.
|
||||
* @typedef {{
|
||||
@@ -45,20 +59,6 @@ Blockly.Theme.BlockStyle;
|
||||
*/
|
||||
Blockly.Theme.CategoryStyle;
|
||||
|
||||
/**
|
||||
* Class for a theme.
|
||||
* @param {!Object.<string, Blockly.Theme.BlockStyle>} blockStyles A map from style
|
||||
* names (strings) to objects with style attributes relating to blocks.
|
||||
* @param {!Object.<string, Blockly.Theme.CategoryStyle>} categoryStyles A map from
|
||||
* style names (strings) to objects with style attributes relating to
|
||||
* categories.
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.Theme = function(blockStyles, categoryStyles) {
|
||||
this.blockStyles_ = blockStyles;
|
||||
this.categoryStyles_ = categoryStyles;
|
||||
};
|
||||
|
||||
/**
|
||||
* Overrides or adds all values from blockStyles to blockStyles_
|
||||
* @param {Object.<string, Blockly.Theme.BlockStyle>} blockStyles Map of
|
||||
|
||||
@@ -30,6 +30,7 @@ goog.require('Blockly.Events');
|
||||
goog.require('Blockly.Events.Ui');
|
||||
goog.require('Blockly.Flyout');
|
||||
goog.require('Blockly.HorizontalFlyout');
|
||||
goog.require('Blockly.navigation');
|
||||
goog.require('Blockly.Touch');
|
||||
goog.require('Blockly.tree.TreeControl');
|
||||
goog.require('Blockly.tree.TreeNode');
|
||||
@@ -77,7 +78,7 @@ Blockly.Toolbox = function(workspace) {
|
||||
|
||||
/**
|
||||
* Configuration constants for Closure's tree UI.
|
||||
* @type {Object.<string,*>}
|
||||
* @type {!Object.<string,*>}
|
||||
* @private
|
||||
*/
|
||||
this.config_ = {
|
||||
@@ -96,7 +97,7 @@ Blockly.Toolbox = function(workspace) {
|
||||
|
||||
/**
|
||||
* Configuration constants for tree separator.
|
||||
* @type {Object.<string,*>}
|
||||
* @type {!Object.<string,*>}
|
||||
* @private
|
||||
*/
|
||||
this.treeSeparatorConfig_ = {
|
||||
@@ -198,12 +199,16 @@ Blockly.Toolbox.prototype.init = function() {
|
||||
this.config_['cleardotPath'] = workspace.options.pathToMedia + '1x1.gif';
|
||||
this.config_['cssCollapsedFolderIcon'] =
|
||||
'blocklyTreeIconClosed' + (workspace.RTL ? 'Rtl' : 'Ltr');
|
||||
var tree = new Blockly.tree.TreeControl(this, this.config_);
|
||||
var tree = new Blockly.tree.TreeControl(this,
|
||||
/** @type {!Blockly.tree.BaseNode.Config} */ (this.config_));
|
||||
this.tree_ = tree;
|
||||
tree.setSelectedItem(null);
|
||||
tree.onBeforeSelected(this.handleBeforeTreeSelected_);
|
||||
tree.onAfterSelected(this.handleAfterTreeSelected_);
|
||||
var openNode = this.populate_(workspace.options.languageTree);
|
||||
var openNode = null;
|
||||
if (workspace.options.languageTree) {
|
||||
openNode = this.populate_(workspace.options.languageTree);
|
||||
}
|
||||
tree.render(this.HtmlDiv);
|
||||
if (openNode) {
|
||||
tree.setSelectedItem(openNode);
|
||||
@@ -214,7 +219,7 @@ Blockly.Toolbox.prototype.init = function() {
|
||||
// Trees have an implicit orientation of vertical, so we only need to set this
|
||||
// when the toolbox is in horizontal mode.
|
||||
if (this.horizontalLayout_) {
|
||||
Blockly.utils.aria.setState(this.tree_.getElement(),
|
||||
Blockly.utils.aria.setState(/** @type {!Element} */ (this.tree_.getElement()),
|
||||
Blockly.utils.aria.State.ORIENTATION, 'horizontal');
|
||||
}
|
||||
};
|
||||
@@ -281,6 +286,32 @@ Blockly.Toolbox.prototype.handleNodeSizeChanged_ = function() {
|
||||
Blockly.svgResize(this.workspace_);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the given Blockly action on a toolbox.
|
||||
* This is only triggered when keyboard accessibility mode is enabled.
|
||||
* @param {!Blockly.Action} action The action to be handled.
|
||||
* @return {boolean} True if the field handled the action, false otherwise.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Toolbox.prototype.onBlocklyAction = function(action) {
|
||||
var selected = this.tree_.getSelectedItem();
|
||||
if (!selected) {
|
||||
return false;
|
||||
}
|
||||
switch (action.name) {
|
||||
case Blockly.navigation.actionNames.PREVIOUS:
|
||||
return selected.selectPrevious();
|
||||
case Blockly.navigation.actionNames.OUT:
|
||||
return selected.selectParent();
|
||||
case Blockly.navigation.actionNames.NEXT:
|
||||
return selected.selectNext();
|
||||
case Blockly.navigation.actionNames.IN:
|
||||
return selected.selectChild();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Dispose of this toolbox.
|
||||
*/
|
||||
@@ -344,7 +375,7 @@ Blockly.Toolbox.prototype.position = function() {
|
||||
/**
|
||||
* Fill the toolbox with categories and blocks.
|
||||
* @param {!Node} newTree DOM tree of blocks.
|
||||
* @return {Node} Tree node to open at startup (or null).
|
||||
* @return {Blockly.tree.BaseNode} Tree node to open at startup (or null).
|
||||
* @private
|
||||
*/
|
||||
Blockly.Toolbox.prototype.populate_ = function(newTree) {
|
||||
@@ -370,7 +401,7 @@ Blockly.Toolbox.prototype.populate_ = function(newTree) {
|
||||
* @param {!Blockly.tree.BaseNode} treeOut The TreeControl or TreeNode
|
||||
* object built from treeIn.
|
||||
* @param {string} pathToMedia The path to the Blockly media directory.
|
||||
* @return {Node} Tree node to open at startup (or null).
|
||||
* @return {Blockly.tree.BaseNode} Tree node to open at startup (or null).
|
||||
* @private
|
||||
*/
|
||||
Blockly.Toolbox.prototype.syncTrees_ = function(treeIn, treeOut, pathToMedia) {
|
||||
@@ -432,7 +463,7 @@ Blockly.Toolbox.prototype.syncTrees_ = function(treeIn, treeOut, pathToMedia) {
|
||||
// Separator between two categories.
|
||||
// <sep></sep>
|
||||
treeOut.add(new Blockly.Toolbox.TreeSeparator(
|
||||
this.treeSeparatorConfig_));
|
||||
/** @type {!Blockly.tree.BaseNode.Config} */ (this.treeSeparatorConfig_)));
|
||||
break;
|
||||
}
|
||||
// Otherwise falls through.
|
||||
@@ -648,9 +679,20 @@ Blockly.Toolbox.prototype.refreshSelection = function() {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Select the first toolbox category if no category is selected.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Toolbox.prototype.selectFirstCategory = function() {
|
||||
var selectedItem = this.tree_.getSelectedItem();
|
||||
if (!selectedItem) {
|
||||
this.tree_.selectFirst();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A blank separator node in the tree.
|
||||
* @param {Blockly.tree.BaseNode.Config} config The configuration for the tree.
|
||||
* @param {!Blockly.tree.BaseNode.Config} config The configuration for the tree.
|
||||
* @constructor
|
||||
* @extends {Blockly.tree.TreeNode}
|
||||
*/
|
||||
|
||||
@@ -1,4 +1,40 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2019 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Navigation tests.
|
||||
* @author aschmiedt@google.com (Abby Schmiedt)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
|
||||
suite('Navigation', function() {
|
||||
function createNavigationWorkspace(enableKeyboardNav) {
|
||||
var toolbox = document.getElementById('toolbox-categories');
|
||||
var workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox});
|
||||
if (enableKeyboardNav) {
|
||||
Blockly.navigation.enableKeyboardAccessibility();
|
||||
Blockly.navigation.currentState_ = Blockly.navigation.STATE_WS;
|
||||
}
|
||||
return workspace;
|
||||
}
|
||||
|
||||
// Test that toolbox key handlers call through to the right functions and
|
||||
// transition correctly between toolbox, workspace, and flyout.
|
||||
@@ -15,8 +51,7 @@ suite('Navigation', function() {
|
||||
}
|
||||
]
|
||||
}]);
|
||||
var toolbox = document.getElementById('toolbox-categories');
|
||||
this.workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox});
|
||||
this.workspace = createNavigationWorkspace(true);
|
||||
Blockly.navigation.focusToolbox_();
|
||||
this.mockEvent = {
|
||||
getModifierState: function() {
|
||||
@@ -31,7 +66,6 @@ suite('Navigation', function() {
|
||||
teardown(function() {
|
||||
delete Blockly.Blocks['basic_block'];
|
||||
this.workspace.dispose();
|
||||
Blockly.navigation.currentCategory_ = null;
|
||||
});
|
||||
|
||||
test('Next', function() {
|
||||
@@ -39,43 +73,45 @@ suite('Navigation', function() {
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_TOOLBOX);
|
||||
chai.assert.equal(Blockly.navigation.currentCategory_,
|
||||
chai.assert.equal(this.workspace.getToolbox().tree_.getSelectedItem(),
|
||||
this.secondCategory_);
|
||||
});
|
||||
|
||||
// Should be a no-op.
|
||||
test('Next at end', function() {
|
||||
Blockly.navigation.nextCategory_();
|
||||
this.workspace.getToolbox().tree_.getSelectedItem().selectNext();
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.S;
|
||||
var startCategory = Blockly.navigation.currentCategory_;
|
||||
// Go forward one so that we can go back one.
|
||||
Blockly.navigation.onKeyPress(this.mockEvent);
|
||||
var startCategory = this.workspace.getToolbox().tree_.getSelectedItem();
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_TOOLBOX);
|
||||
chai.assert.equal(Blockly.navigation.currentCategory_,
|
||||
chai.assert.equal(this.workspace.getToolbox().tree_.getSelectedItem(),
|
||||
startCategory);
|
||||
});
|
||||
|
||||
test('Previous', function() {
|
||||
// Go forward one so that we can go back one:
|
||||
Blockly.navigation.nextCategory_();
|
||||
this.workspace.getToolbox().tree_.getSelectedItem().selectNext();
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.W;
|
||||
chai.assert.equal(Blockly.navigation.currentCategory_,
|
||||
chai.assert.equal(this.workspace.getToolbox().tree_.getSelectedItem(),
|
||||
this.secondCategory_);
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_TOOLBOX);
|
||||
chai.assert.equal(Blockly.navigation.currentCategory_,
|
||||
chai.assert.equal(this.workspace.getToolbox().tree_.getSelectedItem(),
|
||||
this.firstCategory_);
|
||||
});
|
||||
|
||||
// Should be a no-op.
|
||||
test('Previous at start', function() {
|
||||
var startCategory = Blockly.navigation.currentCategory_;
|
||||
var startCategory = this.workspace.getToolbox().tree_.getSelectedItem();
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.W;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_TOOLBOX);
|
||||
chai.assert.equal(Blockly.navigation.currentCategory_,
|
||||
chai.assert.equal(this.workspace.getToolbox().tree_.getSelectedItem(),
|
||||
startCategory);
|
||||
});
|
||||
|
||||
@@ -132,8 +168,7 @@ suite('Navigation', function() {
|
||||
}
|
||||
]
|
||||
}]);
|
||||
var toolbox = document.getElementById('toolbox-categories');
|
||||
this.workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox});
|
||||
this.workspace = createNavigationWorkspace(true);
|
||||
Blockly.navigation.focusToolbox_();
|
||||
Blockly.navigation.focusFlyout_();
|
||||
this.mockEvent = {
|
||||
@@ -146,7 +181,6 @@ suite('Navigation', function() {
|
||||
teardown(function() {
|
||||
delete Blockly.Blocks['basic_block'];
|
||||
this.workspace.dispose();
|
||||
Blockly.navigation.currentCategory_ = null;
|
||||
});
|
||||
|
||||
// Should be a no-op
|
||||
@@ -225,9 +259,7 @@ suite('Navigation', function() {
|
||||
"previousStatement": null,
|
||||
"nextStatement": null
|
||||
}]);
|
||||
var toolbox = document.getElementById('toolbox-categories');
|
||||
this.workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox});
|
||||
Blockly.navigation.enableKeyboardAccessibility();
|
||||
this.workspace = createNavigationWorkspace(true);
|
||||
this.basicBlock = this.workspace.newBlock('basic_block');
|
||||
this.firstCategory_ = this.workspace.getToolbox().tree_.firstChild_;
|
||||
this.mockEvent = {
|
||||
@@ -240,7 +272,6 @@ suite('Navigation', function() {
|
||||
teardown(function() {
|
||||
delete Blockly.Blocks['basic_block'];
|
||||
this.workspace.dispose();
|
||||
Blockly.navigation.currentCategory_ = null;
|
||||
});
|
||||
|
||||
test('Previous', function() {
|
||||
@@ -310,7 +341,7 @@ suite('Navigation', function() {
|
||||
test('Toolbox', function() {
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.T;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
chai.assert.equal(Blockly.navigation.currentCategory_, this.firstCategory_);
|
||||
chai.assert.equal(this.workspace.getToolbox().tree_.getSelectedItem(), this.firstCategory_);
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_TOOLBOX);
|
||||
});
|
||||
@@ -322,6 +353,7 @@ suite('Navigation', function() {
|
||||
Blockly.user.keyMap.setKeyMap(Blockly.user.keyMap.createDefaultKeyMap());
|
||||
Blockly.mainWorkspace = this.workspace;
|
||||
Blockly.keyboardAccessibilityMode = true;
|
||||
Blockly.navigation.currentState_ = Blockly.navigation.STATE_WS;
|
||||
|
||||
this.mockEvent = {
|
||||
getModifierState: function() {
|
||||
@@ -330,7 +362,9 @@ suite('Navigation', function() {
|
||||
};
|
||||
});
|
||||
test('Action does not exist', function() {
|
||||
var block = new Blockly.Block(this.workspace);
|
||||
var field = new Blockly.FieldDropdown([['a','b'], ['c','d']]);
|
||||
field.setSourceBlock(block);
|
||||
sinon.spy(field, 'onBlocklyAction');
|
||||
this.workspace.getCursor().setCurNode(Blockly.ASTNode.createFieldNode(field));
|
||||
|
||||
@@ -343,7 +377,9 @@ suite('Navigation', function() {
|
||||
});
|
||||
|
||||
test('Action exists - field handles action', function() {
|
||||
var block = new Blockly.Block(this.workspace);
|
||||
var field = new Blockly.FieldDropdown([['a','b'], ['c','d']]);
|
||||
field.setSourceBlock(block);
|
||||
sinon.stub(field, 'onBlocklyAction').callsFake(function(){
|
||||
return true;
|
||||
});
|
||||
@@ -357,7 +393,9 @@ suite('Navigation', function() {
|
||||
});
|
||||
|
||||
test('Action exists - field does not handle action', function() {
|
||||
var block = new Blockly.Block(this.workspace);
|
||||
var field = new Blockly.FieldDropdown([['a','b'], ['c','d']]);
|
||||
field.setSourceBlock(block);
|
||||
sinon.spy(field, 'onBlocklyAction');
|
||||
this.workspace.getCursor().setCurNode(Blockly.ASTNode.createFieldNode(field));
|
||||
|
||||
@@ -424,8 +462,10 @@ suite('Navigation', function() {
|
||||
this.workspace = new Blockly.Workspace({readOnly: true});
|
||||
this.workspace.setCursor(new Blockly.Cursor());
|
||||
Blockly.mainWorkspace = this.workspace;
|
||||
this.fieldBlock1 = this.workspace.newBlock('field_block');
|
||||
Blockly.keyboardAccessibilityMode = true;
|
||||
Blockly.navigation.currentState_ = Blockly.navigation.STATE_WS;
|
||||
|
||||
this.fieldBlock1 = this.workspace.newBlock('field_block');
|
||||
this.mockEvent = {
|
||||
getModifierState: function() {
|
||||
return false;
|
||||
@@ -435,7 +475,6 @@ suite('Navigation', function() {
|
||||
|
||||
teardown(function() {
|
||||
delete Blockly.Blocks['field_block'];
|
||||
Blockly.mainWorkspace = null;
|
||||
this.workspace.dispose();
|
||||
});
|
||||
|
||||
@@ -472,8 +511,7 @@ suite('Navigation', function() {
|
||||
"nextStatement": null,
|
||||
}]);
|
||||
|
||||
var toolbox = document.getElementById('toolbox-categories');
|
||||
this.workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox});
|
||||
this.workspace = createNavigationWorkspace(true);
|
||||
|
||||
var basicBlock = this.workspace.newBlock('basic_block');
|
||||
var basicBlock2 = this.workspace.newBlock('basic_block');
|
||||
@@ -485,7 +523,6 @@ suite('Navigation', function() {
|
||||
teardown(function() {
|
||||
delete Blockly.Blocks['basic_block'];
|
||||
this.workspace.dispose();
|
||||
Blockly.navigation.currentCategory_ = null;
|
||||
});
|
||||
|
||||
test('Insert from flyout with a valid connection marked', function() {
|
||||
@@ -562,8 +599,7 @@ suite('Navigation', function() {
|
||||
"helpUrl": ""
|
||||
}]);
|
||||
|
||||
var toolbox = document.getElementById('toolbox-categories');
|
||||
this.workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox});
|
||||
this.workspace = createNavigationWorkspace(true);
|
||||
|
||||
var basicBlock = this.workspace.newBlock('basic_block');
|
||||
var basicBlock2 = this.workspace.newBlock('basic_block');
|
||||
@@ -592,7 +628,6 @@ suite('Navigation', function() {
|
||||
teardown(function() {
|
||||
delete Blockly.Blocks['basic_block'];
|
||||
this.workspace.dispose();
|
||||
Blockly.navigation.currentCategory_ = null;
|
||||
});
|
||||
|
||||
test('Connect cursor on previous into stack', function() {
|
||||
@@ -655,9 +690,7 @@ suite('Navigation', function() {
|
||||
"previousStatement": null,
|
||||
"nextStatement": null,
|
||||
}]);
|
||||
var toolbox = document.getElementById('toolbox-categories');
|
||||
this.workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox});
|
||||
Blockly.navigation.enableKeyboardAccessibility();
|
||||
this.workspace = createNavigationWorkspace(true);
|
||||
this.basicBlockA = this.workspace.newBlock('basic_block');
|
||||
this.basicBlockB = this.workspace.newBlock('basic_block');
|
||||
});
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
*/
|
||||
|
||||
goog.require('Blockly.Blocks.procedures');
|
||||
goog.require('Blockly.Msg.en');
|
||||
goog.require('Blockly.Msg');
|
||||
|
||||
suite('Procedures XML', function() {
|
||||
suite('Deserialization', function() {
|
||||
|
||||
Reference in New Issue
Block a user