diff options
author | estade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-06-23 15:52:47 +0000 |
---|---|---|
committer | estade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-06-23 15:52:47 +0000 |
commit | 590742dc369f718055cded657f197d154639493e (patch) | |
tree | 18e1f2a321d3c05eb3fdcadd34e7d0d937b12334 | |
parent | 24101d60efb5aa465e40df635fc47a1c453cfab6 (diff) | |
download | chromium_src-590742dc369f718055cded657f197d154639493e.zip chromium_src-590742dc369f718055cded657f197d154639493e.tar.gz chromium_src-590742dc369f718055cded657f197d154639493e.tar.bz2 |
ntp4: add app context menu
BUG=none
TEST=manual (see below)
1: all menu items work
2. middle clicking or ctrl-clicking on an app affects the launch type
3: when you change the app launch type, that change is visible both on the current NTP and all other currently open NTP tabs
Review URL: http://codereview.chromium.org/7215035
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@90218 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/resources/ntp4/apps_page.css | 26 | ||||
-rw-r--r-- | chrome/browser/resources/ntp4/apps_page.js | 193 | ||||
-rw-r--r-- | chrome/browser/resources/ntp4/new_tab.html | 1 | ||||
-rw-r--r-- | chrome/browser/resources/ntp4/new_tab.js | 17 | ||||
-rw-r--r-- | chrome/browser/resources/shared/js/cr/ui/context_menu_handler.js | 5 | ||||
-rw-r--r-- | chrome/browser/ui/webui/ntp/app_launcher_handler.cc | 7 | ||||
-rw-r--r-- | chrome/browser/ui/webui/ntp/ntp_resource_cache.cc | 2 |
7 files changed, 209 insertions, 42 deletions
diff --git a/chrome/browser/resources/ntp4/apps_page.css b/chrome/browser/resources/ntp4/apps_page.css index 4ede0da..c40035e 100644 --- a/chrome/browser/resources/ntp4/apps_page.css +++ b/chrome/browser/resources/ntp4/apps_page.css @@ -26,30 +26,8 @@ -webkit-mask-size: 100% 100%; } -/* Pressed is set when an app is first touched. - By using the mask, pressing causes a darkening effect of just the image */ -.app.grabber-pressed img { - opacity: 0.8; -} - -/* Grabbed is set (and pressed is cleared) when the app has been held. */ -.grabber-grabbed { - opacity: 0.8; - -webkit-transform: scale3d(1.4, 1.4, 1); -} - -/* Dragging is set (without grabbed being cleared) when a grabbed app is - moved */ -.grabber-dragging { - /* We need to ensure there is no animation applied to its position - (or the drag touch may stop being over top of it */ - -webkit-transition: none !important; - - /* Ensure the element has a large z-index so that we can get events - for it as it moves over other elements. This is animated as the - element flys back, so we want a large value that will stay large until - its almost home. */ - z-index: 100; +.app-context-menu > button:first-child { + font-weight: bold; } /* Link (bookmark) tiles. */ diff --git a/chrome/browser/resources/ntp4/apps_page.js b/chrome/browser/resources/ntp4/apps_page.js index 0346ea0..d2f33cc 100644 --- a/chrome/browser/resources/ntp4/apps_page.js +++ b/chrome/browser/resources/ntp4/apps_page.js @@ -5,7 +5,153 @@ cr.define('ntp4', function() { 'use strict'; - var TilePage = ntp4.TilePage; + var localStrings = new LocalStrings; + + var APP_LAUNCH = { + // The histogram buckets (keep in sync with extension_constants.h). + NTP_APPS_MAXIMIZED: 0, + NTP_APPS_COLLAPSED: 1, + NTP_APPS_MENU: 2, + NTP_MOST_VISITED: 3, + NTP_RECENTLY_CLOSED: 4, + NTP_APP_RE_ENABLE: 16 + }; + + /** + * App context menu. The class is designed to be used as a singleton with + * the app that is currently showing a context menu stored in this.app_. + * @constructor + */ + function AppContextMenu() { + this.__proto__ = AppContextMenu.prototype; + this.initialize(); + }; + cr.addSingletonGetter(AppContextMenu); + + AppContextMenu.prototype = { + initialize: function() { + var menu = new cr.ui.Menu; + cr.ui.decorate(menu, cr.ui.Menu); + menu.classList.add('app-context-menu'); + this.menu = menu; + + this.launch_ = this.appendMenuItem_(); + this.launch_.addEventListener('activate', this.onLaunch_.bind(this)); + + menu.appendChild(cr.ui.MenuItem.createSeparator()); + this.launchRegularTab_ = this.appendMenuItem_('applaunchtyperegular'); + this.launchPinnedTab_ = this.appendMenuItem_('applaunchtypepinned'); + if (!cr.isMac) + this.launchNewWindow_ = this.appendMenuItem_('applaunchtypewindow'); + this.launchFullscreen_ = this.appendMenuItem_('applaunchtypefullscreen'); + + var self = this; + this.forAllLaunchTypes_(function(launchTypeButton, id) { + launchTypeButton.addEventListener('activate', + self.onLaunchTypeChanged_.bind(self)); + }); + + menu.appendChild(cr.ui.MenuItem.createSeparator()); + this.options_ = this.appendMenuItem_('appoptions'); + this.uninstall_ = this.appendMenuItem_('appuninstall'); + this.options_.addEventListener('activate', + this.onShowOptions_.bind(this)); + this.uninstall_.addEventListener('activate', + this.onUninstall_.bind(this)); + + if (!cr.isMac && !cr.isChromeOs) { + menu.appendChild(cr.ui.MenuItem.createSeparator()); + this.createShortcut_ = this.appendMenuItem_('appcreateshortcut'); + this.createShortcut_.addEventListener( + 'activate', this.onCreateShortcut_.bind(this)); + } + + menu.hidden = true; + document.body.appendChild(menu); + }, + + /** + * Appends a menu item to |this.menu_|. + * @param {?String} textId If non-null, the ID for the localized string + * that acts as the item's label. + */ + appendMenuItem_: function(textId) { + var button = cr.doc.createElement('button'); + this.menu.appendChild(button); + cr.ui.decorate(button, cr.ui.MenuItem); + if (textId) + button.textContent = localStrings.getString(textId); + return button; + }, + + /** + * Iterates over all the launch type menu items. + * @param {function(cr.ui.MenuItem, number)} f The function to call for each + * menu item. The parameters to the function include the menu item and + * the associated launch ID. + */ + forAllLaunchTypes_: function(f) { + // Order matters: index matches launchType id. + var launchTypes = [ this.launchPinnedTab_, + this.launchRegularTab_, + this.launchFullscreen_, + this.launchNewWindow_ ]; + + for (var i = 0; i < launchTypes.length; ++i) { + if (!launchTypes[i]) + continue; + + f(launchTypes[i], i); + } + }, + + /** + * Does all the necessary setup to show the menu for the give app. + * @param {App} app The App object that will be showing a context menu. + */ + setupForApp: function(app) { + this.app_ = app; + + this.launch_.textContent = app.appData.name; + + this.forAllLaunchTypes_(function(launchTypeButton, id) { + launchTypeButton.disabled = false; + launchTypeButton.checked = app.appData.launch_type == id; + }); + + this.options_.disabled = !app.appData.options_url; + }, + + /** + * Handlers for menu item activation. + * @param {Event} e The activation event. + * @private + */ + onLaunch_: function(e) { + chrome.send('launchApp', [this.app_.appId, APP_LAUNCH.NTP_APPS_MENU]); + }, + onLaunchTypeChanged_: function(e) { + var pressed = e.currentTarget; + var app = this.app_; + this.forAllLaunchTypes_(function(launchTypeButton, id) { + if (launchTypeButton == pressed) { + chrome.send('setLaunchType', [app.appId, id]); + // Manually update the launch type. We will only get + // appsPrefChangedCallback calls after changes to other NTP instances. + app.appData.launch_type = id; + } + }); + }, + onShowOptions_: function(e) { + window.location = this.app_.appData.options_url; + }, + onUninstall_: function(e) { + chrome.send('uninstallApp', [this.app_.appData.id]); + }, + onCreateShortcut_: function(e) { + chrome.send('createAppShortcut', [this.app_.appData.id]); + }, + }; /** * Creates a new App object. @@ -26,15 +172,15 @@ cr.define('ntp4', function() { __proto__: HTMLDivElement.prototype, initialize: function() { - assert(this.appData.id, 'Got an app without an ID'); + assert(this.appData_.id, 'Got an app without an ID'); this.className = 'app'; var appImg = this.ownerDocument.createElement('img'); - appImg.src = this.appData.icon_big; + appImg.src = this.appData_.icon_big; // We use a mask of the same image so CSS rules can highlight just the // image when it's touched. - appImg.style.WebkitMaskImage = url(this.appData.icon_big); + appImg.style.WebkitMaskImage = url(this.appData_.icon_big); // We put a click handler just on the app image - so clicking on the // margins between apps doesn't do anything. appImg.addEventListener('click', this.onClick_.bind(this)); @@ -42,10 +188,12 @@ cr.define('ntp4', function() { this.appImg_ = appImg; var appSpan = this.ownerDocument.createElement('span'); - appSpan.textContent = this.appData.name; + appSpan.textContent = this.appData_.name; this.appendChild(appSpan); - /* TODO(estade): grabber */ + this.addEventListener('contextmenu', cr.ui.contextMenuHandler); + this.addEventListener('keydown', cr.ui.contextMenuHandler); + this.addEventListener('keyup', cr.ui.contextMenuHandler); }, /** @@ -69,16 +217,39 @@ cr.define('ntp4', function() { * @private */ onClick_: function(e) { - // Tell chrome to launch the app. - var NTP_APPS_MAXIMIZED = 0; - chrome.send('launchApp', [this.appData.id, NTP_APPS_MAXIMIZED]); + var args = [this.appId, APP_LAUNCH.NTP_APPS_MAXIMIZED]; + args.push(e.altKey, e.ctrlKey, e.metaKey, e.shiftKey, e.button); + chrome.send('launchApp', args); // Don't allow the click to trigger a link or anything e.preventDefault(); }, + /** + * The data and preferences for this app. + * @type {Object} + */ + set appData(data) { + this.appData_ = data; + }, + get appData() { + return this.appData_; + }, + get appId() { - return this.appData.id; + return this.appData_.id; + }, + + /** + * Returns a pointer to the context menu for this app. All apps share the + * singleton AppContextMenu. This function is called by the + * ContextMenuHandler in response to the 'contextmenu' event. + * @type {cr.ui.Menu} + */ + get contextMenu() { + var menu = AppContextMenu.getInstance(); + menu.setupForApp(this); + return menu.menu; }, }; @@ -124,6 +295,8 @@ cr.define('ntp4', function() { }, }; + var TilePage = ntp4.TilePage; + // The fraction of the app tile size that the icon uses. var APP_IMG_SIZE_FRACTION = 4 / 5; diff --git a/chrome/browser/resources/ntp4/new_tab.html b/chrome/browser/resources/ntp4/new_tab.html index fec26ae..8b7f71d 100644 --- a/chrome/browser/resources/ntp4/new_tab.html +++ b/chrome/browser/resources/ntp4/new_tab.html @@ -40,6 +40,7 @@ document.documentElement.setAttribute('touchui', true); <script src="../shared/js/cr/ui/menu_item.js"></script> <script src="../shared/js/cr/ui/position_util.js"></script> <script src="../shared/js/cr/ui/menu_button.js"></script> +<script src="../shared/js/cr/ui/context_menu_handler.js"></script> <script src="tile_page.js"></script> <script src="apps_page.js"></script> diff --git a/chrome/browser/resources/ntp4/new_tab.js b/chrome/browser/resources/ntp4/new_tab.js index b47c401..98631ac 100644 --- a/chrome/browser/resources/ntp4/new_tab.js +++ b/chrome/browser/resources/ntp4/new_tab.js @@ -245,14 +245,21 @@ cr.define('ntp4', function() { /** * Callback invoked by chrome whenever an app preference changes. - * The normal NTP uses this to keep track of the current launch-type of an - * app, updating the choices in the context menu. We don't have such a menu - * so don't use this at all (but it still needs to be here for chrome to - * call). * @param {Object} data An object with all the data on available - * applications. + * applications. */ function appsPrefChangeCallback(data) { + var apps = document.querySelectorAll('.app'); + + // This is an expensive operation. We minimize how frequently it's called + // by only calling it for changes across different instances of the NTP + // (i.e. two separate tabs both showing NTP). + for (var j = 0; j < data.apps.length; ++j) { + for (var i = 0; i < apps.length; ++i) { + if (data.apps[j]['id'] == apps[i].appId) + apps[i].appData = data.apps[j]; + } + } } function getCardSlider() { diff --git a/chrome/browser/resources/shared/js/cr/ui/context_menu_handler.js b/chrome/browser/resources/shared/js/cr/ui/context_menu_handler.js index e3e2fd5..ae998e0 100644 --- a/chrome/browser/resources/shared/js/cr/ui/context_menu_handler.js +++ b/chrome/browser/resources/shared/js/cr/ui/context_menu_handler.js @@ -1,10 +1,9 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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() { - const positionPopupAtPoint = cr.ui.positionPopupAtPoint; const Menu = cr.ui.Menu; /** @@ -95,7 +94,7 @@ cr.define('cr.ui', function() { y = e.clientY; } - positionPopupAtPoint(x, y, menu); + cr.ui.positionPopupAtPoint(x, y, menu); }, /** diff --git a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc index 67ca03a..5ca61e374 100644 --- a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc +++ b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc @@ -248,6 +248,8 @@ void AppLauncherHandler::FillAppDictionary(DictionaryValue* dictionary) { dictionary->Set("apps", list); + // TODO(estade): remove these settings when the old NTP is removed. The new + // NTP does it in js. #if defined(OS_MACOSX) // App windows are not yet implemented on mac. dictionary->SetBoolean("disableAppWindowLaunch", true); @@ -420,6 +422,11 @@ void AppLauncherHandler::HandleSetLaunchType(const ListValue* args) { extensions_service_->GetExtensionById(extension_id, true); CHECK(extension); + // Don't update the page; it already knows about the launch type change. + scoped_ptr<AutoReset<bool> > auto_reset; + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kNewTabPage4)) + auto_reset.reset(new AutoReset<bool>(&ignore_changes_, true)); + extensions_service_->extension_prefs()->SetLaunchType( extension_id, static_cast<ExtensionPrefs::LaunchType>( diff --git a/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc b/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc index 3475652..2b093de 100644 --- a/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc +++ b/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc @@ -262,6 +262,8 @@ void NTPResourceCache::CreateNewTabIncognitoHTML() { } void NTPResourceCache::CreateNewTabHTML() { + // TODO(estade): these strings should be defined in their relevant handlers + // (in GetLocalizedValues) and should have more legible names. // Show the profile name in the title and most visited labels if the current // profile is not the default. string16 apps = l10n_util::GetStringUTF16(IDS_NEW_TAB_APPS); |