summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorestade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-06-23 15:52:47 +0000
committerestade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-06-23 15:52:47 +0000
commit590742dc369f718055cded657f197d154639493e (patch)
tree18e1f2a321d3c05eb3fdcadd34e7d0d937b12334
parent24101d60efb5aa465e40df635fc47a1c453cfab6 (diff)
downloadchromium_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.css26
-rw-r--r--chrome/browser/resources/ntp4/apps_page.js193
-rw-r--r--chrome/browser/resources/ntp4/new_tab.html1
-rw-r--r--chrome/browser/resources/ntp4/new_tab.js17
-rw-r--r--chrome/browser/resources/shared/js/cr/ui/context_menu_handler.js5
-rw-r--r--chrome/browser/ui/webui/ntp/app_launcher_handler.cc7
-rw-r--r--chrome/browser/ui/webui/ntp/ntp_resource_cache.cc2
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);