2121static const void * kTrayIconIdKey = &kTrayIconIdKey ;
2222
2323@interface NSStatusBarButtonTarget : NSObject
24- @property (nonatomic , copy ) TrayIconClickedBlock leftClickedBlock ;
25- @property (nonatomic , copy ) TrayIconRightClickedBlock rightClickedBlock ;
26- @property (nonatomic , copy ) TrayIconDoubleClickedBlock doubleClickedBlock ;
24+ @property (nonatomic , copy ) TrayIconClickedBlock left_clicked_callback ;
25+ @property (nonatomic , copy ) TrayIconRightClickedBlock right_clicked_callback ;
26+ @property (nonatomic , copy ) TrayIconDoubleClickedBlock double_clicked_callback ;
2727- (void )handleStatusItemEvent : (id )sender ;
2828@end
2929
@@ -37,7 +37,8 @@ - (void)handleStatusItemEvent:(id)sender;
3737 Impl (NSStatusItem * status_item)
3838 : ns_status_item_(status_item),
3939 ns_status_bar_button_target_ (nil ),
40- menu_closed_listener_id_(0 ) {
40+ menu_closed_listener_id_(0 ),
41+ click_handler_setup_(false ) {
4142 if (status_item) {
4243 // Check if ID already exists in the associated object
4344 NSNumber * allocated_id = objc_getAssociatedObject (status_item, kTrayIconIdKey );
@@ -50,16 +51,6 @@ - (void)handleStatusItemEvent:(id)sender;
5051 objc_setAssociatedObject (status_item, kTrayIconIdKey , [NSNumber numberWithLong: id_],
5152 OBJC_ASSOCIATION_RETAIN_NONATOMIC );
5253 }
53-
54- // Create and set up button target
55- ns_status_bar_button_target_ = [[NSStatusBarButtonTarget alloc ] init ];
56-
57- // Set up event handlers
58- [status_item.button setTarget: ns_status_bar_button_target_];
59- [status_item.button setAction: @selector (handleStatusItemEvent: )];
60-
61- // Enable right-click handling
62- [status_item.button sendActionOn: NSEventMaskLeftMouseUp | NSEventMaskRightMouseUp];
6354 }
6455 }
6556
@@ -70,21 +61,13 @@ - (void)handleStatusItemEvent:(id)sender;
7061 menu_closed_listener_id_ = 0 ;
7162 }
7263
73- // Clean up blocks first
74- if (ns_status_bar_button_target_) {
75- ns_status_bar_button_target_.leftClickedBlock = nil ;
76- ns_status_bar_button_target_.rightClickedBlock = nil ;
77- ns_status_bar_button_target_.doubleClickedBlock = nil ;
78- ns_status_bar_button_target_ = nil ;
64+ // Clean up event handlers if they were set up
65+ if (click_handler_setup_) {
66+ CleanupEventHandlers ();
7967 }
8068
8169 // Then clean up the status item
8270 if (ns_status_item_) {
83- // Remove target and action to prevent callbacks after destruction
84- if (ns_status_item_.button ) {
85- [ns_status_item_.button setTarget: nil ];
86- [ns_status_item_.button setAction: nil ];
87- }
8871 // Clear menu reference
8972 ns_status_item_.menu = nil ;
9073
@@ -102,12 +85,57 @@ - (void)handleStatusItemEvent:(id)sender;
10285 }
10386 }
10487
88+ void SetupEventHandlers () {
89+ if (click_handler_setup_) {
90+ return ; // Already set up
91+ }
92+
93+ if (!ns_status_item_ || !ns_status_item_.button ) {
94+ return ;
95+ }
96+
97+ // Create and set up button target
98+ ns_status_bar_button_target_ = [[NSStatusBarButtonTarget alloc ] init ];
99+
100+ // Set up event handlers
101+ [ns_status_item_.button setTarget: ns_status_bar_button_target_];
102+ [ns_status_item_.button setAction: @selector (handleStatusItemEvent: )];
103+
104+ // Enable right-click handling
105+ [ns_status_item_.button sendActionOn: NSEventMaskLeftMouseUp | NSEventMaskRightMouseUp];
106+
107+ click_handler_setup_ = true ;
108+ }
109+
110+ void CleanupEventHandlers () {
111+ if (!click_handler_setup_) {
112+ return ; // Not set up
113+ }
114+
115+ // Clean up blocks first
116+ if (ns_status_bar_button_target_) {
117+ ns_status_bar_button_target_.left_clicked_callback = nil ;
118+ ns_status_bar_button_target_.right_clicked_callback = nil ;
119+ ns_status_bar_button_target_.double_clicked_callback = nil ;
120+ ns_status_bar_button_target_ = nil ;
121+ }
122+
123+ // Remove target and action to prevent callbacks after destruction
124+ if (ns_status_item_ && ns_status_item_.button ) {
125+ [ns_status_item_.button setTarget: nil ];
126+ [ns_status_item_.button setAction: nil ];
127+ }
128+
129+ click_handler_setup_ = false ;
130+ }
131+
105132 NSStatusItem * ns_status_item_;
106133 NSStatusBarButtonTarget* ns_status_bar_button_target_;
107134
108135 TrayIconId id_;
109136 std::shared_ptr<Menu> context_menu_;
110137 size_t menu_closed_listener_id_;
138+ bool click_handler_setup_;
111139};
112140
113141TrayIcon::TrayIcon () : TrayIcon(nullptr ) {}
@@ -126,23 +154,38 @@ - (void)handleStatusItemEvent:(id)sender;
126154 // Initialize the Impl with the status item
127155 pimpl_ = std::make_unique<Impl>(status_item);
128156
129- // Set up click handlers
157+ // Event handlers will be set up automatically when first listener is added
158+ // via StartEventListening() override
159+ }
160+
161+ TrayIcon::~TrayIcon () = default ;
162+
163+ void TrayIcon::StartEventListening () {
164+ // Called automatically when first listener is added
165+ // Set up platform event monitoring
166+ pimpl_->SetupEventHandlers ();
167+
168+ // Set up click handler blocks
130169 if (pimpl_->ns_status_bar_button_target_ ) {
131- pimpl_->ns_status_bar_button_target_ .leftClickedBlock = ^{
170+ pimpl_->ns_status_bar_button_target_ .left_clicked_callback = ^{
132171 Emit<TrayIconClickedEvent>(pimpl_->id_ );
133172 };
134173
135- pimpl_->ns_status_bar_button_target_ .rightClickedBlock = ^{
174+ pimpl_->ns_status_bar_button_target_ .right_clicked_callback = ^{
136175 Emit<TrayIconRightClickedEvent>(pimpl_->id_ );
137176 };
138177
139- pimpl_->ns_status_bar_button_target_ .doubleClickedBlock = ^{
178+ pimpl_->ns_status_bar_button_target_ .double_clicked_callback = ^{
140179 Emit<TrayIconDoubleClickedEvent>(pimpl_->id_ );
141180 };
142181 }
143182}
144183
145- TrayIcon::~TrayIcon () = default ;
184+ void TrayIcon::StopEventListening () {
185+ // Called automatically when last listener is removed
186+ // Clean up platform event monitoring
187+ pimpl_->CleanupEventHandlers ();
188+ }
146189
147190TrayIconId TrayIcon::GetId () {
148191 return pimpl_->id_ ;
@@ -350,18 +393,18 @@ - (void)handleStatusItemEvent:(id)sender {
350393 (event.type == NSEventTypeLeftMouseUp &&
351394 (event.modifierFlags & NSEventModifierFlagControl))) {
352395 // Right click or Ctrl+Left click
353- if (_rightClickedBlock ) {
354- _rightClickedBlock ();
396+ if (_right_clicked_callback ) {
397+ _right_clicked_callback ();
355398 }
356399 } else if (event.type == NSEventTypeLeftMouseUp) {
357400 // Check for double click
358401 if (event.clickCount == 2 ) {
359- if (_doubleClickedBlock ) {
360- _doubleClickedBlock ();
402+ if (_double_clicked_callback ) {
403+ _double_clicked_callback ();
361404 }
362405 } else {
363- if (_leftClickedBlock ) {
364- _leftClickedBlock ();
406+ if (_left_clicked_callback ) {
407+ _left_clicked_callback ();
365408 }
366409 }
367410 }
0 commit comments