mirror of
https://github.com/google/blockly.git
synced 2026-01-11 10:57:07 +01:00
Use a cursor to tab between tab navigable fields. (#3365)
* Use a cursor to tab between tab navigable fields.
This commit is contained in:
@@ -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']);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
183
core/keyboard_nav/tab_navigate_cursor.js
Normal file
183
core/keyboard_nav/tab_navigate_cursor.js
Normal 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;
|
||||
};
|
||||
Reference in New Issue
Block a user