summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/back_forward_menu_model.cc30
-rw-r--r--chrome/browser/back_forward_menu_model.h35
-rw-r--r--chrome/browser/back_forward_menu_model_unittest.cc24
-rw-r--r--chrome/browser/back_forward_menu_model_views.cc96
-rw-r--r--chrome/browser/back_forward_menu_model_views.h44
-rw-r--r--chrome/browser/gtk/back_forward_menu_model_gtk.cc11
-rw-r--r--chrome/browser/views/toolbar_view.cc9
-rw-r--r--views/controls/button/button_dropdown.cc33
-rw-r--r--views/controls/button/button_dropdown.h9
-rw-r--r--views/controls/menu/menu_2.h3
-rw-r--r--views/controls/menu/native_menu_win.cc166
-rw-r--r--views/controls/menu/native_menu_win.h4
12 files changed, 313 insertions, 151 deletions
diff --git a/chrome/browser/back_forward_menu_model.cc b/chrome/browser/back_forward_menu_model.cc
index e623ada..984d06b 100644
--- a/chrome/browser/back_forward_menu_model.cc
+++ b/chrome/browser/back_forward_menu_model.cc
@@ -7,6 +7,7 @@
#include "chrome/browser/back_forward_menu_model.h"
#include "app/l10n_util.h"
+#include "app/resource_bundle.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/metrics/user_metrics.h"
#include "chrome/browser/tab_contents/navigation_controller.h"
@@ -14,16 +15,24 @@
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/url_constants.h"
#include "grit/generated_resources.h"
+#include "grit/theme_resources.h"
#include "net/base/registry_controlled_domain.h"
const int BackForwardMenuModel::kMaxHistoryItems = 12;
const int BackForwardMenuModel::kMaxChapterStops = 5;
+BackForwardMenuModel::BackForwardMenuModel(Browser* browser,
+ ModelType model_type)
+ : browser_(browser),
+ test_tab_contents_(NULL),
+ model_type_(model_type) {
+}
+
int BackForwardMenuModel::GetHistoryItemCount() const {
TabContents* contents = GetTabContents();
int items = 0;
- if (model_type_ == FORWARD_MENU_DELEGATE) {
+ if (model_type_ == FORWARD_MENU) {
// Only count items from n+1 to end (if n is current entry)
items = contents->controller().entry_count() -
contents->controller().GetCurrentEntryIndex() - 1;
@@ -47,7 +56,7 @@ int BackForwardMenuModel::GetChapterStopCount(int history_items) const {
if (history_items == kMaxHistoryItems) {
int chapter_id = current_entry;
- if (model_type_ == FORWARD_MENU_DELEGATE) {
+ if (model_type_ == FORWARD_MENU) {
chapter_id += history_items;
} else {
chapter_id -= history_items;
@@ -55,7 +64,7 @@ int BackForwardMenuModel::GetChapterStopCount(int history_items) const {
do {
chapter_id = GetIndexOfNextChapterStop(chapter_id,
- model_type_ == FORWARD_MENU_DELEGATE);
+ model_type_ == FORWARD_MENU);
if (chapter_id != -1)
++chapter_stops;
} while (chapter_id != -1 && chapter_stops < kMaxChapterStops);
@@ -213,14 +222,17 @@ std::wstring BackForwardMenuModel::GetItemLabel(int menu_id) const {
const SkBitmap& BackForwardMenuModel::GetItemIcon(int menu_id) const {
DCHECK(ItemHasIcon(menu_id));
+ if (menu_id == GetTotalItemCount()) {
+ return *ResourceBundle::GetSharedInstance().GetBitmapNamed(
+ IDR_HISTORY_FAVICON);
+ }
+
NavigationEntry* entry = GetNavigationEntry(menu_id);
return entry->favicon().bitmap();
}
bool BackForwardMenuModel::ItemHasIcon(int menu_id) const {
- // Using "id" not "id - 1" because the last item "Show Full History"
- // doesn't have an icon.
- return menu_id < GetTotalItemCount() && !IsSeparator(menu_id);
+ return menu_id - 1 < GetTotalItemCount() && !IsSeparator(menu_id);
}
bool BackForwardMenuModel::ItemHasCommand(int menu_id) const {
@@ -245,7 +257,7 @@ int BackForwardMenuModel::MenuIdToNavEntryIndex(int menu_id) const {
// Convert anything above the History items separator.
if (menu_id <= history_items) {
- if (model_type_ == FORWARD_MENU_DELEGATE) {
+ if (model_type_ == FORWARD_MENU) {
// The |menu_id| is relative to our current position, so we need to add.
menu_id += contents->controller().GetCurrentEntryIndex();
} else {
@@ -262,7 +274,7 @@ int BackForwardMenuModel::MenuIdToNavEntryIndex(int menu_id) const {
// This menu item is a chapter stop located between the two separators.
menu_id = FindChapterStop(history_items,
- model_type_ == FORWARD_MENU_DELEGATE,
+ model_type_ == FORWARD_MENU,
menu_id - history_items - 1 - 1);
return menu_id;
@@ -278,7 +290,7 @@ std::wstring BackForwardMenuModel::BuildActionName(
DCHECK(!action.empty());
DCHECK(index >= -1);
std::wstring metric_string;
- if (model_type_ == FORWARD_MENU_DELEGATE)
+ if (model_type_ == FORWARD_MENU)
metric_string += L"ForwardMenu_";
else
metric_string += L"BackMenu_";
diff --git a/chrome/browser/back_forward_menu_model.h b/chrome/browser/back_forward_menu_model.h
index 7d0f31c..9f85a7e 100644
--- a/chrome/browser/back_forward_menu_model.h
+++ b/chrome/browser/back_forward_menu_model.h
@@ -26,15 +26,11 @@ class BackForwardMenuModel {
// These are IDs used to identify individual UI elements within the
// browser window using View::GetViewByID.
enum ModelType {
- FORWARD_MENU_DELEGATE = 1,
- BACKWARD_MENU_DELEGATE = 2
+ FORWARD_MENU = 1,
+ BACKWARD_MENU = 2
};
- // Factory function. Defined in back_forward_menu_model_{platform}.cc.
- // This is only used in unit tests. In the browser we use the platform-
- // specific constructors directly.
- static BackForwardMenuModel* Create(Browser* browser, ModelType model_type);
-
+ BackForwardMenuModel(Browser* browser, ModelType model_type);
virtual ~BackForwardMenuModel() { }
// Returns how many history items the menu should show. For example, if the
@@ -108,10 +104,12 @@ class BackForwardMenuModel {
// Does the item does something when you click on it?
bool ItemHasCommand(int menu_id) const;
+#ifdef UNIT_TEST
// Allows the unit test to use its own dummy tab contents.
void set_test_tab_contents(TabContents* test_tab_contents) {
test_tab_contents_ = test_tab_contents;
}
+#endif
// Allow the unit test to use the "Show Full History" label.
std::wstring GetShowFullHistoryLabel() const;
@@ -128,20 +126,6 @@ class BackForwardMenuModel {
static const int kMaxChapterStops;
protected:
- BackForwardMenuModel()
- : browser_(NULL),
- test_tab_contents_(NULL),
- model_type_(FORWARD_MENU_DELEGATE) {}
-
- Browser* browser_;
-
- // The unit tests will provide their own TabContents to use.
- TabContents* test_tab_contents_;
-
- // Represents whether this is the delegate for the forward button or the
- // back button.
- ModelType model_type_;
-
// Converts a menu item id, as passed in through one of the menu delegate
// functions and converts it into an absolute index into the
// NavigationEntryList vector. |menu_id| can point to a separator, or the
@@ -157,6 +141,15 @@ class BackForwardMenuModel {
// An index of -1 means no index.
std::wstring BuildActionName(const std::wstring& name, int index) const;
+ Browser* browser_;
+
+ // The unit tests will provide their own TabContents to use.
+ TabContents* test_tab_contents_;
+
+ // Represents whether this is the delegate for the forward button or the
+ // back button.
+ ModelType model_type_;
+
private:
DISALLOW_COPY_AND_ASSIGN(BackForwardMenuModel);
};
diff --git a/chrome/browser/back_forward_menu_model_unittest.cc b/chrome/browser/back_forward_menu_model_unittest.cc
index 708847d..241a95c 100644
--- a/chrome/browser/back_forward_menu_model_unittest.cc
+++ b/chrome/browser/back_forward_menu_model_unittest.cc
@@ -65,12 +65,12 @@ class BackFwdMenuModelTest : public RenderViewHostTestHarness {
};
TEST_F(BackFwdMenuModelTest, BasicCase) {
- scoped_ptr<BackForwardMenuModel> back_model(BackForwardMenuModel::Create(
- NULL, BackForwardMenuModel::BACKWARD_MENU_DELEGATE));
+ scoped_ptr<BackForwardMenuModel> back_model(new BackForwardMenuModel(
+ NULL, BackForwardMenuModel::BACKWARD_MENU));
back_model->set_test_tab_contents(contents());
- scoped_ptr<BackForwardMenuModel> forward_model(BackForwardMenuModel::Create(
- NULL, BackForwardMenuModel::FORWARD_MENU_DELEGATE));
+ scoped_ptr<BackForwardMenuModel> forward_model(new BackForwardMenuModel(
+ NULL, BackForwardMenuModel::FORWARD_MENU));
forward_model->set_test_tab_contents(contents());
EXPECT_EQ(0, back_model->GetTotalItemCount());
@@ -132,12 +132,12 @@ TEST_F(BackFwdMenuModelTest, BasicCase) {
}
TEST_F(BackFwdMenuModelTest, MaxItemsTest) {
- scoped_ptr<BackForwardMenuModel> back_model(BackForwardMenuModel::Create(
- NULL, BackForwardMenuModel::BACKWARD_MENU_DELEGATE));
+ scoped_ptr<BackForwardMenuModel> back_model(new BackForwardMenuModel(
+ NULL, BackForwardMenuModel::BACKWARD_MENU));
back_model->set_test_tab_contents(contents());
- scoped_ptr<BackForwardMenuModel> forward_model(BackForwardMenuModel::Create(
- NULL, BackForwardMenuModel::FORWARD_MENU_DELEGATE));
+ scoped_ptr<BackForwardMenuModel> forward_model(new BackForwardMenuModel(
+ NULL, BackForwardMenuModel::FORWARD_MENU));
forward_model->set_test_tab_contents(contents());
// Seed the controller with 32 URLs
@@ -214,12 +214,12 @@ TEST_F(BackFwdMenuModelTest, MaxItemsTest) {
}
TEST_F(BackFwdMenuModelTest, ChapterStops) {
- scoped_ptr<BackForwardMenuModel> back_model(BackForwardMenuModel::Create(
- NULL, BackForwardMenuModel::BACKWARD_MENU_DELEGATE));
+ scoped_ptr<BackForwardMenuModel> back_model(new BackForwardMenuModel(
+ NULL, BackForwardMenuModel::BACKWARD_MENU));
back_model->set_test_tab_contents(contents());
- scoped_ptr<BackForwardMenuModel> forward_model(BackForwardMenuModel::Create(
- NULL, BackForwardMenuModel::FORWARD_MENU_DELEGATE));
+ scoped_ptr<BackForwardMenuModel> forward_model(new BackForwardMenuModel(
+ NULL, BackForwardMenuModel::FORWARD_MENU));
forward_model->set_test_tab_contents(contents());
// Seed the controller with 32 URLs.
diff --git a/chrome/browser/back_forward_menu_model_views.cc b/chrome/browser/back_forward_menu_model_views.cc
index f509274..fd21241 100644
--- a/chrome/browser/back_forward_menu_model_views.cc
+++ b/chrome/browser/back_forward_menu_model_views.cc
@@ -4,59 +4,95 @@
#include "chrome/browser/back_forward_menu_model_views.h"
+#include "chrome/app/chrome_dll_resource.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/metrics/user_metrics.h"
-#include "grit/generated_resources.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "views/controls/menu/menu_2.h"
+#include "views/widget/widget.h"
-// static
-BackForwardMenuModel* BackForwardMenuModel::Create(Browser* browser,
- ModelType model_type) {
- return new BackForwardMenuModelViews(browser, model_type);
-}
+////////////////////////////////////////////////////////////////////////////////
+// BackForwardMenuModelViews, public:
BackForwardMenuModelViews::BackForwardMenuModelViews(Browser* browser,
- ModelType model_type) {
- browser_ = browser;
- model_type_ = model_type;
+ ModelType model_type,
+ views::Widget* frame)
+ : BackForwardMenuModel(browser, model_type),
+ frame_(frame) {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// BackForwardMenuModelViews, views::Menu2Model implementation:
+
+bool BackForwardMenuModelViews::HasIcons() const {
+ return true;
}
-std::wstring BackForwardMenuModelViews::GetLabel(int menu_id) const {
- return GetItemLabel(menu_id);
+int BackForwardMenuModelViews::GetItemCount() const {
+ return GetTotalItemCount();
}
-const SkBitmap& BackForwardMenuModelViews::GetIcon(int menu_id) const {
- // Return NULL if the item doesn't have an icon
- if (!ItemHasIcon(menu_id))
- return GetEmptyIcon();
+views::Menu2Model::ItemType BackForwardMenuModelViews::GetTypeAt(
+ int index) const {
+ if (IsSeparator(GetCommandIdAt(index)))
+ return views::Menu2Model::TYPE_SEPARATOR;
+ return views::Menu2Model::TYPE_COMMAND;
+}
+
+int BackForwardMenuModelViews::GetCommandIdAt(int index) const {
+ return index + 1;
+}
+
+std::wstring BackForwardMenuModelViews::GetLabelAt(int index) const {
+ return GetItemLabel(GetCommandIdAt(index));
+}
- return GetItemIcon(menu_id);
+bool BackForwardMenuModelViews::IsLabelDynamicAt(int index) const {
+ return false;
}
-bool BackForwardMenuModelViews::IsItemSeparator(int menu_id) const {
- return IsSeparator(menu_id);
+bool BackForwardMenuModelViews::GetAcceleratorAt(
+ int index,
+ views::Accelerator* accelerator) const {
+ // Look up the history accelerator.
+ if (GetCommandIdAt(index) == GetTotalItemCount())
+ return frame_->GetAccelerator(IDC_SHOW_HISTORY, accelerator);
+ return false;
}
-bool BackForwardMenuModelViews::HasIcon(int menu_id) const {
- return ItemHasIcon(menu_id);
+bool BackForwardMenuModelViews::IsItemCheckedAt(int index) const {
+ return false;
}
-bool BackForwardMenuModelViews::SupportsCommand(int menu_id) const {
- return ItemHasCommand(menu_id);
+int BackForwardMenuModelViews::GetGroupIdAt(int index) const {
+ return -1;
}
-bool BackForwardMenuModelViews::IsCommandEnabled(int menu_id) const {
- return ItemHasCommand(menu_id);
+bool BackForwardMenuModelViews::GetIconAt(int index, SkBitmap* icon) const {
+ if (ItemHasIcon(GetCommandIdAt(index))) {
+ *icon = GetItemIcon(GetCommandIdAt(index));
+ return true;
+ }
+ return false;
}
-void BackForwardMenuModelViews::ExecuteCommand(int menu_id) {
- ExecuteCommandById(menu_id);
+bool BackForwardMenuModelViews::IsEnabledAt(int index) const {
+ return true;
+}
+
+views::Menu2Model* BackForwardMenuModelViews::GetSubmenuModelAt(
+ int index) const {
+ return NULL;
+}
+
+void BackForwardMenuModelViews::HighlightChangedTo(int index) {
+}
+
+void BackForwardMenuModelViews::ActivatedAt(int index) {
+ ExecuteCommandById(GetCommandIdAt(index));
}
void BackForwardMenuModelViews::MenuWillShow() {
UserMetrics::RecordComputedAction(BuildActionName(L"Popup", -1),
browser_->profile());
}
-
-int BackForwardMenuModelViews::GetItemCount() const {
- return GetTotalItemCount();
-}
diff --git a/chrome/browser/back_forward_menu_model_views.h b/chrome/browser/back_forward_menu_model_views.h
index 19a36ace..e97a39a 100644
--- a/chrome/browser/back_forward_menu_model_views.h
+++ b/chrome/browser/back_forward_menu_model_views.h
@@ -8,27 +8,45 @@
#include "base/basictypes.h"
#include "chrome/browser/back_forward_menu_model.h"
-#include "views/controls/menu/menu.h"
+#include "views/controls/menu/menu_2.h"
class SkBitmap;
+namespace views {
+class Widget;
+}
+
class BackForwardMenuModelViews : public BackForwardMenuModel,
- public views::Menu::Delegate {
+ public views::Menu2Model {
public:
- BackForwardMenuModelViews(Browser* browser, ModelType model_type);
-
- // Menu::Delegate
- virtual std::wstring GetLabel(int menu_id) const;
- virtual const SkBitmap& GetIcon(int menu_id) const;
- virtual bool SupportsCommand(int menu_id) const;
- virtual bool IsCommandEnabled(int menu_id) const;
- virtual bool IsItemSeparator(int menu_id) const;
- virtual bool HasIcon(int menu_id) const;
- virtual void ExecuteCommand(int menu_id);
- virtual void MenuWillShow();
+ // Construct a BackForwardMenuModel. |frame| is used to locate the accelerator
+ // for the history item.
+ BackForwardMenuModelViews(Browser* browser,
+ ModelType model_type,
+ views::Widget* frame);
+
+ // Overridden from views::Menu2Model:
+ virtual bool HasIcons() const;
virtual int GetItemCount() const;
+ virtual ItemType GetTypeAt(int index) const;
+ virtual int GetCommandIdAt(int index) const;
+ virtual std::wstring GetLabelAt(int index) const;
+ virtual bool IsLabelDynamicAt(int index) const;
+ virtual bool GetAcceleratorAt(int index,
+ views::Accelerator* accelerator) const;
+ virtual bool IsItemCheckedAt(int index) const;
+ virtual int GetGroupIdAt(int index) const;
+ virtual bool GetIconAt(int index, SkBitmap* icon) const;
+ virtual bool IsEnabledAt(int index) const;
+ virtual Menu2Model* GetSubmenuModelAt(int index) const;
+ virtual void HighlightChangedTo(int index);
+ virtual void ActivatedAt(int index);
+ virtual void MenuWillShow();
private:
+ // The frame we ask about accelerator info.
+ views::Widget* frame_;
+
DISALLOW_COPY_AND_ASSIGN(BackForwardMenuModelViews);
};
diff --git a/chrome/browser/gtk/back_forward_menu_model_gtk.cc b/chrome/browser/gtk/back_forward_menu_model_gtk.cc
index 4e363d0..8bce806 100644
--- a/chrome/browser/gtk/back_forward_menu_model_gtk.cc
+++ b/chrome/browser/gtk/back_forward_menu_model_gtk.cc
@@ -7,18 +7,11 @@
#include "base/string_util.h"
#include "chrome/browser/gtk/back_forward_button_gtk.h"
-// static
-BackForwardMenuModel* BackForwardMenuModel::Create(Browser* browser,
- ModelType model_type) {
- return new BackForwardMenuModelGtk(browser, model_type, NULL);
-}
-
BackForwardMenuModelGtk::BackForwardMenuModelGtk(Browser* browser,
ModelType model_type,
BackForwardButtonGtk* button)
- : button_(button) {
- browser_ = browser;
- model_type_ = model_type;
+ : BackForwardMenuModel(browser, model_type),
+ button_(button) {
}
int BackForwardMenuModelGtk::GetItemCount() const {
diff --git a/chrome/browser/views/toolbar_view.cc b/chrome/browser/views/toolbar_view.cc
index 02f31fc..8a254e7 100644
--- a/chrome/browser/views/toolbar_view.cc
+++ b/chrome/browser/views/toolbar_view.cc
@@ -179,10 +179,6 @@ ToolbarView::ToolbarView(Browser* browser)
browser_->command_updater()->AddCommandObserver(IDC_RELOAD, this);
browser_->command_updater()->AddCommandObserver(IDC_HOME, this);
browser_->command_updater()->AddCommandObserver(IDC_STAR, this);
- back_menu_model_.reset(new BackForwardMenuModelViews(
- browser, BackForwardMenuModel::BACKWARD_MENU_DELEGATE));
- forward_menu_model_.reset(new BackForwardMenuModelViews(
- browser, BackForwardMenuModel::FORWARD_MENU_DELEGATE));
if (browser->type() == Browser::TYPE_NORMAL)
display_mode_ = DISPLAYMODE_NORMAL;
else
@@ -199,6 +195,11 @@ ToolbarView::~ToolbarView() {
}
void ToolbarView::Init(Profile* profile) {
+ back_menu_model_.reset(new BackForwardMenuModelViews(
+ browser_, BackForwardMenuModel::BACKWARD_MENU, GetWidget()));
+ forward_menu_model_.reset(new BackForwardMenuModelViews(
+ browser_, BackForwardMenuModel::FORWARD_MENU, GetWidget()));
+
// Create all the individual Views in the Toolbar.
CreateLeftSideControls();
CreateCenterStack(profile);
diff --git a/views/controls/button/button_dropdown.cc b/views/controls/button/button_dropdown.cc
index f12e276..e59b01c 100644
--- a/views/controls/button/button_dropdown.cc
+++ b/views/controls/button/button_dropdown.cc
@@ -23,9 +23,9 @@ static const int kMenuTimerDelay = 500;
////////////////////////////////////////////////////////////////////////////////
ButtonDropDown::ButtonDropDown(ButtonListener* listener,
- Menu::Delegate* menu_delegate)
+ Menu2Model* model)
: ImageButton(listener),
- menu_delegate_(menu_delegate),
+ model_(model),
y_position_on_lbuttondown_(0),
ALLOW_THIS_IN_INITIALIZER_LIST(show_menu_factory_(this)) {
}
@@ -112,7 +112,7 @@ void ButtonDropDown::ShowContextMenu(int x, int y, bool is_mouse_gesture) {
}
void ButtonDropDown::ShowDropDownMenu(gfx::NativeView window) {
- if (menu_delegate_) {
+ if (model_) {
gfx::Rect lb = GetLocalBounds(true);
// Both the menu position and the menu anchor type change if the UI layout
@@ -122,10 +122,6 @@ void ButtonDropDown::ShowDropDownMenu(gfx::NativeView window) {
if (UILayoutIsRightToLeft())
menu_position.Offset(lb.width() - 1, 0);
- Menu::AnchorPoint anchor = Menu::TOPLEFT;
- if (UILayoutIsRightToLeft())
- anchor = Menu::TOPRIGHT;
-
View::ConvertPointToScreen(this, &menu_position);
#if defined(OS_WIN)
@@ -137,24 +133,11 @@ void ButtonDropDown::ShowDropDownMenu(gfx::NativeView window) {
if (menu_position.x() < left_bound)
menu_position.set_x(left_bound);
- scoped_ptr<Menu> menu(Menu::Create(menu_delegate_, anchor, window));
-
- // ID's for AppendMenu is 1-based because RunMenu will ignore the user
- // selection if id=0 is selected (0 = NO-OP) so we add 1 here and subtract 1
- // in the handlers above to get the actual index
- int item_count = menu_delegate_->GetItemCount();
- for (int i = 0; i < item_count; i++) {
- if (menu_delegate_->IsItemSeparator(i + 1)) {
- menu->AppendSeparator();
- } else {
- if (menu_delegate_->HasIcon(i + 1))
- menu->AppendMenuItemWithIcon(i + 1, L"", SkBitmap());
- else
- menu->AppendMenuItem(i+1, L"", Menu::NORMAL);
- }
- }
-
- menu->RunMenuAt(menu_position.x(), menu_position.y());
+ menu_.reset(new Menu2(model_));
+ Menu2::Alignment align = Menu2::ALIGN_TOPLEFT;
+ if (UILayoutIsRightToLeft())
+ align = Menu2::ALIGN_TOPLEFT;
+ menu_->RunMenuAt(menu_position, align);
// Need to explicitly clear mouse handler so that events get sent
// properly after the menu finishes running. If we don't do this, then
diff --git a/views/controls/button/button_dropdown.h b/views/controls/button/button_dropdown.h
index 4ff4b24..e0ba3e3 100644
--- a/views/controls/button/button_dropdown.h
+++ b/views/controls/button/button_dropdown.h
@@ -7,7 +7,7 @@
#include "base/task.h"
#include "views/controls/button/image_button.h"
-#include "views/controls/menu/menu.h"
+#include "views/controls/menu/menu_2.h"
namespace views {
@@ -21,7 +21,7 @@ namespace views {
////////////////////////////////////////////////////////////////////////////////
class ButtonDropDown : public ImageButton {
public:
- ButtonDropDown(ButtonListener* listener, Menu::Delegate* menu_delegate);
+ ButtonDropDown(ButtonListener* listener, Menu2Model* model);
virtual ~ButtonDropDown();
// Accessibility accessors, overridden from View.
@@ -45,8 +45,9 @@ class ButtonDropDown : public ImageButton {
// Internal function to show the dropdown menu
void ShowDropDownMenu(gfx::NativeView window);
- // Specifies who to delegate populating the menu
- Menu::Delegate* menu_delegate_;
+ // The model that populates the attached menu.
+ Menu2Model* model_;
+ scoped_ptr<Menu2> menu_;
// Y position of mouse when left mouse button is pressed
int y_position_on_lbuttondown_;
diff --git a/views/controls/menu/menu_2.h b/views/controls/menu/menu_2.h
index 68c4833..665b807 100644
--- a/views/controls/menu/menu_2.h
+++ b/views/controls/menu/menu_2.h
@@ -94,6 +94,9 @@ class Menu2Model {
// Called when the item at the specified index has been activated.
virtual void ActivatedAt(int index) = 0;
+ // Called when the menu is about to be shown.
+ virtual void MenuWillShow() {}
+
// Retrieves the model and index that contains a specific command id. Returns
// true if an item with the specified command id is found. |model| is inout,
// and specifies the model to start searching from.
diff --git a/views/controls/menu/native_menu_win.cc b/views/controls/menu/native_menu_win.cc
index c5f8331..dc07dad 100644
--- a/views/controls/menu/native_menu_win.cc
+++ b/views/controls/menu/native_menu_win.cc
@@ -4,15 +4,32 @@
#include "views/controls/menu/native_menu_win.h"
+#include "app/gfx/canvas.h"
+#include "app/gfx/font.h"
#include "app/l10n_util.h"
#include "app/l10n_util_win.h"
#include "base/logging.h"
#include "base/stl_util-inl.h"
+#include "third_party/skia/include/core/SkBitmap.h"
#include "views/accelerator.h"
#include "views/controls/menu/menu_2.h"
namespace views {
+// The width of an icon, including the pixels between the icon and
+// the item label.
+static const int kIconWidth = 23;
+// Margins between the top of the item and the label.
+static const int kItemTopMargin = 3;
+// Margins between the bottom of the item and the label.
+static const int kItemBottomMargin = 4;
+// Margins between the left of the item and the icon.
+static const int kItemLeftMargin = 4;
+// Margins between the right of the item and the label.
+static const int kItemRightMargin = 10;
+// The width for displaying the sub-menu arrow.
+static const int kArrowWidth = 10;
+
struct NativeMenuWin::ItemData {
// The Windows API requires that whoever creates the menus must own the
// strings used for labels, and keep them around for the lifetime of the
@@ -21,6 +38,12 @@ struct NativeMenuWin::ItemData {
// Someone needs to own submenus, it may as well be us.
scoped_ptr<Menu2> submenu;
+
+ // We need a pointer back to the containing menu in various circumstances.
+ NativeMenuWin* native_menu_win;
+
+ // The index of the item within the menu's model.
+ int model_index;
};
// A window that receives messages from Windows relevant to the native menu
@@ -90,6 +113,10 @@ class NativeMenuWin::MenuHostWindow {
return w_param;
}
+ NativeMenuWin::ItemData* GetItemData(ULONG_PTR item_data) {
+ return reinterpret_cast<NativeMenuWin::ItemData*>(item_data);
+ }
+
// Called when the user selects a specific item.
void OnMenuCommand(int position, HMENU menu) {
NativeMenuWin* intergoat = GetNativeMenuWinFromHMENU(menu);
@@ -108,6 +135,112 @@ class NativeMenuWin::MenuHostWindow {
GetNativeMenuWinFromHMENU(menu)->model_->HighlightChangedTo(position);
}
+ // Called by Windows to measure the size of an owner-drawn menu item.
+ void OnMeasureItem(WPARAM w_param, MEASUREITEMSTRUCT* measure_item_struct) {
+ NativeMenuWin::ItemData* data = GetItemData(measure_item_struct->itemData);
+ if (data) {
+ gfx::Font font;
+ measure_item_struct->itemWidth = font.GetStringWidth(data->label) +
+ kIconWidth + kItemLeftMargin + kItemRightMargin -
+ GetSystemMetrics(SM_CXMENUCHECK);
+ if (data->submenu.get())
+ measure_item_struct->itemWidth += kArrowWidth;
+ // If the label contains an accelerator, make room for tab.
+ if (data->label.find(L'\t') != std::wstring::npos)
+ measure_item_struct->itemWidth += font.GetStringWidth(L" ");
+ measure_item_struct->itemHeight =
+ font.height() + kItemBottomMargin + kItemTopMargin;
+ } else {
+ // Measure separator size.
+ measure_item_struct->itemHeight = GetSystemMetrics(SM_CYMENU) / 2;
+ measure_item_struct->itemWidth = 0;
+ }
+ }
+
+ // Called by Windows to paint an owner-drawn menu item.
+ void OnDrawItem(UINT w_param, DRAWITEMSTRUCT* draw_item_struct) {
+ HDC dc = draw_item_struct->hDC;
+ COLORREF prev_bg_color, prev_text_color;
+
+ // Set background color and text color
+ if (draw_item_struct->itemState & ODS_SELECTED) {
+ prev_bg_color = SetBkColor(dc, GetSysColor(COLOR_HIGHLIGHT));
+ prev_text_color = SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ } else {
+ prev_bg_color = SetBkColor(dc, GetSysColor(COLOR_MENU));
+ if (draw_item_struct->itemState & ODS_DISABLED)
+ prev_text_color = SetTextColor(dc, GetSysColor(COLOR_GRAYTEXT));
+ else
+ prev_text_color = SetTextColor(dc, GetSysColor(COLOR_MENUTEXT));
+ }
+
+ if (draw_item_struct->itemData) {
+ NativeMenuWin::ItemData* data = GetItemData(draw_item_struct->itemData);
+ // Draw the background.
+ HBRUSH hbr = CreateSolidBrush(GetBkColor(dc));
+ FillRect(dc, &draw_item_struct->rcItem, hbr);
+ DeleteObject(hbr);
+
+ // Draw the label.
+ RECT rect = draw_item_struct->rcItem;
+ rect.top += kItemTopMargin;
+ // Should we add kIconWidth only when icon.width() != 0 ?
+ rect.left += kItemLeftMargin + kIconWidth;
+ rect.right -= kItemRightMargin;
+ UINT format = DT_TOP | DT_SINGLELINE;
+ // Check whether the mnemonics should be underlined.
+ BOOL underline_mnemonics;
+ SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &underline_mnemonics, 0);
+ if (!underline_mnemonics)
+ format |= DT_HIDEPREFIX;
+ gfx::Font font;
+ HGDIOBJ old_font = static_cast<HFONT>(SelectObject(dc, font.hfont()));
+ int fontsize = font.FontSize();
+
+ // If an accelerator is specified (with a tab delimiting the rest of the
+ // label from the accelerator), we have to justify the fist part on the
+ // left and the accelerator on the right.
+ // TODO(jungshik): This will break in RTL UI. Currently, he/ar use the
+ // window system UI font and will not hit here.
+ std::wstring label = data->label;
+ std::wstring accel;
+ std::wstring::size_type tab_pos = label.find(L'\t');
+ if (tab_pos != std::wstring::npos) {
+ accel = label.substr(tab_pos);
+ label = label.substr(0, tab_pos);
+ }
+ DrawTextEx(dc, const_cast<wchar_t*>(label.data()),
+ static_cast<int>(label.size()), &rect, format | DT_LEFT, NULL);
+ if (!accel.empty())
+ DrawTextEx(dc, const_cast<wchar_t*>(accel.data()),
+ static_cast<int>(accel.size()), &rect,
+ format | DT_RIGHT, NULL);
+ SelectObject(dc, old_font);
+
+ // Draw the icon after the label, otherwise it would be covered
+ // by the label.
+ SkBitmap icon;
+ if (data->native_menu_win->model_->GetIconAt(data->model_index, &icon)) {
+ gfx::Canvas canvas(icon.width(), icon.height(), false);
+ canvas.drawColor(SK_ColorBLACK, SkPorterDuff::kClear_Mode);
+ canvas.DrawBitmapInt(icon, 0, 0);
+ canvas.getTopPlatformDevice().drawToHDC(dc,
+ draw_item_struct->rcItem.left + kItemLeftMargin,
+ draw_item_struct->rcItem.top + (draw_item_struct->rcItem.bottom -
+ draw_item_struct->rcItem.top - icon.height()) / 2, NULL);
+ }
+
+ } else {
+ // Draw the separator
+ draw_item_struct->rcItem.top +=
+ (draw_item_struct->rcItem.bottom - draw_item_struct->rcItem.top) / 3;
+ DrawEdge(dc, &draw_item_struct->rcItem, EDGE_ETCHED, BF_TOP);
+ }
+
+ SetBkColor(dc, prev_bg_color);
+ SetTextColor(dc, prev_text_color);
+ }
+
bool ProcessWindowMessage(HWND window,
UINT message,
WPARAM w_param,
@@ -122,6 +255,14 @@ class NativeMenuWin::MenuHostWindow {
OnMenuSelect(LOWORD(w_param), reinterpret_cast<HMENU>(l_param));
*l_result = 0;
return true;
+ case WM_MEASUREITEM:
+ OnMeasureItem(w_param, reinterpret_cast<MEASUREITEMSTRUCT*>(l_param));
+ *l_result = 0;
+ return true;
+ case WM_DRAWITEM:
+ OnDrawItem(w_param, reinterpret_cast<DRAWITEMSTRUCT*>(l_param));
+ *l_result = 0;
+ return true;
// TODO(beng): bring over owner draw from old menu system.
}
return false;
@@ -213,7 +354,7 @@ void NativeMenuWin::UpdateStates() {
SetMenuItemLabel(menu_index, model_index,
model_->GetLabelAt(model_index));
}
- Menu2* submenu = items_.at(model_index)->submenu.get();
+ Menu2* submenu = items_[model_index]->submenu.get();
if (submenu)
submenu->UpdateStates();
}
@@ -242,9 +383,9 @@ void NativeMenuWin::AddMenuItemAt(int menu_index, int model_index) {
mii.fType = MFT_STRING;
else
mii.fType = MFT_OWNERDRAW;
- mii.dwItemData = reinterpret_cast<ULONG_PTR>(this);
ItemData* item_data = new ItemData;
+ item_data->label = std::wstring();
Menu2Model::ItemType type = model_->GetTypeAt(model_index);
if (type == Menu2Model::TYPE_SUBMENU) {
item_data->submenu.reset(new Menu2(model_->GetSubmenuModelAt(model_index)));
@@ -255,7 +396,10 @@ void NativeMenuWin::AddMenuItemAt(int menu_index, int model_index) {
mii.fType |= MFT_RADIOCHECK;
mii.wID = model_->GetCommandIdAt(model_index);
}
+ item_data->native_menu_win = this;
+ item_data->model_index = model_index;
items_.insert(items_.begin() + model_index, item_data);
+ mii.dwItemData = reinterpret_cast<ULONG_PTR>(item_data);
UpdateMenuItemInfoForString(&mii, model_index,
model_->GetLabelAt(model_index));
InsertMenuItem(menu_, menu_index, TRUE, &mii);
@@ -331,24 +475,6 @@ void NativeMenuWin::UpdateMenuItemInfoForString(
}
}
-NativeMenuWin* NativeMenuWin::GetMenuForCommandId(UINT command_id) const {
- // Menus can have nested submenus. In the views Menu system, each submenu is
- // wrapped in a NativeMenu instance, which may have a different model and
- // delegate from the parent menu. The trouble is, RunMenuAt is called on the
- // parent NativeMenuWin, and so it's not possible to assume that we can just
- // dispatch the command id returned by TrackPopupMenuEx to the parent's
- // delegate. For this reason, we stow a pointer on every menu item we create
- // to the NativeMenuWin that most closely contains it. Fortunately, Windows
- // provides GetMenuItemInfo, which can walk down the menu item tree from
- // the root |menu_| to find the data for a given item even if it's in a
- // submenu.
- MENUITEMINFO mii = {0};
- mii.cbSize = sizeof(mii);
- mii.fMask = MIIM_DATA;
- GetMenuItemInfo(menu_, command_id, FALSE, &mii);
- return reinterpret_cast<NativeMenuWin*>(mii.dwItemData);
-}
-
UINT NativeMenuWin::GetAlignmentFlags(int alignment) const {
bool rtl = l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT;
UINT alignment_flags = TPM_TOPALIGN;
diff --git a/views/controls/menu/native_menu_win.h b/views/controls/menu/native_menu_win.h
index c22313a..428f624 100644
--- a/views/controls/menu/native_menu_win.h
+++ b/views/controls/menu/native_menu_win.h
@@ -71,10 +71,6 @@ class NativeMenuWin : public MenuWrapper {
int model_index,
const std::wstring& label);
- // Returns the NativeMenuWin object that contains an item with the specified
- // command id. This function must only be called from RunMenuAt!
- NativeMenuWin* GetMenuForCommandId(UINT command_id) const;
-
// Returns the alignment flags to be passed to TrackPopupMenuEx, based on the
// supplied alignment and the UI text direction.
UINT GetAlignmentFlags(int alignment) const;