Clean up keystroke handling in treeService.

This commit is contained in:
Sean Lip
2016-06-28 16:41:18 -07:00
parent dddac3a29c
commit ff90218c07
4 changed files with 100 additions and 124 deletions

View File

@@ -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',

View File

@@ -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) {

View File

@@ -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;
}
}

View File

@@ -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) {