mirror of
https://github.com/google/blockly.git
synced 2026-01-08 09:30:06 +01:00
Clean up keystroke handling in treeService.
This commit is contained in:
@@ -25,9 +25,6 @@
|
||||
|
||||
blocklyApp.workspace = new Blockly.Workspace();
|
||||
|
||||
// If the debug flag is true, print console.logs to help with debugging.
|
||||
blocklyApp.debug = false;
|
||||
|
||||
blocklyApp.AppView = ng.core
|
||||
.Component({
|
||||
selector: 'blockly-app',
|
||||
|
||||
@@ -98,7 +98,7 @@ blocklyApp.ToolboxComponent = ng.core
|
||||
this.idMap['Parent' + i] = 'blockly-toolbox-tree-node' + i;
|
||||
}
|
||||
} else {
|
||||
// Create a single category is created with all the top-level blocks.
|
||||
// Create a single category with all the top-level blocks.
|
||||
this.xmlHasCategories = false;
|
||||
this.toolboxCategories = [Array.from(xmlToolboxElt.children)];
|
||||
}
|
||||
@@ -108,8 +108,7 @@ blocklyApp.ToolboxComponent = ng.core
|
||||
// descendant after the ids have been computed.
|
||||
if (this.xmlHasCategories) {
|
||||
this.treeService.setActiveDesc(
|
||||
document.getElementById('blockly-toolbox-tree-node0'),
|
||||
document.getElementById('blockly-toolbox-tree'));
|
||||
'blockly-toolbox-tree-node0', 'blockly-toolbox-tree');
|
||||
}
|
||||
},
|
||||
getToolboxWorkspace: function(categoryNode) {
|
||||
|
||||
@@ -27,12 +27,11 @@
|
||||
blocklyApp.TreeService = ng.core
|
||||
.Class({
|
||||
constructor: function() {
|
||||
// Keeping track of the last key pressed. If the user presses
|
||||
// enter (to edit a text input or press a button), the keyboard
|
||||
// focus shifts to that element. In the next keystroke, if the user
|
||||
// navigates away from the element using the arrow keys, we want
|
||||
// to shift focus back to the tree as a whole.
|
||||
this.previousKey_ = null;
|
||||
// Keeping track of whether the user has just focused into an input
|
||||
// field. In the next keystroke, if the user navigates away from the
|
||||
// field using the arrow keys, we want to shift focus back to the tree as
|
||||
// a whole.
|
||||
this.justFocusedIntoField_ = false;
|
||||
// Stores active descendant ids for each tree in the page.
|
||||
this.activeDescendantIds_ = {};
|
||||
},
|
||||
@@ -76,6 +75,16 @@ blocklyApp.TreeService = ng.core
|
||||
|
||||
return this.getToolboxTreeNode_();
|
||||
},
|
||||
focusOnCurrentTree_: function(treeId) {
|
||||
var trees = this.getAllTreeNodes_();
|
||||
for (var i = 0; i < trees.length; i++) {
|
||||
if (trees[i].id == treeId) {
|
||||
trees[i].focus();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
focusOnNextTree_: function(treeId) {
|
||||
var trees = this.getAllTreeNodes_();
|
||||
for (var i = 0; i < trees.length - 1; i++) {
|
||||
@@ -129,127 +138,102 @@ blocklyApp.TreeService = ng.core
|
||||
}, 0);
|
||||
},
|
||||
// Make a given node the active descendant of a given tree.
|
||||
setActiveDesc: function(newActiveDesc, tree) {
|
||||
this.unmarkActiveDesc_(this.getActiveDescId(tree.id));
|
||||
this.markActiveDesc_(newActiveDesc.id);
|
||||
this.activeDescendantIds_[tree.id] = newActiveDesc.id;
|
||||
setActiveDesc: function(newActiveDescId, treeId) {
|
||||
this.unmarkActiveDesc_(this.getActiveDescId(treeId));
|
||||
this.markActiveDesc_(newActiveDescId);
|
||||
this.activeDescendantIds_[treeId] = newActiveDescId;
|
||||
},
|
||||
onWorkspaceToolbarKeypress: function(e, treeId) {
|
||||
switch (e.keyCode) {
|
||||
case 9:
|
||||
// 16,9: shift, tab
|
||||
if (e.shiftKey) {
|
||||
// If the previous key is shift, we're shift-tabbing mode.
|
||||
this.focusOnPreviousTree_(treeId);
|
||||
} else {
|
||||
// If previous key isn't shift, we're tabbing.
|
||||
this.focusOnNextTree_(treeId);
|
||||
}
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
break;
|
||||
if (e.keyCode == 9) {
|
||||
// Tab key.
|
||||
if (e.shiftKey) {
|
||||
this.focusOnPreviousTree_(treeId);
|
||||
} else {
|
||||
this.focusOnNextTree_(treeId);
|
||||
}
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
},
|
||||
isButtonOrFieldNode_: function(node) {
|
||||
return ['BUTTON', 'INPUT'].indexOf(node.tagName) != -1;
|
||||
},
|
||||
onKeypress: function(e, tree) {
|
||||
var treeId = tree.id;
|
||||
var node = document.getElementById(this.getActiveDescId(treeId));
|
||||
var keepFocus = this.previousKey_ == 13;
|
||||
if (!node) {
|
||||
blocklyApp.debug && console.log('KeyHandler: no active descendant');
|
||||
var activeDesc = document.getElementById(this.getActiveDescId(treeId));
|
||||
if (!activeDesc) {
|
||||
console.log('ERROR: no active descendant for current tree.');
|
||||
return;
|
||||
}
|
||||
switch (e.keyCode) {
|
||||
case 9:
|
||||
// 16,9: shift, tab
|
||||
if (e.shiftKey) {
|
||||
// If the previous key is shift, we're shift-tabbing.
|
||||
this.focusOnPreviousTree_(treeId);
|
||||
} else {
|
||||
// If previous key isn't shift, we're tabbing
|
||||
// we want to go to the run code button.
|
||||
this.focusOnNextTree_(treeId);
|
||||
|
||||
var isFocusingIntoField = false;
|
||||
|
||||
if (e.keyCode == 13) {
|
||||
// Enter key. The user wants to interact with a child.
|
||||
if (activeDesc.children.length == 1) {
|
||||
var child = activeDesc.children[0];
|
||||
if (child.tagName == 'BUTTON') {
|
||||
child.click();
|
||||
this.isFocusingIntoField = true;
|
||||
} else if (child.tagName == 'INPUT') {
|
||||
child.focus();
|
||||
}
|
||||
// Setting the previous key variable in each case because
|
||||
// we only want to save the previous navigation keystroke,
|
||||
// not any typing.
|
||||
this.previousKey_ = e.keyCode;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
break;
|
||||
case 37:
|
||||
// Left-facing arrow: go out a level, if possible. If not, do nothing.
|
||||
var nextNode = node.parentNode;
|
||||
if (node.tagName == 'BUTTON' || node.tagName == 'INPUT') {
|
||||
}
|
||||
} else if (e.keyCode == 9) {
|
||||
// Tab key.
|
||||
if (e.shiftKey) {
|
||||
this.focusOnPreviousTree_(treeId);
|
||||
} else {
|
||||
this.focusOnNextTree_(treeId);
|
||||
}
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
} else if (e.keyCode >= 37 && e.keyCode <= 40) {
|
||||
// Arrow keys.
|
||||
|
||||
// If the user has just focused into a text field, shift focus back to
|
||||
// the main tree.
|
||||
if (this.justFocusedIntoField_) {
|
||||
this.focusOnCurrentTree_(treeId);
|
||||
}
|
||||
|
||||
if (e.keyCode == 37) {
|
||||
// Left arrow key. Go up a level, if possible.
|
||||
var nextNode = activeDesc.parentNode;
|
||||
if (this.isButtonOrFieldNode_(activeDesc)) {
|
||||
nextNode = nextNode.parentNode;
|
||||
}
|
||||
while (nextNode && nextNode.className != 'treeview' &&
|
||||
nextNode.tagName != 'LI') {
|
||||
while (nextNode && nextNode.tagName != 'LI') {
|
||||
nextNode = nextNode.parentNode;
|
||||
}
|
||||
if (!nextNode || nextNode.className == 'treeview') {
|
||||
return;
|
||||
if (nextNode) {
|
||||
this.setActiveDesc(nextNode.id, treeId);
|
||||
}
|
||||
this.setActiveDesc(nextNode, tree);
|
||||
this.previousKey_ = e.keyCode;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
break;
|
||||
case 38:
|
||||
// Up-facing arrow: go up a level, if possible. If not, do nothing.
|
||||
var prevSibling = this.getPreviousSibling(node);
|
||||
if (prevSibling && prevSibling.tagName != 'H1') {
|
||||
this.setActiveDesc(prevSibling, tree);
|
||||
} else {
|
||||
blocklyApp.debug && console.log('no previous sibling');
|
||||
} else if (e.keyCode == 38) {
|
||||
// Up arrow key. Go to the previous sibling, if possible.
|
||||
var prevSibling = this.getPreviousSibling(activeDesc);
|
||||
if (prevSibling) {
|
||||
this.setActiveDesc(prevSibling.id, treeId);
|
||||
}
|
||||
this.previousKey_ = e.keyCode;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
break;
|
||||
case 39:
|
||||
var firstChild = this.getFirstChild(node);
|
||||
} else if (e.keyCode == 39) {
|
||||
// Right arrow key. Go down a level, if possible.
|
||||
var firstChild = this.getFirstChild(activeDesc);
|
||||
if (firstChild) {
|
||||
this.setActiveDesc(firstChild, tree);
|
||||
} else {
|
||||
blocklyApp.debug && console.log('no valid child');
|
||||
this.setActiveDesc(firstChild.id, treeId);
|
||||
}
|
||||
this.previousKey_ = e.keyCode;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
break;
|
||||
case 40:
|
||||
// Down-facing arrow: go down a level, if possible.
|
||||
// If not, do nothing.
|
||||
var nextSibling = this.getNextSibling(node);
|
||||
} else if (e.keyCode == 40) {
|
||||
// Down arrow key. Go to the next sibling, if possible.
|
||||
var nextSibling = this.getNextSibling(activeDesc);
|
||||
if (nextSibling) {
|
||||
this.setActiveDesc(nextSibling, tree);
|
||||
} else {
|
||||
blocklyApp.debug && console.log('no next sibling');
|
||||
this.setActiveDesc(nextSibling.id, treeId);
|
||||
}
|
||||
this.previousKey_ = e.keyCode;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
break;
|
||||
case 13:
|
||||
// If I've pressed enter, I want to interact with a child.
|
||||
var activeDesc = node;
|
||||
if (activeDesc) {
|
||||
var children = activeDesc.children;
|
||||
var child = children[0];
|
||||
if (children.length == 1 && (child.tagName == 'INPUT' ||
|
||||
child.tagName == 'BUTTON')) {
|
||||
if (child.tagName == 'BUTTON') {
|
||||
child.click();
|
||||
}
|
||||
else if (child.tagName == 'INPUT') {
|
||||
child.focus();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
blocklyApp.debug && console.log('no activeDesc');
|
||||
}
|
||||
this.previousKey_ = e.keyCode;
|
||||
break;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
this.justFocusedIntoField_ = isFocusingIntoField;
|
||||
},
|
||||
getFirstChild: function(element) {
|
||||
if (!element) {
|
||||
@@ -273,13 +257,12 @@ blocklyApp.TreeService = ng.core
|
||||
if (element.nextElementSibling) {
|
||||
// If there is a sibling, find the list element child of the sibling.
|
||||
var node = element.nextElementSibling;
|
||||
if (node.tagName != 'LI') {
|
||||
var listElems = node.getElementsByTagName('li');
|
||||
// getElementsByTagName returns in DFS order
|
||||
// therefore the first element is the first relevant list child.
|
||||
return listElems[0];
|
||||
if (node.tagName == 'LI') {
|
||||
return node;
|
||||
} else {
|
||||
return element.nextElementSibling;
|
||||
// getElementsByTagName returns in DFS order, therefore the first
|
||||
// element is the first relevant list child.
|
||||
return node.getElementsByTagName('li')[0];
|
||||
}
|
||||
} else {
|
||||
var parent = element.parentNode;
|
||||
@@ -343,7 +326,6 @@ blocklyApp.TreeService = ng.core
|
||||
}
|
||||
}
|
||||
}
|
||||
blocklyApp.debug && console.log('no last child');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,9 +180,7 @@ blocklyApp.WorkspaceTreeComponent = ng.core
|
||||
|
||||
if (this.tree && this.isTopLevel &&
|
||||
!this.treeService.getActiveDescId(this.tree.id)) {
|
||||
this.treeService.setActiveDesc(
|
||||
document.getElementById(this.idMap['parentList']),
|
||||
this.tree);
|
||||
this.treeService.setActiveDesc(this.idMap['parentList'], this.tree.id);
|
||||
}
|
||||
},
|
||||
hasPreviousConnection: function(block) {
|
||||
|
||||
Reference in New Issue
Block a user