summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authoroshima@chromium.org <oshima@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-12 04:21:08 +0000
committeroshima@chromium.org <oshima@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-12 04:21:08 +0000
commit090bc22fd9703492c5cf2f76a0a9c16ffa580e0a (patch)
tree8ee44ee5a82ba4007b9403c04901e353586ec198 /chrome
parent3f96c4e4b7254361caccc433be724469438cbc95 (diff)
downloadchromium_src-090bc22fd9703492c5cf2f76a0a9c16ffa580e0a.zip
chromium_src-090bc22fd9703492c5cf2f76a0a9c16ffa580e0a.tar.gz
chromium_src-090bc22fd9703492c5cf2f76a0a9c16ffa580e0a.tar.bz2
Switch WrenchMenu to use domui.
* Add WrenchMenu class that implements zoom/edit commands. * changed toolkit_view to use domui menu. * Add accelerator support. * allow subclass to have its own css. this also addresses comments on * removed clearTimeout * move init code to decorate as much as possible * changed init:config to setter * use 'hidden' rule to hide it, instead of not adding it. * I didn't change addHandler as now target and menuitem does not necessary the same. BUG=6497 TEST= Review URL: http://codereview.chromium.org/3601021 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@62244 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/browser_resources.grd1
-rw-r--r--chrome/browser/chromeos/dom_ui/menu_ui.cc74
-rw-r--r--chrome/browser/chromeos/dom_ui/menu_ui.h8
-rw-r--r--chrome/browser/chromeos/dom_ui/wrench_menu_ui.cc63
-rw-r--r--chrome/browser/chromeos/dom_ui/wrench_menu_ui.h20
-rw-r--r--chrome/browser/resources/menu.css14
-rw-r--r--chrome/browser/resources/menu.html1
-rw-r--r--chrome/browser/resources/menu.js218
-rw-r--r--chrome/browser/resources/wrench_menu.css43
-rw-r--r--chrome/browser/resources/wrench_menu.js308
-rw-r--r--chrome/browser/views/toolbar_view.cc21
-rw-r--r--chrome/browser/views/toolbar_view.h10
12 files changed, 657 insertions, 124 deletions
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index f981d4f..cecfec6 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -76,6 +76,7 @@ without changes to the corresponding grd file. eter -->
<include name="IDR_TALK_APP_MANIFEST" file="resources\chat_manager\manifest.json" type="BINDATA" />
<include name="IDR_MENU_HTML" file="resources\menu.html" flattenhtml="true" type="BINDATA" />
<include name="IDR_WRENCH_MENU_JS" file="resources\wrench_menu.js" flattenhtml="true" type="BINDATA" />
+ <include name="IDR_WRENCH_MENU_CSS" file="resources\wrench_menu.css" flattenhtml="true" type="BINDATA" />
</if>
</includes>
</release>
diff --git a/chrome/browser/chromeos/dom_ui/menu_ui.cc b/chrome/browser/chromeos/dom_ui/menu_ui.cc
index 85264fa..19989b0 100644
--- a/chrome/browser/chromeos/dom_ui/menu_ui.cc
+++ b/chrome/browser/chromeos/dom_ui/menu_ui.cc
@@ -32,6 +32,7 @@
#include "gfx/font.h"
#include "grit/app_resources.h"
#include "grit/browser_resources.h"
+#include "views/accelerator.h"
#include "views/controls/menu/menu_config.h"
#include "views/controls/menu/radio_button_image_gtk.h"
#include "views/widget/widget_gtk.h"
@@ -39,7 +40,7 @@
namespace {
// a fake resource id for not loading extra resource.
-const int kNoExtraSource = -1;
+const int kNoExtraResource = -1;
// A utility function that generates css font property from gfx::Font.
std::wstring GetFontShorthand(const gfx::Font* font) {
@@ -119,12 +120,16 @@ const std::string& GetImageDataUrlForRadio(bool on) {
* Generates a html file that uses |menu_class| as a menu implementation.
* |menu_source_id| specifies the source that contains the definition of the
* |menu_class|, or empty string to use plain "Menu".
+ *
+ * TODO(oshima): make this template to avoid repeatedly loading the
+ * same source/css files.
*/
std::string GetMenuUIHTMLSourceFromString(
const chromeos::MenuUI& menu_ui,
const base::StringPiece& menu_template,
const std::string& menu_class,
- int menu_source_id) {
+ int menu_source_id,
+ int menu_css_id) {
#define SET_INTEGER_PROPERTY(prop) \
value_config.SetInteger(#prop, menu_config.prop)
@@ -177,13 +182,20 @@ std::string GetMenuUIHTMLSourceFromString(
menu_class + ".decorate(document.getElementById('viewport'));" +
" init(" + json_config + ");");
- if (menu_source_id == kNoExtraSource) {
+ if (menu_source_id == kNoExtraResource) {
strings.SetString("menu_source", "");
} else {
base::StringPiece menu_source(
ResourceBundle::GetSharedInstance().GetRawDataResource(menu_source_id));
strings.SetString("menu_source", menu_source.as_string());
}
+ if (menu_css_id == kNoExtraResource) {
+ strings.SetString("menu_css", "");
+ } else {
+ base::StringPiece menu_css(
+ ResourceBundle::GetSharedInstance().GetRawDataResource(menu_css_id));
+ strings.SetString("menu_css", menu_css.as_string());
+ }
return jstemplate_builder::GetI18nTemplateHtml(menu_template, &strings);
}
@@ -194,7 +206,8 @@ class MenuUIHTMLSource : public ChromeURLDataManager::DataSource,
MenuUIHTMLSource(const chromeos::MenuUI& menu_ui,
const std::string& source_name,
const std::string& menu_class,
- int menu_source_id);
+ int menu_source_id,
+ int menu_css_id);
// Called when the network layer has requested a resource underneath
// the path we registered.
@@ -222,8 +235,12 @@ class MenuUIHTMLSource : public ChromeURLDataManager::DataSource,
// The name of JS Menu class to use.
const std::string menu_class_;
- // The resource id of the file of the menu subclass.
+ // The resource id of the JS file of the menu subclass.
int menu_source_id_;
+
+ // The resource id of the CSS file of the menu subclass.
+ int menu_css_id_;
+
#ifndef NDEBUG
int request_id_;
#endif
@@ -301,11 +318,13 @@ class MenuHandler : public chromeos::MenuHandlerBase,
MenuUIHTMLSource::MenuUIHTMLSource(const chromeos::MenuUI& menu_ui,
const std::string& source_name,
const std::string& menu_class,
- int menu_source_id)
+ int menu_source_id,
+ int menu_css_id)
: DataSource(source_name, MessageLoop::current()),
menu_ui_(menu_ui),
menu_class_(menu_class),
- menu_source_id_(menu_source_id)
+ menu_source_id_(menu_source_id),
+ menu_css_id_(menu_css_id)
#ifndef NDEBUG
, request_id_(-1)
#endif
@@ -334,7 +353,7 @@ void MenuUIHTMLSource::StartDataRequest(const std::string& path,
// The resource string should be pure code and should not contain
// i18n string.
const std::string menu_html = GetMenuUIHTMLSourceFromString(
- menu_ui_, menu_template, menu_class_, menu_source_id_);
+ menu_ui_, menu_template, menu_class_, menu_source_id_, menu_css_id_);
scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes);
@@ -354,7 +373,7 @@ void MenuUIHTMLSource::OnURLFetchComplete(const URLFetcher* source,
#ifndef NDEBUG
// This should not be called in release build.
const std::string menu_html = GetMenuUIHTMLSourceFromString(
- menu_ui_, data, menu_class_, menu_source_id_);
+ menu_ui_, data, menu_class_, menu_source_id_, menu_css_id_);
scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes);
@@ -588,27 +607,33 @@ void MenuUI::ModelUpdated(const menus::MenuModel* model) {
ListValue* items = new ListValue();
json_model.Set("items", items);
int max_icon_width = 0;
+ bool has_accelerator = false;
for (int index = 0; index < model->GetItemCount(); ++index) {
menus::MenuModel::ItemType type = model->GetTypeAt(index);
DictionaryValue* item;
switch (type) {
case menus::MenuModel::TYPE_SEPARATOR:
- item = CreateMenuItem(model, index, "separator", &max_icon_width);
+ item = CreateMenuItem(model, index, "separator",
+ &max_icon_width, &has_accelerator);
break;
case menus::MenuModel::TYPE_RADIO:
max_icon_width = std::max(max_icon_width, 12);
- item = CreateMenuItem(model, index, "radio", &max_icon_width);
+ item = CreateMenuItem(model, index, "radio",
+ &max_icon_width, &has_accelerator);
break;
case menus::MenuModel::TYPE_SUBMENU:
- item = CreateMenuItem(model, index, "submenu", &max_icon_width);
+ item = CreateMenuItem(model, index, "submenu",
+ &max_icon_width, &has_accelerator);
break;
case menus::MenuModel::TYPE_COMMAND:
- item = CreateMenuItem(model, index, "command", &max_icon_width);
+ item = CreateMenuItem(model, index, "command",
+ &max_icon_width, &has_accelerator);
break;
case menus::MenuModel::TYPE_CHECK:
// Add space even when unchecked.
max_icon_width = std::max(max_icon_width, 12);
- item = CreateMenuItem(model, index, "check", &max_icon_width);
+ item = CreateMenuItem(model, index, "check",
+ &max_icon_width, &has_accelerator);
break;
default:
// TODO(oshima): We don't support BUTTOM_ITEM for now.
@@ -626,13 +651,15 @@ void MenuUI::ModelUpdated(const menus::MenuModel* model) {
DCHECK(widget);
json_model.SetInteger("maxIconWidth", max_icon_width);
json_model.SetBoolean("isRoot", widget->is_root());
+ json_model.SetBoolean("hasAccelerator", has_accelerator);
CallJavascriptFunction(L"updateModel", json_model);
}
DictionaryValue* MenuUI::CreateMenuItem(const menus::MenuModel* model,
int index,
const char* type,
- int* max_icon_width) const {
+ int* max_icon_width,
+ bool* has_accel) const {
// Note: DOM UI uses '&' as mnemonic.
string16 label16 = model->GetLabelAt(index);
DictionaryValue* item = new DictionaryValue();
@@ -650,6 +677,11 @@ DictionaryValue* MenuUI::CreateMenuItem(const menus::MenuModel* model,
item->SetString("icon", dom_ui_util::GetImageDataUrl(icon));
*max_icon_width = std::max(*max_icon_width, icon.width());
}
+ views::Accelerator menu_accelerator;
+ if (model->GetAcceleratorAt(index, &menu_accelerator)) {
+ item->SetString("accel", WideToUTF16(menu_accelerator.GetShortcutText()));
+ *has_accel = true;
+ }
return item;
}
@@ -657,15 +689,21 @@ ChromeURLDataManager::DataSource* MenuUI::CreateDataSource() {
return CreateMenuUIHTMLSource(*this,
chrome::kChromeUIMenu,
"Menu" /* class name */,
- kNoExtraSource);
+ kNoExtraResource,
+ kNoExtraResource);
}
ChromeURLDataManager::DataSource* MenuUI::CreateMenuUIHTMLSource(
const MenuUI& menu_ui,
const std::string& source_name,
const std::string& menu_class,
- int menu_source_id) {
- return new MenuUIHTMLSource(menu_ui, source_name, menu_class, menu_source_id);
+ int menu_source_id,
+ int menu_css_id) {
+ return new MenuUIHTMLSource(menu_ui,
+ source_name,
+ menu_class,
+ menu_source_id,
+ menu_css_id);
}
} // namespace chromeos
diff --git a/chrome/browser/chromeos/dom_ui/menu_ui.h b/chrome/browser/chromeos/dom_ui/menu_ui.h
index 66cdf7f..6127d64 100644
--- a/chrome/browser/chromeos/dom_ui/menu_ui.h
+++ b/chrome/browser/chromeos/dom_ui/menu_ui.h
@@ -33,13 +33,14 @@ class MenuUI : public DOMUI {
virtual DictionaryValue* CreateMenuItem(const menus::MenuModel* model,
int index,
const char* type,
- int* max_icon_width) const;
+ int* max_icon_width,
+ bool* has_accel) const;
// Subclass can add extra parameters or replaces default configuration.
virtual void AddCustomConfigValues(DictionaryValue* config) const {};
// A utility function which creates a concrete html file from
- // template file |menu_resource_id| for given |menu_class|.
+ // template file |menu_resource_id| and |menu_css_id| for given |menu_class|.
// The resource_name is the host part of DOMUI's url.
// Caution: This calls MenuUI::GetProfile() when creating the data source,
// thus, it has to be initialized.
@@ -47,7 +48,8 @@ class MenuUI : public DOMUI {
const MenuUI& menu_ui,
const std::string& source_name,
const std::string& menu_class,
- int menu_source_res_id);
+ int menu_source_res_id,
+ int menu_css_res_id);
protected:
// A constructor for subclass to initialize the MenuUI with
diff --git a/chrome/browser/chromeos/dom_ui/wrench_menu_ui.cc b/chrome/browser/chromeos/dom_ui/wrench_menu_ui.cc
index c79316b5..21a2913 100644
--- a/chrome/browser/chromeos/dom_ui/wrench_menu_ui.cc
+++ b/chrome/browser/chromeos/dom_ui/wrench_menu_ui.cc
@@ -4,13 +4,23 @@
#include "chrome/browser/chromeos/dom_ui/wrench_menu_ui.h"
-#include "base/values.h"
+#include "app/l10n_util.h"
+#include "base/string_number_conversions.h"
#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "base/weak_ptr.h"
#include "chrome/app/chrome_dll_resource.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_list.h"
#include "chrome/browser/chromeos/views/native_menu_domui.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/common/notification_registrar.h"
+#include "chrome/common/notification_source.h"
#include "chrome/common/url_constants.h"
#include "googleurl/src/gurl.h"
#include "grit/browser_resources.h"
+#include "grit/generated_resources.h"
#include "views/controls/menu/menu_2.h"
namespace chromeos {
@@ -27,14 +37,59 @@ WrenchMenuUI::WrenchMenuUI(TabContents* contents)
ALLOW_THIS_IN_INITIALIZER_LIST(
CreateMenuUIHTMLSource(*this,
chrome::kChromeUIWrenchMenu,
- "Menu",
- IDR_WRENCH_MENU_JS))) {
+ "WrenchMenu" /* class name */,
+ IDR_WRENCH_MENU_JS,
+ IDR_WRENCH_MENU_CSS))) {
+ registrar_.Add(this, NotificationType::ZOOM_LEVEL_CHANGED,
+ Source<Profile>(GetProfile()));
+}
+
+void WrenchMenuUI::ModelUpdated(const menus::MenuModel* new_model) {
+ MenuUI::ModelUpdated(new_model);
+ UpdateZoomControls();
}
void WrenchMenuUI::AddCustomConfigValues(DictionaryValue* config) const {
- // These command ids are to create customized menu items for wrench menu.
+ // Resources that are necessary to build wrench menu.
config->SetInteger("IDC_CUT", IDC_CUT);
+ config->SetInteger("IDC_COPY", IDC_COPY);
+ config->SetInteger("IDC_PASTE", IDC_PASTE);
config->SetInteger("IDC_ZOOM_MINUS", IDC_ZOOM_MINUS);
+ config->SetInteger("IDC_ZOOM_PLUS", IDC_ZOOM_PLUS);
+ config->SetInteger("IDC_FULLSCREEN", IDC_FULLSCREEN);
+
+ config->SetString("IDS_EDIT2", WideToUTF8(l10n_util::GetString(IDS_EDIT2)));
+ config->SetString("IDS_ZOOM_MENU2",
+ WideToUTF8(l10n_util::GetString(IDS_ZOOM_MENU2)));
+ config->SetString("IDS_CUT", WideToUTF8(l10n_util::GetString(IDS_CUT)));
+ config->SetString("IDS_COPY", WideToUTF8(l10n_util::GetString(IDS_COPY)));
+ config->SetString("IDS_PASTE", WideToUTF8(l10n_util::GetString(IDS_PASTE)));
+}
+
+void WrenchMenuUI::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK_EQ(NotificationType::ZOOM_LEVEL_CHANGED, type.value);
+ UpdateZoomControls();
+}
+
+void WrenchMenuUI::UpdateZoomControls() {
+ Browser* browser = BrowserList::GetLastActive();
+ if (!browser)
+ return;
+ TabContents* selected_tab = browser->GetSelectedTabContents();
+ bool enable_increment = false;
+ bool enable_decrement = false;
+ int zoom = 100;
+ if (selected_tab)
+ zoom = selected_tab->GetZoomPercent(&enable_increment, &enable_decrement);
+
+ DictionaryValue params;
+ params.SetBoolean("plus", enable_increment);
+ params.SetBoolean("minus", enable_decrement);
+ params.SetString("percent", l10n_util::GetStringFUTF16(
+ IDS_ZOOM_PERCENT, UTF8ToUTF16(base::IntToString(zoom))));
+ CallJavascriptFunction(L"updateZoomControls", params);
}
views::Menu2* WrenchMenuUI::CreateMenu2(menus::MenuModel* model) {
diff --git a/chrome/browser/chromeos/dom_ui/wrench_menu_ui.h b/chrome/browser/chromeos/dom_ui/wrench_menu_ui.h
index 04fd44e..cb0cd80 100644
--- a/chrome/browser/chromeos/dom_ui/wrench_menu_ui.h
+++ b/chrome/browser/chromeos/dom_ui/wrench_menu_ui.h
@@ -7,6 +7,12 @@
#pragma once
#include "chrome/browser/chromeos/dom_ui/menu_ui.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
+#include "chrome/common/notification_type.h"
+
+class NotificationSource;
+class NotificationDetails;
namespace views {
class Menu2;
@@ -18,17 +24,29 @@ class MenuModel;
namespace chromeos {
-class WrenchMenuUI : public MenuUI {
+class WrenchMenuUI : public MenuUI,
+ public NotificationObserver {
public:
explicit WrenchMenuUI(TabContents* contents);
// MenuUI overrides:
+ virtual void ModelUpdated(const menus::MenuModel* new_model);
virtual void AddCustomConfigValues(DictionaryValue* config) const;
+ // NotificationObserver:
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ // Updates zoom controls to reflect the current zooming state.
+ void UpdateZoomControls();
+
// A convenient factory method to create Menu2 for wrench menu.
static views::Menu2* CreateMenu2(menus::MenuModel* model);
private:
+ NotificationRegistrar registrar_;
+
DISALLOW_COPY_AND_ASSIGN(WrenchMenuUI);
};
diff --git a/chrome/browser/resources/menu.css b/chrome/browser/resources/menu.css
index a279618..2c9b055 100644
--- a/chrome/browser/resources/menu.css
+++ b/chrome/browser/resources/menu.css
@@ -10,10 +10,13 @@ body {
.menu-item {
white-space: nowrap;
margin: 0;
- padding-top: 0;
- padding-bottom: 0;
+ padding-top: 1px;
+ padding-bottom: 1px;
background-repeat: no-repeat;
-webkit-padding-end: 19px;
+ display: -webkit-box;
+ -webkit-box-orient: horizontal;
+ overflow: hidden;
}
.disabled {
@@ -25,10 +28,15 @@ body {
}
.menu-label {
- display: inline-block;
+ -webkit-box-flex: 1;
vertical-align: middle;
}
+.accelerator {
+ opacity: 0.3;
+ -webkit-margin-start: 15px;
+}
+
.left-icon {
background-position: 4px center;
}
diff --git a/chrome/browser/resources/menu.html b/chrome/browser/resources/menu.html
index f35e2fd..f6acc6a 100644
--- a/chrome/browser/resources/menu.html
+++ b/chrome/browser/resources/menu.html
@@ -6,6 +6,7 @@
<script src="shared/js/cr.js"></script>
<script src="shared/js/cr/ui.js"></script>
<script src="menu.js"></script>
+ <style i18n-content="menu_css"></style>
<script i18n-content="menu_source"></script>
</head>
<body>
diff --git a/chrome/browser/resources/menu.js b/chrome/browser/resources/menu.js
index 67e34fa..805550c 100644
--- a/chrome/browser/resources/menu.js
+++ b/chrome/browser/resources/menu.js
@@ -15,9 +15,14 @@ var MNEMONIC_REGEXP = /([^&]*)&(.)(.*)/;
/**
* Sends 'activate' DOMUI message.
+ * @param {number} index The index of menu item to activate in menu model.
+ * @param {string} mode The activation mode, one of 'close_and_activate', or
+ * 'activate_no_close'.
+ * TODO(oshima): change these string to enum numbers once it becomes possible
+ * to pass number to C++.
*/
-function sendActivate(index) {
- chrome.send('activate', [String(index), 'close_and_activate']);
+function sendActivate(index, mode) {
+ chrome.send('activate', [String(index), mode]);
}
/**
@@ -42,9 +47,11 @@ MenuItem.prototype = {
* @param {Object} attrs JSON object that represents this menu items
* properties. This is created from menu model in C code. See
* chromeos/views/native_menu_domui.cc.
- * @param {number} leftIconWidth The left icon's width. 0 if no icon.
+ * @param {Object} model The model object.
*/
- init: function(menu, attrs, leftIconWidth) {
+ init: function(menu, attrs, model) {
+ // The left icon's width. 0 if no icon.
+ var leftIconWidth = model.maxIconWidth;
this.menu_ = menu;
this.attrs = attrs;
var attrs = this.attrs;
@@ -54,24 +61,27 @@ MenuItem.prototype = {
attrs.type == 'submenu' ||
attrs.type == 'check' ||
attrs.type == 'radio') {
- this.initMenuItem_(leftIconWidth);
+ this.initMenuItem_();
+ this.initPadding_(leftIconWidth);
} else {
+ // This should not happend.
this.classList.add('disabled');
this.textContent = 'unknown';
}
- this.classList.add(leftIconWidth ? 'has-icon' : 'no-icon');
- if (attrs.visible) {
- menu.appendChild(this);
+ menu.appendChild(this);
+ if (!attrs.visible) {
+ this.classList.add('hidden');
}
},
/**
* Changes the selection state of the menu item.
- * @param {boolean} b True to set the selection, or false otherwise.
+ * @param {boolean} selected True to set the selection, or false
+ * otherwise.
*/
- set selected(b) {
- if (b) {
+ set selected(selected) {
+ if (selected) {
this.classList.add('selected');
this.menu_.selectedItem = this;
} else {
@@ -87,7 +97,8 @@ MenuItem.prototype = {
this.menu_.openSubmenu(this);
} else if (this.attrs.type != 'separator' &&
this.className.indexOf('selected') >= 0) {
- sendActivate(this.menu_.getMenuItemIndexOf(this));
+ sendActivate(this.menu_.getMenuItemIndexOf(this),
+ 'close_and_activate');
}
},
@@ -104,56 +115,30 @@ MenuItem.prototype = {
* Internal method to initiailze the MenuItem.
* @private
*/
- initMenuItem_: function(leftIconWidth) {
+ initMenuItem_: function() {
var attrs = this.attrs;
this.className = 'menu-item ' + attrs.type;
- this.menu_.addHandlers(this);
- var mnemonic = MNEMONIC_REGEXP.exec(attrs.label);
- if (mnemonic) {
- var c = mnemonic[2];
- this.menu_.registerMnemonicKey(c, this);
- }
- if (leftIconWidth > 0) {
- this.classList.add('left-icon');
-
- var url;
- if (attrs.type == 'radio') {
- url = attrs.checked ?
- this.menu_.config_.radioOnUrl :
- this.menu_.config_.radioOffUrl;
- } else if (attrs.icon) {
- url = attrs.icon;
- } else if (attrs.type == 'check' && attrs.checked) {
- url = this.menu_.config_.checkUrl;
- }
- if (url) {
- this.style.backgroundImage = 'url(' + url + ')';
- }
- // TODO(oshima): figure out how to update left padding in rule.
- // 4 is the padding on left side of icon.
- var padding =
- 4 + leftIconWidth + this.menu_.config_.icon_to_label_padding;
- this.style.paddingLeft = padding + 'px';
- }
+ this.menu_.addHandlers(this, this);
var label = document.createElement('div');
label.className = 'menu-label';
-
- if (!mnemonic) {
- label.textContent = attrs.label;
- } else {
- label.appendChild(document.createTextNode(mnemonic[1]));
- label.appendChild(document.createElement('span'));
- label.appendChild(document.createTextNode(mnemonic[3]));
- label.childNodes[1].className = 'mnemonic';
- label.childNodes[1].textContent = mnemonic[2];
- }
+ this.menu_.addLabelTo(this, attrs.label, label,
+ true /* enable mnemonic */);
if (attrs.font) {
label.style.font = attrs.font;
}
this.appendChild(label);
+
+ if (attrs.accel) {
+ var accel = document.createElement('div');
+ accel.className = 'accelerator';
+ accel.textContent = attrs.accel;
+ accel.style.font = attrs.font;
+ this.appendChild(accel);
+ }
+
if (attrs.type == 'submenu') {
// This overrides left-icon's position, but it's OK as submenu
// shoudln't have left-icon.
@@ -161,6 +146,34 @@ MenuItem.prototype = {
this.style.backgroundImage = 'url(' + this.menu_.config_.arrowUrl + ')';
}
},
+
+ initPadding_: function(leftIconWidth) {
+ if (leftIconWidth <= 0) {
+ this.classList.add('no-icon');
+ return;
+ }
+ this.classList.add('left-icon');
+
+ var url;
+ var attrs = this.attrs;
+ if (attrs.type == 'radio') {
+ url = attrs.checked ?
+ this.menu_.config_.radioOnUrl :
+ this.menu_.config_.radioOffUrl;
+ } else if (attrs.icon) {
+ url = attrs.icon;
+ } else if (attrs.type == 'check' && attrs.checked) {
+ url = this.menu_.config_.checkUrl;
+ }
+ if (url) {
+ this.style.backgroundImage = 'url(' + url + ')';
+ }
+ // TODO(oshima): figure out how to update left padding in rule.
+ // 4 is the padding on left side of icon.
+ var padding =
+ 4 + leftIconWidth + this.menu_.config_.icon_to_label_padding;
+ this.style.WebkitPaddingStart = padding + 'px';
+ },
};
/**
@@ -274,6 +287,37 @@ Menu.prototype = {
},
/**
+ * Adds a label to {@code targetDiv}. A label may contain
+ * mnemonic key, preceded by '&'.
+ * @param {MenuItem} item The menu item to be activated by mnemonic
+ * key.
+ * @param {string} label The label string to be added to
+ * {@code targetDiv}.
+ * @param {HTMLElement} div The div element the label is added to.
+ * @param {boolean} enableMnemonic True to enable mnemonic, or false
+ * to not to interprete mnemonic key. The function removes '&'
+ * from the label in both cases.
+ */
+ addLabelTo: function(item, label, targetDiv, enableMnemonic) {
+ var mnemonic = MNEMONIC_REGEXP.exec(label);
+ if (mnemonic && enableMnemonic) {
+ var c = mnemonic[2].toLowerCase();
+ this.mnemonics_[c] = item;
+ }
+ if (!mnemonic) {
+ targetDiv.textContent = label;
+ } else if (enableMnemonic) {
+ targetDiv.appendChild(document.createTextNode(mnemonic[1]));
+ targetDiv.appendChild(document.createElement('span'));
+ targetDiv.appendChild(document.createTextNode(mnemonic[3]));
+ targetDiv.childNodes[1].className = 'mnemonic';
+ targetDiv.childNodes[1].textContent = mnemonic[2];
+ } else {
+ targetDiv.textContent = mnemonic.splice(1, 3).join('');
+ }
+ },
+
+ /**
* Returns the index of the {@code item}.
*/
getMenuItemIndexOf: function(item) {
@@ -281,10 +325,12 @@ Menu.prototype = {
},
/**
- * A template method to create MenuItem object.
- * Subclass class can override to return custom menu item.
+ * A template method to create an item object. It can be a subclass
+ * of MenuItem, or any HTMLElement that implements {@code init},
+ * {@code activate} methods as well as {@code selected} attribute.
+ * @param {Object} attrs The menu item's properties passed from C++.
*/
- createMenuItem: function() {
+ createMenuItem: function(attrs) {
return new MenuItem();
},
@@ -301,7 +347,7 @@ Menu.prototype = {
for (var i = 0; i < model.items.length; i++) {
var attrs = model.items[i];
var item = this.createMenuItem(attrs);
- item.init(this, attrs, model.maxIconWidth);
+ item.init(this, attrs, model);
this.items_.push(item);
}
this.onResize_();
@@ -320,28 +366,19 @@ Menu.prototype = {
},
/**
- * Registers mnemonic key.
- * @param {string} c A mnemonic key to activate item.
- * @param {MenuItem} item An item to be activated when {@code c} is pressed.
- */
- registerMnemonicKey: function(c, item) {
- this.mnemonics_[c.toLowerCase()] = item;
- },
-
- /**
* Add event handlers for the item.
*/
- addHandlers: function(item) {
+ addHandlers: function(item, target) {
var menu = this;
- item.addEventListener('mouseover', function(event) {
+ target.addEventListener('mouseover', function(event) {
menu.onMouseover_(event, item);
});
if (item.attrs.enabled) {
- item.addEventListener('mouseup', function(event) {
+ target.addEventListener('mouseup', function(event) {
menu.onClick_(event, item);
});
} else {
- item.classList.add('disabled');
+ target.classList.add('disabled');
}
},
@@ -395,7 +432,7 @@ Menu.prototype = {
/**
* Open submenu {@code item}. It does nothing if the submenu is
* already opened.
- * @param {MenuItem} item the submenu item to open.
+ * @param {MenuItem} item The submenu item to open.
*/
openSubmenu: function(item) {
this.cancelSubmenuTimer_();
@@ -462,8 +499,10 @@ Menu.prototype = {
// Handles mnemonic.
var c = String.fromCharCode(event.keyCode);
var item = this.mnemonics_[c.toLowerCase()];
- if (item)
+ if (item) {
+ item.selected = true;
item.activate();
+ }
},
// Mouse Event handlers
@@ -556,7 +595,7 @@ Menu.prototype = {
* Find a next selectable item. If nothing is selected, the 1st
* selectable item will be chosen. Returns null if nothing is
* selectable.
- * @param {number} incr specifies the direction to search, 1 to
+ * @param {number} incr Specifies the direction to search, 1 to
* downwards and -1 for upwards.
* @private
*/
@@ -571,7 +610,8 @@ Menu.prototype = {
for (var i = 0; i < len; i++) {
index = (index + incr + len) % len;
var item = this.items_[index];
- if (item.attrs.enabled && item.attrs.type != 'separator')
+ if (item.attrs.enabled && item.attrs.type != 'separator' &&
+ !item.classList.contains('hidden'))
return item;
}
return null;
@@ -582,32 +622,26 @@ Menu.prototype = {
* @private
*/
cancelSubmenuTimer_: function() {
- if (this.openSubmenuTimer_) {
- clearTimeout(this.openSubmenuTimer_);
- this.openSubmenuTimer_ = 0;
- }
- if (this.closeSubmenuTimer_) {
- clearTimeout(this.closeSubmenuTimer_);
- this.closeSubmenuTimer_ = 0;
- }
+ clearTimeout(this.openSubmenuTimer_);
+ this.openSubmenuTimer_ = 0;
+ clearTimeout(this.closeSubmenuTimer_);
+ this.closeSubmenuTimer_ = 0;
},
/**
* Starts auto scroll.
- * @param {number} tick the number of pixels to scroll.
+ * @param {number} tick The number of pixels to scroll.
* @private
*/
autoScroll_: function(tick) {
var previous = this.scrollTop;
this.scrollTop += tick;
- if (this.scrollTop != previous) {
- var menu = this;
- this.scrollTimer_ = setTimeout(
- function() {
- menu.autoScroll_(tick);
- },
- SCROLL_INTERVAL_MS);
- }
+ var menu = this;
+ this.scrollTimer_ = setTimeout(
+ function() {
+ menu.autoScroll_(tick);
+ },
+ SCROLL_INTERVAL_MS);
},
/**
@@ -615,10 +649,8 @@ Menu.prototype = {
* @private
*/
stopScroll_: function () {
- if (this.scrollTimer_) {
- clearTimeout(this.scrollTimer_);
- this.scrollTimer_ = 0;
- }
+ clearTimeout(this.scrollTimer_);
+ this.scrollTimer_ = 0;
},
/**
diff --git a/chrome/browser/resources/wrench_menu.css b/chrome/browser/resources/wrench_menu.css
new file mode 100644
index 0000000..b696068
--- /dev/null
+++ b/chrome/browser/resources/wrench_menu.css
@@ -0,0 +1,43 @@
+.fullscreen {
+ background-image: url('../../app/theme/fullscreen_menu_button.png');
+ background-position: center center;
+ background-repeat: no-repeat;
+ border: solid 1px rgba(0, 0, 0, 0.3);
+ border-radius: 3px;
+ width: 23px;
+ height: 23px;
+ -webkit-margin-start: 4px;
+}
+
+.edit-button {
+ padding: 2px;
+ text-align: center;
+ min-width: 35px;
+}
+
+.left-button {
+ border: solid 1px rgba(0, 0, 0, 0.3);
+ border-top-left-radius: 3px;
+ border-bottom-left-radius: 3px;
+}
+
+.right-button {
+ border: solid 1px rgba(0, 0, 0, 0.3);
+ border-top-right-radius: 3px;
+ border-bottom-right-radius: 3px;
+}
+
+.center-button {
+ border-top: solid 1px rgba(0, 0, 0, 0.3);
+ border-bottom: solid 1px rgba(0, 0, 0, 0.3);
+}
+
+.zoom-button {
+ text-align: center;
+ width: 15px;
+}
+
+.zoom-percent {
+ min-width: 40px;
+ text-align: center;
+}
diff --git a/chrome/browser/resources/wrench_menu.js b/chrome/browser/resources/wrench_menu.js
index c45e432..8566270 100644
--- a/chrome/browser/resources/wrench_menu.js
+++ b/chrome/browser/resources/wrench_menu.js
@@ -2,4 +2,310 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// T.B.D.
+/**
+ * ButtonCommand class for small buttons on menu items.
+ */
+var ButtonCommand = cr.ui.define('div');
+
+ButtonCommand.prototype = {
+ __proto__: HTMLDivElement.prototype,
+
+ /**
+ * Decorate Button item.
+ */
+ decorate: function() {
+ },
+
+ /**
+ * Changes the selection state of the menu item.
+ * @param {boolean} selected True to set the selection, or false otherwise.
+ */
+ set selected(selected) {
+ if (selected) {
+ this.classList.add('selected');
+ this.menu_.selectedItem = this;
+ } else {
+ this.classList.remove('selected');
+ }
+ },
+
+ /**
+ * Activate the menu item.
+ */
+ activate: function() {
+ sendActivate(this.menu_.getMenuItemIndexOf(this),
+ 'close_and_activate');
+ },
+};
+
+/**
+ * EditCommand implements Copy and Paste command.
+ */
+var EditCommand = cr.ui.define('div');
+
+EditCommand.prototype = {
+ __proto__: ButtonCommand.prototype,
+
+ /**
+ * Initialize the menu item.
+ * @override
+ */
+ init: function(menu, attrs, model) {
+ this.menu_ = menu;
+ this.attrs = attrs;
+ if (this.attrs.font) {
+ this.style.font = attrs.font;
+ }
+ menu.addHandlers(this, this);
+ if (attrs.command_id == menu.config_.IDC_COPY) {
+ menu.addLabelTo(this, menu.config_.IDS_COPY, this,
+ false /* no mnemonic */);
+ } else {
+ menu.addLabelTo(this, menu.config_.IDS_PASTE, this,
+ false /* no mnemonic */);
+ }
+ },
+};
+
+/**
+ * EditMenuItem which has Copy and Paste commands inside.
+ */
+var EditMenuItem = cr.ui.define('div');
+
+EditMenuItem.prototype = {
+ __proto__: MenuItem.prototype,
+
+ /**
+ * Initialize
+ */
+ decorate: function() {
+ this.className = 'menu-item';
+ this.label_ = document.createElement('div');
+ this.label_.className = 'menu-label';
+ this.cut_ = document.createElement('div');
+ this.cut_.className = 'edit-button left-button';
+ this.copy_ = new EditCommand();
+ this.copy_.className = 'edit-button center-button';
+ this.paste_ = new EditCommand();
+ this.paste_.className = 'edit-button right-button';
+
+ this.appendChild(this.label_);
+ this.appendChild(this.cut_);
+ this.appendChild(this.copy_);
+ this.appendChild(this.paste_);
+ },
+
+ /**
+ * Activates the command.
+ * @override
+ */
+ activate: function() {
+ sendActivate(this.menu_.getMenuItemIndexOf(this),
+ 'close_and_activate');
+ },
+
+ /**
+ * @override
+ */
+ set selected(selected) {
+ if (selected) {
+ this.cut_.classList.add('selected');
+ this.menu_.selectedItem = this;
+ } else {
+ this.cut_.classList.remove('selected');
+ }
+ },
+
+ /**
+ * Initialize the edit items with configuration info.
+ * @override
+ */
+ initMenuItem_: function() {
+ this.label_.textContent =
+ this.menu_.config_.IDS_EDIT2;
+ if (this.attrs.font) {
+ this.label_.style.font = this.attrs.font;
+ this.cut_.style.font = this.attrs.font;
+ }
+ this.menu_.addLabelTo(
+ this, this.menu_.config_.IDS_CUT, this.cut_,
+ false /* no mnemonic */);
+ this.menu_.addHandlers(this, this.cut_);
+ },
+};
+
+/**
+ * ZoomCommand class implements Zoom plus and fullscreen.
+ */
+var ZoomCommand = cr.ui.define('div');
+
+ZoomCommand.prototype = {
+ __proto__: ButtonCommand.prototype,
+
+ /**
+ * Initialize the menu item.
+ * @override
+ */
+ init: function(menu, attrs, model) {
+ this.menu_ = menu;
+ this.attrs = attrs;
+ menu.addHandlers(this, this);
+ if (attrs.command_id == menu.config_.IDC_ZOOM_PLUS) {
+ this.textContent = '+';
+ }
+ if (this.attrs.font) {
+ this.style.font = attrs.font;
+ }
+ },
+
+ /**
+ * Activate zoom plus and full screen commands.
+ * @override
+ */
+ activate: function() {
+ sendActivate(this.menu_.getMenuItemIndexOf(this),
+ this.attrs.command_id == this.menu_.config_.IDC_ZOOM_PLUS ?
+ 'activate_no_close' : 'close_and_activate');
+ },
+};
+
+/**
+ * ZoomMenuItem which has plus and fullscreen buttons inside.
+ */
+var ZoomMenuItem = cr.ui.define('div');
+
+ZoomMenuItem.prototype = {
+ __proto__: MenuItem.prototype,
+
+ /**
+ * Decorate Zoom button item.
+ */
+ decorate: function() {
+ this.className = 'menu-item';
+
+ this.label_ = document.createElement('div');
+ this.label_.className = 'menu-label';
+ this.minus_ = document.createElement('div');
+ this.minus_.className = 'zoom-button left-button';
+ this.minus_.textContent = '-';
+ this.plus_ = new ZoomCommand();
+ this.plus_.className = 'zoom-button right-button';
+ this.percent_ = document.createElement('div');
+ this.percent_.className = 'zoom-percent center-button';
+ this.fullscreen_ = new ZoomCommand();
+ this.fullscreen_.className = 'fullscreen';
+
+ this.appendChild(this.label_);
+ this.appendChild(this.minus_);
+ this.appendChild(this.percent_);
+ this.appendChild(this.plus_);
+ this.appendChild(this.fullscreen_);
+ },
+
+ /**
+ * Activates the cut command.
+ * @override
+ */
+ activate: function() {
+ sendActivate(this.menu_.getMenuItemIndexOf(this),
+ 'activate_no_close');
+ },
+
+ /**
+ * Updates zoom controls.
+ * @params {JSON} params JSON object to configure zoom controls.
+ */
+ updateZoomControls: function(params) {
+ this.attrs.enabled = params.plus;
+ if (params.plus) {
+ this.plus_.classList.remove('disabled');
+ } else {
+ this.plus_.classList.add('disabled');
+ }
+ this.attrs.enabled = params.minus;
+ if (params.minus) {
+ this.classList.remove('disabled');
+ } else {
+ this.classList.add('disabled');
+ }
+ this.percent_.textContent = params.percent;
+ },
+
+ /**
+ * @override
+ */
+ set selected(selected) {
+ if (selected) {
+ this.minus_.classList.add('selected');
+ this.menu_.selectedItem = this;
+ } else {
+ this.minus_.classList.remove('selected');
+ }
+ },
+
+ /**
+ * Initializes the zoom menu item with configuration info.
+ * @override
+ */
+ initMenuItem_: function() {
+ this.label_.textContent =
+ this.menu_.config_.IDS_ZOOM_MENU2;
+ this.menu_.addHandlers(this, this.minus_);
+
+ if (this.attrs.font) {
+ this.label_.style.font = this.attrs.font;
+ this.minus_.style.font = this.attrs.font;
+ this.percent_.style.font = this.attrs.font;
+ }
+ },
+};
+
+/**
+ * WrenchMenu
+ */
+var WrenchMenu = cr.ui.define('div');
+
+WrenchMenu.prototype = {
+ __proto__: Menu.prototype,
+
+ /**
+ * Decorate Zoom button item.
+ */
+ decorate: function() {
+ Menu.prototype.decorate.call(this);
+ this.edit_ = new EditMenuItem();
+ this.zoom_ = new ZoomMenuItem();
+ },
+
+ /**
+ * Create a MenuItem for given {@code attrs}.
+ * @override
+ */
+ createMenuItem: function(attrs) {
+ switch(attrs.command_id) {
+ case this.config_.IDC_CUT:
+ return this.edit_;
+ case this.config_.IDC_COPY:
+ return this.edit_.copy_;
+ case this.config_.IDC_PASTE:
+ return this.edit_.paste_;
+ case this.config_.IDC_ZOOM_MINUS:
+ return this.zoom_;
+ case this.config_.IDC_ZOOM_PLUS:
+ return this.zoom_.plus_;
+ case this.config_.IDC_FULLSCREEN:
+ return this.zoom_.fullscreen_;
+ default:
+ return new MenuItem();
+ }
+ },
+
+ updateZoomControls: function(params) {
+ this.zoom_.updateZoomControls(params);
+ },
+};
+
+function updateZoomControls(params) {
+ var menu = document.getElementById('viewport');
+ menu.updateZoomControls(params);
+}
diff --git a/chrome/browser/views/toolbar_view.cc b/chrome/browser/views/toolbar_view.cc
index 974059a..48e7f69 100644
--- a/chrome/browser/views/toolbar_view.cc
+++ b/chrome/browser/views/toolbar_view.cc
@@ -22,7 +22,12 @@
#include "chrome/browser/views/browser_actions_container.h"
#include "chrome/browser/views/event_utils.h"
#include "chrome/browser/views/frame/browser_view.h"
+#if !defined(OS_CHROMEOS)
#include "chrome/browser/views/wrench_menu.h"
+#else
+#include "views/controls/menu/menu_2.h"
+#include "chrome/browser/chromeos/dom_ui/wrench_menu_ui.h"
+#endif
#include "chrome/browser/wrench_menu_model.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/pref_names.h"
@@ -257,18 +262,32 @@ bool ToolbarView::GetAcceleratorInfo(int id, menus::Accelerator* accel) {
////////////////////////////////////////////////////////////////////////////////
// ToolbarView, views::MenuDelegate implementation:
-void ToolbarView::RunMenu(views::View* source, const gfx::Point& /*pt*/) {
+void ToolbarView::RunMenu(views::View* source, const gfx::Point& /* pt */) {
DCHECK_EQ(VIEW_ID_APP_MENU, source->GetID());
bool destroyed_flag = false;
destroyed_flag_ = &destroyed_flag;
+#if defined(OS_CHROMEOS)
+ wrench_menu_.reset(
+ chromeos::WrenchMenuUI::CreateMenu2(wrench_menu_model_.get()));
+#else
wrench_menu_ = new WrenchMenu(browser_);
wrench_menu_->Init(wrench_menu_model_.get());
+#endif
for (size_t i = 0; i < menu_listeners_.size(); ++i)
menu_listeners_[i]->OnMenuOpened();
+#if defined(OS_CHROMEOS)
+ gfx::Point screen_loc;
+ views::View::ConvertPointToScreen(app_menu_, &screen_loc);
+ gfx::Rect bounds(screen_loc, app_menu_->size());
+ // TODO(oshima): Support RTL.
+ wrench_menu_->RunMenuAt(gfx::Point(bounds.right(), bounds.bottom()),
+ views::Menu2::ALIGN_TOPRIGHT);
+#else
wrench_menu_->RunMenu(app_menu_);
+#endif
if (destroyed_flag)
return;
diff --git a/chrome/browser/views/toolbar_view.h b/chrome/browser/views/toolbar_view.h
index e757f2d..f79ec25 100644
--- a/chrome/browser/views/toolbar_view.h
+++ b/chrome/browser/views/toolbar_view.h
@@ -27,7 +27,13 @@
class BrowserActionsContainer;
class Browser;
class Profile;
+#if defined(OS_CHROMEOS)
+namespace views {
+class Menu2;
+} // namespace views
+#else
class WrenchMenu;
+#endif
// The Browser Window's toolbar.
class ToolbarView : public AccessibleToolbarView,
@@ -193,7 +199,11 @@ class ToolbarView : public AccessibleToolbarView,
scoped_ptr<menus::SimpleMenuModel> wrench_menu_model_;
// Wrench menu.
+#if defined(OS_CHROMEOS)
+ scoped_ptr<views::Menu2> wrench_menu_;
+#else
scoped_refptr<WrenchMenu> wrench_menu_;
+#endif
// Vector of listeners to receive callbacks when the menu opens.
std::vector<views::MenuListener*> menu_listeners_;