diff options
author | arv@chromium.org <arv@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-30 19:35:01 +0000 |
---|---|---|
committer | arv@chromium.org <arv@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-30 19:35:01 +0000 |
commit | 68a0de92891f02995af12029bd3bc2fe0a5088f2 (patch) | |
tree | 4047de5f38f5439dc404459f2e006f940aa1e9f7 /chrome/browser/resources/shared | |
parent | ae5253a4c4dae677129db8946a4342486a8fef2e (diff) | |
download | chromium_src-68a0de92891f02995af12029bd3bc2fe0a5088f2.zip chromium_src-68a0de92891f02995af12029bd3bc2fe0a5088f2.tar.gz chromium_src-68a0de92891f02995af12029bd3bc2fe0a5088f2.tar.bz2 |
Selection model refactoring.
BUG=None
TEST=None
Review URL: http://codereview.chromium.org/3025014
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@54357 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/resources/shared')
3 files changed, 295 insertions, 269 deletions
diff --git a/chrome/browser/resources/shared/js/cr/ui/list.js b/chrome/browser/resources/shared/js/cr/ui/list.js index 032d280..9ee1cbb 100644 --- a/chrome/browser/resources/shared/js/cr/ui/list.js +++ b/chrome/browser/resources/shared/js/cr/ui/list.js @@ -10,6 +10,7 @@ cr.define('cr.ui', function() { const ListSelectionModel = cr.ui.ListSelectionModel; + const ListSelectionController = cr.ui.ListSelectionController; const ArrayDataModel = cr.ui.ArrayDataModel; /** @@ -149,6 +150,7 @@ cr.define('cr.ui', function() { } this.selectionModel_ = sm; + this.selectionController_ = new ListSelectionController(sm); if (sm) { sm.addEventListener('change', this.boundHandleOnChange_); @@ -265,13 +267,13 @@ cr.define('cr.ui', function() { } if (!target) { - this.selectionModel.handleMouseDownUp(e, -1); + this.selectionController_.handleMouseDownUp(e, -1); } else { var cs = getComputedStyle(target); var top = target.offsetTop - parseFloat(cs.marginTop); var index = Math.floor(top / this.itemHeight_); - this.selectionModel.handleMouseDownUp(e, index); + this.selectionController_.handleMouseDownUp(e, index); } }, @@ -281,7 +283,7 @@ cr.define('cr.ui', function() { * @return {boolean} Whether the key event was handled. */ handleKeyDown: function(e) { - return this.selectionModel.handleKeyDown(e); + return this.selectionController_.handleKeyDown(e); }, /** diff --git a/chrome/browser/resources/shared/js/cr/ui/list_selection_controller.js b/chrome/browser/resources/shared/js/cr/ui/list_selection_controller.js new file mode 100644 index 0000000..7b7610d --- /dev/null +++ b/chrome/browser/resources/shared/js/cr/ui/list_selection_controller.js @@ -0,0 +1,261 @@ +// Copyright (c) 2010 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() { + /** + * Creates a selection controller that is to be used with lists. This is + * implemented for vertical lists but changing the behavior for horizontal + * lists or icon views is a matter of overriding {@code getIndexBefore}, + * {@code getIndexAfter}, {@code getIndexAbove} as well as + * {@code getIndexBelow}. + * + * @param {cr.ui.ListSelectionModel} selectionModel The selection model to + * interact with. + * + * @constructor + * @extends {!cr.EventTarget} + */ + function ListSelectionController(selectionModel) { + this.selectionModel_ = selectionModel; + } + + ListSelectionController.prototype = { + + /** + * The selection model we are interacting with. + * @type {cr.ui.ListSelectionModel} + */ + get selectionModel() { + return this.selectionModel_; + }, + + /** + * Returns the index below (y axis) the given element. + * @param {number} index The index to get the index below. + * @return {number} The index below or -1 if not found. + */ + getIndexBelow: function(index) { + if (index == this.getLastIndex()) + return -1; + return index + 1; + }, + + /** + * Returns the index above (y axis) the given element. + * @param {number} index The index to get the index above. + * @return {number} The index below or -1 if not found. + */ + getIndexAbove: function(index) { + return index - 1; + }, + + /** + * Returns the index before (x axis) the given element. This returns -1 + * by default but override this for icon view and horizontal selection + * models. + * + * @param {number} index The index to get the index before. + * @return {number} The index before or -1 if not found. + */ + getIndexBefore: function(index) { + return -1; + }, + + /** + * Returns the index after (x axis) the given element. This returns -1 + * by default but override this for icon view and horizontal selection + * models. + * + * @param {number} index The index to get the index after. + * @return {number} The index after or -1 if not found. + */ + getIndexAfter: function(index) { + return -1; + }, + + /** + * Returns the next list index. This is the next logical and should not + * depend on any kind of layout of the list. + * @param {number} index The index to get the next index for. + * @return {number} The next index or -1 if not found. + */ + getNextIndex: function(index) { + if (index == this.getLastIndex()) + return -1; + return index + 1; + }, + + /** + * Returns the prevous list index. This is the previous logical and should + * not depend on any kind of layout of the list. + * @param {number} index The index to get the previous index for. + * @return {number} The previous index or -1 if not found. + */ + getPreviousIndex: function(index) { + return index - 1; + }, + + /** + * @return {number} The first index. + */ + getFirstIndex: function() { + return 0; + }, + + /** + * @return {number} The last index. + */ + getLastIndex: function() { + return this.selectionModel.length - 1; + }, + + /** + * Called by the view when the user does a mousedown or mouseup on the list. + * @param {!Event} e The browser mousedown event. + * @param {number} index The index that was under the mouse pointer, -1 if + * none. + */ + handleMouseDownUp: function(e, index) { + var sm = this.selectionModel; + var anchorIndex = sm.anchorIndex; + var isDown = e.type == 'mousedown'; + + sm.beginChange(); + + if (index == -1) { + // On Mac we always clear the selection if the user clicks a blank area. + // On Windows, we only clear the selection if neither Shift nor Ctrl are + // pressed. + if (cr.isMac) { + sm.leadIndex = sm.anchorIndex = -1; + sm.unselectAll(); + } else if (!isDown && !e.shiftKey && !e.ctrlKey) + // Keep anchor and lead indexes. Note that this is intentionally + // different than on the Mac. + sm.unselectAll(); + } else { + if (cr.isMac ? e.metaKey : e.ctrlKey) { + // Selection is handled at mouseUp on windows/linux, mouseDown on mac. + if (cr.isMac? isDown : !isDown) { + // toggle the current one and make it anchor index + sm.setIndexSelected(index, !sm.getIndexSelected(index)); + sm.leadIndex = index; + sm.anchorIndex = index; + } + } else if (e.shiftKey && anchorIndex != -1 && anchorIndex != index) { + // Shift is done in mousedown + if (isDown) { + sm.unselectAll(); + sm.leadIndex = index; + sm.selectRange(anchorIndex, index); + } + } else { + // Right click for a context menu need to not clear the selection. + var isRightClick = e.button == 2; + + // If the index is selected this is handled in mouseup. + var indexSelected = sm.getIndexSelected(index); + if ((indexSelected && !isDown || !indexSelected && isDown) && + !(indexSelected && isRightClick)) { + sm.unselectAll(); + sm.setIndexSelected(index, true); + sm.leadIndex = index; + sm.anchorIndex = index; + } + } + } + + sm.endChange(); + }, + + /** + * Called by the view when it recieves a keydown event. + * @param {Event} e The keydown event. + */ + handleKeyDown: function(e) { + var sm = this.selectionModel; + var newIndex = -1; + var leadIndex = sm.leadIndex; + var prevent = true; + + // Ctrl/Meta+A + if (e.keyCode == 65 && + (cr.isMac && e.metaKey || !cr.isMac && e.ctrlKey)) { + sm.selectAll(); + e.preventDefault(); + return; + } + + // Space + if (e.keyCode == 32) { + if (leadIndex != -1) { + var selected = sm.getIndexSelected(leadIndex); + if (e.ctrlKey || !selected) { + sm.setIndexSelected(leadIndex, !selected); + return; + } + } + } + + switch (e.keyIdentifier) { + case 'Home': + newIndex = this.getFirstIndex(); + break; + case 'End': + newIndex = this.getLastIndex(); + break; + case 'Up': + newIndex = leadIndex == -1 ? + this.getLastIndex() : this.getIndexAbove(leadIndex); + break; + case 'Down': + newIndex = leadIndex == -1 ? + this.getFirstIndex() : this.getIndexBelow(leadIndex); + break; + case 'Left': + newIndex = leadIndex == -1 ? + this.getLastIndex() : this.getIndexBefore(leadIndex); + break; + case 'Right': + newIndex = leadIndex == -1 ? + this.getFirstIndex() : this.getIndexAfter(leadIndex); + break; + default: + prevent = false; + } + + if (newIndex != -1) { + sm.beginChange(); + + sm.leadIndex = newIndex; + if (e.shiftKey) { + var anchorIndex = sm.anchorIndex; + sm.unselectAll(); + if (anchorIndex == -1) { + sm.setIndexSelected(newIndex, true); + sm.anchorIndex = newIndex; + } else { + sm.selectRange(anchorIndex, newIndex); + } + } else if (e.ctrlKey && !cr.isMac) { + // Setting the lead index is done above + // Mac does not allow you to change the lead. + } else { + sm.unselectAll(); + sm.setIndexSelected(newIndex, true); + sm.anchorIndex = newIndex; + } + + sm.endChange(); + + if (prevent) + e.preventDefault(); + } + } + }; + + return { + ListSelectionController: ListSelectionController + }; +}); diff --git a/chrome/browser/resources/shared/js/cr/ui/list_selection_model.js b/chrome/browser/resources/shared/js/cr/ui/list_selection_model.js index 9ed0c0a..e9a7cb2 100644 --- a/chrome/browser/resources/shared/js/cr/ui/list_selection_model.js +++ b/chrome/browser/resources/shared/js/cr/ui/list_selection_model.js @@ -2,18 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// TODO(arv): Refactor parts of this into a SelectionController. - cr.define('cr.ui', function() { const Event = cr.Event; const EventTarget = cr.EventTarget; /** - * Creates a new selection model that is to be used with lists. This is - * implemented for vertical lists but changing the behavior for horizontal - * lists or icon views is a matter of overriding {@code getIndexBefore}, - * {@code getIndexAfter}, {@code getIndexAbove} as well as - * {@code getIndexBelow}. + * Creates a new selection model that is to be used with lists. * * @param {number=} opt_length The number items in the selection. * @@ -39,236 +33,14 @@ cr.define('cr.ui', function() { }, /** - * Returns the index below (y axis) the given element. - * @param {*} index The index to get the index below. - * @return {*} The index below or -1 if not found. - */ - getIndexBelow: function(index) { - if (index == this.getLastIndex()) - return -1; - return index + 1; - }, - - /** - * Returns the index above (y axis) the given element. - * @param {*} index The index to get the index above. - * @return {*} The index below or -1 if not found. - */ - getIndexAbove: function(index) { - return index - 1; - }, - - /** - * Returns the index before (x axis) the given element. This returns -1 - * by default but override this for icon view and horizontal selection - * models. - * - * @param {*} index The index to get the index before. - * @return {*} The index before or -1 if not found. - */ - getIndexBefore: function(index) { - return -1; - }, - - /** - * Returns the index after (x axis) the given element. This returns -1 - * by default but override this for icon view and horizontal selection - * models. - * - * @param {*} index The index to get the index after. - * @return {*} The index after or -1 if not found. - */ - getIndexAfter: function(index) { - return -1; - }, - - /** - * Returns the next list index. This is the next logical and should not - * depend on any kind of layout of the list. - * @param {*} index The index to get the next index for. - * @return {*} The next index or -1 if not found. - */ - getNextIndex: function(index) { - if (index == this.getLastIndex()) - return -1; - return index + 1; - }, - - /** - * Returns the prevous list index. This is the previous logical and should - * not depend on any kind of layout of the list. - * @param {*} index The index to get the previous index for. - * @return {*} The previous index or -1 if not found. - */ - getPreviousIndex: function(index) { - return index - 1; - }, - - /** - * @return {*} The first index. - */ - getFirstIndex: function() { - return 0; - }, - - /** - * @return {*} The last index. - */ - getLastIndex: function() { - return this.length_ - 1; - }, - - /** - * Called by the view when the user does a mousedown or mouseup on the list. - * @param {!Event} e The browser mousedown event. - * @param {*} index The index that was under the mouse pointer, -1 if none. - */ - handleMouseDownUp: function(e, index) { - var anchorIndex = this.anchorIndex; - var isDown = e.type == 'mousedown'; - - this.beginChange_(); - - if (index == -1) { - // On Mac we always clear the selection if the user clicks a blank area. - // On Windows, we only clear the selection if neither Shift nor Ctrl are - // pressed. - if (cr.isMac) { - this.clear_(); - } else if (!isDown && !e.shiftKey && !e.ctrlKey) - // Keep anchor and lead indexes. Note that this is intentionally - // different than on the Mac. - this.clearAllSelected_(); - } else { - if (cr.isMac ? e.metaKey : e.ctrlKey) { - // Selection is handled at mouseUp on windows/linux, mouseDown on mac. - if (cr.isMac? isDown : !isDown) { - // toggle the current one and make it anchor index - this.setIndexSelected(index, !this.getIndexSelected(index)); - this.leadIndex = index; - this.anchorIndex = index; - } - } else if (e.shiftKey && anchorIndex != -1 && anchorIndex != index) { - // Shift is done in mousedown - if (isDown) { - this.clearAllSelected_(); - this.leadIndex = index; - this.selectRange(anchorIndex, index); - } - } else { - // Right click for a context menu need to not clear the selection. - var isRightClick = e.button == 2; - - // If the index is selected this is handled in mouseup. - var indexSelected = this.getIndexSelected(index); - if ((indexSelected && !isDown || !indexSelected && isDown) && - !(indexSelected && isRightClick)) { - this.clearAllSelected_(); - this.setIndexSelected(index, true); - this.leadIndex = index; - this.anchorIndex = index; - } - } - } - - this.endChange_(); - }, - - /** - * Called by the view when it recieves a keydown event. - * @param {Event} e The keydown event. - */ - handleKeyDown: function(e) { - var newIndex = -1; - var leadIndex = this.leadIndex; - var prevent = true; - - // Ctrl/Meta+A - if (e.keyCode == 65 && - (cr.isMac && e.metaKey || !cr.isMac && e.ctrlKey)) { - this.selectAll(); - e.preventDefault(); - return; - } - - // Space - if (e.keyCode == 32) { - if (leadIndex != -1) { - var selected = this.getIndexSelected(leadIndex); - if (e.ctrlKey || !selected) { - this.beginChange_(); - this.setIndexSelected(leadIndex, !selected); - this.endChange_(); - return; - } - } - } - - switch (e.keyIdentifier) { - case 'Home': - newIndex = this.getFirstIndex(); - break; - case 'End': - newIndex = this.getLastIndex(); - break; - case 'Up': - newIndex = leadIndex == -1 ? - this.getLastIndex() : this.getIndexAbove(leadIndex); - break; - case 'Down': - newIndex = leadIndex == -1 ? - this.getFirstIndex() : this.getIndexBelow(leadIndex); - break; - case 'Left': - newIndex = leadIndex == -1 ? - this.getLastIndex() : this.getIndexBefore(leadIndex); - break; - case 'Right': - newIndex = leadIndex == -1 ? - this.getFirstIndex() : this.getIndexAfter(leadIndex); - break; - default: - prevent = false; - } - - if (newIndex != -1) { - this.beginChange_(); - - this.leadIndex = newIndex; - if (e.shiftKey) { - var anchorIndex = this.anchorIndex; - this.clearAllSelected_(); - if (anchorIndex == -1) { - this.setIndexSelected(newIndex, true); - this.anchorIndex = newIndex; - } else { - this.selectRange(anchorIndex, newIndex); - } - } else if (e.ctrlKey && !cr.isMac) { - // Setting the lead index is done above - // Mac does not allow you to change the lead. - } else { - this.clearAllSelected_(); - this.setIndexSelected(newIndex, true); - this.anchorIndex = newIndex; - } - - this.endChange_(); - - if (prevent) - e.preventDefault(); - } - }, - - /** * @type {!Array} The selected indexes. */ get selectedIndexes() { return Object.keys(this.selectedIndexes_).map(Number); }, set selectedIndexes(selectedIndexes) { - this.beginChange_(); - this.clearAllSelected_(); + this.beginChange(); + this.unselectAll(); for (var i = 0; i < selectedIndexes.length; i++) { this.setIndexSelected(selectedIndexes[i], true); } @@ -277,12 +49,12 @@ cr.define('cr.ui', function() { } else { this.leadIndex = this.anchorIndex = -1; } - this.endChange_(); + this.endChange(); }, /** * Convenience getter which returns the first selected index. - * @type {*} + * @type {number} */ get selectedIndex() { for (var i in this.selectedIndexes_) { @@ -291,21 +63,21 @@ cr.define('cr.ui', function() { return -1; }, set selectedIndex(selectedIndex) { - this.beginChange_(); - this.clearAllSelected_(); + this.beginChange(); + this.unselectAll(); if (selectedIndex != -1) { this.selectedIndexes = [selectedIndex]; } else { this.leadIndex = this.anchorIndex = -1; } - this.endChange_(); + this.endChange(); }, /** * Selects a range of indexes, starting with {@code start} and ends with * {@code end}. - * @param {*} start The first index to select. - * @param {*} end The last index to select. + * @param {number} start The first index to select. + * @param {number} end The last index to select. */ selectRange: function(start, end) { // Swap if starts comes after end. @@ -315,55 +87,48 @@ cr.define('cr.ui', function() { end = tmp; } - this.beginChange_(); + this.beginChange(); for (var index = start; index != end; index++) { this.setIndexSelected(index, true); } this.setIndexSelected(end, true); - this.endChange_(); + this.endChange(); }, /** * Selects all indexes. */ selectAll: function() { - this.selectRange(this.getFirstIndex(), this.getLastIndex()); + this.selectRange(0, this.length); }, /** * Clears the selection */ clear: function() { - this.beginChange_(); + this.beginChange(); this.length_ = 0; - this.clear_(); - this.endChange_(); - }, - - /** - * Clears all selected as well as the lead and anchor index. - * @private - */ - clear_: function() { this.anchorIndex = this.leadIndex = -1; - this.clearAllSelected_(); + this.unselectAll(); + this.endChange(); }, /** - * Clears the selection and updates the view. - * @private + * Unselects all selected items. */ - clearAllSelected_: function() { + unselectAll: function() { + this.beginChange(); for (var i in this.selectedIndexes_) { this.setIndexSelected(i, false); } + this.endChange(); }, /** * Sets the selected state for an index. - * @param {*} index The index to set the selected state for. + * @param {number} index The index to set the selected state for. * @param {boolean} b Whether to select the index or not. */ setIndexSelected: function(index, b) { @@ -376,7 +141,7 @@ cr.define('cr.ui', function() { else delete this.selectedIndexes_[index]; - this.beginChange_(); + this.beginChange(); // Changing back? if (index in this.changedIndexes_ && this.changedIndexes_[index] == !b) { @@ -386,12 +151,12 @@ cr.define('cr.ui', function() { } // End change dispatches an event which in turn may update the view. - this.endChange_(); + this.endChange(); }, /** * Whether a given index is selected or not. - * @param {*} index The index to check. + * @param {number} index The index to check. * @return {boolean} Whether an index is selected. */ getIndexSelected: function(index) { @@ -399,11 +164,10 @@ cr.define('cr.ui', function() { }, /** - * This is used to begin batching changes. Call {@code endChange_} when you + * This is used to begin batching changes. Call {@code endChange} when you * are done making changes. - * @private */ - beginChange_: function() { + beginChange: function() { if (!this.changeCount_) { this.changeCount_ = 0; this.changedIndexes_ = {}; @@ -414,9 +178,8 @@ cr.define('cr.ui', function() { /** * Call this after changes are done and it will dispatch a change event if * any changes were actually done. - * @private */ - endChange_: function() { + endChange: function() { this.changeCount_--; if (!this.changeCount_) { var indexes = Object.keys(this.changedIndexes_); @@ -440,7 +203,7 @@ cr.define('cr.ui', function() { /** * The leadIndex is used with multiple selection and it is the index that * the user is moving using the arrow keys. - * @type {*} + * @type {number} */ get leadIndex() { return this.leadIndex_; @@ -458,7 +221,7 @@ cr.define('cr.ui', function() { /** * The anchorIndex is used with multiple selection. - * @type {*} + * @type {number} */ get anchorIndex() { return this.anchorIndex_; |