diff options
-rw-r--r-- | chrome/browser/back_forward_menu_model.cc | 30 | ||||
-rw-r--r-- | chrome/browser/back_forward_menu_model.h | 35 | ||||
-rw-r--r-- | chrome/browser/back_forward_menu_model_unittest.cc | 24 | ||||
-rw-r--r-- | chrome/browser/back_forward_menu_model_views.cc | 96 | ||||
-rw-r--r-- | chrome/browser/back_forward_menu_model_views.h | 44 | ||||
-rw-r--r-- | chrome/browser/gtk/back_forward_menu_model_gtk.cc | 11 | ||||
-rw-r--r-- | chrome/browser/views/toolbar_view.cc | 9 | ||||
-rw-r--r-- | views/controls/button/button_dropdown.cc | 33 | ||||
-rw-r--r-- | views/controls/button/button_dropdown.h | 9 | ||||
-rw-r--r-- | views/controls/menu/menu_2.h | 3 | ||||
-rw-r--r-- | views/controls/menu/native_menu_win.cc | 166 | ||||
-rw-r--r-- | views/controls/menu/native_menu_win.h | 4 |
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; |