diff options
author | ryoh <ryoh@chromium.org> | 2016-03-18 02:16:00 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-03-18 09:17:02 +0000 |
commit | a7ec78c615697fd49c5a9e4c797a54ab9f0aeea4 (patch) | |
tree | 5d45b1c16c7ec2b8fc6447a68cdcfd528255a570 | |
parent | fa7d8b396b85d9b6440a4f430ff90eb6a4c4a680 (diff) | |
download | chromium_src-a7ec78c615697fd49c5a9e4c797a54ab9f0aeea4.zip chromium_src-a7ec78c615697fd49c5a9e4c797a54ab9f0aeea4.tar.gz chromium_src-a7ec78c615697fd49c5a9e4c797a54ab9f0aeea4.tar.bz2 |
Files app: Implement details panel for multiple files.
BUG=274045
TEST=manually
Review URL: https://codereview.chromium.org/1806433002
Cr-Commit-Position: refs/heads/master@{#381917}
11 files changed, 281 insertions, 44 deletions
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp index ca45478..2b9f41e 100644 --- a/chrome/app/chromeos_strings.grdp +++ b/chrome/app/chromeos_strings.grdp @@ -166,6 +166,12 @@ Press any key to continue exploring. <message name="IDS_FILE_BROWSER_STATUS_COLUMN_LABEL" desc="Status column label. This column reflects the cloud import status of files. The status is an icon depicting whether a file has been imported, and if so, where to - for example, a Drive icon if a file has been imported to Google Drive. For unimported files, this column is blank. "> Status </message> + <message name="IDS_FILE_BROWSER_TOTAL_FILE_COUNT_LABEL" desc="Total count of files that user selects in Files app"> + Total file count + </message> + <message name="IDS_FILE_BROWSER_TOTAL_FILE_SIZE_LABEL" desc="Total size of files that user selects in Files app"> + Total file size + </message> <message name="IDS_FILE_BROWSER_TYPE_COLUMN_LABEL" desc="Type column label."> Type </message> diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc index 4c7f171..cf4cff8 100644 --- a/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc +++ b/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc @@ -556,6 +556,8 @@ bool FileManagerPrivateGetStringsFunction::RunSync() { SET_STRING("SIZE_TB", IDS_FILE_BROWSER_SIZE_TB); SET_STRING("SPACE_AVAILABLE", IDS_FILE_BROWSER_SPACE_AVAILABLE); SET_STRING("STATUS_COLUMN_LABEL", IDS_FILE_BROWSER_STATUS_COLUMN_LABEL); + SET_STRING("TOTAL_FILE_SIZE", IDS_FILE_BROWSER_TOTAL_FILE_SIZE_LABEL); + SET_STRING("TOTAL_FILE_COUNT", IDS_FILE_BROWSER_TOTAL_FILE_COUNT_LABEL); SET_STRING("SUGGEST_DIALOG_INSTALLATION_FAILED", IDS_FILE_BROWSER_SUGGEST_DIALOG_INSTALLATION_FAILED); SET_STRING("SUGGEST_DIALOG_LINK_TO_WEBSTORE", diff --git a/ui/file_manager/file_manager/foreground/css/file_manager.css b/ui/file_manager/file_manager/foreground/css/file_manager.css index 012b594..ad4a3dd 100644 --- a/ui/file_manager/file_manager/foreground/css/file_manager.css +++ b/ui/file_manager/file_manager/foreground/css/file_manager.css @@ -148,52 +148,22 @@ a:focus { display: none !important; } -.details-container > #single-file-details { - display: flex; - flex: auto; - flex-direction: column; - width: 100%; -} - -#single-file-details > .details-list { - flex: auto; - width: 100%; -} - -/* Filename header of details panel for single file */ - -#single-file-details > .filename-container { - align-items: center; - display: flex; - flex-direction: row; - height: 40px; - padding: 0 6px; - width: 100%; -} - -.filename-container > .filename { - -webkit-padding-end: 6px; - flex: auto; - font-weight: 500; - overflow: hidden; -} +/* Filetype icons in details panel */ -/* Filetype icon of details panel for single file */ - -#single-file-details > .thumbnail-container { +.details-container .thumbnail-container { box-sizing: border-box; padding: 6px; position: relative; width: 100%; } -#single-file-details > .thumbnail-container:before { +.details-container .thumbnail-container:before { content: ""; display: block; padding-top: 100%; } -.thumbnail-container > .thumbnail { +.details-container .thumbnail-container > .thumbnail { background-color: rgb(230, 230, 230); background-position: center; position: absolute; @@ -203,23 +173,21 @@ a:focus { right: 0; } -.thumbnail-container > .thumbnail.loaded { - background-image: none; -} +/* File thumbnails/quick previews in details panel */ -.thumbnail > img { +.details-container .thumbnail > img { height: 100%; object-fit: contain; width: 100%; } -.thumbnail > video { +.details-container .thumbnail > video { height: 100%; object-fit: contain; width: 100%; } -.thumbnail > audio { +.details-container .thumbnail > audio { bottom: 0px; left: 0px; height: auto; @@ -227,10 +195,58 @@ a:focus { width: 100%; } -.details-list > li:not(.available) { +/* Details panel for single file */ + +.details-container > #single-file-details { + display: flex; + flex: auto; + flex-direction: column; + width: 100%; +} + +#single-file-details > .filename-container { + align-items: center; + display: flex; + flex-direction: row; + height: 40px; + padding: 0 6px; + width: 100%; +} + +#single-file-details > .filename-container > .filename { + -webkit-padding-end: 6px; + flex: auto; + font-weight: 500; + overflow: hidden; +} + +#single-file-details > .thumbnail-container > .thumbnail.loaded { + background-image: none; +} + +#single-file-details > .details-list { + flex: auto; + width: 100%; +} + +#single-file-details > .details-list > li:not(.available) { display: none; } +/* Filetype icon of details panel for multiple files */ + +.details-container > #multi-file-details { + display: flex; + flex: auto; + flex-direction: column; + width: 100%; +} + +#multi-file-details > .details-list { + flex: auto; + width: 100%; +} + /* Directory tree at the left. */ .dialog-navigation-list { -webkit-border-end: 1px solid rgba(0, 0, 0, 0.15); diff --git a/ui/file_manager/file_manager/foreground/js/compiled_resources.gyp b/ui/file_manager/file_manager/foreground/js/compiled_resources.gyp index 69a9a78..8fd66ac 100644 --- a/ui/file_manager/file_manager/foreground/js/compiled_resources.gyp +++ b/ui/file_manager/file_manager/foreground/js/compiled_resources.gyp @@ -142,6 +142,7 @@ './ui/gear_menu.js', './ui/list_container.js', './ui/location_line.js', + './ui/multi_file_details.js', './ui/multi_profile_share_dialog.js', './ui/progress_center_panel.js', './ui/providers_menu.js', diff --git a/ui/file_manager/file_manager/foreground/js/file_manager.js b/ui/file_manager/file_manager/foreground/js/file_manager.js index 4cb38ae..e33e6fd 100644 --- a/ui/file_manager/file_manager/foreground/js/file_manager.js +++ b/ui/file_manager/file_manager/foreground/js/file_manager.js @@ -839,12 +839,18 @@ FileManager.prototype = /** @struct */ { assertInstanceof(singlePanel, HTMLDivElement), this.metadataModel_); + var multiPanel = queryRequiredElement('#multi-file-details', dom); + MultiFileDetailsPanel.decorate( + assertInstanceof(multiPanel, HTMLDivElement), + this.metadataModel_); + this.addHistoryObserver_(); this.ui_.initAdditionalUI( assertInstanceof(table, FileTable), assertInstanceof(grid, FileGrid), assertInstanceof(singlePanel, SingleFileDetailsPanel), + assertInstanceof(multiPanel, MultiFileDetailsPanel), new LocationLine( queryRequiredElement('#location-breadcrumbs', dom), this.volumeManager_)); diff --git a/ui/file_manager/file_manager/foreground/js/main_scripts.js b/ui/file_manager/file_manager/foreground/js/main_scripts.js index 821acd7..953bd12 100644 --- a/ui/file_manager/file_manager/foreground/js/main_scripts.js +++ b/ui/file_manager/file_manager/foreground/js/main_scripts.js @@ -158,6 +158,7 @@ //<include src="ui/gear_menu.js"> //<include src="ui/list_container.js"> //<include src="ui/location_line.js"> +//<include src="ui/multi_file_details.js"> //<include src="ui/multi_profile_share_dialog.js"> //<include src="ui/progress_center_panel.js"> //<include src="ui/providers_menu.js"> diff --git a/ui/file_manager/file_manager/foreground/js/ui/details_container.js b/ui/file_manager/file_manager/foreground/js/ui/details_container.js index be21762..11541b3c 100644 --- a/ui/file_manager/file_manager/foreground/js/ui/details_container.js +++ b/ui/file_manager/file_manager/foreground/js/ui/details_container.js @@ -5,13 +5,14 @@ /** * @param {!HTMLElement} element * @param {!SingleFileDetailsPanel} singlePanel + * @param {!MultiFileDetailsPanel} multiPanel * @param {!Element} splitter * @param {!Element} button * @param {!FilesToggleRipple} toggleRipple * @constructor * @struct */ -function DetailsContainer(element, singlePanel, splitter, button, +function DetailsContainer(element, singlePanel, multiPanel, splitter, button, toggleRipple) { /** * Container element. @@ -44,6 +45,12 @@ function DetailsContainer(element, singlePanel, splitter, button, */ this.singlePanel_ = singlePanel; /** + * Details panel for a multiple files. + * @private {!MultiFileDetailsPanel} + * @const + */ + this.multiPanel_ = multiPanel; + /** * @type {boolean} */ this.visible = false; @@ -71,12 +78,18 @@ DetailsContainer.prototype.onFileSelectionChanged = function(event) { DetailsContainer.prototype.display_ = function(entries) { if (entries.length === 0) { this.singlePanel_.removeAttribute('activated'); + this.multiPanel_.removeAttribute('activated'); // TODO(ryoh): make a panel for empty selection } else if (entries.length === 1) { this.singlePanel_.setAttribute('activated', ''); + this.multiPanel_.removeAttribute('activated'); this.singlePanel_.onFileSelectionChanged(entries[0]); + this.multiPanel_.cancelLoading(); } else { - // TODO(ryoh): make a panel for multiple selection + this.singlePanel_.removeAttribute('activated'); + this.multiPanel_.setAttribute('activated', ''); + this.multiPanel_.onFileSelectionChanged(entries); + this.singlePanel_.cancelLoading(); } }; diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js b/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js index d9c182b..5e11627 100644 --- a/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js +++ b/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js @@ -339,10 +339,11 @@ function FileManagerUI(providersModel, element, launchParam) { * @param {!FileTable} table * @param {!FileGrid} grid * @param {!SingleFileDetailsPanel} singlePanel + * @param {!MultiFileDetailsPanel} multiPanel * @param {!LocationLine} locationLine */ FileManagerUI.prototype.initAdditionalUI = function( - table, grid, singlePanel, locationLine) { + table, grid, singlePanel, multiPanel, locationLine) { // List container. this.listContainer = new ListContainer( queryRequiredElement('#list-container', this.element), table, grid); @@ -358,6 +359,7 @@ FileManagerUI.prototype.initAdditionalUI = function( this.detailsContainer = new DetailsContainer( queryRequiredElement('#details-container', this.element), singlePanel, + multiPanel, listDetailsSplitter, this.detailsButton, this.detailsButtonToggleRipple_); diff --git a/ui/file_manager/file_manager/foreground/js/ui/multi_file_details.js b/ui/file_manager/file_manager/foreground/js/ui/multi_file_details.js new file mode 100644 index 0000000..f2b54cf --- /dev/null +++ b/ui/file_manager/file_manager/foreground/js/ui/multi_file_details.js @@ -0,0 +1,168 @@ +// Copyright 2016 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. + + +/** + * MultiFileDetailsPanel constructor. + * + * Represents grid for the details panel for a single file in Files app. + * @constructor + * @extends {HTMLDivElement} + */ +function MultiFileDetailsPanel() { + throw new Error('Use MultiFileDetailsPanel.decorate'); +} + +/** + * Inherits from cr.ui.List. + */ +MultiFileDetailsPanel.prototype = { + __proto__: HTMLDivElement.prototype, + + /** + * @param {!Array<!FileEntry>} entries + */ + onFileSelectionChanged: function(entries) { + this.ticket_++; + this.lastTargetEntries_ = entries; + this.aggregateRateLimitter_.run(); + }, + + startAggregation: function() { + var aggregator = new MultiFileDetailsPanel.Aggregator(this.ticket_, + this.metadataModel_, + this.onAggregated_.bind(this)); + aggregator.enqueue(this.lastTargetEntries_); + }, + + /** + * @param {number} ticket + * @param {number} totalCount Total file count. + * @param {number} totalSize Sum of file size. + * @return {boolean} Whether should we continue the aggregation or not. + */ + onAggregated_: function(ticket, totalCount, totalSize) { + if (ticket !== this.ticket_) { + return false; + } + this.lastTotalSize_ = totalSize; + this.lastTotalCount_ = totalCount; + this.viewUpdateRateLimitter_.run(); + return true; + }, + + /** + * @private + */ + updateView_: function() { + queryRequiredElement('.file-size > .content', this.list_).textContent = + this.formatter_.formatSize(this.lastTotalSize_); + queryRequiredElement('.file-count > .content', this.list_).textContent = + this.lastTotalCount_; + }, + + /** + * Cancel loading task. + */ + cancelLoading: function() { + this.ticket_++; + } +}; + +/** + * Aggregator class. That count files and calculate a sum of file size. + * @param {number} ticket + * @param {!MetadataModel} metadataModel + * @param {function(number, number, number)} callback Callback to update views. + * @constructor + */ +MultiFileDetailsPanel.Aggregator = function(ticket, metadataModel, callback) { + this.queue_ = []; + this.totalCount_ = 0; + this.totalSize_ = 0; + this.ticket_ = ticket; + this.metadataModel_ = metadataModel; + this.callback_ = callback; +}; + +/** + * Aggregates data of given files and enqueue directories to queue. + * @param {!Array<!FileEntry>} entries + * @private + */ +MultiFileDetailsPanel.Aggregator.prototype.enqueue = function(entries) { + var files = []; + var dirs = []; + var self = this; + for (var i = 0; i < entries.length; i++) { + var entry = entries[i]; + if (entry.isFile) { + files.push(entry); + } else { + dirs.push(entry); + } + } + Array.prototype.push.apply(this.queue_, dirs); + this.metadataModel_.get(files, ['size']) + .then(function(metadatas) { + for (var i = 0; i < metadatas.length; i++) { + var metadata = metadatas[i]; + self.totalCount_++; + self.totalSize_ += metadata.size; + } + if (self.update_()) { + self.dequeue_(); + } + }, function(err) { + console.error(err); + }).then(function () { + if (self.update_()) { + self.dequeue_(); + } + }); +}; + +/** + * Updates views with current aggregate results. + * @return {boolean} Whether we should continue the aggregation or not. + * @private + */ +MultiFileDetailsPanel.Aggregator.prototype.update_ = function() { + return this.callback_(this.ticket_, this.totalCount_, this.totalSize_); +}; + +/** + * Gets one directory from queue and fetch metadata + * @private + */ +MultiFileDetailsPanel.Aggregator.prototype.dequeue_ = function() { + if (this.queue_.length === 0) { + return; + } + var self = this; + var next = this.queue_.shift(); + var reader = next.createReader(); + reader.readEntries(function(results) { + self.enqueue(results); + }); +}; + +/** + * Decorates an HTML element to be a SingleFileDetailsList. + * @param {!HTMLDivElement} self The grid to decorate. + * @param {!MetadataModel} metadataModel File system metadata. + */ +MultiFileDetailsPanel.decorate = function(self, metadataModel) { + self.__proto__ = MultiFileDetailsPanel.prototype; + self.formatter_ = new FileMetadataFormatter(); + self.metadataModel_ = metadataModel; + self.ticket_ = 0; + self.lastTotalSize_ = 0; + self.lastTotalCount_ = 0; + self.list_ = queryRequiredElement('.details-list', self); + self.aggregateRateLimitter_ = + new AsyncUtil.RateLimiter(self.startAggregation.bind(self)); + self.viewUpdateRateLimitter_ = + new AsyncUtil.RateLimiter(self.updateView_.bind(self)); +}; diff --git a/ui/file_manager/file_manager/foreground/js/ui/single_file_details.js b/ui/file_manager/file_manager/foreground/js/ui/single_file_details.js index 7cfe071..035b04c 100644 --- a/ui/file_manager/file_manager/foreground/js/ui/single_file_details.js +++ b/ui/file_manager/file_manager/foreground/js/ui/single_file_details.js @@ -241,6 +241,13 @@ SingleFileDetailsPanel.prototype = { this.preview_.pause(); } } + }, + + /** + * Cancel loading task. + */ + cancelLoading: function() { + this.ticket_++; } }; diff --git a/ui/file_manager/file_manager/main.html b/ui/file_manager/file_manager/main.html index cd28482..a1c4612 100644 --- a/ui/file_manager/file_manager/main.html +++ b/ui/file_manager/file_manager/main.html @@ -430,6 +430,21 @@ </li> </ul> </div> + <div id="multi-file-details"> + <div class="thumbnail-container"> + <div class="thumbnail" generic-thumbnail="folder"></div> + </div> + <ul class="details-list"> + <li class='file-count'> + <span i18n-content="TOTAL_FILE_COUNT"></span>: + <span class='content'></span> + </li> + <li class='file-size'> + <span i18n-content="TOTAL_FILE_SIZE"></span>: + <span class='content'></span> + </li> + </ul> + </div> </div> </div> <div class="dialog-footer progressable" tabindex="-1" |