Commit de5361ff authored by Zach Helfinstein's avatar Zach Helfinstein Committed by Commit Bot

Expose default action of non-leaf nodes in SwitchAccess

Bug: 873016
Change-Id: Ia69e01e4839d4ae215d8cc97d588cc33abbd17b4
Reviewed-on: https://chromium-review.googlesource.com/1170907
Commit-Queue: Zach Helfinstein <zhelfins@chromium.org>
Reviewed-by: default avatarDavid Tseng <dtseng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#583127}
parent 3015ad4b
...@@ -54,7 +54,7 @@ AutoScanManager.prototype = { ...@@ -54,7 +54,7 @@ AutoScanManager.prototype = {
*/ */
start_: function() { start_: function() {
this.intervalID_ = window.setInterval( this.intervalID_ = window.setInterval(
this.switchAccess_.moveToNode.bind(this.switchAccess_, true), this.switchAccess_.moveForward.bind(this.switchAccess_),
this.scanTime_); this.scanTime_);
}, },
......
...@@ -20,8 +20,7 @@ function FakeSwitchAccess() { ...@@ -20,8 +20,7 @@ function FakeSwitchAccess() {
FakeSwitchAccess.prototype = { FakeSwitchAccess.prototype = {
/** @override */ /** @override */
moveToNode: function(doNext) { moveForward: function() {
if (doNext)
this.moveToNextCount += 1; this.moveToNextCount += 1;
}, },
}; };
......
...@@ -40,6 +40,12 @@ function AutomationManager(desktop) { ...@@ -40,6 +40,12 @@ function AutomationManager(desktop) {
*/ */
this.scopeStack_ = []; this.scopeStack_ = [];
/**
* Keeps track of when we're visiting the current scope as an actionable node.
* @private {boolean}
*/
this.visitingScopeAsActionable_ = false;
this.init_(); this.init_();
} }
...@@ -47,6 +53,7 @@ function AutomationManager(desktop) { ...@@ -47,6 +53,7 @@ function AutomationManager(desktop) {
* Highlight colors for the focus ring to distinguish between different types * Highlight colors for the focus ring to distinguish between different types
* of nodes. * of nodes.
* *
* @enum {string}
* @const * @const
*/ */
AutomationManager.Color = { AutomationManager.Color = {
...@@ -101,7 +108,7 @@ AutomationManager.prototype = { ...@@ -101,7 +108,7 @@ AutomationManager.prototype = {
this.printNode_(this.node_); this.printNode_(this.node_);
this.updateFocusRing_(); this.updateFocusRing_();
} else } else
this.moveToNode(true); this.moveForward();
}, },
/** /**
...@@ -165,42 +172,96 @@ AutomationManager.prototype = { ...@@ -165,42 +172,96 @@ AutomationManager.prototype = {
// moves to it or focus changes to it), won't need to move to a new node. // moves to it or focus changes to it), won't need to move to a new node.
window.setTimeout(function() { window.setTimeout(function() {
if (!this.node_.role) if (!this.node_.role)
this.moveToNode(true); this.moveForward();
}.bind(this), 100); }.bind(this), 100);
}, },
/** /**
* Set this.node_ to the next/previous interesting node, and then highlight * Find the next interesting node, and update |this.node_|. If there is no
* it on the screen. If no interesting node is found, set this.node_ to the * next node, |this.node_| will be set equal to |this.scope_| to loop again.
* first/last interesting node. If |doNext| is true, will search for next
* node. Otherwise, will search for previous node.
*
* @param {boolean} doNext
*/ */
moveToNode: function(doNext) { moveForward: function() {
// If node is invalid, set node to last valid scope. // If node is invalid, set node to last valid scope.
this.startAtValidNode_(); this.startAtValidNode_();
let treeWalker = new AutomationTreeWalker( let treeWalker = new AutomationTreeWalker(
this.node_, doNext ? constants.Dir.FORWARD : constants.Dir.BACKWARD, this.node_, constants.Dir.FORWARD,
SwitchAccessPredicate.restrictions(this.scope_)); SwitchAccessPredicate.restrictions(this.scope_));
let node = treeWalker.next().node; // Special case: Scope is actionable.
if (this.node_ === this.scope_ && !this.visitingScopeAsActionable_) {
this.showScopeAsActionable_();
return;
}
this.visitingScopeAsActionable_ = false;
let node = treeWalker.next().node;
// If treeWalker returns undefined, that means we're at the end of the tree // If treeWalker returns undefined, that means we're at the end of the tree
// and we should start over. // and we should start over.
if (!node) { if (!node)
if (doNext) node = this.scope_;
node = this.scope_;
else this.setCurrentNode_(node);
node = this.youngestDescendant_(this.scope_); },
/**
* Find the previous interesting node and update |this.node_|. If there is no
* previous node, |this.node_| will be set to the youngest descendant in the
* SwitchAccess scope tree to loop again.
*/
moveBackward: function() {
// If node is invalid, set node to last valid scope.
this.startAtValidNode_();
let treeWalker = new AutomationTreeWalker(
this.node_, constants.Dir.BACKWARD,
SwitchAccessPredicate.restrictions(this.scope_));
// Special case: Scope is actionable
if (this.node_ === this.scope_ && this.visitingScopeAsActionable_) {
this.visitingScopeAsActionable_ = false;
this.setCurrentNode_(this.node_);
return;
} }
let node = treeWalker.next().node;
// Special case: Scope is actionable
if (node === this.scope_) {
this.showScopeAsActionable_();
return;
}
// If treeWalker returns undefined, that means we're at the end of the tree
// and we should start over.
if (!node)
node = this.youngestDescendant_(this.scope_);
this.setCurrentNode_(node);
},
/**
* Set |this.node_| to |node|, and update its appearance onscreen.
*
* @param {!chrome.automation.AutomationNode} node
*/
setCurrentNode_: function(node) {
this.node_ = node; this.node_ = node;
this.printNode_(this.node_); this.printNode_(this.node_);
this.updateFocusRing_(); this.updateFocusRing_();
}, },
/**
* Show the current scope as an actionable item.
*/
showScopeAsActionable_: function() {
this.node_ = this.scope_;
this.visitingScopeAsActionable_ = true;
this.printNode_(this.node_);
this.updateFocusRing_(AutomationManager.Color.LEAF);
},
/** /**
* Select the currently highlighted node. If the node is the current scope, * Select the currently highlighted node. If the node is the current scope,
* go back to the previous scope (i.e., create a new tree walker rooted at * go back to the previous scope (i.e., create a new tree walker rooted at
...@@ -214,6 +275,12 @@ AutomationManager.prototype = { ...@@ -214,6 +275,12 @@ AutomationManager.prototype = {
return; return;
if (this.node_ === this.scope_) { if (this.node_ === this.scope_) {
// If we're visiting the scope as actionable, perform the default action.
if (this.visitingScopeAsActionable_) {
this.node_.doDefault();
return;
}
// Don't let user select the top-level root node (i.e., the desktop node). // Don't let user select the top-level root node (i.e., the desktop node).
if (this.scopeStack_.length === 0) if (this.scopeStack_.length === 0)
return; return;
...@@ -234,7 +301,7 @@ AutomationManager.prototype = { ...@@ -234,7 +301,7 @@ AutomationManager.prototype = {
this.scopeStack_.push(this.scope_); this.scopeStack_.push(this.scope_);
this.scope_ = this.node_; this.scope_ = this.node_;
console.log('Entered scope'); console.log('Entered scope');
this.moveToNode(true); this.moveForward();
return; return;
} }
...@@ -246,9 +313,10 @@ AutomationManager.prototype = { ...@@ -246,9 +313,10 @@ AutomationManager.prototype = {
/** /**
* Set the focus ring for the current node and determine the color for it. * Set the focus ring for the current node and determine the color for it.
* *
* @param {AutomationManager.Color=} opt_color
* @private * @private
*/ */
updateFocusRing_: function() { updateFocusRing_: function(opt_color) {
let color; let color;
if (this.node_ === this.scope_) if (this.node_ === this.scope_)
color = AutomationManager.Color.SCOPE; color = AutomationManager.Color.SCOPE;
...@@ -256,11 +324,15 @@ AutomationManager.prototype = { ...@@ -256,11 +324,15 @@ AutomationManager.prototype = {
color = AutomationManager.Color.GROUP; color = AutomationManager.Color.GROUP;
else else
color = AutomationManager.Color.LEAF; color = AutomationManager.Color.LEAF;
color = opt_color || color;
chrome.accessibilityPrivate.setFocusRing([this.node_.location], color); chrome.accessibilityPrivate.setFocusRing([this.node_.location], color);
}, },
/** /**
* If this.node_ is invalid, set this.node_ to a valid scope. Will check the * Checks if this.node_ is valid. If so, do nothing.
*
* If this.node_ is not valid, set this.node_ to a valid scope. Will check the
* current scope and past scopes until a valid scope is found. this.node_ * current scope and past scopes until a valid scope is found. this.node_
* is set to that valid scope. * is set to that valid scope.
* *
......
...@@ -65,11 +65,11 @@ Commands.prototype = { ...@@ -65,11 +65,11 @@ Commands.prototype = {
return { return {
'next': { 'next': {
'defaultKeyCode': 49, /* '1' key */ 'defaultKeyCode': 49, /* '1' key */
'binding': this.switchAccess_.moveToNode.bind(this.switchAccess_, true) 'binding': this.switchAccess_.moveForward.bind(this.switchAccess_)
}, },
'previous': { 'previous': {
'defaultKeyCode': 50, /* '2' key */ 'defaultKeyCode': 50, /* '2' key */
'binding': this.switchAccess_.moveToNode.bind(this.switchAccess_, false) 'binding': this.switchAccess_.moveBackward.bind(this.switchAccess_)
}, },
'select': { 'select': {
'defaultKeyCode': 51, /* '3' key */ 'defaultKeyCode': 51, /* '3' key */
......
...@@ -71,15 +71,21 @@ SwitchAccess.prototype = { ...@@ -71,15 +71,21 @@ SwitchAccess.prototype = {
}, },
/** /**
* Move to the next/previous interesting node. If |doNext| is true, move to * Move to the next interesting node.
* the next node. Otherwise, move to the previous node. * @override
* */
* @param {boolean} doNext moveForward: function() {
if (this.automationManager_)
this.automationManager_.moveForward();
},
/**
* Move to the previous interesting node.
* @override * @override
*/ */
moveToNode: function(doNext) { moveBackward: function() {
if (this.automationManager_) if (this.automationManager_)
this.automationManager_.moveToNode(doNext); this.automationManager_.moveBackward();
}, },
/** /**
......
...@@ -11,12 +11,14 @@ function SwitchAccessInterface() {} ...@@ -11,12 +11,14 @@ function SwitchAccessInterface() {}
SwitchAccessInterface.prototype = { SwitchAccessInterface.prototype = {
/** /**
* Move to the next/previous interesting node. If |doNext| is true, move to * Move to the next interesting node.
* the next node. Otherwise, move to the previous node. */
* moveForward: function() {},
* @param {boolean} doNext
/**
* Move to the previous interesting node.
*/ */
moveToNode: function(doNext) {}, moveBackward: function() {},
/** /**
* Perform the default action on the current node. * Perform the default action on the current node.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment