blob: 2e76ca10c03009f2d5d72e5a781989e1754a00f4 [file] [log] [blame]
// Copyright 2014 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.
cr.define('cr.ui', function() {
/**
* A class to manage grid of focusable elements in a 2D grid. For example,
* given this grid:
*
* focusable [focused] focusable (row: 0, col: 1)
* focusable focusable focusable
* focusable focusable focusable
*
* Pressing the down arrow would result in the focus moving down 1 row and
* keeping the same column:
*
* focusable focusable focusable
* focusable [focused] focusable (row: 1, col: 1)
* focusable focusable focusable
*
* And pressing right at this point would move the focus to:
*
* focusable focusable focusable
* focusable focusable [focused] (row: 1, col: 2)
* focusable focusable focusable
*
* @param {Node=} opt_boundary Ignore focus events outside this node.
* @param {cr.ui.FocusRow.Observer=} opt_observer An observer of rows.
* @implements {cr.ui.FocusRow.Delegate}
* @constructor
*/
function FocusGrid(opt_boundary, opt_observer) {
/** @type {Node|undefined} */
this.boundary_ = opt_boundary;
/** @private {cr.ui.FocusRow.Observer|undefined} */
this.observer_ = opt_observer;
/** @type {!Array.<!cr.ui.FocusRow>} */
this.rows = [];
}
FocusGrid.prototype = {
/**
* Unregisters event handlers and removes all |this.rows|.
*/
destroy: function() {
this.rows.forEach(function(row) { row.destroy(); });
this.rows.length = 0;
},
/**
* @param {EventTarget} target A target item to find in this grid.
* @return {?{row: number, col: number}} A position or null if not found.
*/
getPositionForTarget: function(target) {
for (var i = 0; i < this.rows.length; ++i) {
for (var j = 0; j < this.rows[i].items.length; ++j) {
if (target == this.rows[i].items[j])
return {row: i, col: j};
}
}
return null;
},
/** @override */
onKeydown: function(keyRow, e) {
var rowIndex = this.rows.indexOf(keyRow);
assert(rowIndex >= 0);
var row = -1;
if (e.keyIdentifier == 'Up')
row = rowIndex - 1;
else if (e.keyIdentifier == 'Down')
row = rowIndex + 1;
else if (e.keyIdentifier == 'PageUp')
row = 0;
else if (e.keyIdentifier == 'PageDown')
row = this.rows.length - 1;
if (!this.rows[row])
return false;
var colIndex = keyRow.items.indexOf(e.target);
var col = Math.min(colIndex, this.rows[row].items.length - 1);
this.rows[row].focusIndex(col);
e.preventDefault();
return true;
},
/** @override */
onMousedown: function(row, e) {
return false;
},
/**
* @param {!Array.<!NodeList|!Array.<!Element>>} grid A 2D array of nodes.
*/
setGrid: function(grid) {
this.destroy();
this.rows = grid.map(function(row) {
return new cr.ui.FocusRow(row, this.boundary_, this, this.observer_);
}, this);
if (!this.getPositionForTarget(document.activeElement) && this.rows[0])
this.rows[0].activeIndex = 0;
},
};
return {
FocusGrid: FocusGrid,
};
});