Skip to content

Commit a8a2ecb

Browse files
committed
Add proper icon and bitmap support for Windows menu items
Implements handling of menu item icons with transparency by converting images to HICON and drawing them onto a 32-bit ARGB bitmap with system menu background color. Ensures cleanup of icon and bitmap resources, and sets the bitmap on the menu item for correct display in Windows menus.
1 parent 10c3065 commit a8a2ecb

File tree

1 file changed

+89
-2
lines changed

1 file changed

+89
-2
lines changed

src/platform/windows/menu_windows.cpp

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include <windows.h>
2+
#include <cstring>
23
#include <functional>
34
#include <iostream>
45
#include <memory>
@@ -13,6 +14,8 @@
1314

1415
namespace nativeapi {
1516

17+
HICON ImageToHICON(const Image* image, int width, int height);
18+
1619
// Helper function to convert KeyboardAccelerator to Windows accelerator
1720
std::pair<UINT, UINT> ConvertAccelerator(const KeyboardAccelerator& accelerator) {
1821
UINT key = 0;
@@ -95,6 +98,8 @@ class MenuItem::Impl {
9598
MenuItemType type_;
9699
std::optional<std::string> label_;
97100
std::shared_ptr<Image> image_;
101+
HICON menu_icon_; // Stored icon for menu item with transparency support
102+
HBITMAP menu_bitmap_; // Stored bitmap with icon drawn on menu background
98103
std::optional<std::string> tooltip_;
99104
KeyboardAccelerator accelerator_;
100105
bool has_accelerator_;
@@ -110,6 +115,8 @@ class MenuItem::Impl {
110115
: id_(id),
111116
parent_menu_(parent_menu),
112117
type_(type),
118+
menu_icon_(nullptr),
119+
menu_bitmap_(nullptr),
113120
accelerator_("", KeyboardAccelerator::None),
114121
has_accelerator_(false),
115122
state_(MenuItemState::Unchecked),
@@ -134,6 +141,18 @@ class MenuItem::Impl {
134141
submenu_closed_listener_id_ = 0;
135142
}
136143

144+
// Clean up menu icon
145+
if (menu_icon_) {
146+
DestroyIcon(menu_icon_);
147+
menu_icon_ = nullptr;
148+
}
149+
150+
// Clean up menu bitmap
151+
if (menu_bitmap_) {
152+
DeleteObject(menu_bitmap_);
153+
menu_bitmap_ = nullptr;
154+
}
155+
137156
// Windows menu items are automatically cleaned up when the menu is
138157
// destroyed
139158
}
@@ -225,9 +244,77 @@ std::optional<std::string> MenuItem::GetLabel() const {
225244
}
226245

227246
void MenuItem::SetIcon(std::shared_ptr<Image> image) {
247+
// Clean up previous resources
248+
if (pimpl_->menu_icon_) {
249+
DestroyIcon(pimpl_->menu_icon_);
250+
pimpl_->menu_icon_ = nullptr;
251+
}
252+
if (pimpl_->menu_bitmap_) {
253+
DeleteObject(pimpl_->menu_bitmap_);
254+
pimpl_->menu_bitmap_ = nullptr;
255+
}
256+
228257
pimpl_->image_ = image;
229-
// Windows menu icons would require HBITMAP handling
230-
// This is a placeholder implementation
258+
259+
if (image && pimpl_->parent_menu_) {
260+
// Use 32x32 for menu icons
261+
const int iconSize = 32;
262+
263+
// Convert image to HICON first for proper transparency support
264+
HICON hIcon = ImageToHICON(image.get(), iconSize, iconSize);
265+
266+
if (hIcon) {
267+
pimpl_->menu_icon_ = hIcon;
268+
269+
// Create a 32-bit ARGB bitmap
270+
HDC hdc = GetDC(nullptr);
271+
272+
// Create BITMAPINFO for 32-bit ARGB
273+
BITMAPINFO bmi = {};
274+
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
275+
bmi.bmiHeader.biWidth = iconSize;
276+
bmi.bmiHeader.biHeight = -iconSize; // Negative for top-down DIB
277+
bmi.bmiHeader.biPlanes = 1;
278+
bmi.bmiHeader.biBitCount = 32;
279+
bmi.bmiHeader.biCompression = BI_RGB;
280+
281+
void* pBits = nullptr;
282+
HBITMAP hBmp = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &pBits, nullptr, 0);
283+
284+
if (hBmp && pBits) {
285+
// Fill with system menu background color
286+
COLORREF bgColor = GetSysColor(COLOR_MENU);
287+
DWORD bgPixel = (0xFF << 24) | (GetRValue(bgColor) << 16) | (GetGValue(bgColor) << 8) | GetBValue(bgColor);
288+
289+
DWORD* pixels = static_cast<DWORD*>(pBits);
290+
for (int i = 0; i < iconSize * iconSize; i++) {
291+
pixels[i] = bgPixel; // System menu background color (ARGB)
292+
}
293+
294+
// Draw the icon on the bitmap
295+
HDC hdcMem = CreateCompatibleDC(hdc);
296+
HBITMAP hOldBmp = (HBITMAP)SelectObject(hdcMem, hBmp);
297+
298+
// Draw icon with proper blending
299+
DrawIconEx(hdcMem, 0, 0, hIcon, iconSize, iconSize, 0, nullptr, DI_NORMAL);
300+
301+
SelectObject(hdcMem, hOldBmp);
302+
DeleteDC(hdcMem);
303+
304+
// Store the bitmap for cleanup
305+
pimpl_->menu_bitmap_ = hBmp;
306+
307+
// Set the bitmap on the menu item
308+
MENUITEMINFOW mii = {};
309+
mii.cbSize = sizeof(MENUITEMINFOW);
310+
mii.fMask = MIIM_BITMAP;
311+
mii.hbmpItem = hBmp;
312+
SetMenuItemInfoW(pimpl_->parent_menu_, pimpl_->id_, FALSE, &mii);
313+
}
314+
315+
ReleaseDC(nullptr, hdc);
316+
}
317+
}
231318
}
232319

233320
std::shared_ptr<Image> MenuItem::GetIcon() const {

0 commit comments

Comments
 (0)