Skip to content

Commit f087cee

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 f087cee

File tree

3 files changed

+64
-173
lines changed

3 files changed

+64
-173
lines changed

src/platform/windows/menu_windows.cpp

Lines changed: 15 additions & 65 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
}
@@ -352,36 +321,17 @@ class Menu::Impl {
352321
}
353322
};
354323

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-
}
371324

372325
Menu::Menu(void* native_menu)
373326
: id(g_next_menu_id++),
374327
pimpl_(std::make_unique<Impl>(static_cast<HMENU>(native_menu))) {
375-
// Register the Menu for event emission
376-
g_menu_registry[id] = this;
377328
}
378329

379-
Menu::Menu() : id(0) { // Will be set in Create method
330+
Menu::Menu() : id(g_next_menu_id++),
331+
pimpl_(std::make_unique<Impl>(CreatePopupMenu())) {
380332
}
381333

382334
Menu::~Menu() {
383-
// Unregister from event registry
384-
g_menu_registry.erase(id);
385335
}
386336

387337
void Menu::AddItem(std::shared_ptr<MenuItem> item) {
@@ -482,12 +432,12 @@ void Menu::Clear() {
482432
}
483433

484434
void Menu::AddSeparator() {
485-
auto separator = MenuItem::CreateSeparator();
435+
auto separator = std::make_shared<MenuItem>("", MenuItemType::Separator);
486436
AddItem(separator);
487437
}
488438

489439
void Menu::InsertSeparator(size_t index) {
490-
auto separator = MenuItem::CreateSeparator();
440+
auto separator = std::make_shared<MenuItem>("", MenuItemType::Separator);
491441
InsertItem(index, separator);
492442
}
493443

@@ -588,15 +538,15 @@ bool Menu::IsEnabled() const {
588538
}
589539

590540
std::shared_ptr<MenuItem> Menu::CreateAndAddItem(const std::string& text) {
591-
auto item = MenuItem::Create(text, MenuItemType::Normal);
541+
auto item = std::make_shared<MenuItem>(text, MenuItemType::Normal);
592542
AddItem(item);
593543
return item;
594544
}
595545

596546
std::shared_ptr<MenuItem> Menu::CreateAndAddItem(
597547
const std::string& text,
598548
const std::optional<std::string>& icon) {
599-
auto item = MenuItem::Create(text, MenuItemType::Normal);
549+
auto item = std::make_shared<MenuItem>(text, MenuItemType::Normal);
600550
if (icon.has_value()) {
601551
item->SetIcon(icon);
602552
}
@@ -607,7 +557,7 @@ std::shared_ptr<MenuItem> Menu::CreateAndAddItem(
607557
std::shared_ptr<MenuItem> Menu::CreateAndAddSubmenu(
608558
const std::string& text,
609559
std::shared_ptr<Menu> submenu) {
610-
auto item = MenuItem::Create(text, MenuItemType::Submenu);
560+
auto item = std::make_shared<MenuItem>(text, MenuItemType::Submenu);
611561
item->SetSubmenu(submenu);
612562
AddItem(item);
613563
return item;

src/platform/windows/tray_icon_windows.cpp

Lines changed: 46 additions & 42 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
@@ -187,33 +180,35 @@ void TrayIcon::SetIcon(std::string icon) {
187180
}
188181
}
189182

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);
183+
void TrayIcon::SetTitle(std::optional<std::string> title) {
184+
(void)title; // Unused on Windows
185+
// Windows tray icons don't support title
195186
}
196187

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

201-
void TrayIcon::SetTooltip(std::string tooltip) {
202-
pimpl_->tooltip_ = tooltip;
203-
193+
void TrayIcon::SetTooltip(std::optional<std::string> tooltip) {
204194
if (pimpl_->hwnd_) {
205-
strncpy_s(pimpl_->nid_.szTip, tooltip.c_str(),
195+
const char* tooltip_str = tooltip.has_value() ? tooltip->c_str() : "";
196+
strncpy_s(pimpl_->nid_.szTip, tooltip_str,
206197
sizeof(pimpl_->nid_.szTip) - 1);
207198
pimpl_->nid_.szTip[sizeof(pimpl_->nid_.szTip) - 1] = '\0';
208199

209-
if (pimpl_->visible_) {
200+
// Update if icon is visible (check if hIcon is set as indicator)
201+
if (pimpl_->nid_.hIcon) {
210202
Shell_NotifyIcon(NIM_MODIFY, &pimpl_->nid_);
211203
}
212204
}
213205
}
214206

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

219214
void TrayIcon::SetContextMenu(std::shared_ptr<Menu> menu) {
@@ -251,28 +246,33 @@ bool TrayIcon::SetVisible(bool visible) {
251246
return false;
252247
}
253248

254-
if (visible && !pimpl_->visible_) {
249+
bool currently_visible = IsVisible();
250+
251+
if (visible && !currently_visible) {
255252
// 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_) {
253+
return Shell_NotifyIcon(NIM_ADD, &pimpl_->nid_) == TRUE;
254+
} else if (!visible && currently_visible) {
261255
// Hide the tray icon
262-
if (Shell_NotifyIcon(NIM_DELETE, &pimpl_->nid_) == TRUE) {
263-
pimpl_->visible_ = false;
264-
return true;
265-
}
256+
return Shell_NotifyIcon(NIM_DELETE, &pimpl_->nid_) == TRUE;
266257
} else {
267258
// Already in the desired state
268259
return true;
269260
}
270-
271-
return false;
272261
}
273262

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

278278
bool TrayIcon::OpenContextMenu(double x, double y) {
@@ -309,6 +309,10 @@ bool TrayIcon::CloseContextMenu() {
309309
return pimpl_->context_menu_->Close();
310310
}
311311

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

0 commit comments

Comments
 (0)