Skip to content

Commit a5fb966

Browse files
committed
Merge pull request nwjs#369 from zhchbin/master
Prevent the "NativeMenuWin" from being a owner-draw control. Fix nwjs#349
2 parents dfdff62 + b92e1cc commit a5fb966

File tree

4 files changed

+115
-7
lines changed

4 files changed

+115
-7
lines changed

src/api/menu/menu.h

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,23 @@ class NativeWindowGtk;
5151
namespace nw {
5252
class NativeWindowWin;
5353
}
54+
55+
namespace ui {
56+
57+
// A derived class to override |HasIcons| to prevent the |NativeMenuWin| from
58+
// being a owner-draw control.
59+
// Note: this method maybe confused when the menuitem has an icon. I always
60+
// return |false| here, and set the icon by using |SetMenuItemBitmaps|.
61+
class NwMenuModel : public SimpleMenuModel {
62+
public:
63+
NwMenuModel(Delegate* delegate);
64+
65+
// Overridden from MenuModel:
66+
virtual bool HasIcons() const OVERRIDE;
67+
};
68+
69+
} // namespace ui
70+
5471
#endif
5572

5673
namespace content {
@@ -92,15 +109,18 @@ class Menu : public Base {
92109
#elif defined(OS_WIN)
93110
friend class nw::NativeWindowWin;
94111

95-
void Rebuild();
112+
void Rebuild(const gfx::NativeMenu *parent_menu = NULL);
96113

97114
// Flag to indicate the menu has been modified since last show, so we should
98115
// rebuild the menu before next show.
99116
bool is_menu_modified_;
100117

101118
scoped_ptr<MenuDelegate> menu_delegate_;
102-
scoped_ptr<ui::SimpleMenuModel> menu_model_;
119+
scoped_ptr<ui::NwMenuModel> menu_model_;
103120
scoped_ptr<views::NativeMenuWin> menu_;
121+
122+
// A container for the handles of the icon bitmap.
123+
std::vector<HBITMAP> icon_bitmaps_;
104124
#endif
105125

106126
DISALLOW_COPY_AND_ASSIGN(Menu);

src/api/menu/menu_win.cc

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,67 @@
2121
#include "content/nw/src/api/menu/menu.h"
2222

2323
#include "base/values.h"
24+
#include "content/nw/src/api/dispatcher_host.h"
2425
#include "content/nw/src/api/menuitem/menuitem.h"
2526
#include "content/nw/src/browser/native_window_win.h"
2627
#include "content/nw/src/nw_shell.h"
2728
#include "content/public/browser/web_contents.h"
2829
#include "content/public/browser/web_contents_view.h"
30+
#include "skia/ext/image_operations.h"
31+
#include "ui/gfx/gdi_util.h"
32+
#include "ui/gfx/icon_util.h"
2933
#include "ui/views/controls/menu/menu_2.h"
3034
#include "ui/views/widget/widget.h"
3135

36+
namespace {
37+
38+
HBITMAP GetNativeBitmapFromSkBitmap(const SkBitmap& bitmap) {
39+
int width = bitmap.width();
40+
int height = bitmap.height();
41+
42+
BITMAPV4HEADER native_bitmap_header;
43+
gfx::CreateBitmapV4Header(width, height, &native_bitmap_header);
44+
45+
HDC dc = ::GetDC(NULL);
46+
void* bits;
47+
HBITMAP native_bitmap = ::CreateDIBSection(dc,
48+
reinterpret_cast<BITMAPINFO*>(&native_bitmap_header),
49+
DIB_RGB_COLORS,
50+
&bits,
51+
NULL,
52+
0);
53+
DCHECK(native_bitmap);
54+
::ReleaseDC(NULL, dc);
55+
bitmap.copyPixelsTo(bits, width * height * 4, width * 4);
56+
return native_bitmap;
57+
}
58+
59+
} // namespace
60+
61+
62+
namespace ui {
63+
64+
NwMenuModel::NwMenuModel(Delegate* delegate) : SimpleMenuModel(delegate) {
65+
}
66+
67+
bool NwMenuModel::HasIcons() const {
68+
// Always return false, see the comment about |NwMenuModel|.
69+
return false;
70+
}
71+
72+
} // namespace ui
73+
3274
namespace api {
3375

76+
// The width of the icon for the menuitem
77+
static const int kIconWidth = 16;
78+
// The height of the icon for the menuitem
79+
static const int kIconHeight = 16;
80+
3481
void Menu::Create(const base::DictionaryValue& option) {
3582
is_menu_modified_ = true;
3683
menu_delegate_.reset(new MenuDelegate(dispatcher_host()));
37-
menu_model_.reset(new ui::SimpleMenuModel(menu_delegate_.get()));
84+
menu_model_.reset(new ui::NwMenuModel(menu_delegate_.get()));
3885
menu_.reset(new views::NativeMenuWin(menu_model_.get(), NULL));
3986

4087
std::string type;
@@ -43,6 +90,9 @@ void Menu::Create(const base::DictionaryValue& option) {
4390
}
4491

4592
void Menu::Destroy() {
93+
for (size_t index = 0; index < icon_bitmaps_.size(); ++index) {
94+
::DeleteObject(icon_bitmaps_[index]);
95+
}
4696
}
4797

4898
void Menu::Append(MenuItem* menu_item) {
@@ -90,10 +140,49 @@ void Menu::Popup(int x, int y, content::Shell* shell) {
90140
views::Menu2::ALIGN_TOPLEFT);
91141
}
92142

93-
void Menu::Rebuild() {
143+
void Menu::Rebuild(const gfx::NativeMenu *parent_menu) {
94144
if (is_menu_modified_) {
95145
// Refresh menu before show.
96146
menu_->Rebuild();
147+
for (size_t index = 0; index < icon_bitmaps_.size(); ++index) {
148+
::DeleteObject(icon_bitmaps_[index]);
149+
}
150+
icon_bitmaps_.clear();
151+
152+
gfx::NativeMenu native_menu = parent_menu == NULL ?
153+
menu_->GetNativeMenu() : *parent_menu;
154+
155+
int first_item_index =
156+
menu_model_->GetFirstItemIndex(native_menu);
157+
for (int menu_index = first_item_index;
158+
menu_index < first_item_index + menu_model_->GetItemCount();
159+
++menu_index) {
160+
int model_index = menu_index - first_item_index;
161+
int command_id = menu_model_->GetCommandIdAt(model_index);
162+
163+
if (menu_model_->GetTypeAt(model_index) == ui::MenuModel::TYPE_COMMAND ||
164+
menu_model_->GetTypeAt(model_index) == ui::MenuModel::TYPE_SUBMENU) {
165+
gfx::Image icon;
166+
menu_model_->GetIconAt(model_index, &icon);
167+
if (!icon.IsEmpty()) {
168+
SkBitmap resized_bitmap =
169+
skia::ImageOperations::Resize(*icon.ToSkBitmap(),
170+
skia::ImageOperations::RESIZE_GOOD,
171+
kIconWidth,
172+
kIconHeight);
173+
HBITMAP icon_bitmap = GetNativeBitmapFromSkBitmap(resized_bitmap);
174+
::SetMenuItemBitmaps(native_menu, command_id, MF_BYCOMMAND,
175+
icon_bitmap, icon_bitmap);
176+
icon_bitmaps_.push_back(icon_bitmap);
177+
}
178+
}
179+
180+
MenuItem* item = dispatcher_host()->GetApiObject<MenuItem>(command_id);
181+
if (item != NULL && item->submenu_) {
182+
item->submenu_->Rebuild(&native_menu);
183+
}
184+
}
185+
97186
is_menu_modified_ = false;
98187
}
99188
}

src/api/menuitem/menuitem.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ MenuItem.prototype.__defineSetter__('submenu', function(val) {
151151
throw new String("'submenu' property requries a valid Menu");
152152

153153
v8_util.setHiddenValue(this, 'submenu', val);
154-
nw.callObjectMethod(this, 'SetMenu', [ val.id ]);
154+
nw.callObjectMethod(this, 'SetSubmenu', [ val.id ]);
155155
});
156156

157157
MenuItem.prototype.handleEvent = function(ev) {

src/browser/native_window_win.cc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -387,8 +387,7 @@ void NativeWindowWin::SetMenu(api::Menu* menu) {
387387
menu->Rebuild();
388388

389389
// menu is api::Menu, menu->menu_ is NativeMenuWin,
390-
// we use menu->menu_->menu() to get real HMENU, ugly here.
391-
::SetMenu(window_->GetNativeWindow(), menu->menu_->menu());
390+
::SetMenu(window_->GetNativeWindow(), menu->menu_->GetNativeMenu());
392391
}
393392

394393
void NativeWindowWin::SetTitle(const std::string& title) {

0 commit comments

Comments
 (0)