mirror of
https://github.com/google/blockly.git
synced 2026-01-09 10:00:09 +01:00
Remove keyboard navigation from core (#4593)
This commit is contained in:
@@ -24,7 +24,6 @@ goog.require('Blockly.Events.BlockMove');
|
||||
goog.require('Blockly.Extensions');
|
||||
goog.require('Blockly.fieldRegistry');
|
||||
goog.require('Blockly.Input');
|
||||
goog.require('Blockly.navigation');
|
||||
goog.require('Blockly.Tooltip');
|
||||
goog.require('Blockly.utils');
|
||||
goog.require('Blockly.utils.deprecation');
|
||||
|
||||
@@ -23,7 +23,6 @@ goog.require('Blockly.Events');
|
||||
goog.require('Blockly.Events.BlockMove');
|
||||
goog.require('Blockly.Events.Selected');
|
||||
goog.require('Blockly.Msg');
|
||||
goog.require('Blockly.navigation');
|
||||
goog.require('Blockly.RenderedConnection');
|
||||
goog.require('Blockly.TabNavigateCursor');
|
||||
goog.require('Blockly.Tooltip');
|
||||
@@ -692,11 +691,12 @@ Blockly.BlockSvg.prototype.tab = function(start, forward) {
|
||||
var tabCursor = new Blockly.TabNavigateCursor();
|
||||
tabCursor.setCurNode(Blockly.ASTNode.createFieldNode(start));
|
||||
var currentNode = tabCursor.getCurNode();
|
||||
var actionName = forward ?
|
||||
Blockly.navigation.actionNames.NEXT : Blockly.navigation.actionNames.PREVIOUS;
|
||||
|
||||
tabCursor.onBlocklyAction(
|
||||
/** @type {!Blockly.ShortcutRegistry.KeyboardShortcut} */ ({name: actionName}));
|
||||
if (forward) {
|
||||
tabCursor.next();
|
||||
} else {
|
||||
tabCursor.prev();
|
||||
}
|
||||
|
||||
var nextNode = tabCursor.getCurNode();
|
||||
if (nextNode && nextNode !== currentNode) {
|
||||
@@ -907,10 +907,6 @@ Blockly.BlockSvg.prototype.dispose = function(healStack, animate) {
|
||||
Blockly.ContextMenu.hide();
|
||||
}
|
||||
|
||||
if (this.workspace.keyboardAccessibilityMode) {
|
||||
Blockly.navigation.moveCursorOnBlockDelete(this);
|
||||
}
|
||||
|
||||
if (animate && this.rendered) {
|
||||
this.unplug(healStack);
|
||||
Blockly.blockAnimations.disposeUiEffect(this);
|
||||
@@ -1660,7 +1656,8 @@ Blockly.BlockSvg.prototype.updateMarkers_ = function() {
|
||||
this.workspace.getCursor().draw();
|
||||
}
|
||||
if (this.workspace.keyboardAccessibilityMode && this.pathObject.markerSvg) {
|
||||
this.workspace.getMarker(Blockly.navigation.MARKER_NAME).draw();
|
||||
// TODO(#4592): Update all markers on the block.
|
||||
this.workspace.getMarker(Blockly.MarkerManager.LOCAL_MARKER).draw();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ goog.require('Blockly.utils.userAgent');
|
||||
goog.requireType('Blockly.blockRendering.ConstantProvider');
|
||||
goog.requireType('Blockly.IASTNodeLocationSvg');
|
||||
goog.requireType('Blockly.IASTNodeLocationWithBlock');
|
||||
goog.requireType('Blockly.IBlocklyActionable');
|
||||
goog.requireType('Blockly.IKeyboardAccessible');
|
||||
goog.requireType('Blockly.IRegistrable');
|
||||
goog.requireType('Blockly.ShortcutRegistry');
|
||||
|
||||
@@ -46,7 +46,7 @@ goog.requireType('Blockly.ShortcutRegistry');
|
||||
* @constructor
|
||||
* @implements {Blockly.IASTNodeLocationSvg}
|
||||
* @implements {Blockly.IASTNodeLocationWithBlock}
|
||||
* @implements {Blockly.IBlocklyActionable}
|
||||
* @implements {Blockly.IKeyboardAccessible}
|
||||
* @implements {Blockly.IRegistrable}
|
||||
*/
|
||||
Blockly.Field = function(value, opt_validator, opt_config) {
|
||||
@@ -1024,13 +1024,12 @@ Blockly.Field.prototype.isTabNavigable = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the given action.
|
||||
* This is only triggered when keyboard accessibility mode is enabled.
|
||||
* @param {!Blockly.ShortcutRegistry.KeyboardShortcut} _action The action to be handled.
|
||||
* @return {boolean} True if the field handled the action, false otherwise.
|
||||
* @package
|
||||
* Handles the given keyboard shortcut.
|
||||
* @param {!Blockly.ShortcutRegistry.KeyboardShortcut} _shortcut The shortcut to be handled.
|
||||
* @return {boolean} True if the shortcut has been handled, false otherwise.
|
||||
* @public
|
||||
*/
|
||||
Blockly.Field.prototype.onBlocklyAction = function(_action) {
|
||||
Blockly.Field.prototype.onShortcut = function(_shortcut) {
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -1077,6 +1076,7 @@ Blockly.Field.prototype.updateMarkers_ = function() {
|
||||
workspace.getCursor().draw();
|
||||
}
|
||||
if (workspace.keyboardAccessibilityMode && this.markerSvg_) {
|
||||
workspace.getMarker(Blockly.navigation.MARKER_NAME).draw();
|
||||
// TODO(#4592): Update all markers on the field.
|
||||
workspace.getMarker(Blockly.MarkerManager.LOCAL_MARKER).draw();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -18,7 +18,6 @@ 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');
|
||||
@@ -382,35 +381,6 @@ Blockly.FieldColour.prototype.onKeyDown_ = function(e) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the given action.
|
||||
* This is only triggered when keyboard accessibility mode is enabled.
|
||||
* @param {!Blockly.ShortcutRegistry.KeyboardShortcut} action The action to be handled.
|
||||
* @return {boolean} True if the field handled the action, false otherwise.
|
||||
* @package
|
||||
*/
|
||||
Blockly.FieldColour.prototype.onBlocklyAction = function(action) {
|
||||
if (this.picker_) {
|
||||
switch (action.name) {
|
||||
case Blockly.navigation.actionNames.PREVIOUS:
|
||||
this.moveHighlightBy_(0, -1);
|
||||
return true;
|
||||
case Blockly.navigation.actionNames.NEXT:
|
||||
this.moveHighlightBy_(0, 1);
|
||||
return true;
|
||||
case Blockly.navigation.actionNames.OUT:
|
||||
this.moveHighlightBy_(-1, 0);
|
||||
return true;
|
||||
case Blockly.navigation.actionNames.IN:
|
||||
this.moveHighlightBy_(1, 0);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return Blockly.FieldColour.superClass_.onBlocklyAction.call(this, action);
|
||||
};
|
||||
|
||||
/**
|
||||
* Move the currently highlighted position by dx and dy.
|
||||
* @param {number} dx Change of x
|
||||
|
||||
@@ -20,7 +20,6 @@ goog.require('Blockly.Field');
|
||||
goog.require('Blockly.fieldRegistry');
|
||||
goog.require('Blockly.Menu');
|
||||
goog.require('Blockly.MenuItem');
|
||||
goog.require('Blockly.navigation');
|
||||
goog.require('Blockly.utils');
|
||||
goog.require('Blockly.utils.aria');
|
||||
goog.require('Blockly.utils.Coordinate');
|
||||
@@ -738,28 +737,4 @@ Blockly.FieldDropdown.validateOptions_ = function(options) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the given action.
|
||||
* This is only triggered when keyboard accessibility mode is enabled.
|
||||
* @param {!Blockly.ShortcutRegistry.KeyboardShortcut} action The action to be handled.
|
||||
* @return {boolean} True if the field handled the action, false otherwise.
|
||||
* @package
|
||||
*/
|
||||
Blockly.FieldDropdown.prototype.onBlocklyAction = function(action) {
|
||||
if (this.menu_) {
|
||||
switch (action.name) {
|
||||
case Blockly.navigation.actionNames.PREVIOUS:
|
||||
this.menu_.highlightPrevious();
|
||||
return true;
|
||||
case Blockly.navigation.actionNames.NEXT:
|
||||
this.menu_.highlightNext();
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return Blockly.FieldDropdown.superClass_.onBlocklyAction.call(this, action);
|
||||
};
|
||||
|
||||
|
||||
Blockly.fieldRegistry.register('field_dropdown', Blockly.FieldDropdown);
|
||||
|
||||
@@ -17,7 +17,6 @@ goog.require('Blockly.blockRendering');
|
||||
goog.require('Blockly.Events');
|
||||
goog.require('Blockly.Events.BlockCreate');
|
||||
goog.require('Blockly.Events.VarCreate');
|
||||
goog.require('Blockly.FlyoutCursor');
|
||||
goog.require('Blockly.Gesture');
|
||||
goog.require('Blockly.Marker');
|
||||
goog.require('Blockly.Scrollbar');
|
||||
@@ -31,7 +30,6 @@ goog.require('Blockly.utils.toolbox');
|
||||
goog.require('Blockly.WorkspaceSvg');
|
||||
goog.require('Blockly.Xml');
|
||||
|
||||
goog.requireType('Blockly.IBlocklyActionable');
|
||||
goog.requireType('Blockly.IDeleteArea');
|
||||
goog.requireType('Blockly.IFlyout');
|
||||
goog.requireType('Blockly.ShortcutRegistry');
|
||||
@@ -44,7 +42,6 @@ goog.requireType('Blockly.utils.Metrics');
|
||||
* workspace.
|
||||
* @constructor
|
||||
* @abstract
|
||||
* @implements {Blockly.IBlocklyActionable}
|
||||
* @implements {Blockly.IDeleteArea}
|
||||
* @implements {Blockly.IFlyout}
|
||||
*/
|
||||
@@ -253,7 +250,6 @@ Blockly.Flyout.prototype.createDom = function(tagName) {
|
||||
this.svgBackground_, 'flyoutBackgroundColour', 'fill');
|
||||
this.workspace_.getThemeManager().subscribe(
|
||||
this.svgBackground_, 'flyoutOpacity', 'fill-opacity');
|
||||
this.workspace_.getMarkerManager().setCursor(new Blockly.FlyoutCursor());
|
||||
return this.svgGroup_;
|
||||
};
|
||||
|
||||
@@ -995,18 +991,6 @@ Blockly.Flyout.prototype.placeNewBlock_ = function(oldBlock) {
|
||||
return block;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the given action.
|
||||
* This is only triggered when keyboard accessibility mode is enabled.
|
||||
* @param {!Blockly.ShortcutRegistry.KeyboardShortcut} 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);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the deletion rectangle for this flyout in viewport coordinates.
|
||||
* @return {Blockly.utils.Rect} Rectangle in which to delete.
|
||||
|
||||
@@ -21,7 +21,6 @@ goog.require('Blockly.constants');
|
||||
goog.require('Blockly.Events');
|
||||
goog.require('Blockly.Events.Click');
|
||||
goog.require('Blockly.FlyoutDragger');
|
||||
goog.require('Blockly.navigation');
|
||||
goog.require('Blockly.Tooltip');
|
||||
goog.require('Blockly.Touch');
|
||||
goog.require('Blockly.utils');
|
||||
@@ -488,13 +487,7 @@ Blockly.Gesture.prototype.doStart = function(e) {
|
||||
Blockly.Tooltip.block();
|
||||
|
||||
if (this.targetBlock_) {
|
||||
if (!this.targetBlock_.isInFlyout && e.shiftKey &&
|
||||
this.targetBlock_.workspace.keyboardAccessibilityMode) {
|
||||
this.creatorWorkspace_.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createTopNode(this.targetBlock_));
|
||||
} else {
|
||||
this.targetBlock_.select();
|
||||
}
|
||||
this.targetBlock_.select();
|
||||
}
|
||||
|
||||
if (Blockly.utils.isRightButton(e)) {
|
||||
@@ -582,7 +575,7 @@ Blockly.Gesture.prototype.handleUp = function(e) {
|
||||
} else if (this.isBlockClick_()) {
|
||||
this.doBlockClick_();
|
||||
} else if (this.isWorkspaceClick_()) {
|
||||
this.doWorkspaceClick_(e);
|
||||
this.doWorkspaceClick_();
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
@@ -654,9 +647,6 @@ Blockly.Gesture.prototype.handleWsStart = function(e, ws) {
|
||||
this.setStartWorkspace_(ws);
|
||||
this.mostRecentEvent_ = e;
|
||||
this.doStart(e);
|
||||
if (this.startWorkspace_.keyboardAccessibilityMode) {
|
||||
Blockly.navigation.setState(Blockly.navigation.STATE_WS);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -767,17 +757,11 @@ Blockly.Gesture.prototype.doBlockClick_ = function() {
|
||||
/**
|
||||
* Execute a workspace click. When in accessibility mode shift clicking will
|
||||
* move the cursor.
|
||||
* @param {!Event} e A mouse up or touch end event.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Gesture.prototype.doWorkspaceClick_ = function(e) {
|
||||
Blockly.Gesture.prototype.doWorkspaceClick_ = function() {
|
||||
var ws = this.creatorWorkspace_;
|
||||
if (e.shiftKey && ws.keyboardAccessibilityMode) {
|
||||
var screenCoord = new Blockly.utils.Coordinate(e.clientX, e.clientY);
|
||||
var wsCoord = Blockly.utils.screenToWsCoordinates(ws, screenCoord);
|
||||
var wsNode = Blockly.ASTNode.createWorkspaceNode(ws, wsCoord);
|
||||
ws.getCursor().setCurNode(wsNode);
|
||||
} else if (Blockly.selected) {
|
||||
if (Blockly.selected) {
|
||||
Blockly.selected.unselect();
|
||||
}
|
||||
this.fireWorkspaceClick_(this.startWorkspace_ || ws);
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
goog.provide('Blockly.IASTNodeLocation');
|
||||
goog.provide('Blockly.IASTNodeLocationSvg');
|
||||
goog.provide('Blockly.IASTNodeLocationWithBlock');
|
||||
goog.provide('Blockly.IBlocklyActionable');
|
||||
goog.provide('Blockly.IKeyboardAccessible');
|
||||
goog.requireType('Blockly.ShortcutRegistry');
|
||||
|
||||
/**
|
||||
@@ -59,15 +59,14 @@ Blockly.IASTNodeLocationWithBlock.prototype.getSourceBlock;
|
||||
|
||||
|
||||
/**
|
||||
* An interface for an object that handles Blockly actions when keyboard
|
||||
* navigation is enabled.
|
||||
* An interface for an object that handles keyboard shortcuts.
|
||||
* @interface
|
||||
*/
|
||||
Blockly.IBlocklyActionable = function() {};
|
||||
Blockly.IKeyboardAccessible = function() {};
|
||||
|
||||
/**
|
||||
* Handles the given action.
|
||||
* @param {!Blockly.ShortcutRegistry.KeyboardShortcut} action The action to be handled.
|
||||
* @return {boolean} True if the action has been handled, false otherwise.
|
||||
* Handles the given keyboard shortcut.
|
||||
* @param {!Blockly.ShortcutRegistry.KeyboardShortcut} shortcut The shortcut to be handled.
|
||||
* @return {boolean} True if the shortcut has been handled, false otherwise.
|
||||
*/
|
||||
Blockly.IBlocklyActionable.prototype.onBlocklyAction;
|
||||
Blockly.IKeyboardAccessible.prototype.onShortcut;
|
||||
|
||||
@@ -15,10 +15,8 @@ goog.provide('Blockly.Cursor');
|
||||
|
||||
goog.require('Blockly.ASTNode');
|
||||
goog.require('Blockly.Marker');
|
||||
goog.require('Blockly.navigation');
|
||||
goog.require('Blockly.utils.object');
|
||||
|
||||
goog.requireType('Blockly.IBlocklyActionable');
|
||||
goog.requireType('Blockly.ShortcutRegistry');
|
||||
|
||||
|
||||
@@ -27,7 +25,6 @@ goog.requireType('Blockly.ShortcutRegistry');
|
||||
* A cursor controls how a user navigates the Blockly AST.
|
||||
* @constructor
|
||||
* @extends {Blockly.Marker}
|
||||
* @implements {Blockly.IBlocklyActionable}
|
||||
*/
|
||||
Blockly.Cursor = function() {
|
||||
Blockly.Cursor.superClass_.constructor.call(this);
|
||||
@@ -43,7 +40,7 @@ Blockly.utils.object.inherits(Blockly.Cursor, Blockly.Marker);
|
||||
* 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
|
||||
* @public
|
||||
*/
|
||||
Blockly.Cursor.prototype.next = function() {
|
||||
var curNode = this.getCurNode();
|
||||
@@ -68,7 +65,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
|
||||
* @public
|
||||
*/
|
||||
Blockly.Cursor.prototype.in = function() {
|
||||
var curNode = this.getCurNode();
|
||||
@@ -93,7 +90,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
|
||||
* @public
|
||||
*/
|
||||
Blockly.Cursor.prototype.prev = function() {
|
||||
var curNode = this.getCurNode();
|
||||
@@ -118,7 +115,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
|
||||
* @public
|
||||
*/
|
||||
Blockly.Cursor.prototype.out = function() {
|
||||
var curNode = this.getCurNode();
|
||||
@@ -136,35 +133,3 @@ Blockly.Cursor.prototype.out = function() {
|
||||
}
|
||||
return newNode;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the given action.
|
||||
* This is only triggered when keyboard navigation is enabled.
|
||||
* @param {!Blockly.ShortcutRegistry.KeyboardShortcut} 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 &&
|
||||
(/** @type {!Blockly.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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2019 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview The class representing a cursor used to navigate the flyout.
|
||||
* Used primarily for keyboard navigation.
|
||||
* @author aschmiedt@google.com (Abby Schmiedt)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.FlyoutCursor');
|
||||
|
||||
goog.require('Blockly.Cursor');
|
||||
goog.require('Blockly.navigation');
|
||||
goog.require('Blockly.utils.object');
|
||||
|
||||
goog.requireType('Blockly.ShortcutRegistry');
|
||||
|
||||
|
||||
/**
|
||||
* Class for a flyout cursor.
|
||||
* This controls how a user navigates blocks in the flyout.
|
||||
* @constructor
|
||||
* @extends {Blockly.Cursor}
|
||||
*/
|
||||
Blockly.FlyoutCursor = function() {
|
||||
Blockly.FlyoutCursor.superClass_.constructor.call(this);
|
||||
};
|
||||
Blockly.utils.object.inherits(Blockly.FlyoutCursor, Blockly.Cursor);
|
||||
|
||||
/**
|
||||
* Handles the given action.
|
||||
* This is only triggered when keyboard navigation is enabled.
|
||||
* @param {!Blockly.ShortcutRegistry.KeyboardShortcut} 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
|
||||
* not set or there is no next value.
|
||||
* @override
|
||||
*/
|
||||
Blockly.FlyoutCursor.prototype.next = function() {
|
||||
var curNode = this.getCurNode();
|
||||
if (!curNode) {
|
||||
return null;
|
||||
}
|
||||
var newNode = curNode.next();
|
||||
|
||||
if (newNode) {
|
||||
this.setCurNode(newNode);
|
||||
}
|
||||
return newNode;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a no-op since a flyout cursor can not go in.
|
||||
* @return {null} Always null.
|
||||
* @override
|
||||
*/
|
||||
Blockly.FlyoutCursor.prototype.in = function() {
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @override
|
||||
*/
|
||||
Blockly.FlyoutCursor.prototype.prev = function() {
|
||||
var curNode = this.getCurNode();
|
||||
if (!curNode) {
|
||||
return null;
|
||||
}
|
||||
var newNode = curNode.prev();
|
||||
|
||||
if (newNode) {
|
||||
this.setCurNode(newNode);
|
||||
}
|
||||
return newNode;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a no-op since a flyout cursor can not go out.
|
||||
* @return {null} Always null.
|
||||
* @override
|
||||
*/
|
||||
Blockly.FlyoutCursor.prototype.out = function() {
|
||||
return null;
|
||||
};
|
||||
@@ -14,7 +14,6 @@
|
||||
goog.provide('Blockly.Marker');
|
||||
|
||||
goog.require('Blockly.ASTNode');
|
||||
goog.require('Blockly.navigation');
|
||||
|
||||
|
||||
/**
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -52,6 +52,13 @@ Blockly.MarkerManager = function(workspace){
|
||||
this.workspace_ = workspace;
|
||||
};
|
||||
|
||||
/**
|
||||
* The name of the local marker.
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
Blockly.MarkerManager.LOCAL_MARKER = 'local_marker_1';
|
||||
|
||||
/**
|
||||
* Register the marker by adding it to the map of markers.
|
||||
* @param {string} id A unique identifier for the marker.
|
||||
|
||||
@@ -18,7 +18,6 @@ goog.require('Blockly.Events');
|
||||
goog.require('Blockly.Events.BlockChange');
|
||||
goog.require('Blockly.Events.BubbleOpen');
|
||||
goog.require('Blockly.Icon');
|
||||
goog.require('Blockly.navigation');
|
||||
goog.require('Blockly.utils');
|
||||
goog.require('Blockly.utils.dom');
|
||||
goog.require('Blockly.utils.global');
|
||||
@@ -414,11 +413,6 @@ Blockly.Mutator.prototype.workspaceChanged_ = function(e) {
|
||||
// Mutation may have added some elements that need initializing.
|
||||
block.initSvg();
|
||||
|
||||
if ((/** @type {!Blockly.WorkspaceSvg} */ (Blockly.getMainWorkspace()))
|
||||
.keyboardAccessibilityMode) {
|
||||
Blockly.navigation.moveCursorOnBlockMutation(block);
|
||||
}
|
||||
|
||||
if (block.rendered) {
|
||||
block.render();
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
|
||||
goog.provide('Blockly.ShortcutRegistry');
|
||||
|
||||
goog.require('Blockly.navigation');
|
||||
goog.require('Blockly.ShortcutItems');
|
||||
goog.require('Blockly.utils.object');
|
||||
|
||||
@@ -43,7 +42,6 @@ Blockly.ShortcutRegistry = function() {
|
||||
this.keyMap_ = Object.create(null);
|
||||
|
||||
Blockly.ShortcutItems.registerDefaultShortcuts();
|
||||
Blockly.navigation.registerNavigationShortcuts();
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,7 +17,6 @@ goog.require('Blockly.constants');
|
||||
goog.require('Blockly.Css');
|
||||
goog.require('Blockly.Events');
|
||||
goog.require('Blockly.Events.ToolboxItemSelect');
|
||||
goog.require('Blockly.navigation');
|
||||
goog.require('Blockly.registry');
|
||||
goog.require('Blockly.Touch');
|
||||
goog.require('Blockly.utils');
|
||||
@@ -26,7 +25,7 @@ goog.require('Blockly.utils.dom');
|
||||
goog.require('Blockly.utils.Rect');
|
||||
goog.require('Blockly.utils.toolbox');
|
||||
|
||||
goog.requireType('Blockly.IBlocklyActionable');
|
||||
goog.requireType('Blockly.IKeyboardAccessible');
|
||||
goog.requireType('Blockly.ICollapsibleToolboxItem');
|
||||
goog.requireType('Blockly.IDeleteArea');
|
||||
goog.requireType('Blockly.IFlyout');
|
||||
@@ -44,7 +43,7 @@ goog.requireType('Blockly.WorkspaceSvg');
|
||||
* @param {!Blockly.WorkspaceSvg} workspace The workspace in which to create new
|
||||
* blocks.
|
||||
* @constructor
|
||||
* @implements {Blockly.IBlocklyActionable}
|
||||
* @implements {Blockly.IKeyboardAccessible}
|
||||
* @implements {Blockly.IDeleteArea}
|
||||
* @implements {Blockly.IStyleable}
|
||||
* @implements {Blockly.IToolbox}
|
||||
@@ -155,6 +154,16 @@ Blockly.Toolbox = function(workspace) {
|
||||
this.boundEvents_ = [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the given keyboard shortcut.
|
||||
* @param {!Blockly.ShortcutRegistry.KeyboardShortcut} _shortcut The shortcut to be handled.
|
||||
* @return {boolean} True if the shortcut has been handled, false otherwise.
|
||||
* @public
|
||||
*/
|
||||
Blockly.Toolbox.prototype.onShortcut = function(_shortcut) {
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes the toolbox
|
||||
* @public
|
||||
@@ -811,32 +820,6 @@ Blockly.Toolbox.prototype.fireSelectEvent_ = function(oldItem, newItem) {
|
||||
Blockly.Events.fire(event);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the given Blockly action on a toolbox.
|
||||
* This is only triggered when keyboard accessibility mode is enabled.
|
||||
* @param {!Blockly.ShortcutRegistry.KeyboardShortcut} 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.selectedItem_;
|
||||
if (!selected) {
|
||||
return false;
|
||||
}
|
||||
switch (action.name) {
|
||||
case Blockly.navigation.actionNames.PREVIOUS:
|
||||
return this.selectPrevious_();
|
||||
case Blockly.navigation.actionNames.OUT:
|
||||
return this.selectParent_();
|
||||
case Blockly.navigation.actionNames.NEXT:
|
||||
return this.selectNext_();
|
||||
case Blockly.navigation.actionNames.IN:
|
||||
return this.selectChild_();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Closes the current item if it is expanded, or selects the parent.
|
||||
* @return {boolean} True if a parent category was selected, false otherwise.
|
||||
|
||||
@@ -25,7 +25,6 @@ goog.require('Blockly.Gesture');
|
||||
goog.require('Blockly.Grid');
|
||||
goog.require('Blockly.MarkerManager');
|
||||
goog.require('Blockly.Msg');
|
||||
goog.require('Blockly.navigation');
|
||||
goog.require('Blockly.Options');
|
||||
goog.require('Blockly.registry');
|
||||
goog.require('Blockly.ThemeManager');
|
||||
@@ -790,8 +789,6 @@ Blockly.WorkspaceSvg.prototype.createDom = function(opt_backgroundClass) {
|
||||
this.recordDeleteAreas();
|
||||
|
||||
this.markerManager_.setCursor(new Blockly.Cursor());
|
||||
this.markerManager_.registerMarker(Blockly.navigation.MARKER_NAME,
|
||||
new Blockly.Marker());
|
||||
|
||||
this.renderer_.createDom(this.svgGroup_, this.getTheme());
|
||||
return this.svgGroup_;
|
||||
@@ -1358,17 +1355,6 @@ Blockly.WorkspaceSvg.prototype.pasteBlock_ = function(xmlBlock) {
|
||||
try {
|
||||
var block = Blockly.Xml.domToBlock(xmlBlock, this);
|
||||
|
||||
// Handle paste for keyboard navigation
|
||||
var markedNode = this.getMarker(Blockly.navigation.MARKER_NAME).getCurNode();
|
||||
if (this.keyboardAccessibilityMode && markedNode &&
|
||||
markedNode.isConnection()) {
|
||||
var markedLocation =
|
||||
/** @type {!Blockly.RenderedConnection} */ (markedNode.getLocation());
|
||||
Blockly.navigation.insertBlock(/** @type {!Blockly.BlockSvg} */ (block),
|
||||
markedLocation);
|
||||
return;
|
||||
}
|
||||
|
||||
// Move the duplicate to original position.
|
||||
var blockX = parseInt(xmlBlock.getAttribute('x'), 10);
|
||||
var blockY = parseInt(xmlBlock.getAttribute('y'), 10);
|
||||
|
||||
@@ -88,16 +88,4 @@ suite('Gesture', function() {
|
||||
var block = getTopFlyoutBlock(flyout);
|
||||
testGestureIsFieldClick(block, true, this.eventsFireStub);
|
||||
});
|
||||
|
||||
test('Shift click in accessibility mode - moves the cursor', function() {
|
||||
this.workspace.keyboardAccessibilityMode = true;
|
||||
|
||||
var eventTarget = this.workspace.svgGroup_;
|
||||
simulateClick(eventTarget, {shiftKey: true});
|
||||
|
||||
var cursor = this.workspace.getCursor();
|
||||
var cursorNode = cursor.getCurNode();
|
||||
chai.assert.exists(cursorNode);
|
||||
chai.assert.equal(cursorNode.getType(), Blockly.ASTNode.types.WORKSPACE);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -86,8 +86,6 @@
|
||||
<script src="logic_ternary_test.js"></script>
|
||||
<script src="metrics_test.js"></script>
|
||||
<script src="names_test.js"></script>
|
||||
<script src="navigation_modify_test.js"></script>
|
||||
<script src="navigation_test.js"></script>
|
||||
<script src="procedures_test_helpers.js"></script>
|
||||
<script src="procedures_test.js"></script>
|
||||
<script src="registry_test.js"></script>
|
||||
@@ -113,22 +111,6 @@
|
||||
<button text="insert" callbackkey="insertConnectionRows"></button>
|
||||
<label text="tooltips"></label>
|
||||
</xml>
|
||||
|
||||
<xml xmlns="https://developers.google.com/blockly/xml" id="toolbox-minimal" style="display: none">
|
||||
<block type="basic_block"></block>
|
||||
</xml>
|
||||
<xml xmlns="https://developers.google.com/blockly/xml" id="toolbox-incorrect" style="display: none">
|
||||
<block type="basic_block"></block>
|
||||
<category name="First">
|
||||
<block type="basic_block">
|
||||
<field name="TEXT">FirstCategory-FirstBlock</field>
|
||||
</block>
|
||||
<block type="basic_block">
|
||||
<field name="TEXT">FirstCategory-SecondBlock</field>
|
||||
</block>
|
||||
</category>
|
||||
</xml>
|
||||
|
||||
<xml xmlns="https://developers.google.com/blockly/xml" id="toolbox-categories" style="display: none">
|
||||
<category name="First" css-container="something">
|
||||
<block type="basic_block">
|
||||
@@ -144,7 +126,6 @@
|
||||
</block>
|
||||
</category>
|
||||
</xml>
|
||||
|
||||
<xml xmlns="https://developers.google.com/blockly/xml" id="toolbox-test" style="display: none">
|
||||
<category name="First" expanded="true" categorystyle="logic_category">
|
||||
<sep gap="-1"></sep>
|
||||
@@ -162,10 +143,6 @@
|
||||
</category>
|
||||
<category name="lastItem"></category>
|
||||
</xml>
|
||||
<xml xmlns="https://developers.google.com/blockly/xml" id="toolbox-connections" style="display: none">
|
||||
<block type="stack_block"></block>
|
||||
<block type="row_block"></block>
|
||||
</xml>
|
||||
|
||||
<xml xmlns="https://developers.google.com/blockly/xml" id="gesture-test-toolbox" style="display: none">
|
||||
<block type="test_field_block"></block>
|
||||
|
||||
@@ -1,420 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2019 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
suite('Insert/Modify', function() {
|
||||
function assertModifyFails() {
|
||||
var modifyResult;
|
||||
var warnings = captureWarnings(function() {
|
||||
modifyResult = Blockly.navigation.modify_();
|
||||
});
|
||||
chai.assert.isFalse(modifyResult);
|
||||
chai.assert.equal(warnings.length, 1,
|
||||
'Expecting 1 warnings for why modify failed.');
|
||||
}
|
||||
setup(function() {
|
||||
sharedTestSetup.call(this);
|
||||
// NOTE: block positions chosen such that they aren't unintentionally
|
||||
// bumped out of bounds during tests.
|
||||
var xmlText = '<xml xmlns="https://developers.google.com/blockly/xml">' +
|
||||
'<block type="stack_block" id="stack_block_1" x="22" y="38"></block>' +
|
||||
'<block type="stack_block" id="stack_block_2" x="22" y="113"></block>' +
|
||||
'<block type="row_block" id="row_block_1" x="23" y="213"></block>' +
|
||||
'<block type="row_block" id="row_block_2" x="22" y="288"></block>' +
|
||||
'<block type="statement_block" id="statement_block_1" x="22" y="288"></block>' +
|
||||
'<block type="statement_block" id="statement_block_2" x="22" y="288"></block>' +
|
||||
'</xml>';
|
||||
defineStackBlock(this.sharedCleanup);
|
||||
defineRowBlock(this.sharedCleanup);
|
||||
defineStatementBlock(this.sharedCleanup);
|
||||
|
||||
var toolbox = document.getElementById('toolbox-connections');
|
||||
this.workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox});
|
||||
Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xmlText), this.workspace);
|
||||
|
||||
this.stack_block_1 = this.workspace.getBlockById('stack_block_1');
|
||||
this.stack_block_2 = this.workspace.getBlockById('stack_block_2');
|
||||
this.row_block_1 = this.workspace.getBlockById('row_block_1');
|
||||
this.row_block_2 = this.workspace.getBlockById('row_block_2');
|
||||
this.statement_block_1 = this.workspace.getBlockById('statement_block_1');
|
||||
this.statement_block_2 = this.workspace.getBlockById('statement_block_2');
|
||||
|
||||
Blockly.navigation.enableKeyboardAccessibility();
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
sharedTestTeardown.call(this);
|
||||
});
|
||||
|
||||
suite('Marked Connection', function() {
|
||||
// TODO: Marked connection or cursor connection is already connected.
|
||||
suite('Marker on next', function() {
|
||||
setup(function() {
|
||||
this.workspace.getMarker(Blockly.navigation.MARKER_NAME).setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(
|
||||
this.stack_block_1.nextConnection));
|
||||
});
|
||||
test('Cursor on workspace', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createWorkspaceNode(this.workspace,
|
||||
new Blockly.utils.Coordinate(0, 0)));
|
||||
assertModifyFails();
|
||||
});
|
||||
test('Cursor on compatible connection', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(
|
||||
this.stack_block_2.previousConnection));
|
||||
chai.assert.isTrue(Blockly.navigation.modify_());
|
||||
chai.assert.equal(this.stack_block_1.getNextBlock().id, 'stack_block_2');
|
||||
});
|
||||
test('Cursor on incompatible connection', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(
|
||||
this.stack_block_2.nextConnection));
|
||||
// Connect method will try to find a way to connect blocks with
|
||||
// incompatible types.
|
||||
chai.assert.isTrue(Blockly.navigation.modify_());
|
||||
chai.assert.equal(this.stack_block_1.getNextBlock(), this.stack_block_2);
|
||||
});
|
||||
test('Cursor on really incompatible connection', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(
|
||||
this.row_block_1.outputConnection));
|
||||
assertModifyFails();
|
||||
chai.assert.isNull(this.stack_block_1.getNextBlock());
|
||||
});
|
||||
test('Cursor on block', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createBlockNode(
|
||||
this.stack_block_2));
|
||||
chai.assert.isTrue(Blockly.navigation.modify_());
|
||||
chai.assert.equal(this.stack_block_1.getNextBlock().id, 'stack_block_2');
|
||||
});
|
||||
});
|
||||
|
||||
suite('Marker on previous', function() {
|
||||
setup(function() {
|
||||
this.workspace.getMarker(Blockly.navigation.MARKER_NAME).setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(
|
||||
this.stack_block_1.previousConnection));
|
||||
});
|
||||
test('Cursor on compatible connection', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(
|
||||
this.stack_block_2.nextConnection));
|
||||
chai.assert.isTrue(Blockly.navigation.modify_());
|
||||
chai.assert.equal(this.stack_block_1.getPreviousBlock().id, 'stack_block_2');
|
||||
});
|
||||
test('Cursor on incompatible connection', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(
|
||||
this.stack_block_2.previousConnection));
|
||||
assertModifyFails();
|
||||
chai.assert.isNull(this.stack_block_1.getPreviousBlock());
|
||||
});
|
||||
test('Cursor on really incompatible connection', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(
|
||||
this.row_block_1.outputConnection));
|
||||
assertModifyFails();
|
||||
chai.assert.isNull(this.stack_block_1.getNextBlock());
|
||||
});
|
||||
test('Cursor on block', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createBlockNode(
|
||||
this.stack_block_2));
|
||||
chai.assert.isTrue(Blockly.navigation.modify_());
|
||||
chai.assert.equal(this.stack_block_1.getPreviousBlock().id, 'stack_block_2');
|
||||
});
|
||||
test('Cursor on incompatible block', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createBlockNode(
|
||||
this.row_block_1));
|
||||
assertModifyFails();
|
||||
chai.assert.isNull(this.stack_block_1.getPreviousBlock());
|
||||
});
|
||||
});
|
||||
|
||||
suite('Marker on value input', function() {
|
||||
setup(function() {
|
||||
this.workspace.getMarker(Blockly.navigation.MARKER_NAME).setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(
|
||||
this.row_block_1.inputList[0].connection));
|
||||
});
|
||||
test('Cursor on compatible connection', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(
|
||||
this.row_block_2.outputConnection));
|
||||
chai.assert.isTrue(Blockly.navigation.modify_());
|
||||
chai.assert.equal(this.row_block_2.getParent().id, 'row_block_1');
|
||||
});
|
||||
test('Cursor on incompatible connection', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(
|
||||
this.row_block_2.inputList[0].connection));
|
||||
// Connect method will try to find a way to connect blocks with
|
||||
// incompatible types.
|
||||
chai.assert.isTrue(Blockly.navigation.modify_());
|
||||
chai.assert.equal(this.row_block_1.inputList[0].connection.targetBlock(),
|
||||
this.row_block_2);
|
||||
});
|
||||
test('Cursor on really incompatible connection', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(
|
||||
this.stack_block_1.previousConnection));
|
||||
assertModifyFails();
|
||||
});
|
||||
test('Cursor on block', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createBlockNode(
|
||||
this.row_block_2));
|
||||
chai.assert.isTrue(Blockly.navigation.modify_());
|
||||
chai.assert.equal(this.row_block_2.getParent().id, 'row_block_1');
|
||||
});
|
||||
});
|
||||
|
||||
suite('Marked Statement input', function() {
|
||||
setup(function() {
|
||||
this.statement_block_1.inputList[0].connection.connect(
|
||||
this.stack_block_1.previousConnection);
|
||||
this.stack_block_1.nextConnection.connect(this.stack_block_2.previousConnection);
|
||||
this.workspace.getMarker(Blockly.navigation.MARKER_NAME).setCurNode(
|
||||
Blockly.ASTNode.createInputNode(
|
||||
this.statement_block_1.inputList[0]));
|
||||
});
|
||||
test('Cursor on block inside statement', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(
|
||||
this.stack_block_2.previousConnection));
|
||||
chai.assert.isTrue(Blockly.navigation.modify_());
|
||||
chai.assert.equal(this.stack_block_2.previousConnection.targetBlock(),
|
||||
this.statement_block_1);
|
||||
});
|
||||
test('Cursor on stack', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createStackNode(
|
||||
this.statement_block_2));
|
||||
chai.assert.isTrue(Blockly.navigation.modify_());
|
||||
chai.assert.equal(this.statement_block_2.getParent().id, 'statement_block_1');
|
||||
});
|
||||
test('Cursor on incompatible type', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(
|
||||
this.row_block_1.outputConnection));
|
||||
assertModifyFails();
|
||||
chai.assert.isNull(this.row_block_1.getParent());
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('Marker on output', function() {
|
||||
setup(function() {
|
||||
this.workspace.getMarker(Blockly.navigation.MARKER_NAME).setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(
|
||||
this.row_block_1.outputConnection));
|
||||
});
|
||||
test('Cursor on compatible connection', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(
|
||||
this.row_block_2.inputList[0].connection));
|
||||
chai.assert.isTrue(Blockly.navigation.modify_());
|
||||
chai.assert.equal(this.row_block_1.getParent().id, 'row_block_2');
|
||||
});
|
||||
test('Cursor on incompatible connection', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(
|
||||
this.row_block_2.outputConnection));
|
||||
assertModifyFails();
|
||||
});
|
||||
test('Cursor on really incompatible connection', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(
|
||||
this.stack_block_1.previousConnection));
|
||||
assertModifyFails();
|
||||
});
|
||||
test('Cursor on block', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createBlockNode(
|
||||
this.row_block_2));
|
||||
chai.assert.isTrue(Blockly.navigation.modify_());
|
||||
chai.assert.equal(this.row_block_1.getParent().id, 'row_block_2');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
suite('Marked Workspace', function() {
|
||||
setup(function() {
|
||||
this.workspace.getMarker(Blockly.navigation.MARKER_NAME).drawer_ = null;
|
||||
this.workspace.getMarker(Blockly.navigation.MARKER_NAME).setCurNode(
|
||||
Blockly.ASTNode.createWorkspaceNode(
|
||||
this.workspace, new Blockly.utils.Coordinate(100, 200)));
|
||||
});
|
||||
test('Cursor on row block', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createBlockNode(
|
||||
this.row_block_1));
|
||||
chai.assert.isTrue(Blockly.navigation.modify_());
|
||||
var pos = this.row_block_1.getRelativeToSurfaceXY();
|
||||
chai.assert.equal(pos.x, 100);
|
||||
chai.assert.equal(pos.y, 200);
|
||||
});
|
||||
|
||||
test('Cursor on output connection', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(
|
||||
this.row_block_1.outputConnection));
|
||||
chai.assert.isTrue(Blockly.navigation.modify_());
|
||||
var pos = this.row_block_1.getRelativeToSurfaceXY();
|
||||
chai.assert.equal(pos.x, 100);
|
||||
chai.assert.equal(pos.y, 200);
|
||||
});
|
||||
|
||||
test('Cursor on previous connection', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(
|
||||
this.stack_block_1.previousConnection));
|
||||
chai.assert.isTrue(Blockly.navigation.modify_());
|
||||
var pos = this.stack_block_1.getRelativeToSurfaceXY();
|
||||
chai.assert.equal(pos.x, 100);
|
||||
chai.assert.equal(pos.y, 200);
|
||||
});
|
||||
|
||||
test('Cursor on input connection', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(
|
||||
this.row_block_1.inputList[0].connection));
|
||||
// Move the source block to the marked location on the workspace.
|
||||
chai.assert.isTrue(Blockly.navigation.modify_());
|
||||
});
|
||||
|
||||
test('Cursor on next connection', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(
|
||||
this.stack_block_1.nextConnection));
|
||||
// Move the source block to the marked location on the workspace.
|
||||
chai.assert.isTrue(Blockly.navigation.modify_());
|
||||
});
|
||||
|
||||
test('Cursor on child block (row)', function() {
|
||||
this.row_block_1.inputList[0].connection.connect(
|
||||
this.row_block_2.outputConnection);
|
||||
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createBlockNode(
|
||||
this.row_block_2));
|
||||
|
||||
chai.assert.isTrue(Blockly.navigation.modify_());
|
||||
chai.assert.isNull(this.row_block_2.getParent());
|
||||
var pos = this.row_block_2.getRelativeToSurfaceXY();
|
||||
chai.assert.equal(pos.x, 100);
|
||||
chai.assert.equal(pos.y, 200);
|
||||
});
|
||||
|
||||
test('Cursor on child block (stack)', function() {
|
||||
this.stack_block_1.nextConnection.connect(
|
||||
this.stack_block_2.previousConnection);
|
||||
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createBlockNode(
|
||||
this.stack_block_2));
|
||||
|
||||
chai.assert.isTrue(Blockly.navigation.modify_());
|
||||
chai.assert.isNull(this.stack_block_2.getParent());
|
||||
var pos = this.stack_block_2.getRelativeToSurfaceXY();
|
||||
chai.assert.equal(pos.x, 100);
|
||||
chai.assert.equal(pos.y, 200);
|
||||
});
|
||||
|
||||
test('Cursor on workspace', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createWorkspaceNode(
|
||||
this.workspace, new Blockly.utils.Coordinate(100, 100)));
|
||||
assertModifyFails();
|
||||
});
|
||||
});
|
||||
|
||||
suite('Marked Block', function() {
|
||||
// TODO: Decide whether it ever makes sense to mark a block, and what to do
|
||||
// if so. For now all of these attempted modifications will fail.
|
||||
suite('Marked any block', function() {
|
||||
// These tests are using a stack block, but do not depend on the type of
|
||||
// the block.
|
||||
setup(function() {
|
||||
this.workspace.getMarker(Blockly.navigation.MARKER_NAME).setCurNode(
|
||||
Blockly.ASTNode.createBlockNode(
|
||||
this.stack_block_1));
|
||||
});
|
||||
test('Cursor on workspace', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createWorkspaceNode(
|
||||
this.workspace, new Blockly.utils.Coordinate(100, 100)));
|
||||
assertModifyFails();
|
||||
});
|
||||
});
|
||||
suite('Marked stack block', function() {
|
||||
setup(function() {
|
||||
this.workspace.getMarker(Blockly.navigation.MARKER_NAME).setCurNode(
|
||||
Blockly.ASTNode.createBlockNode(
|
||||
this.stack_block_1));
|
||||
});
|
||||
test('Cursor on row block', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createBlockNode(
|
||||
this.row_block_1));
|
||||
assertModifyFails();
|
||||
});
|
||||
test('Cursor on stack block', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createBlockNode(
|
||||
this.stack_block_1));
|
||||
assertModifyFails();
|
||||
});
|
||||
test('Cursor on next connection', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(
|
||||
this.stack_block_2.nextConnection));
|
||||
assertModifyFails();
|
||||
});
|
||||
test('Cursor on previous connection', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(
|
||||
this.stack_block_2.previousConnection));
|
||||
assertModifyFails();
|
||||
});
|
||||
});
|
||||
suite('Marked row block', function() {
|
||||
setup(function() {
|
||||
this.workspace.getMarker(Blockly.navigation.MARKER_NAME).setCurNode(
|
||||
Blockly.ASTNode.createBlockNode(
|
||||
this.row_block_1));
|
||||
});
|
||||
test('Cursor on stack block', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createBlockNode(
|
||||
this.stack_block_1));
|
||||
assertModifyFails();
|
||||
});
|
||||
test('Cursor on row block', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createBlockNode(
|
||||
this.row_block_1));
|
||||
assertModifyFails();
|
||||
});
|
||||
test('Cursor on value input connection', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(
|
||||
this.row_block_2.inputList[0].connection));
|
||||
assertModifyFails();
|
||||
});
|
||||
test('Cursor on output connection', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(
|
||||
this.row_block_2.outputConnection));
|
||||
assertModifyFails();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,803 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2019 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Navigation tests.
|
||||
* @author aschmiedt@google.com (Abby Schmiedt)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
|
||||
suite('Navigation', function() {
|
||||
function createNavigationWorkspace(enableKeyboardNav, readOnly) {
|
||||
var toolbox = document.getElementById('toolbox-categories');
|
||||
var workspace =
|
||||
Blockly.inject('blocklyDiv', {toolbox: toolbox, readOnly: readOnly});
|
||||
if (enableKeyboardNav) {
|
||||
Blockly.navigation.enableKeyboardAccessibility();
|
||||
Blockly.navigation.currentState_ = Blockly.navigation.STATE_WS;
|
||||
}
|
||||
return workspace;
|
||||
}
|
||||
|
||||
setup(function() {
|
||||
sharedTestSetup.call(this);
|
||||
});
|
||||
teardown(function() {
|
||||
sharedTestTeardown.call(this);
|
||||
});
|
||||
|
||||
// Test that toolbox key handlers call through to the right functions and
|
||||
// transition correctly between toolbox, workspace, and flyout.
|
||||
suite('Tests toolbox keys', function() {
|
||||
setup(function() {
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "basic_block",
|
||||
"message0": "%1",
|
||||
"args0": [
|
||||
{
|
||||
"type": "field_input",
|
||||
"name": "TEXT",
|
||||
"text": "default"
|
||||
}
|
||||
]
|
||||
}]);
|
||||
this.workspace = createNavigationWorkspace(true);
|
||||
Blockly.navigation.focusToolbox_(this.workspace);
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
workspaceTeardown.call(this, this.workspace);
|
||||
});
|
||||
|
||||
var testCases = [
|
||||
[
|
||||
'Calls toolbox selectNext_',
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.S, 'NotAField'), 'selectNext_'
|
||||
],
|
||||
[
|
||||
'Calls toolbox selectPrevious_',
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.W, 'NotAField'),
|
||||
'selectPrevious_'
|
||||
],
|
||||
[
|
||||
'Calls toolbox selectParent_',
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.D, 'NotAField'),
|
||||
'selectChild_'
|
||||
],
|
||||
[
|
||||
'Calls toolbox selectChild_',
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.A, 'NotAField'),
|
||||
'selectParent_'
|
||||
]
|
||||
];
|
||||
|
||||
testCases.forEach(function(testCase) {
|
||||
var testCaseName = testCase[0];
|
||||
var mockEvent = testCase[1];
|
||||
var stubName = testCase[2];
|
||||
test(testCaseName, function() {
|
||||
var toolbox = this.workspace.getToolbox();
|
||||
var selectStub = sinon.stub(toolbox, stubName);
|
||||
toolbox.selectedItem_ = toolbox.contents_[0];
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
sinon.assert.called(selectStub);
|
||||
});
|
||||
});
|
||||
|
||||
test('Go to flyout', function() {
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.D, 'NotAField');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_FLYOUT);
|
||||
|
||||
var flyoutCursor = Blockly.navigation.getFlyoutCursor_();
|
||||
chai.assert.equal(flyoutCursor.getCurNode().getLocation().getFieldValue("TEXT"),
|
||||
"FirstCategory-FirstBlock");
|
||||
});
|
||||
|
||||
test('Focuses workspace from toolbox (e)', function() {
|
||||
Blockly.navigation.currentState_ = Blockly.navigation.STATE_TOOLBOX;
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.E, 'NotAField');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
});
|
||||
test('Focuses workspace from toolbox (escape)', function() {
|
||||
Blockly.navigation.currentState_ = Blockly.navigation.STATE_TOOLBOX;
|
||||
var mockEvent =
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.ESC, 'NotAField');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
});
|
||||
// More tests:
|
||||
// - nested categories
|
||||
});
|
||||
|
||||
// Test that flyout key handlers call through to the right functions and
|
||||
// transition correctly between toolbox, workspace, and flyout.
|
||||
suite('Tests flyout keys', function() {
|
||||
setup(function() {
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "basic_block",
|
||||
"message0": "%1",
|
||||
"args0": [
|
||||
{
|
||||
"type": "field_input",
|
||||
"name": "TEXT",
|
||||
"text": "default"
|
||||
}
|
||||
]
|
||||
}]);
|
||||
this.workspace = createNavigationWorkspace(true);
|
||||
Blockly.navigation.focusToolbox_(this.workspace);
|
||||
Blockly.navigation.focusFlyout_(this.workspace);
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
workspaceTeardown.call(this, this.workspace);
|
||||
});
|
||||
|
||||
// Should be a no-op
|
||||
test('Previous at beginning', function() {
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.W, 'NotAField');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_FLYOUT);
|
||||
chai.assert.equal(Blockly.navigation.getFlyoutCursor_().getCurNode().getLocation().getFieldValue("TEXT"),
|
||||
"FirstCategory-FirstBlock");
|
||||
});
|
||||
|
||||
test('Previous', function() {
|
||||
var flyoutBlocks = this.workspace.getFlyout().getWorkspace().getTopBlocks();
|
||||
Blockly.navigation.getFlyoutCursor_().setCurNode(
|
||||
Blockly.ASTNode.createStackNode(flyoutBlocks[1]));
|
||||
var flyoutBlock = Blockly.navigation.getFlyoutCursor_().getCurNode().getLocation();
|
||||
chai.assert.equal(flyoutBlock.getFieldValue("TEXT"),
|
||||
"FirstCategory-SecondBlock");
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.W, 'NotAField');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_FLYOUT);
|
||||
flyoutBlock = Blockly.navigation.getFlyoutCursor_().getCurNode().getLocation();
|
||||
chai.assert.equal(flyoutBlock.getFieldValue("TEXT"),
|
||||
"FirstCategory-FirstBlock");
|
||||
});
|
||||
|
||||
test('Next', function() {
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.S, 'NotAField');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_FLYOUT);
|
||||
var flyoutBlock = Blockly.navigation.getFlyoutCursor_().getCurNode().getLocation();
|
||||
chai.assert.equal(flyoutBlock.getFieldValue("TEXT"),
|
||||
"FirstCategory-SecondBlock");
|
||||
});
|
||||
|
||||
test('Out', function() {
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.A, 'NotAField');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_TOOLBOX);
|
||||
});
|
||||
|
||||
test('Mark', function() {
|
||||
var mockEvent =
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.ENTER, 'NotAField');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
chai.assert.equal(this.workspace.getTopBlocks().length, 1);
|
||||
});
|
||||
|
||||
test('Exit', function() {
|
||||
var mockEvent =
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.ESC, 'NotAField');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
});
|
||||
});
|
||||
|
||||
// Test that workspace key handlers call through to the right functions and
|
||||
// transition correctly between toolbox, workspace, and flyout.
|
||||
suite('Tests workspace keys', function() {
|
||||
setup(function() {
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "basic_block",
|
||||
"message0": "%1",
|
||||
"args0": [
|
||||
{
|
||||
"type": "field_input",
|
||||
"name": "TEXT",
|
||||
"text": "default"
|
||||
}
|
||||
],
|
||||
"previousStatement": null,
|
||||
"nextStatement": null
|
||||
}]);
|
||||
this.workspace = createNavigationWorkspace(true);
|
||||
this.basicBlock = this.workspace.newBlock('basic_block');
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
workspaceTeardown.call(this, this.workspace);
|
||||
});
|
||||
|
||||
test('Previous', function() {
|
||||
var prevSpy = sinon.spy(this.workspace.getCursor(), 'prev');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
var wEvent = createKeyDownEvent(Blockly.utils.KeyCodes.W, '');
|
||||
|
||||
Blockly.onKeyDown(wEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
sinon.assert.calledOnce(prevSpy);
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
});
|
||||
|
||||
test('Next', function() {
|
||||
var nextSpy = sinon.spy(this.workspace.getCursor(), 'next');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
var sEvent = createKeyDownEvent(Blockly.utils.KeyCodes.S, '');
|
||||
|
||||
Blockly.onKeyDown(sEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
sinon.assert.calledOnce(nextSpy);
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
});
|
||||
|
||||
test('Out', function() {
|
||||
var outSpy = sinon.spy(this.workspace.getCursor(), 'out');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
var aEvent = createKeyDownEvent(Blockly.utils.KeyCodes.A, '');
|
||||
|
||||
Blockly.onKeyDown(aEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
sinon.assert.calledOnce(outSpy);
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
});
|
||||
|
||||
test('In', function() {
|
||||
var inSpy = sinon.spy(this.workspace.getCursor(), 'in');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
var dEvent = createKeyDownEvent(Blockly.utils.KeyCodes.D, '');
|
||||
|
||||
Blockly.onKeyDown(dEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
sinon.assert.calledOnce(inSpy);
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
});
|
||||
|
||||
test('Insert', function() {
|
||||
// Stub modify as we are not testing its behavior, only if it was called.
|
||||
// Otherwise, there is a warning because there is no marked node.
|
||||
var modifyStub = sinon.stub(Blockly.navigation, 'modify_').returns(true);
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
var iEvent = createKeyDownEvent(Blockly.utils.KeyCodes.I, '');
|
||||
|
||||
Blockly.onKeyDown(iEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
sinon.assert.calledOnce(modifyStub);
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
});
|
||||
|
||||
test('Mark', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(this.basicBlock.previousConnection));
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
var enterEvent = createKeyDownEvent(Blockly.utils.KeyCodes.ENTER, '');
|
||||
|
||||
Blockly.onKeyDown(enterEvent);
|
||||
|
||||
var markedNode = this.workspace.getMarker(Blockly.navigation.MARKER_NAME).getCurNode();
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(markedNode.getLocation(), this.basicBlock.previousConnection);
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
});
|
||||
|
||||
test('Toolbox', function() {
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
var tEvent = createKeyDownEvent(Blockly.utils.KeyCodes.T, '');
|
||||
|
||||
Blockly.onKeyDown(tEvent);
|
||||
|
||||
var firstCategory = this.workspace.getToolbox().contents_[0];
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
this.workspace.getToolbox().getSelectedItem(), firstCategory);
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_TOOLBOX);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Test key press', function() {
|
||||
setup(function() {
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "basic_block",
|
||||
"message0": "%1",
|
||||
"args0": [
|
||||
{
|
||||
"type": "field_dropdown",
|
||||
"name": "OP",
|
||||
"options": [
|
||||
["%{BKY_MATH_ADDITION_SYMBOL}", "ADD"],
|
||||
["%{BKY_MATH_SUBTRACTION_SYMBOL}", "MINUS"],
|
||||
["%{BKY_MATH_MULTIPLICATION_SYMBOL}", "MULTIPLY"],
|
||||
["%{BKY_MATH_DIVISION_SYMBOL}", "DIVIDE"],
|
||||
["%{BKY_MATH_POWER_SYMBOL}", "POWER"]
|
||||
]
|
||||
}
|
||||
]
|
||||
}]);
|
||||
this.workspace = createNavigationWorkspace(true);
|
||||
this.workspace.getCursor().drawer_ = null;
|
||||
this.basicBlock = this.workspace.newBlock('basic_block');
|
||||
});
|
||||
teardown(function() {
|
||||
workspaceTeardown.call(this, this.workspace);
|
||||
});
|
||||
|
||||
|
||||
test('Action does not exist', function() {
|
||||
var block = this.workspace.getTopBlocks()[0];
|
||||
var field = block.inputList[0].fieldRow[0];
|
||||
var fieldSpy = sinon.spy(field, 'onBlocklyAction');
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.N, '');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
this.workspace.getCursor().setCurNode(Blockly.ASTNode.createFieldNode(field));
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isFalse(keyDownSpy.returned(true));
|
||||
sinon.assert.notCalled(fieldSpy);
|
||||
});
|
||||
|
||||
test('Action exists - field handles action', function() {
|
||||
var block = this.workspace.getTopBlocks()[0];
|
||||
var field = block.inputList[0].fieldRow[0];
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.A, '');
|
||||
var fieldSpy = sinon.stub(field, 'onBlocklyAction').returns(true);
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
this.workspace.getCursor().setCurNode(Blockly.ASTNode.createFieldNode(field));
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
sinon.assert.calledOnce(fieldSpy);
|
||||
|
||||
});
|
||||
|
||||
test('Action exists - field does not handle action', function() {
|
||||
var block = this.workspace.getTopBlocks()[0];
|
||||
var field = block.inputList[0].fieldRow[0];
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.A, '');
|
||||
var fieldSpy = sinon.spy(field, 'onBlocklyAction');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
this.workspace.getCursor().setCurNode(Blockly.ASTNode.createFieldNode(field));
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
sinon.assert.calledOnce(fieldSpy);
|
||||
});
|
||||
|
||||
test('Toggle Action Off', function() {
|
||||
var mockEvent = createKeyDownEvent(
|
||||
Blockly.utils.KeyCodes.K, '',
|
||||
[Blockly.utils.KeyCodes.SHIFT, Blockly.utils.KeyCodes.CTRL]);
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
this.workspace.keyboardAccessibilityMode = true;
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.isFalse(this.workspace.keyboardAccessibilityMode);
|
||||
});
|
||||
|
||||
test('Toggle Action On', function() {
|
||||
var mockEvent = createKeyDownEvent(
|
||||
Blockly.utils.KeyCodes.K, '',
|
||||
[Blockly.utils.KeyCodes.SHIFT, Blockly.utils.KeyCodes.CTRL]);
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
this.workspace.keyboardAccessibilityMode = false;
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.isTrue(this.workspace.keyboardAccessibilityMode);
|
||||
});
|
||||
|
||||
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 = createNavigationWorkspace(true, true);
|
||||
|
||||
Blockly.mainWorkspace = this.workspace;
|
||||
this.workspace.getCursor().drawer_ = null;
|
||||
|
||||
this.fieldBlock1 = this.workspace.newBlock('field_block');
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
workspaceTeardown.call(this, this.workspace);
|
||||
});
|
||||
|
||||
test('Perform valid action for read only', function() {
|
||||
var astNode = Blockly.ASTNode.createBlockNode(this.fieldBlock1);
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.S, '');
|
||||
this.workspace.getCursor().setCurNode(astNode);
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
});
|
||||
|
||||
test('Perform invalid action for read only', function() {
|
||||
var astNode = Blockly.ASTNode.createBlockNode(this.fieldBlock1);
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.I, '');
|
||||
this.workspace.getCursor().setCurNode(astNode);
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(false));
|
||||
});
|
||||
|
||||
test('Try to perform action on a field', function() {
|
||||
var field = this.fieldBlock1.inputList[0].fieldRow[0];
|
||||
var astNode = Blockly.ASTNode.createFieldNode(field);
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.ENTER, '');
|
||||
this.workspace.getCursor().setCurNode(astNode);
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(false));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('Insert Functions', function() {
|
||||
setup(function() {
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "basic_block",
|
||||
"message0": "%1",
|
||||
"args0": [
|
||||
{
|
||||
"type": "field_input",
|
||||
"name": "TEXT",
|
||||
"text": "default"
|
||||
}
|
||||
],
|
||||
"previousStatement": null,
|
||||
"nextStatement": null,
|
||||
}]);
|
||||
|
||||
this.workspace = createNavigationWorkspace(true);
|
||||
|
||||
var basicBlock = this.workspace.newBlock('basic_block');
|
||||
var basicBlock2 = this.workspace.newBlock('basic_block');
|
||||
|
||||
this.basicBlock = basicBlock;
|
||||
this.basicBlock2 = basicBlock2;
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
workspaceTeardown.call(this, this.workspace);
|
||||
});
|
||||
|
||||
test('Insert from flyout with a valid connection marked', function() {
|
||||
var previousConnection = this.basicBlock.previousConnection;
|
||||
var prevNode = Blockly.ASTNode.createConnectionNode(previousConnection);
|
||||
this.workspace.getMarker(Blockly.navigation.MARKER_NAME).setCurNode(prevNode);
|
||||
|
||||
Blockly.navigation.focusToolbox_(this.workspace);
|
||||
Blockly.navigation.focusFlyout_(this.workspace);
|
||||
Blockly.navigation.insertFromFlyout(this.workspace);
|
||||
|
||||
var insertedBlock = this.basicBlock.previousConnection.targetBlock();
|
||||
|
||||
chai.assert.isTrue(insertedBlock !== null);
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_WS);
|
||||
});
|
||||
|
||||
test('Insert Block from flyout without marking a connection', function() {
|
||||
Blockly.navigation.focusToolbox_(this.workspace);
|
||||
Blockly.navigation.focusFlyout_(this.workspace);
|
||||
Blockly.navigation.insertFromFlyout(this.workspace);
|
||||
|
||||
var numBlocks = this.workspace.getTopBlocks().length;
|
||||
|
||||
// Make sure the block was not connected to anything
|
||||
chai.assert.isNull(this.basicBlock.previousConnection.targetConnection);
|
||||
chai.assert.isNull(this.basicBlock.nextConnection.targetConnection);
|
||||
|
||||
// Make sure that the block was added to the workspace
|
||||
chai.assert.equal(numBlocks, 3);
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_WS);
|
||||
});
|
||||
|
||||
test('Connect two blocks that are on the workspace', function() {
|
||||
var targetNode = Blockly.ASTNode.createConnectionNode(this.basicBlock.previousConnection);
|
||||
this.workspace.getMarker(Blockly.navigation.MARKER_NAME).setCurNode(targetNode);
|
||||
|
||||
var sourceNode = Blockly.ASTNode.createConnectionNode(this.basicBlock2.nextConnection);
|
||||
this.workspace.getCursor().setCurNode(sourceNode);
|
||||
|
||||
Blockly.navigation.modify_();
|
||||
var insertedBlock = this.basicBlock.previousConnection.targetBlock();
|
||||
|
||||
chai.assert.isNotNull(insertedBlock);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Connect Blocks', function() {
|
||||
setup(function() {
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "basic_block",
|
||||
"message0": "",
|
||||
"previousStatement": null,
|
||||
"nextStatement": null,
|
||||
},
|
||||
{
|
||||
"type": "inline_block",
|
||||
"message0": "%1 %2",
|
||||
"args0": [
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "NAME"
|
||||
},
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "NAME"
|
||||
}
|
||||
],
|
||||
"inputsInline": true,
|
||||
"output": null,
|
||||
"tooltip": "",
|
||||
"helpUrl": ""
|
||||
}]);
|
||||
|
||||
this.workspace = createNavigationWorkspace(true);
|
||||
|
||||
var basicBlock = this.workspace.newBlock('basic_block');
|
||||
var basicBlock2 = this.workspace.newBlock('basic_block');
|
||||
var basicBlock3 = this.workspace.newBlock('basic_block');
|
||||
var basicBlock4 = this.workspace.newBlock('basic_block');
|
||||
|
||||
var inlineBlock1 = this.workspace.newBlock('inline_block');
|
||||
var inlineBlock2 = this.workspace.newBlock('inline_block');
|
||||
|
||||
|
||||
this.basicBlock = basicBlock;
|
||||
this.basicBlock2 = basicBlock2;
|
||||
this.basicBlock3 = basicBlock3;
|
||||
this.basicBlock4 = basicBlock4;
|
||||
|
||||
this.inlineBlock1 = inlineBlock1;
|
||||
this.inlineBlock2 = inlineBlock2;
|
||||
|
||||
this.basicBlock.nextConnection.connect(this.basicBlock2.previousConnection);
|
||||
|
||||
this.basicBlock3.nextConnection.connect(this.basicBlock4.previousConnection);
|
||||
|
||||
this.inlineBlock1.inputList[0].connection.connect(this.inlineBlock2.outputConnection);
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
workspaceTeardown.call(this, this.workspace);
|
||||
});
|
||||
|
||||
test('Connect cursor on previous into stack', function() {
|
||||
var markedLocation = this.basicBlock2.previousConnection;
|
||||
var cursorLocation = this.basicBlock3.previousConnection;
|
||||
|
||||
Blockly.navigation.connect_(cursorLocation, markedLocation);
|
||||
|
||||
chai.assert.equal(this.basicBlock.nextConnection.targetBlock(), this.basicBlock3);
|
||||
chai.assert.equal(this.basicBlock2.previousConnection.targetBlock(), this.basicBlock4);
|
||||
});
|
||||
|
||||
test('Connect marker on previous into stack', function() {
|
||||
var markedLocation = this.basicBlock3.previousConnection;
|
||||
var cursorLocation = this.basicBlock2.previousConnection;
|
||||
|
||||
Blockly.navigation.connect_(cursorLocation, markedLocation);
|
||||
|
||||
chai.assert.equal(this.basicBlock.nextConnection.targetBlock(), this.basicBlock3);
|
||||
chai.assert.equal(this.basicBlock2.previousConnection.targetBlock(), this.basicBlock4);
|
||||
|
||||
});
|
||||
|
||||
test('Connect cursor on next into stack', function() {
|
||||
var markedLocation = this.basicBlock2.previousConnection;
|
||||
var cursorLocation = this.basicBlock4.nextConnection;
|
||||
|
||||
Blockly.navigation.connect_(cursorLocation, markedLocation);
|
||||
|
||||
chai.assert.equal(this.basicBlock.nextConnection.targetBlock(), this.basicBlock4);
|
||||
chai.assert.isNull(this.basicBlock3.nextConnection.targetConnection);
|
||||
});
|
||||
|
||||
test('Connect cursor with parents', function() {
|
||||
var markedLocation = this.basicBlock3.previousConnection;
|
||||
var cursorLocation = this.basicBlock2.nextConnection;
|
||||
|
||||
Blockly.navigation.connect_(cursorLocation, markedLocation);
|
||||
|
||||
chai.assert.equal(this.basicBlock3.previousConnection.targetBlock(), this.basicBlock2);
|
||||
});
|
||||
|
||||
test('Try to connect input that is descendant of output', function() {
|
||||
var markedLocation = this.inlineBlock2.inputList[0].connection;
|
||||
var cursorLocation = this.inlineBlock1.outputConnection;
|
||||
|
||||
Blockly.navigation.connect_(cursorLocation, markedLocation);
|
||||
|
||||
chai.assert.isNull(this.inlineBlock2.outputConnection.targetBlock());
|
||||
chai.assert.equal(this.inlineBlock1.outputConnection.targetBlock(), this.inlineBlock2);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
suite('Test cursor move on block delete', function() {
|
||||
setup(function() {
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "basic_block",
|
||||
"message0": "",
|
||||
"previousStatement": null,
|
||||
"nextStatement": null,
|
||||
}]);
|
||||
this.workspace = createNavigationWorkspace(true);
|
||||
this.basicBlockA = this.workspace.newBlock('basic_block');
|
||||
this.basicBlockB = this.workspace.newBlock('basic_block');
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
workspaceTeardown.call(this, this.workspace);
|
||||
});
|
||||
|
||||
test('Delete block - has parent ', function() {
|
||||
this.basicBlockA.nextConnection.connect(this.basicBlockB.previousConnection);
|
||||
var astNode = Blockly.ASTNode.createBlockNode(this.basicBlockB);
|
||||
// Set the cursor to be on the child block
|
||||
this.workspace.getCursor().setCurNode(astNode);
|
||||
// Remove the child block
|
||||
this.basicBlockB.dispose();
|
||||
chai.assert.equal(this.workspace.getCursor().getCurNode().getType(),
|
||||
Blockly.ASTNode.types.NEXT);
|
||||
});
|
||||
|
||||
test('Delete block - no parent ', function() {
|
||||
var astNode = Blockly.ASTNode.createBlockNode(this.basicBlockB);
|
||||
this.workspace.getCursor().setCurNode(astNode);
|
||||
this.basicBlockB.dispose();
|
||||
chai.assert.equal(this.workspace.getCursor().getCurNode().getType(),
|
||||
Blockly.ASTNode.types.WORKSPACE);
|
||||
});
|
||||
|
||||
test('Delete parent block', function() {
|
||||
this.basicBlockA.nextConnection.connect(this.basicBlockB.previousConnection);
|
||||
var astNode = Blockly.ASTNode.createBlockNode(this.basicBlockB);
|
||||
// Set the cursor to be on the child block
|
||||
this.workspace.getCursor().setCurNode(astNode);
|
||||
// Remove the parent block
|
||||
this.basicBlockA.dispose();
|
||||
chai.assert.equal(this.workspace.getCursor().getCurNode().getType(),
|
||||
Blockly.ASTNode.types.WORKSPACE);
|
||||
});
|
||||
|
||||
test('Delete top block in stack', function() {
|
||||
this.basicBlockA.nextConnection.connect(this.basicBlockB.previousConnection);
|
||||
var astNode = Blockly.ASTNode.createStackNode(this.basicBlockA);
|
||||
// Set the cursor to be on the stack
|
||||
this.workspace.getCursor().setCurNode(astNode);
|
||||
// Remove the top block in the stack
|
||||
this.basicBlockA.dispose();
|
||||
chai.assert.equal(this.workspace.getCursor().getCurNode().getType(),
|
||||
Blockly.ASTNode.types.WORKSPACE);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user