summaryrefslogtreecommitdiffstats
path: root/views/controls/menu/native_menu_win.cc
diff options
context:
space:
mode:
Diffstat (limited to 'views/controls/menu/native_menu_win.cc')
-rw-r--r--views/controls/menu/native_menu_win.cc166
1 files changed, 146 insertions, 20 deletions
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;