Commit e144d302 authored by fukino's avatar fukino Committed by Commit bot

Files.app: Introduce check-select mode.

Check-select mode spec is here: https://docs.google.com/document/d/1kCG0AFtZSlxlkuipNHlKLY2nvjPdDCViG6RKhMI5-dc/edit

Mode transition rules:
 + Entering check-select mode
   - When two or more items are selected.
   - When checkmark area is clicked.
 + Leaving check-select mode
   - When all items are unselected.
   - When non-checkmark area on an item is clicked.
   - When direction key(UP,LEFT,etc...) results in single selection.

BUG=460220
TEST=manually tested

Review URL: https://codereview.chromium.org/962103002

Cr-Commit-Position: refs/heads/master@{#318658}
parent 083c38c0
...@@ -1037,24 +1037,3 @@ util.addEventListenerToBackgroundComponent = function(target, type, handler) { ...@@ -1037,24 +1037,3 @@ util.addEventListenerToBackgroundComponent = function(target, type, handler) {
target.removeEventListener(type, handler); target.removeEventListener(type, handler);
}); });
}; };
/**
* Repeats a given event to a given target with event.ctrlKey set true.
* @param {!EventTarget} target Event target which receives the repeated event.
* @param {!Event} event Original mouse event.
*/
util.repeatMouseEventWithCtrlKey = function(target, event) {
event.stopPropagation();
event.preventDefault();
event = assertInstanceof(event, MouseEvent);
var eventWithCtrl = new MouseEvent(event.type, {
bubbles: event.bubbles,
button: event.button,
clientX: event.clientX,
clientY: event.clientY,
ctrlKey: true,
shiftKey: event.shiftKey,
target: event.target
});
target.dispatchEvent(eventWithCtrl);
};
...@@ -252,7 +252,7 @@ div.splitter { ...@@ -252,7 +252,7 @@ div.splitter {
transition: background 220ms ease; transition: background 220ms ease;
} }
body.selecting .dialog-header { body.check-select .dialog-header {
background: white; background: white;
border-bottom: 1px solid rgb(219, 219, 219); border-bottom: 1px solid rgb(219, 219, 219);
color: rgb(90, 90, 90); color: rgb(90, 90, 90);
...@@ -272,8 +272,8 @@ body.selecting .dialog-header { ...@@ -272,8 +272,8 @@ body.selecting .dialog-header {
color: white; color: white;
} }
body.selecting button, body.check-select button,
body.selecting button:hover { body.check-select button:hover {
color: rgb(90, 90, 90); color: rgb(90, 90, 90);
} }
...@@ -297,7 +297,7 @@ body.selecting button:hover { ...@@ -297,7 +297,7 @@ body.selecting button:hover {
color: black; color: black;
} }
body.selecting .dialog-header .icon-button paper-ripple { body.check-select .dialog-header .icon-button paper-ripple {
rgb(90, 90, 90); rgb(90, 90, 90);
} }
...@@ -308,10 +308,10 @@ body.selecting .dialog-header .icon-button paper-ripple { ...@@ -308,10 +308,10 @@ body.selecting .dialog-header .icon-button paper-ripple {
background-color: rgba(90, 90, 90, 0.15); background-color: rgba(90, 90, 90, 0.15);
} }
body.selecting .dialog-header .icon-button:hover, body.check-select .dialog-header .icon-button:hover,
body.selecting .dialog-header .icon-button:focus, body.check-select .dialog-header .icon-button:focus,
body.selecting .dialog-header .combobutton:hover, body.check-select .dialog-header .combobutton:hover,
body.selecting .dialog-header .combobutton:focus { body.check-select .dialog-header .combobutton:focus {
background-color: rgba(153, 153, 153, 0.20); background-color: rgba(153, 153, 153, 0.20);
} }
...@@ -355,7 +355,7 @@ body.selecting .dialog-header .combobutton:focus { ...@@ -355,7 +355,7 @@ body.selecting .dialog-header .combobutton:focus {
url(../images/files/ui/2x/search_white.png) 2x); url(../images/files/ui/2x/search_white.png) 2x);
} }
body.selecting #search-button { body.check-select #search-button {
background-image: -webkit-image-set( background-image: -webkit-image-set(
url(../images/files/ui/search.png) 1x, url(../images/files/ui/search.png) 1x,
url(../images/files/ui/2x/search.png) 2x); url(../images/files/ui/2x/search.png) 2x);
...@@ -367,7 +367,7 @@ body.selecting #search-button { ...@@ -367,7 +367,7 @@ body.selecting #search-button {
url(../images/files/ui/2x/share_white.png) 2x); url(../images/files/ui/2x/share_white.png) 2x);
} }
body.selecting #share-button { body.check-select #share-button {
background-image: -webkit-image-set( background-image: -webkit-image-set(
url(../images/files/ui/share.png) 1x, url(../images/files/ui/share.png) 1x,
url(../images/files/ui/2x/share.png) 2x); url(../images/files/ui/2x/share.png) 2x);
...@@ -379,7 +379,7 @@ body.selecting #share-button { ...@@ -379,7 +379,7 @@ body.selecting #share-button {
url(../images/files/ui/2x/delete_white.png) 2x); url(../images/files/ui/2x/delete_white.png) 2x);
} }
body.selecting #delete-button { body.check-select #delete-button {
background-image: -webkit-image-set( background-image: -webkit-image-set(
url(../images/files/ui/delete.png) 1x, url(../images/files/ui/delete.png) 1x,
url(../images/files/ui/2x/delete.png) 2x); url(../images/files/ui/2x/delete.png) 2x);
...@@ -391,10 +391,8 @@ body.selecting #delete-button { ...@@ -391,10 +391,8 @@ body.selecting #delete-button {
url(../images/files/ui/2x/view_list_white.png) 2x); url(../images/files/ui/2x/view_list_white.png) 2x);
} }
body.selecting #view-button { body.check-select #view-button {
background-image: -webkit-image-set( display: none;
url(../images/files/ui/view_list.png) 1x,
url(../images/files/ui/2x/view_list.png) 2x);
} }
#view-button.thumbnail { #view-button.thumbnail {
...@@ -403,10 +401,8 @@ body.selecting #view-button { ...@@ -403,10 +401,8 @@ body.selecting #view-button {
url(../images/files/ui/2x/view_thumbnail_white.png) 2x); url(../images/files/ui/2x/view_thumbnail_white.png) 2x);
} }
body.selecting #view-button.thumbnail { body.check-select #view-button.thumbnail {
background-image: -webkit-image-set( display: none;
url(../images/files/ui/view_thumbnail.png) 1x,
url(../images/files/ui/2x/view_thumbnail.png) 2x);
} }
#gear-button { #gear-button {
...@@ -415,10 +411,8 @@ body.selecting #view-button.thumbnail { ...@@ -415,10 +411,8 @@ body.selecting #view-button.thumbnail {
url(../images/files/ui/2x/menu_white.png) 2x); url(../images/files/ui/2x/menu_white.png) 2x);
} }
body.selecting #gear-button { body.check-select #gear-button {
background-image: -webkit-image-set( display: none;
url(../images/files/ui/menu.png) 1x,
url(../images/files/ui/2x/menu.png) 2x);
} }
#cloud-import-details-button { #cloud-import-details-button {
...@@ -502,7 +496,7 @@ body.selecting #gear-button { ...@@ -502,7 +496,7 @@ body.selecting #gear-button {
margin: 0 10px; margin: 0 10px;
} }
body.selecting #files-selected-label { body.check-select #files-selected-label {
display: block; display: block;
} }
...@@ -531,12 +525,12 @@ body.selecting #files-selected-label { ...@@ -531,12 +525,12 @@ body.selecting #files-selected-label {
-webkit-margin-end: 6px; -webkit-margin-end: 6px;
} }
body.selecting #cancel-selection-button { body.check-select #cancel-selection-button {
display: inline-block; display: inline-block;
} }
/* Search button */ /* Search button */
body.selecting #search-button { body.check-select #search-button {
display: none; display: none;
} }
...@@ -546,7 +540,7 @@ body.selecting #search-button { ...@@ -546,7 +540,7 @@ body.selecting #search-button {
flex: none; flex: none;
} }
body.selecting #search-box { body.check-select #search-box {
display: none; display: none;
} }
...@@ -791,7 +785,7 @@ body.selecting #search-box { ...@@ -791,7 +785,7 @@ body.selecting #search-box {
padding-top: 1px; padding-top: 1px;
} }
body.selecting .breadcrumbs { body.check-select .breadcrumbs {
display: none; display: none;
} }
...@@ -1048,13 +1042,20 @@ body[type='full-page'] .detail-name .detail-icon { ...@@ -1048,13 +1042,20 @@ body[type='full-page'] .detail-name .detail-icon {
} }
body.selecting .thumbnail-grid .checkmark { body.selecting .thumbnail-grid .checkmark {
background-image: -webkit-image-set(
url(../images/files/ui/thumbnail_unchecked.png) 1x,
url(../images/files/ui/2x/thumbnail_unchecked.png) 2x);
opacity: 0.6;
}
body.check-select .thumbnail-grid .thumbnail-item .checkmark {
background-image: -webkit-image-set( background-image: -webkit-image-set(
url(../images/files/ui/thumbnail_unchecked.png) 1x, url(../images/files/ui/thumbnail_unchecked.png) 1x,
url(../images/files/ui/2x/thumbnail_unchecked.png) 2x); url(../images/files/ui/2x/thumbnail_unchecked.png) 2x);
opacity: 1; opacity: 1;
} }
body.selecting .thumbnail-grid .thumbnail-item[selected] .checkmark { body.check-select .thumbnail-grid .thumbnail-item[selected] .checkmark {
background-image: -webkit-image-set( background-image: -webkit-image-set(
url(../images/files/ui/thumbnail_checked.png) 1x, url(../images/files/ui/thumbnail_checked.png) 1x,
url(../images/files/ui/2x/thumbnail_checked.png) 2x); url(../images/files/ui/2x/thumbnail_checked.png) 2x);
...@@ -1364,7 +1365,7 @@ li[renaming=''] .badge { ...@@ -1364,7 +1365,7 @@ li[renaming=''] .badge {
width: 28px; width: 28px;
} }
#list-container list li[selected] .detail-checkmark { body.check-select #list-container list li[selected] .detail-checkmark {
opacity: 1; opacity: 1;
} }
...@@ -1382,10 +1383,14 @@ li[renaming=''] .badge { ...@@ -1382,10 +1383,14 @@ li[renaming=''] .badge {
width: 28px; width: 28px;
} }
#list-container list li:not([selected]) .detail-thumbnail.loaded { #list-container list li .detail-thumbnail.loaded {
opacity: 1; opacity: 1;
} }
body.check-select #list-container list li[selected] .detail-thumbnail.loaded {
opacity: 0;
}
#tasks-menu cr-menu-item:not(.change-default) { #tasks-menu cr-menu-item:not(.change-default) {
background-position: left 10px center; background-position: left 10px center;
padding-left: 32px; padding-left: 32px;
......
...@@ -117,6 +117,7 @@ ...@@ -117,6 +117,7 @@
'./ui/drag_selector.js', './ui/drag_selector.js',
'./ui/error_dialog.js', './ui/error_dialog.js',
'./ui/file_grid.js', './ui/file_grid.js',
'./ui/file_list_selection_model.js',
'./ui/file_manager_ui.js', './ui/file_manager_ui.js',
'./ui/file_table.js', './ui/file_table.js',
'./ui/file_table_list.js', './ui/file_table_list.js',
......
...@@ -26,7 +26,7 @@ function DirectoryModel(singleSelection, fileFilter, ...@@ -26,7 +26,7 @@ function DirectoryModel(singleSelection, fileFilter,
metadataProviderCache, fileSystemMetadata, metadataProviderCache, fileSystemMetadata,
volumeManager, fileOperationManager) { volumeManager, fileOperationManager) {
this.fileListSelection_ = singleSelection ? this.fileListSelection_ = singleSelection ?
new cr.ui.ListSingleSelectionModel() : new cr.ui.ListSelectionModel(); new FileListSingleSelectionModel() : new FileListSelectionModel();
this.runningScan_ = null; this.runningScan_ = null;
this.pendingScan_ = null; this.pendingScan_ = null;
......
...@@ -136,6 +136,7 @@ ...@@ -136,6 +136,7 @@
//<include src="ui/error_dialog.js"> //<include src="ui/error_dialog.js">
//<include src="ui/file_grid.js"> //<include src="ui/file_grid.js">
//<include src="ui/file_manager_ui.js"> //<include src="ui/file_manager_ui.js">
//<include src="ui/file_list_selection_model.js">
//<include src="ui/file_table.js"> //<include src="ui/file_table.js">
//<include src="ui/file_table_list.js"> //<include src="ui/file_table_list.js">
//<include src="ui/gear_menu.js"> //<include src="ui/gear_menu.js">
......
...@@ -121,8 +121,13 @@ ToolbarController.prototype.onSelectionChanged_ = function() { ...@@ -121,8 +121,13 @@ ToolbarController.prototype.onSelectionChanged_ = function() {
// update the checkmark visibility on grid view. This should be moved to a // update the checkmark visibility on grid view. This should be moved to a
// controller which controls whole app window. Or, both toolbar and FileGrid // controller which controls whole app window. Or, both toolbar and FileGrid
// should listen to the FileSelectionHandler. // should listen to the FileSelectionHandler.
this.filesSelectedLabel_.ownerDocument.body.classList.toggle( if (this.directoryModel_.getFileListSelection().multiple) {
'selecting', selection.totalCount > 0); this.filesSelectedLabel_.ownerDocument.body.classList.toggle(
'selecting', selection.totalCount > 0);
this.filesSelectedLabel_.ownerDocument.body.classList.toggle(
'check-select',
this.directoryModel_.getFileListSelection().getCheckSelectMode());
}
} }
/** /**
......
...@@ -114,6 +114,13 @@ FileGrid.prototype.mergeItems = function(beginIndex, endIndex) { ...@@ -114,6 +114,13 @@ FileGrid.prototype.mergeItems = function(beginIndex, endIndex) {
this.listThumbnailLoader_.setHighPriorityRange(beginIndex, endIndex); this.listThumbnailLoader_.setHighPriorityRange(beginIndex, endIndex);
}; };
/**
* @override
*/
FileGrid.prototype.createSelectionController = function(sm) {
return new FileGridSelectionController(assert(sm), this);
};
/** /**
* Updates items to reflect metadata changes. * Updates items to reflect metadata changes.
* @param {string} type Type of metadata changed. * @param {string} type Type of metadata changed.
...@@ -195,10 +202,6 @@ FileGrid.decorateThumbnail_ = function( ...@@ -195,10 +202,6 @@ FileGrid.decorateThumbnail_ = function(
var checkmark = li.ownerDocument.createElement('div'); var checkmark = li.ownerDocument.createElement('div');
checkmark.className = 'checkmark'; checkmark.className = 'checkmark';
checkmark.addEventListener(
'mouseup', util.repeatMouseEventWithCtrlKey.bind(null, li));
checkmark.addEventListener(
'mousedown', util.repeatMouseEventWithCtrlKey.bind(null, li));
frame.appendChild(checkmark); frame.appendChild(checkmark);
var bottom = li.ownerDocument.createElement('div'); var bottom = li.ownerDocument.createElement('div');
...@@ -439,3 +442,31 @@ FileGrid.prototype.getHitElements = function(x, y, opt_width, opt_height) { ...@@ -439,3 +442,31 @@ FileGrid.prototype.getHitElements = function(x, y, opt_width, opt_height) {
} }
return currentSelection; return currentSelection;
}; };
/**
* Selection controller for the file grid.
* @param {!cr.ui.ListSelectionModel} selectionModel The selection model to
* interact with.
* @param {!cr.ui.Grid} grid The grid to interact with.
* @constructor
* @extends {cr.ui.GridSelectionController}
* @struct
* @suppress {checkStructDictInheritance}
*/
function FileGridSelectionController(selectionModel, grid) {
cr.ui.GridSelectionController.call(this, selectionModel, grid);
}
FileGridSelectionController.prototype = /** @struct */ {
__proto__: cr.ui.GridSelectionController.prototype
};
/** @override */
FileGridSelectionController.prototype.handlePointerDownUp = function(e, index) {
filelist.handlePointerDownUp.call(this, e, index);
};
/** @override */
FileGridSelectionController.prototype.handleKeyDown = function(e) {
filelist.handleKeyDown.call(this, e);
};
// Copyright 2015 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.
/**
* @param {number=} opt_length The number items in the selection.
* @constructor
* @extends {cr.ui.ListSelectionModel}
* @struct
* @suppress {checkStructDictInheritance}
*/
function FileListSelectionModel(opt_length) {
cr.ui.ListSelectionModel.call(this, opt_length);
/** @private {boolean} */
this.isCheckSelectMode_ = false;
this.addEventListener('change', this.onChangeEvent_.bind(this));
}
FileListSelectionModel.prototype = /** @struct */ {
__proto__: cr.ui.ListSelectionModel.prototype
};
/**
* Updates the check-select mode.
* @param {boolean} enabled True if check-select mode should be enabled.
*/
FileListSelectionModel.prototype.setCheckSelectMode = function(enabled) {
this.isCheckSelectMode_ = enabled;
};
/**
* Gets the check-select mode.
* @return {boolean} True if check-select mode is enabled.
*/
FileListSelectionModel.prototype.getCheckSelectMode = function() {
return this.isCheckSelectMode_;
};
/**
* Handles change event to update isCheckSelectMode_ BEFORE the change event is
* dispatched to other listeners.
* @param {!Event} event Event object of 'change' event.
* @private
*/
FileListSelectionModel.prototype.onChangeEvent_ = function(event) {
// When the number of selected item is not one, update che check-select mode.
// When the number of selected item is one, the mode depends on the last
// keyboard/mouse operation. In this case, the mode is controlled from
// outside. See filelist.handlePointerDownUp and filelist.handleKeyDown.
var selectedIndexes = this.selectedIndexes;
if (selectedIndexes.length === 0) {
this.isCheckSelectMode_ = false;
} else if (selectedIndexes.length >= 2) {
this.isCheckSelectMode_ = true;
}
};
/**
* @param {number=} opt_length The number items in the selection.
* @constructor
* @extends {cr.ui.ListSingleSelectionModel}
* @struct
* @suppress {checkStructDictInheritance}
*/
function FileListSingleSelectionModel(opt_length) {
cr.ui.ListSingleSelectionModel.call(this, opt_length);
}
FileListSingleSelectionModel.prototype = /** @struct */ {
__proto__: cr.ui.ListSingleSelectionModel.prototype
};
/**
* Updates the check-select mode.
* @param {boolean} enabled True if check-select mode should be enabled.
*/
FileListSingleSelectionModel.prototype.setCheckSelectMode = function(enabled) {
// Do nothing, as check-select mode is invalid in single selection model.
};
/**
* Gets the check-select mode.
* @return {boolean} True if check-select mode is enabled.
*/
FileListSingleSelectionModel.prototype.getCheckSelectMode = function() {
return false;
};
...@@ -530,10 +530,6 @@ FileTable.prototype.renderName_ = function(entry, columnId, table) { ...@@ -530,10 +530,6 @@ FileTable.prototype.renderName_ = function(entry, columnId, table) {
if (FileType.isImage(entry) || FileType.isVideo(entry)) if (FileType.isImage(entry) || FileType.isVideo(entry))
icon.appendChild(this.renderThumbnail_(entry)); icon.appendChild(this.renderThumbnail_(entry));
icon.appendChild(this.renderCheckmark_()); icon.appendChild(this.renderCheckmark_());
icon.addEventListener(
'mouseup', util.repeatMouseEventWithCtrlKey.bind(null, label));
icon.addEventListener(
'mousedown', util.repeatMouseEventWithCtrlKey.bind(null, label));
label.appendChild(icon); label.appendChild(icon);
label.entry = entry; label.entry = entry;
...@@ -967,3 +963,214 @@ filelist.updateListItemExternalProps = function(li, externalProps) { ...@@ -967,3 +963,214 @@ filelist.updateListItemExternalProps = function(li, externalProps) {
if (li.classList.contains('directory')) if (li.classList.contains('directory'))
iconDiv.classList.toggle('shared', externalProps.shared); iconDiv.classList.toggle('shared', externalProps.shared);
}; };
/**
* Handles mouseup/mousedown events on file list to change the selection state.
*
* Basically the content of this function is identical to
* cr.ui.ListSelectionController's handlePointerDownUp(), but following
* handlings are inserted to control the check-select mode.
*
* 1) When checkmark area is clicked, toggle item selection and enable the
* check-select mode.
* 2) When non-checkmark area is clicked in check-select mode, disable the
* check-select mode.
*
* @param {!Event} e The browser mouse event.
* @param {number} index The index that was under the mouse pointer, -1 if
* none.
* @this {cr.ui.ListSelectionController}
*/
filelist.handlePointerDownUp = function(e, index) {
var sm = /** @type {!FileListSelectionModel|!FileListSingleSelectionModel} */
(this.selectionModel);
var anchorIndex = sm.anchorIndex;
var isDown = (e.type == 'mousedown');
var isTargetCheckmark = e.target.classList.contains('detail-checkmark') ||
e.target.classList.contains('checkmark');
// If multiple selection is allowed and the checkmark is clicked without
// modifiers(Ctrl/Shift), the click should toggle the item's selection.
// (i.e. same behavior as Ctrl+Click)
var isClickOnCheckmark = isTargetCheckmark && sm.multiple && index != -1 &&
!e.shiftKey && !e.ctrlKey && e.button == 0;
sm.beginChange();
if (index == -1) {
sm.leadIndex = sm.anchorIndex = -1;
sm.unselectAll();
} else {
if (sm.multiple && (e.ctrlKey || isClickOnCheckmark) && !e.shiftKey) {
// Selection is handled at mouseUp.
if (!isDown) {
// 1) When checkmark area is clicked, toggle item selection and enable
// the check-select mode.
if (isClickOnCheckmark) {
// If a selected item's checkmark is clicked when the selection mode
// is not check-select, we should avoid toggling(unselecting) the
// item. It is done here by toggling the selection twice.
if (!sm.getCheckSelectMode() && sm.getIndexSelected(index))
sm.setIndexSelected(index, !sm.getIndexSelected(index));
// Always enables check-select mode on clicks on checkmark.
sm.setCheckSelectMode(true);
}
// 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;
if (sm.multiple)
sm.selectRange(anchorIndex, index);
else
sm.setIndexSelected(index, true);
}
} else {
// Right click for a context menu needs 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)) {
// 2) When non-checkmark area is clicked in check-select mode, disable
// the check-select mode.
if (sm.getCheckSelectMode()) {
// Unselect all items once to ensure that the check-select mode is
// terminated.
sm.endChange();
sm.unselectAll();
sm.beginChange();
}
sm.selectedIndex = index;
}
}
}
sm.endChange();
}
/**
* Handles key events on file list to change the selection state.
*
* Basically the content of this function is identical to
* cr.ui.ListSelectionController's handleKeyDown(), but following handlings is
* inserted to control the check-select mode.
*
* 1) When pressing direction key results in a single selection, the
* check-select mode should be terminated.
*
* @param {Event} e The keydown event.
* @this {cr.ui.ListSelectionController}
*/
filelist.handleKeyDown = function(e) {
var SPACE_KEY_CODE = 32;
var tagName = e.target.tagName;
// If focus is in an input field of some kind, only handle navigation keys
// that aren't likely to conflict with input interaction (e.g., text
// editing, or changing the value of a checkbox or select).
if (tagName == 'INPUT') {
var inputType = e.target.type;
// Just protect space (for toggling) for checkbox and radio.
if (inputType == 'checkbox' || inputType == 'radio') {
if (e.keyCode == SPACE_KEY_CODE)
return;
// Protect all but the most basic navigation commands in anything else.
} else if (e.keyIdentifier != 'Up' && e.keyIdentifier != 'Down') {
return;
}
}
// Similarly, don't interfere with select element handling.
if (tagName == 'SELECT')
return;
var sm = /** @type {!FileListSelectionModel|!FileListSingleSelectionModel} */
(this.selectionModel);
var newIndex = -1;
var leadIndex = sm.leadIndex;
var prevent = true;
// Ctrl/Meta+A
if (sm.multiple && e.keyCode == 65 &&
(cr.isMac && e.metaKey || !cr.isMac && e.ctrlKey)) {
sm.selectAll();
e.preventDefault();
return;
}
// Space
if (e.keyCode == SPACE_KEY_CODE) {
if (leadIndex != -1) {
var selected = sm.getIndexSelected(leadIndex);
if (e.ctrlKey || !selected) {
sm.setIndexSelected(leadIndex, !selected || !sm.multiple);
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':
case 'MediaPreviousTrack':
newIndex = leadIndex == -1 ?
this.getLastIndex() : this.getIndexBefore(leadIndex);
break;
case 'Right':
case 'MediaNextTrack':
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;
if (sm.multiple)
sm.unselectAll();
if (anchorIndex == -1) {
sm.setIndexSelected(newIndex, true);
sm.anchorIndex = newIndex;
} else {
sm.selectRange(anchorIndex, newIndex);
}
} else {
// 1) When pressing direction key results in a single selection, the
// check-select mode should be terminated.
sm.setCheckSelectMode(false);
if (sm.multiple)
sm.unselectAll();
sm.setIndexSelected(newIndex, true);
sm.anchorIndex = newIndex;
}
sm.endChange();
if (prevent)
e.preventDefault();
}
}
...@@ -39,3 +39,35 @@ FileTableList.prototype.mergeItems = function(beginIndex, endIndex) { ...@@ -39,3 +39,35 @@ FileTableList.prototype.mergeItems = function(beginIndex, endIndex) {
this.table.updateHighPriorityRange(beginIndex, endIndex); this.table.updateHighPriorityRange(beginIndex, endIndex);
} }
/** @override */
FileTableList.prototype.createSelectionController = function(sm) {
return new FileListSelectionController(assert(sm));
}
/**
* Selection controller for the file table list.
* @param {!cr.ui.ListSelectionModel} selectionModel The selection model to
* interact with.
* @constructor
* @extends {cr.ui.ListSelectionController}
* @struct
* @suppress {checkStructDictInheritance}
*/
function FileListSelectionController(selectionModel) {
cr.ui.ListSelectionController.call(this, selectionModel);
}
FileListSelectionController.prototype = /** @struct */ {
__proto__: cr.ui.ListSelectionController.prototype
};
/** @override */
FileListSelectionController.prototype.handlePointerDownUp = function(e, index) {
filelist.handlePointerDownUp.call(this, e, index);
};
/** @override */
FileListSelectionController.prototype.handleKeyDown = function(e) {
filelist.handleKeyDown.call(this, e);
};
...@@ -157,6 +157,7 @@ ...@@ -157,6 +157,7 @@
<script src="foreground/js/ui/error_dialog.js"></script> <script src="foreground/js/ui/error_dialog.js"></script>
<script src="foreground/js/ui/file_grid.js"></script> <script src="foreground/js/ui/file_grid.js"></script>
<script src="foreground/js/ui/file_manager_ui.js"></script> <script src="foreground/js/ui/file_manager_ui.js"></script>
<script src="foreground/js/ui/file_list_selection_model.js"></script>
<script src="foreground/js/ui/file_table.js"></script> <script src="foreground/js/ui/file_table.js"></script>
<script src="foreground/js/ui/file_table_list.js"></script> <script src="foreground/js/ui/file_table_list.js"></script>
<script src="foreground/js/ui/gear_menu.js"></script> <script src="foreground/js/ui/gear_menu.js"></script>
...@@ -333,27 +334,27 @@ ...@@ -333,27 +334,27 @@
i18n-values="aria-label:TASKS_BUTTON_LABEL"> i18n-values="aria-label:TASKS_BUTTON_LABEL">
<paper-ripple fit></paper-ripple> <paper-ripple fit></paper-ripple>
</button> </button>
<button id="search-button" class="icon-button" tabindex="10"
i18n-values="aria-label:SEARCH_TEXT_LABEL">
<paper-ripple fit class="recenteringTouch circle"></paper-ripple>
</button>
<div id="search-box">
<paper-input-decorator i18n-values="label:SEARCH_TEXT_LABEL">
<input is="core-input" type="search" tabindex="11"
i18n-values="aria-label:SEARCH_TEXT_LABEL">
</paper-input-decorator>
</div>
<button id="share-button" class="icon-button" command="#share" <button id="share-button" class="icon-button" command="#share"
tabindex="12" tabindex="10"
i18n-values="aria-label:SHARE_BUTTON_LABEL" i18n-values="aria-label:SHARE_BUTTON_LABEL"
visibleif="full-page"> visibleif="full-page">
<paper-ripple fit class="recenteringTouch circle"></paper-ripple> <paper-ripple fit class="recenteringTouch circle"></paper-ripple>
</button> </button>
<button id="delete-button" class="icon-button" tabindex="13" <button id="delete-button" class="icon-button" tabindex="11"
i18n-values="aria-label:DELETE_BUTTON_LABEL" i18n-values="aria-label:DELETE_BUTTON_LABEL"
visibleif="full-page"> visibleif="full-page">
<paper-ripple fit class="recenteringTouch circle"></paper-ripple> <paper-ripple fit class="recenteringTouch circle"></paper-ripple>
</button> </button>
<button id="search-button" class="icon-button" tabindex="12"
i18n-values="aria-label:SEARCH_TEXT_LABEL">
<paper-ripple fit class="recenteringTouch circle"></paper-ripple>
</button>
<div id="search-box">
<paper-input-decorator i18n-values="label:SEARCH_TEXT_LABEL">
<input is="core-input" type="search" tabindex="13"
i18n-values="aria-label:SEARCH_TEXT_LABEL">
</paper-input-decorator>
</div>
<button id="cloud-import-button" <button id="cloud-import-button"
class="icon-button manual-display" class="icon-button manual-display"
tabindex="14" tabindex="14"
......
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