Commit c47494fa authored by Naoki Fukino's avatar Naoki Fukino Committed by Commit Bot

Files app: Add "Open with..." sub menu in the context menu.

In the context menu, we have "More actions..." menu item to open a task
picker dialog for all actions.
We are going to have separated following two menu items to organize them.
"Open with..."    <= Open a task picker for OPEN actions.
"More actions..." <= Open a task picker for any other actions.

Bug: 740821
Cq-Include-Trybots: master.tryserver.chromium.linux:closure_compilation
Change-Id: I4e75c27ac32c2a6da23adfc787db9078662fa19f
Reviewed-on: https://chromium-review.googlesource.com/594759
Commit-Queue: Naoki Fukino <fukino@chromium.org>
Reviewed-by: default avatarTatsuhisa Yamaguchi <yamaguchi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#491311}
parent 534bb7ba
...@@ -603,6 +603,7 @@ ExtensionFunction::ResponseAction FileManagerPrivateGetStringsFunction::Run() { ...@@ -603,6 +603,7 @@ ExtensionFunction::ResponseAction FileManagerPrivateGetStringsFunction::Run() {
SET_STRING("OPEN_IN_OTHER_DESKTOP_MESSAGE_PLURAL", SET_STRING("OPEN_IN_OTHER_DESKTOP_MESSAGE_PLURAL",
IDS_FILE_BROWSER_OPEN_IN_OTHER_DESKTOP_MESSAGE_PLURAL); IDS_FILE_BROWSER_OPEN_IN_OTHER_DESKTOP_MESSAGE_PLURAL);
SET_STRING("OPEN_LABEL", IDS_FILE_BROWSER_OPEN_LABEL); SET_STRING("OPEN_LABEL", IDS_FILE_BROWSER_OPEN_LABEL);
SET_STRING("OPEN_WITH_BUTTON_LABEL", IDS_FILE_BROWSER_OPEN_WITH_BUTTON_LABEL);
SET_STRING("MORE_ACTIONS_BUTTON_LABEL", SET_STRING("MORE_ACTIONS_BUTTON_LABEL",
IDS_FILE_BROWSER_MORE_ACTIONS_BUTTON_LABEL); IDS_FILE_BROWSER_MORE_ACTIONS_BUTTON_LABEL);
SET_STRING("OPEN_WITH_VERB_BUTTON_LABEL", SET_STRING("OPEN_WITH_VERB_BUTTON_LABEL",
......
...@@ -1041,7 +1041,7 @@ CommandHandler.COMMANDS_['default-task'] = /** @type {Command} */ ({ ...@@ -1041,7 +1041,7 @@ CommandHandler.COMMANDS_['default-task'] = /** @type {Command} */ ({
}); });
/** /**
* Displays "open with"/"more actions" dialog for current selection. * Displays "open with" dialog for current selection.
* @type {Command} * @type {Command}
*/ */
CommandHandler.COMMANDS_['open-with'] = /** @type {Command} */ ({ CommandHandler.COMMANDS_['open-with'] = /** @type {Command} */ ({
...@@ -1050,19 +1050,52 @@ CommandHandler.COMMANDS_['open-with'] = /** @type {Command} */ ({ ...@@ -1050,19 +1050,52 @@ CommandHandler.COMMANDS_['open-with'] = /** @type {Command} */ ({
* @param {!CommandHandlerDeps} fileManager CommandHandlerDeps to use. * @param {!CommandHandlerDeps} fileManager CommandHandlerDeps to use.
*/ */
execute: function(event, fileManager) { execute: function(event, fileManager) {
fileManager.taskController.getFileTasks().then(function(tasks) { fileManager.taskController.getFileTasks()
tasks.showTaskPicker(fileManager.ui.defaultTaskPicker, .then(function(tasks) {
str('MORE_ACTIONS_BUTTON_LABEL'), tasks.showTaskPicker(
'', fileManager.ui.defaultTaskPicker, str('OPEN_WITH_BUTTON_LABEL'),
function(task) { '', function(task) {
tasks.execute(task.taskId); tasks.execute(task.taskId);
}, }, FileTasks.TaskPickerType.OpenWith);
false); })
}) .catch(function(error) {
.catch(function(error) { if (error)
if (error) console.error(error.stack || error);
console.error(error.stack || error); });
}); },
/**
* @param {!Event} event Command event.
* @param {!CommandHandlerDeps} fileManager CommandHandlerDeps to use.
*/
canExecute: function(event, fileManager) {
var canExecute = fileManager.taskController.canExecuteOpenActions();
event.canExecute = canExecute;
event.command.setHidden(!canExecute);
}
});
/**
* Displays "More actions" dialog for current selection.
* @type {Command}
*/
CommandHandler.COMMANDS_['more-actions'] = /** @type {Command} */ ({
/**
* @param {!Event} event Command event.
* @param {!CommandHandlerDeps} fileManager CommandHandlerDeps to use.
*/
execute: function(event, fileManager) {
fileManager.taskController.getFileTasks()
.then(function(tasks) {
tasks.showTaskPicker(
fileManager.ui.defaultTaskPicker,
str('MORE_ACTIONS_BUTTON_LABEL'), '', function(task) {
tasks.execute(task.taskId);
}, FileTasks.TaskPickerType.MoreActions);
})
.catch(function(error) {
if (error)
console.error(error.stack || error);
});
}, },
/** /**
* @param {!Event} event Command event. * @param {!Event} event Command event.
......
...@@ -109,6 +109,16 @@ FileTasks.TaskMenuButtonItemType = { ...@@ -109,6 +109,16 @@ FileTasks.TaskMenuButtonItemType = {
ChangeDefaultTask: 'ChangeDefaultTask' ChangeDefaultTask: 'ChangeDefaultTask'
}; };
/**
* Dialog types to show a task picker.
* @enum {string}
*/
FileTasks.TaskPickerType = {
ChangeDefault: 'ChangeDefault',
OpenWith: 'OpenWith',
MoreActions: 'MoreActions'
};
/** /**
* Creates an instance of FileTasks for the specified list of entries with mime * Creates an instance of FileTasks for the specified list of entries with mime
* types. * types.
...@@ -154,12 +164,28 @@ FileTasks.create = function( ...@@ -154,12 +164,28 @@ FileTasks.create = function(
/** /**
* Obtains the task items. * Obtains the task items.
* @return {Array<!Object>} * @return {!Array<!Object>}
*/ */
FileTasks.prototype.getTaskItems = function() { FileTasks.prototype.getTaskItems = function() {
return this.tasks_; return this.tasks_;
}; };
/**
* Obtain tasks which are categorized as OPEN tasks.
* @return {!Array<!Object>}
*/
FileTasks.prototype.getOpenTaskItems = function() {
return this.tasks_.filter(FileTasks.isOpenTask);
};
/**
* Obtain tasks which are not categorized as OPEN tasks.
* @return {!Array<!Object>}
*/
FileTasks.prototype.getNonOpenTaskItems = function() {
return this.tasks_.filter(task => !FileTasks.isOpenTask(task));
};
/** /**
* Opens the suggest file dialog. * Opens the suggest file dialog.
* *
...@@ -873,17 +899,16 @@ FileTasks.prototype.createCombobuttonItem_ = function(task, opt_title, ...@@ -873,17 +899,16 @@ FileTasks.prototype.createCombobuttonItem_ = function(task, opt_title,
* @param {string} title Title to use. * @param {string} title Title to use.
* @param {string} message Message to use. * @param {string} message Message to use.
* @param {function(Object)} onSuccess Callback to pass selected task. * @param {function(Object)} onSuccess Callback to pass selected task.
* @param {boolean=} opt_forChangeDefault Whether to return items which are for * @param {FileTasks.TaskPickerType} pickerType Task picker type.
* change-default dialog.
*/ */
FileTasks.prototype.showTaskPicker = function( FileTasks.prototype.showTaskPicker = function(
taskDialog, title, message, onSuccess, opt_forChangeDefault) { taskDialog, title, message, onSuccess, pickerType) {
var items = !opt_forChangeDefault ? var tasks = pickerType == FileTasks.TaskPickerType.MoreActions ?
this.createItems_(this.tasks_) : this.getNonOpenTaskItems() :
this.createItems_(this.tasks_.filter(FileTasks.isOpenTask)) this.getOpenTaskItems();
.filter(function(item) { var items = this.createItems_(tasks);
return !item.isGenericFileHandler; if (pickerType == FileTasks.TaskPickerType.ChangeDefault)
}); items = items.filter(item => !item.isGenericFileHandler);
var defaultIdx = 0; var defaultIdx = 0;
for (var j = 0; j < items.length; j++) { for (var j = 0; j < items.length; j++) {
......
...@@ -71,6 +71,11 @@ function TaskController( ...@@ -71,6 +71,11 @@ function TaskController(
*/ */
this.canExecuteDefaultTask_ = false; this.canExecuteDefaultTask_ = false;
/**
* @private {boolean}
*/
this.canExecuteOpenActions_ = false;
/** /**
* @private {boolean} * @private {boolean}
*/ */
...@@ -89,9 +94,18 @@ function TaskController( ...@@ -89,9 +94,18 @@ function TaskController(
* @private {!cr.ui.Command} * @private {!cr.ui.Command}
* @const * @const
*/ */
this.moreActionsCommand_ = this.openWithCommand_ =
assertInstanceof(document.querySelector('#open-with'), cr.ui.Command); assertInstanceof(document.querySelector('#open-with'), cr.ui.Command);
/**
* More actions command that uses #open-with as selector due to the open-with
* command used previously for the same task.
* @private {!cr.ui.Command}
* @const
*/
this.moreActionsCommand_ =
assertInstanceof(document.querySelector('#more-actions'), cr.ui.Command);
/** /**
* @private {Promise<!FileTasks>} * @private {Promise<!FileTasks>}
*/ */
...@@ -200,7 +214,8 @@ TaskController.prototype.onTaskItemClicked_ = function(event) { ...@@ -200,7 +214,8 @@ TaskController.prototype.onTaskItemClicked_ = function(event) {
this.ui_.defaultTaskPicker, this.ui_.defaultTaskPicker,
loadTimeData.getString('CHANGE_DEFAULT_MENU_ITEM'), loadTimeData.getString('CHANGE_DEFAULT_MENU_ITEM'),
strf('CHANGE_DEFAULT_CAPTION', format), strf('CHANGE_DEFAULT_CAPTION', format),
this.changeDefaultTask_.bind(this, selection), true); this.changeDefaultTask_.bind(this, selection),
FileTasks.TaskPickerType.ChangeDefault);
break; break;
default: default:
assertNotReached('Unknown task.'); assertNotReached('Unknown task.');
...@@ -303,11 +318,12 @@ TaskController.prototype.onSelectionChanged_ = function() { ...@@ -303,11 +318,12 @@ TaskController.prototype.onSelectionChanged_ = function() {
// Show disabled items for position calculation of the menu. They will be // Show disabled items for position calculation of the menu. They will be
// overridden in this.updateTasks_(). // overridden in this.updateTasks_().
this.updateContextMenuTaskItems_( this.updateContextMenuTaskItems_(
[TaskController.createTemporaryDisabledTaskItem_()],
[TaskController.createTemporaryDisabledTaskItem_()]); [TaskController.createTemporaryDisabledTaskItem_()]);
} }
} else { } else {
// Update context menu. // Update context menu.
this.updateContextMenuTaskItems_([]); this.updateContextMenuTaskItems_([], []);
} }
this.lastSelectedEntries_ = selection.entries; this.lastSelectedEntries_ = selection.entries;
}; };
...@@ -323,7 +339,8 @@ TaskController.prototype.updateTasks_ = function() { ...@@ -323,7 +339,8 @@ TaskController.prototype.updateTasks_ = function() {
this.getFileTasks() this.getFileTasks()
.then(function(tasks) { .then(function(tasks) {
tasks.display(this.ui_.taskMenuButton, this.ui_.shareMenuButton); tasks.display(this.ui_.taskMenuButton, this.ui_.shareMenuButton);
this.updateContextMenuTaskItems_(tasks.getTaskItems()); this.updateContextMenuTaskItems_(
tasks.getOpenTaskItems(), tasks.getNonOpenTaskItems());
}.bind(this)) }.bind(this))
.catch(function(error) { .catch(function(error) {
if (error) if (error)
...@@ -376,6 +393,14 @@ TaskController.prototype.canExecuteDefaultTask = function() { ...@@ -376,6 +393,14 @@ TaskController.prototype.canExecuteDefaultTask = function() {
return this.canExecuteDefaultTask_; return this.canExecuteDefaultTask_;
}; };
/**
* Returns whether open with command can be executed or not.
* @return {boolean} True if open with command is executable.
*/
TaskController.prototype.canExecuteOpenActions = function() {
return this.canExecuteOpenActions_;
};
/** /**
* Returns whether open with command can be executed or not. * Returns whether open with command can be executed or not.
* @return {boolean} True if open with command is executable. * @return {boolean} True if open with command is executable.
...@@ -387,16 +412,18 @@ TaskController.prototype.canExecuteMoreActions = function() { ...@@ -387,16 +412,18 @@ TaskController.prototype.canExecuteMoreActions = function() {
/** /**
* Updates tasks menu item to match passed task items. * Updates tasks menu item to match passed task items.
* *
* @param {!Array<!Object>} items List of items. * @param {!Array<!Object>} openTasks List of OPEN tasks.
* @param {!Array<!Object>} nonOpenTasks List of non-OPEN tasks.
* @private * @private
*/ */
TaskController.prototype.updateContextMenuTaskItems_ = function(items) { TaskController.prototype.updateContextMenuTaskItems_ = function(
openTasks, nonOpenTasks) {
// Always show a default item in case at least one task is available, even // Always show a default item in case at least one task is available, even
// if there is no corresponding default task (i.e. the available task is // if there is no corresponding default task (i.e. the available task is
// a generic handler). // a generic handler).
if (items.length >= 1) { if (openTasks.length >= 1) {
var defaultTask = FileTasks.getDefaultTask( var defaultTask = FileTasks.getDefaultTask(
items, items[0] /* task to use in case of no default */); openTasks, openTasks[0] /* task to use in case of no default */);
if (defaultTask.iconType) { if (defaultTask.iconType) {
this.ui_.fileContextMenu.defaultTaskMenuItem.style.backgroundImage = ''; this.ui_.fileContextMenu.defaultTaskMenuItem.style.backgroundImage = '';
...@@ -417,13 +444,17 @@ TaskController.prototype.updateContextMenuTaskItems_ = function(items) { ...@@ -417,13 +444,17 @@ TaskController.prototype.updateContextMenuTaskItems_ = function(items) {
this.ui_.fileContextMenu.defaultTaskMenuItem.taskId = defaultTask.taskId; this.ui_.fileContextMenu.defaultTaskMenuItem.taskId = defaultTask.taskId;
} }
this.canExecuteDefaultTask_ = items.length >= 1; this.canExecuteDefaultTask_ = openTasks.length >= 1;
this.defaultTaskCommand_.canExecuteChange(this.ui_.listContainer.element); this.defaultTaskCommand_.canExecuteChange(this.ui_.listContainer.element);
this.canExecuteMoreActions_ = items.length > 1; this.canExecuteOpenActions_ = openTasks.length > 1;
this.openWithCommand_.canExecuteChange(this.ui_.listContainer.element);
this.canExecuteMoreActions_ = nonOpenTasks.length >= 1;
this.moreActionsCommand_.canExecuteChange(this.ui_.listContainer.element); this.moreActionsCommand_.canExecuteChange(this.ui_.listContainer.element);
this.ui_.fileContextMenu.tasksSeparator.hidden = items.length === 0; this.ui_.fileContextMenu.tasksSeparator.hidden =
openTasks.length === 0 && nonOpenTasks.length == 0;
}; };
/** /**
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
--> -->
<command id="default-task"> <command id="default-task">
<command id="open-with"> <command id="open-with">
<command id="more-actions">
<script src="../../../../../ui/webui/resources/js/assert.js"></script> <script src="../../../../../ui/webui/resources/js/assert.js"></script>
<script src="../../../../../ui/webui/resources/js/cr.js"></script> <script src="../../../../../ui/webui/resources/js/cr.js"></script>
......
...@@ -141,7 +141,8 @@ ...@@ -141,7 +141,8 @@
shortcut=".|Ctrl" hide-shortcut-text> shortcut=".|Ctrl" hide-shortcut-text>
<command id="default-task"> <command id="default-task">
<command id="open-with" i18n-values="label:MORE_ACTIONS_BUTTON_LABEL"> <command id="open-with" i18n-values="label:OPEN_WITH_BUTTON_LABEL">
<command id="more-actions" i18n-values="label:MORE_ACTIONS_BUTTON_LABEL">
<command id="zip-selection" <command id="zip-selection"
i18n-values="label:ZIP_SELECTION_BUTTON_LABEL"> i18n-values="label:ZIP_SELECTION_BUTTON_LABEL">
<command id="set-wallpaper" <command id="set-wallpaper"
...@@ -176,6 +177,8 @@ ...@@ -176,6 +177,8 @@
visibleif="full-page" class="hide-on-toolbar" hidden></cr-menu-item> visibleif="full-page" class="hide-on-toolbar" hidden></cr-menu-item>
<cr-menu-item command="#open-with" <cr-menu-item command="#open-with"
visibleif="full-page" class="hide-on-toolbar" hidden></cr-menu-item> visibleif="full-page" class="hide-on-toolbar" hidden></cr-menu-item>
<cr-menu-item command="#more-actions"
visibleif="full-page" class="hide-on-toolbar" hidden></cr-menu-item>
<hr id="tasks-separator" visibleif="full-page" class="hide-on-toolbar" hidden> <hr id="tasks-separator" visibleif="full-page" class="hide-on-toolbar" hidden>
<hr id="actions-separator" hidden> <hr id="actions-separator" hidden>
<cr-menu-item command="#cut" visibleif="full-page"></cr-menu-item> <cr-menu-item command="#cut" visibleif="full-page"></cr-menu-item>
......
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