blob: 4c0a4982c5dad9987dc506b704d4318c9e44a00b [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* This class handles the grouping of nodes that are not grouped in the
* automation tree. They are defined by their parent and child nodes.
* Ex: Nodes in the virtual keyboard have no intermediate grouping, but should
* be grouped by row.
*/
class GroupNode extends SAChildNode {
/**
* @param {!Array<!SAChildNode>} children The nodes that this group contains.
* Should not include the back button.
* @private
*/
constructor(children) {
super();
/** @type {!Array<!SAChildNode>} */
this.children_ = children;
}
// ================= Getters and setters =================
/** @override */
get actions() {
return [SwitchAccessMenuAction.SELECT];
}
/** @override */
get automationNode() {
return null;
}
/** @override */
get location() {
const childLocations =
this.children_.filter(c => c.isValidAndVisible()).map(c => c.location);
return RectUtil.unionAll(childLocations);
}
/** @override */
get role() {
return chrome.automation.RoleType.GROUP;
}
// ================= General methods =================
/** @override */
asRootNode() {
const root = new SARootNode();
// Make a copy of the children array.
const children = [...this.children_];
children.push(new BackButtonNode(root));
root.children = children;
return root;
}
/** @override */
equals(other) {
if (!(other instanceof GroupNode)) {
return false;
}
other = /** @type {GroupNode} */ (other);
if (other.children_.length !== this.children_.length) {
return false;
}
for (let i = 0; i < this.children_.length; i++) {
if (!other.children_[i].equals(this.children_[i])) {
return false;
}
}
return true;
}
/** @override */
isEquivalentTo(node) {
if (node instanceof GroupNode) {
return this.equals(node);
}
for (const child of this.children_) {
if (child.isEquivalentTo(node)) {
return true;
}
}
return false;
}
/** @override */
isGroup() {
return true;
}
/** @override */
isValidAndVisible() {
for (const child of this.children_) {
if (child.isValidAndVisible()) {
return super.isValidAndVisible();
}
}
return false;
}
/** @override */
performAction(action) {
if (action === SwitchAccessMenuAction.SELECT) {
NavigationManager.enterGroup();
return SAConstants.ActionResponse.CLOSE_MENU;
}
return SAConstants.ActionResponse.NO_ACTION_TAKEN;
}
// ================= Static methods =================
/**
* Assumes nodes are visually in rows.
* @param {!Array<!SAChildNode>} nodes
* @return {!Array<!GroupNode>}
*/
static separateByRow(nodes) {
const result = [];
for (let i = 0; i < nodes.length;) {
const children = [];
children.push(nodes[i]);
i++;
while (i < nodes.length &&
RectUtil.sameRow(children[0].location, nodes[i].location)) {
children.push(nodes[i]);
i++;
}
result.push(new GroupNode(children));
}
return result;
}
}