Use a cursor to tab between tab navigable fields. (#3365)

* Use a cursor to tab between tab navigable fields.
This commit is contained in:
Sam El-Husseini
2019-10-30 12:41:04 -07:00
committed by GitHub
parent cd412b678e
commit eb50bb33e7
3 changed files with 194 additions and 43 deletions

View File

@@ -26,7 +26,7 @@ goog.addDependency("../../core/block_animations.js", ['Blockly.blockAnimations']
goog.addDependency("../../core/block_drag_surface.js", ['Blockly.BlockDragSurfaceSvg'], ['Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom']);
goog.addDependency("../../core/block_dragger.js", ['Blockly.BlockDragger'], ['Blockly.blockAnimations', 'Blockly.Events', 'Blockly.Events.BlockMove', 'Blockly.Events.Ui', 'Blockly.InsertionMarkerManager', 'Blockly.utils.Coordinate', 'Blockly.utils.dom']);
goog.addDependency("../../core/block_events.js", ['Blockly.Events.BlockBase', 'Blockly.Events.BlockChange', 'Blockly.Events.BlockCreate', 'Blockly.Events.BlockDelete', 'Blockly.Events.BlockMove', 'Blockly.Events.Change', 'Blockly.Events.Create', 'Blockly.Events.Delete', 'Blockly.Events.Move'], ['Blockly.Events', 'Blockly.Events.Abstract', 'Blockly.utils.Coordinate', 'Blockly.utils.object', 'Blockly.utils.xml']);
goog.addDependency("../../core/block_svg.js", ['Blockly.BlockSvg'], ['Blockly.Block', 'Blockly.blockAnimations', 'Blockly.blockRendering.IPathObject', 'Blockly.ContextMenu', 'Blockly.Events', 'Blockly.Events.Ui', 'Blockly.Events.BlockMove', 'Blockly.Msg', 'Blockly.RenderedConnection', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.Rect', 'Blockly.Warning']);
goog.addDependency("../../core/block_svg.js", ['Blockly.BlockSvg'], ['Blockly.Block', 'Blockly.blockAnimations', 'Blockly.blockRendering.IPathObject', 'Blockly.ContextMenu', 'Blockly.Events', 'Blockly.Events.Ui', 'Blockly.Events.BlockMove', 'Blockly.Msg', 'Blockly.RenderedConnection', 'Blockly.TabNavigateCursor', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.Rect', 'Blockly.Warning']);
goog.addDependency("../../core/blockly.js", ['Blockly'], ['Blockly.constants', 'Blockly.Events', 'Blockly.Events.Ui', 'Blockly.inject', 'Blockly.navigation', 'Blockly.Procedures', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.utils', 'Blockly.utils.colour', 'Blockly.Variables', 'Blockly.WidgetDiv', 'Blockly.WorkspaceSvg', 'Blockly.Xml']);
goog.addDependency("../../core/blocks.js", ['Blockly.Blocks'], []);
goog.addDependency("../../core/bubble.js", ['Blockly.Bubble'], ['Blockly.Scrollbar', 'Blockly.Touch', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom', 'Blockly.utils.math', 'Blockly.utils.userAgent', 'Blockly.Workspace']);
@@ -81,6 +81,7 @@ goog.addDependency("../../core/keyboard_nav/flyout_cursor.js", ['Blockly.FlyoutC
goog.addDependency("../../core/keyboard_nav/key_map.js", ['Blockly.user.keyMap'], ['Blockly.utils.KeyCodes', 'Blockly.utils.object']);
goog.addDependency("../../core/keyboard_nav/marker_cursor.js", ['Blockly.MarkerCursor'], ['Blockly.Cursor', 'Blockly.utils.object']);
goog.addDependency("../../core/keyboard_nav/navigation.js", ['Blockly.navigation'], ['Blockly.Action', 'Blockly.ASTNode', 'Blockly.utils.Coordinate', 'Blockly.user.keyMap']);
goog.addDependency("../../core/keyboard_nav/tab_navigate_cursor.js", ['Blockly.TabNavigateCursor'], ['Blockly.ASTNode', 'Blockly.Cursor', 'Blockly.utils.object']);
goog.addDependency("../../core/msg.js", ['Blockly.Msg'], ['Blockly.utils.global']);
goog.addDependency("../../core/mutator.js", ['Blockly.Mutator'], ['Blockly.Bubble', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Events.Ui', 'Blockly.Icon', 'Blockly.utils', 'Blockly.utils.dom', 'Blockly.utils.global', 'Blockly.utils.object', 'Blockly.utils.xml', 'Blockly.WorkspaceSvg', 'Blockly.Xml']);
goog.addDependency("../../core/names.js", ['Blockly.Names'], ['Blockly.Msg']);
@@ -99,7 +100,7 @@ goog.addDependency("../../core/renderers/geras/drawer.js", ['Blockly.geras.Drawe
goog.addDependency("../../core/renderers/geras/highlight_constants.js", ['Blockly.geras.HighlightConstantProvider'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.utils.svgPaths']);
goog.addDependency("../../core/renderers/geras/highlighter.js", ['Blockly.geras.Highlighter'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.Types', 'Blockly.utils.svgPaths']);
goog.addDependency("../../core/renderers/geras/info.js", ['Blockly.geras', 'Blockly.geras.RenderInfo'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.Types', 'Blockly.blockRendering.ExternalValueInput', 'Blockly.geras.InlineInput', 'Blockly.geras.StatementInput', 'Blockly.utils.object']);
goog.addDependency("../../core/renderers/geras/measurables/inputs.js", ['Blockly.geras.InlineInput', 'Blockly.geras.StatementInput'], ['Blockly.blockRendering.Connection', 'Blockly.utils.object']);
goog.addDependency("../../core/renderers/geras/measurables/inputs.js", ['Blockly.geras.InlineInput', 'Blockly.geras.StatementInput'], ['Blockly.utils.object']);
goog.addDependency("../../core/renderers/geras/path_object.js", ['Blockly.geras.PathObject'], ['Blockly.blockRendering.IPathObject', 'Blockly.utils.dom']);
goog.addDependency("../../core/renderers/geras/renderer.js", ['Blockly.geras.Renderer'], ['Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.geras.ConstantProvider', 'Blockly.geras.Drawer', 'Blockly.geras.HighlightConstantProvider', 'Blockly.geras.PathObject', 'Blockly.geras.RenderInfo', 'Blockly.utils.object']);
goog.addDependency("../../core/renderers/measurables/base.js", ['Blockly.blockRendering.Measurable'], ['Blockly.blockRendering.Types']);

View File

@@ -32,6 +32,7 @@ goog.require('Blockly.Events.Ui');
goog.require('Blockly.Events.BlockMove');
goog.require('Blockly.Msg');
goog.require('Blockly.RenderedConnection');
goog.require('Blockly.TabNavigateCursor');
goog.require('Blockly.Tooltip');
goog.require('Blockly.Touch');
goog.require('Blockly.utils');
@@ -693,52 +694,18 @@ Blockly.BlockSvg.prototype.setCollapsed = function(collapsed) {
/**
* Open the next (or previous) FieldTextInput.
* @param {Blockly.Field|Blockly.Block} start Current location.
* @param {!Blockly.Field} start Current field.
* @param {boolean} forward If true go forward, otherwise backward.
*/
Blockly.BlockSvg.prototype.tab = function(start, forward) {
var list = this.createTabList_();
var i = start != null ? list.indexOf(start) : -1;
if (i == -1) {
// No start location, start at the beginning or end.
i = forward ? -1 : list.length;
}
var target = list[forward ? i + 1 : i - 1];
if (!target) {
// Ran off of list.
var parent = this.getParent();
if (parent) {
parent.tab(this, forward);
}
} else if (target instanceof Blockly.Field) {
target.showEditor();
} else {
target.tab(null, forward);
}
};
var tabCursor = new Blockly.TabNavigateCursor();
tabCursor.setCurNode(Blockly.ASTNode.createFieldNode(start));
/**
* Create an ordered list of all text fields and connected inputs.
* @return {!Array.<!Blockly.Field|!Blockly.Block>} The ordered list.
* @private
*/
Blockly.BlockSvg.prototype.createTabList_ = function() {
// This function need not be efficient since it runs once on a keypress.
var list = [];
for (var i = 0, input; input = this.inputList[i]; i++) {
for (var j = 0, field; field = input.fieldRow[j]; j++) {
if (field.isTabNavigable() && field.isVisible()) {
list.push(field);
}
}
if (input.connection) {
var block = input.connection.targetBlock();
if (block) {
list.push(block);
}
}
var nextNode = forward ? tabCursor.next() : tabCursor.prev();
if (nextNode) {
var nextField = /** @type {!Blockly.Field} */ (nextNode.getLocation());
nextField.showEditor();
}
return list;
};
/**

View File

@@ -0,0 +1,183 @@
/**
* @license
* Copyright 2019 Google LLC
*
* 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 The class representing a cursor that is used to navigate
* between tab navigable fields.
* @author samelh@google.com (Sam El-Husseini)
*/
'use strict';
goog.provide('Blockly.TabNavigateCursor');
goog.require('Blockly.ASTNode');
goog.require('Blockly.Cursor');
goog.require('Blockly.utils.object');
/**
* A cursor for navigating between tab navigable fields.
* @constructor
* @extends {Blockly.Cursor}
*/
Blockly.TabNavigateCursor = function() {
Blockly.TabNavigateCursor.superClass_.constructor.call(this);
};
Blockly.utils.object.inherits(Blockly.TabNavigateCursor, Blockly.Cursor);
/**
* Find the next node in the pre order traversal.
* @override
*/
Blockly.TabNavigateCursor.prototype.next = function() {
var curNode = this.getCurNode();
if (!curNode) {
return null;
}
var newNode = this.getNextNode_(curNode);
if (newNode) {
this.setCurNode(newNode);
}
return newNode;
};
/**
* Find the previous node in the pre order traversal.
* @override
*/
Blockly.TabNavigateCursor.prototype.prev = function() {
var curNode = this.getCurNode();
if (!curNode) {
return null;
}
var newNode = this.getPreviousNode_(curNode);
if (newNode) {
this.setCurNode(newNode);
}
return newNode;
};
/**
* Skip all nodes except for tab navigable fields.
* @param {Blockly.ASTNode} node The AST node to check whether it is valid.
* @return {boolean} True if the node should be visited, false otherwise.
* @private
*/
Blockly.TabNavigateCursor.prototype.validNode_ = function(node) {
var isValid = false;
var type = node && node.getType();
if (node) {
var location = node.getLocation();
if (type == Blockly.ASTNode.types.FIELD &&
location && location.isTabNavigable() &&
(/** @type {!Blockly.Field} */ (location)).isClickable()) {
isValid = true;
}
}
return isValid;
};
/**
* From a given node find either the next valid sibling or parent.
* @param {Blockly.ASTNode} node The current position in the AST.
* @return {Blockly.ASTNode} The parent AST node or null if there are no
* valid parents.
* @private
*/
Blockly.TabNavigateCursor.prototype.findSiblingOrParent_ = function(node) {
if (!node) {
return null;
}
var nextNode = node.next();
if (nextNode) {
return nextNode;
}
return this.findSiblingOrParent_(node.out());
};
/**
* Navigate the Blockly AST using pre-order traversal.
* @param {Blockly.ASTNode} node The current position in the AST.
* @return {Blockly.ASTNode} The next node in the traversal.
* @private
*/
Blockly.TabNavigateCursor.prototype.getNextNode_ = function(node) {
if (!node) {
return null;
}
var newNode = node.in() || node.next();
if (this.validNode_(newNode)) {
return newNode;
} else if (newNode) {
return this.getNextNode_(newNode);
}
var siblingOrParent = this.findSiblingOrParent_(node.out());
if (this.validNode_(siblingOrParent)) {
return siblingOrParent;
} else if (siblingOrParent) {
return this.getNextNode_(siblingOrParent);
}
return null;
};
/**
* Get the right most child of a node.
* @param {Blockly.ASTNode} node The node to find the right most child of.
* @return {Blockly.ASTNode} The right most child of the given node, or the node
* if no child exists.
* @private
*/
Blockly.TabNavigateCursor.prototype.getRightMostChild_ = function(node) {
if (!node.in()) {
return node;
}
var newNode = node.in();
while (newNode.next()) {
newNode = newNode.next();
}
return this.getRightMostChild_(newNode);
};
/**
* Use reverse pre-order traversal in order to find the previous node.
* @param {Blockly.ASTNode} node The current position in the AST.
* @return {Blockly.ASTNode} The previous node in the traversal or null if no
* previous node exists.
* @private
*/
Blockly.TabNavigateCursor.prototype.getPreviousNode_ = function(node) {
if (!node) {
return null;
}
var newNode = node.prev();
if (newNode) {
newNode = this.getRightMostChild_(newNode);
} else {
newNode = node.out();
}
if (this.validNode_(newNode)) {
return newNode;
} else if (newNode) {
return this.getPreviousNode_(newNode);
}
return null;
};