Skip to content

Commit 599f1b3

Browse files
committed
Refactor Windows menu and tray icon management
Removed global registries and simplified Menu/MenuItem creation logic. Updated tray icon implementation to use std::optional for title and tooltip, improved visibility checks, and removed unused fields. Refactored TrayManager to eliminate hidden window management and related Windows-specific code, focusing on core tray icon tracking.
1 parent 624d7c6 commit 599f1b3

File tree

3 files changed

+76
-180
lines changed

3 files changed

+76
-180
lines changed

src/platform/windows/menu_windows.cpp

Lines changed: 21 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
#include <windows.h>
2-
#include <algorithm>
32
#include <atomic>
4-
#include <iostream>
3+
#include <memory>
54
#include <optional>
6-
#include <unordered_map>
75
#include <vector>
86
#include "../../menu.h"
97
#include "../../menu_event.h"
@@ -14,9 +12,6 @@ namespace nativeapi {
1412
static std::atomic<MenuItemID> g_next_menu_item_id{1};
1513
static std::atomic<MenuID> g_next_menu_id{1};
1614

17-
// Global registry to map native objects to C++ objects for event emission
18-
static std::unordered_map<MenuItemID, MenuItem*> g_menu_item_registry;
19-
static std::unordered_map<MenuID, Menu*> g_menu_registry;
2015

2116
// Helper function to convert KeyboardAccelerator to Windows accelerator
2217
std::pair<UINT, UINT> ConvertAccelerator(
@@ -131,38 +126,19 @@ class MenuItem::Impl {
131126
}
132127
};
133128

134-
// MenuItem implementation
135-
std::shared_ptr<MenuItem> MenuItem::Create(const std::string& text,
136-
MenuItemType type) {
137-
auto item = std::shared_ptr<MenuItem>(new MenuItem(text, type));
138-
item->pimpl_ = std::make_unique<Impl>(nullptr, 0, type);
139-
item->id = g_next_menu_item_id++;
140-
item->pimpl_->text_ = text;
141-
142-
// Register the MenuItem for event emission
143-
g_menu_item_registry[item->id] = item.get();
144-
145-
return item;
146-
}
147-
148-
std::shared_ptr<MenuItem> MenuItem::CreateSeparator() {
149-
return Create("", MenuItemType::Separator);
150-
}
151129

152130
MenuItem::MenuItem(void* native_item)
153131
: id(g_next_menu_item_id++),
154132
pimpl_(std::make_unique<Impl>(nullptr, 0, MenuItemType::Normal)) {
155-
// Register the MenuItem for event emission
156-
g_menu_item_registry[id] = this;
157133
}
158134

159135
MenuItem::MenuItem(const std::string& text, MenuItemType type)
160-
: id(0) { // Will be set in Create method
136+
: id(g_next_menu_item_id++),
137+
pimpl_(std::make_unique<Impl>(nullptr, 0, type)) {
138+
pimpl_->text_ = text;
161139
}
162140

163141
MenuItem::~MenuItem() {
164-
// Unregister from event registry
165-
g_menu_item_registry.erase(id);
166142
}
167143

168144
MenuItemType MenuItem::GetType() const {
@@ -262,17 +238,10 @@ void MenuItem::SetState(MenuItemState state) {
262238
// Handle radio button group logic
263239
if (pimpl_->type_ == MenuItemType::Radio &&
264240
state == MenuItemState::Checked && pimpl_->radio_group_ >= 0) {
265-
for (auto& pair : g_menu_item_registry) {
266-
MenuItem* otherItem = pair.second;
267-
if (otherItem != this && otherItem->GetType() == MenuItemType::Radio &&
268-
otherItem->GetRadioGroup() == pimpl_->radio_group_) {
269-
otherItem->pimpl_->state_ = MenuItemState::Unchecked;
270-
if (otherItem->pimpl_->parent_menu_) {
271-
CheckMenuItem(otherItem->pimpl_->parent_menu_,
272-
otherItem->pimpl_->menu_item_id_, MF_UNCHECKED);
273-
}
274-
}
275-
}
241+
// Note: Radio group logic would need to be implemented differently
242+
// without global registry. This could be done by maintaining group
243+
// information in the parent menu or through other means.
244+
// For now, this functionality is disabled.
276245
}
277246
}
278247
}
@@ -319,22 +288,19 @@ bool MenuItem::Trigger() {
319288
if (!pimpl_->enabled_)
320289
return false;
321290

322-
EmitSelectedEvent(pimpl_->text_);
291+
try {
292+
std::string text = pimpl_->text_.has_value() ? pimpl_->text_.value() : "";
293+
EmitSync<MenuItemClickedEvent>(id, text);
294+
} catch (...) {
295+
// Protect against event emission exceptions
296+
}
323297
return true;
324298
}
325299

326300
void* MenuItem::GetNativeObjectInternal() const {
327301
return reinterpret_cast<void*>(static_cast<uintptr_t>(pimpl_->menu_item_id_));
328302
}
329303

330-
void MenuItem::EmitSelectedEvent(const std::string& item_text) {
331-
EmitSync<MenuItemClickedEvent>(id, item_text);
332-
}
333-
334-
void MenuItem::EmitStateChangedEvent(bool checked) {
335-
// This method is kept for compatibility
336-
}
337-
338304
// Menu::Impl implementation
339305
class Menu::Impl {
340306
public:
@@ -352,36 +318,17 @@ class Menu::Impl {
352318
}
353319
};
354320

355-
// Menu implementation
356-
std::shared_ptr<Menu> Menu::Create() {
357-
HMENU hmenu = CreatePopupMenu();
358-
if (!hmenu) {
359-
return nullptr;
360-
}
361-
362-
auto menu = std::shared_ptr<Menu>(new Menu());
363-
menu->pimpl_ = std::make_unique<Impl>(hmenu);
364-
menu->id = g_next_menu_id++;
365-
366-
// Register the Menu for event emission
367-
g_menu_registry[menu->id] = menu.get();
368-
369-
return menu;
370-
}
371321

372322
Menu::Menu(void* native_menu)
373323
: id(g_next_menu_id++),
374324
pimpl_(std::make_unique<Impl>(static_cast<HMENU>(native_menu))) {
375-
// Register the Menu for event emission
376-
g_menu_registry[id] = this;
377325
}
378326

379-
Menu::Menu() : id(0) { // Will be set in Create method
327+
Menu::Menu() : id(g_next_menu_id++),
328+
pimpl_(std::make_unique<Impl>(CreatePopupMenu())) {
380329
}
381330

382331
Menu::~Menu() {
383-
// Unregister from event registry
384-
g_menu_registry.erase(id);
385332
}
386333

387334
void Menu::AddItem(std::shared_ptr<MenuItem> item) {
@@ -482,12 +429,12 @@ void Menu::Clear() {
482429
}
483430

484431
void Menu::AddSeparator() {
485-
auto separator = MenuItem::CreateSeparator();
432+
auto separator = std::make_shared<MenuItem>("", MenuItemType::Separator);
486433
AddItem(separator);
487434
}
488435

489436
void Menu::InsertSeparator(size_t index) {
490-
auto separator = MenuItem::CreateSeparator();
437+
auto separator = std::make_shared<MenuItem>("", MenuItemType::Separator);
491438
InsertItem(index, separator);
492439
}
493440

@@ -588,15 +535,15 @@ bool Menu::IsEnabled() const {
588535
}
589536

590537
std::shared_ptr<MenuItem> Menu::CreateAndAddItem(const std::string& text) {
591-
auto item = MenuItem::Create(text, MenuItemType::Normal);
538+
auto item = std::make_shared<MenuItem>(text, MenuItemType::Normal);
592539
AddItem(item);
593540
return item;
594541
}
595542

596543
std::shared_ptr<MenuItem> Menu::CreateAndAddItem(
597544
const std::string& text,
598545
const std::optional<std::string>& icon) {
599-
auto item = MenuItem::Create(text, MenuItemType::Normal);
546+
auto item = std::make_shared<MenuItem>(text, MenuItemType::Normal);
600547
if (icon.has_value()) {
601548
item->SetIcon(icon);
602549
}
@@ -607,7 +554,7 @@ std::shared_ptr<MenuItem> Menu::CreateAndAddItem(
607554
std::shared_ptr<MenuItem> Menu::CreateAndAddSubmenu(
608555
const std::string& text,
609556
std::shared_ptr<Menu> submenu) {
610-
auto item = MenuItem::Create(text, MenuItemType::Submenu);
557+
auto item = std::make_shared<MenuItem>(text, MenuItemType::Submenu);
611558
item->SetSubmenu(submenu);
612559
AddItem(item);
613560
return item;

src/platform/windows/tray_icon_windows.cpp

Lines changed: 49 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
#include <shellapi.h>
21
#include <windows.h>
3-
#include <iostream>
2+
#include <shellapi.h>
3+
#include <memory>
44
#include <string>
5-
#include <vector>
65

7-
#include "../../geometry.h"
6+
#include "../../foundation/geometry.h"
87
#include "../../menu.h"
98
#include "../../tray_icon.h"
109
#include "../../tray_icon_event.h"
@@ -15,10 +14,10 @@ namespace nativeapi {
1514
class TrayIcon::Impl {
1615
public:
1716
Impl()
18-
: hwnd_(nullptr), icon_id_(0), visible_(false), icon_handle_(nullptr) {}
17+
: hwnd_(nullptr), icon_id_(0), icon_handle_(nullptr) {}
1918

2019
Impl(HWND hwnd, UINT icon_id)
21-
: hwnd_(hwnd), icon_id_(icon_id), visible_(false), icon_handle_(nullptr) {
20+
: hwnd_(hwnd), icon_id_(icon_id), icon_handle_(nullptr) {
2221
// Initialize NOTIFYICONDATA structure
2322
ZeroMemory(&nid_, sizeof(NOTIFYICONDATA));
2423
nid_.cbSize = sizeof(NOTIFYICONDATA);
@@ -80,7 +79,7 @@ class TrayIcon::Impl {
8079
}
8180

8281
~Impl() {
83-
if (visible_) {
82+
if (hwnd_) {
8483
Shell_NotifyIcon(NIM_DELETE, &nid_);
8584
}
8685
if (icon_handle_) {
@@ -92,15 +91,10 @@ class TrayIcon::Impl {
9291
UINT icon_id_;
9392
NOTIFYICONDATA nid_;
9493
std::shared_ptr<Menu> context_menu_;
95-
std::string title_;
96-
std::string tooltip_;
97-
bool visible_;
9894
HICON icon_handle_;
9995
};
10096

101-
TrayIcon::TrayIcon() : pimpl_(std::make_unique<Impl>()) {
102-
id = -1;
103-
97+
TrayIcon::TrayIcon() : id(-1), pimpl_(std::make_unique<Impl>()) {
10498
// Create a hidden window for this tray icon
10599
HINSTANCE hInstance = GetModuleHandle(nullptr);
106100

@@ -132,8 +126,7 @@ TrayIcon::TrayIcon() : pimpl_(std::make_unique<Impl>()) {
132126
}
133127
}
134128

135-
TrayIcon::TrayIcon(void* tray) : pimpl_(std::make_unique<Impl>()) {
136-
id = -1; // Will be set by TrayManager when created
129+
TrayIcon::TrayIcon(void* tray) : id(-1), pimpl_(std::make_unique<Impl>()) {
137130
// In a real implementation, you'd extract HWND and icon ID from the tray
138131
// parameter For now, this constructor is mainly used by TrayManager for
139132
// creating uninitialized icons
@@ -181,39 +174,42 @@ void TrayIcon::SetIcon(std::string icon) {
181174
pimpl_->icon_handle_ = hIcon;
182175
pimpl_->nid_.hIcon = hIcon;
183176

184-
if (pimpl_->visible_) {
177+
// Update the icon if it's currently visible
178+
if (IsVisible()) {
185179
Shell_NotifyIcon(NIM_MODIFY, &pimpl_->nid_);
186180
}
187181
}
188182
}
189183

190-
void TrayIcon::SetTitle(std::string title) {
191-
pimpl_->title_ = title;
192-
// Windows tray icons don't have a separate title, only tooltip
193-
// We'll use title as part of the tooltip
194-
SetTooltip(title);
184+
void TrayIcon::SetTitle(std::optional<std::string> title) {
185+
(void)title; // Unused on Windows
186+
// Windows tray icons don't support title
195187
}
196188

197-
std::string TrayIcon::GetTitle() {
198-
return pimpl_->title_;
189+
std::optional<std::string> TrayIcon::GetTitle() {
190+
// Windows tray icons don't support title
191+
return std::nullopt;
199192
}
200193

201-
void TrayIcon::SetTooltip(std::string tooltip) {
202-
pimpl_->tooltip_ = tooltip;
203-
194+
void TrayIcon::SetTooltip(std::optional<std::string> tooltip) {
204195
if (pimpl_->hwnd_) {
205-
strncpy_s(pimpl_->nid_.szTip, tooltip.c_str(),
196+
const char* tooltip_str = tooltip.has_value() ? tooltip->c_str() : "";
197+
strncpy_s(pimpl_->nid_.szTip, tooltip_str,
206198
sizeof(pimpl_->nid_.szTip) - 1);
207199
pimpl_->nid_.szTip[sizeof(pimpl_->nid_.szTip) - 1] = '\0';
208200

209-
if (pimpl_->visible_) {
201+
// Update if icon is visible (check if hIcon is set as indicator)
202+
if (pimpl_->nid_.hIcon) {
210203
Shell_NotifyIcon(NIM_MODIFY, &pimpl_->nid_);
211204
}
212205
}
213206
}
214207

215-
std::string TrayIcon::GetTooltip() {
216-
return pimpl_->tooltip_;
208+
std::optional<std::string> TrayIcon::GetTooltip() {
209+
if (pimpl_->hwnd_ && pimpl_->nid_.szTip[0] != '\0') {
210+
return std::string(pimpl_->nid_.szTip);
211+
}
212+
return std::nullopt;
217213
}
218214

219215
void TrayIcon::SetContextMenu(std::shared_ptr<Menu> menu) {
@@ -227,7 +223,7 @@ std::shared_ptr<Menu> TrayIcon::GetContextMenu() {
227223
Rectangle TrayIcon::GetBounds() {
228224
Rectangle bounds = {0, 0, 0, 0};
229225

230-
if (pimpl_->hwnd_ && pimpl_->visible_) {
226+
if (pimpl_->hwnd_ && IsVisible()) {
231227
RECT rect;
232228
NOTIFYICONIDENTIFIER niid = {};
233229
niid.cbSize = sizeof(NOTIFYICONIDENTIFIER);
@@ -251,28 +247,33 @@ bool TrayIcon::SetVisible(bool visible) {
251247
return false;
252248
}
253249

254-
if (visible && !pimpl_->visible_) {
250+
bool currently_visible = IsVisible();
251+
252+
if (visible && !currently_visible) {
255253
// Show the tray icon
256-
if (Shell_NotifyIcon(NIM_ADD, &pimpl_->nid_) == TRUE) {
257-
pimpl_->visible_ = true;
258-
return true;
259-
}
260-
} else if (!visible && pimpl_->visible_) {
254+
return Shell_NotifyIcon(NIM_ADD, &pimpl_->nid_) == TRUE;
255+
} else if (!visible && currently_visible) {
261256
// Hide the tray icon
262-
if (Shell_NotifyIcon(NIM_DELETE, &pimpl_->nid_) == TRUE) {
263-
pimpl_->visible_ = false;
264-
return true;
265-
}
257+
return Shell_NotifyIcon(NIM_DELETE, &pimpl_->nid_) == TRUE;
266258
} else {
267259
// Already in the desired state
268260
return true;
269261
}
270-
271-
return false;
272262
}
273263

274264
bool TrayIcon::IsVisible() {
275-
return pimpl_->visible_;
265+
if (!pimpl_->hwnd_) {
266+
return false;
267+
}
268+
269+
// Check if the tray icon is visible by querying its bounds
270+
NOTIFYICONIDENTIFIER niid = {};
271+
niid.cbSize = sizeof(NOTIFYICONIDENTIFIER);
272+
niid.hWnd = pimpl_->hwnd_;
273+
niid.uID = pimpl_->icon_id_;
274+
275+
RECT rect;
276+
return Shell_NotifyIconGetRect(&niid, &rect) == S_OK;
276277
}
277278

278279
bool TrayIcon::OpenContextMenu(double x, double y) {
@@ -309,6 +310,10 @@ bool TrayIcon::CloseContextMenu() {
309310
return pimpl_->context_menu_->Close();
310311
}
311312

313+
void* TrayIcon::GetNativeObjectInternal() const {
314+
return reinterpret_cast<void*>(static_cast<uintptr_t>(pimpl_->icon_id_));
315+
}
316+
312317
// Note: Windows-specific functionality is now handled internally by the Impl
313318
// class The SetWindowsData and HandleWindowsMessage methods have been moved to
314319
// the Impl class to maintain proper encapsulation and avoid exposing

0 commit comments

Comments
 (0)