Skip to content

Commit 2bf6efd

Browse files
committed
Add menu item state API and submenu events
- Introduce MenuItemState enum for checkbox/radio items - Add native_menu_item_set_state/get_state to C API - Replace set_checked/is_checked with state-based API - Add submenu opened/closed events and listeners - Update examples to use new state and event APIs - Implement radio group mutual exclusion for checked state - Remove automatic state toggling from macOS platform code
1 parent a570cf0 commit 2bf6efd

File tree

8 files changed

+339
-117
lines changed

8 files changed

+339
-117
lines changed

examples/menu_c_example/main.c

Lines changed: 66 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,28 @@
55
#include "../../src/capi/app_runner_c.h"
66

77
// Event callback functions
8-
void on_menu_item_selected(const void* event, void* user_data) {
9-
const native_menu_item_selected_event_t* selected_event = (const native_menu_item_selected_event_t*)event;
8+
void on_menu_item_clicked(const void* event, void* user_data) {
9+
const native_menu_item_clicked_event_t* clicked_event = (const native_menu_item_clicked_event_t*)event;
1010
const char* item_name = (const char*)user_data;
1111

12-
printf("[EVENT] Menu item selected: %s (ID: %ld, Text: %s)\n",
13-
item_name, selected_event->item_id, selected_event->item_text);
12+
printf("[EVENT] Menu item clicked: %s (ID: %ld, Text: %s)\n",
13+
item_name, clicked_event->item_id, clicked_event->item_text);
1414
}
1515

16-
void on_menu_item_state_changed(const void* event, void* user_data) {
17-
const native_menu_item_state_changed_event_t* state_event = (const native_menu_item_state_changed_event_t*)event;
16+
void on_menu_item_submenu_opened(const void* event, void* user_data) {
17+
const native_menu_item_submenu_opened_event_t* submenu_event = (const native_menu_item_submenu_opened_event_t*)event;
1818
const char* item_name = (const char*)user_data;
1919

20-
printf("[EVENT] Menu item state changed: %s (ID: %ld, Checked: %s)\n",
21-
item_name, state_event->item_id, state_event->checked ? "true" : "false");
20+
printf("[EVENT] Menu item submenu opened: %s (ID: %ld)\n",
21+
item_name, submenu_event->item_id);
22+
}
23+
24+
void on_menu_item_submenu_closed(const void* event, void* user_data) {
25+
const native_menu_item_submenu_closed_event_t* submenu_event = (const native_menu_item_submenu_closed_event_t*)event;
26+
const char* item_name = (const char*)user_data;
27+
28+
printf("[EVENT] Menu item submenu closed: %s (ID: %ld)\n",
29+
item_name, submenu_event->item_id);
2230
}
2331

2432
void on_menu_opened(const void* event, void* user_data) {
@@ -63,7 +71,7 @@ int main() {
6371
// Set up radio group
6472
native_menu_item_set_radio_group(radio_item1, 1);
6573
native_menu_item_set_radio_group(radio_item2, 1);
66-
native_menu_item_set_checked(radio_item1, true);
74+
native_menu_item_set_state(radio_item1, NATIVE_MENU_ITEM_STATE_CHECKED);
6775

6876
// Set keyboard accelerators
6977
native_keyboard_accelerator_t ctrl_n = { NATIVE_ACCELERATOR_MODIFIER_CTRL, "N" };
@@ -74,23 +82,14 @@ int main() {
7482
printf("Setting up event listeners using new event system...\n");
7583

7684
// Add event listeners using the new event system
77-
int file_listener = native_menu_item_add_listener(file_item, NATIVE_MENU_ITEM_EVENT_SELECTED,
78-
on_menu_item_selected, (void*)"New File");
79-
80-
int checkbox_select_listener = native_menu_item_add_listener(checkbox_item, NATIVE_MENU_ITEM_EVENT_SELECTED,
81-
on_menu_item_selected, (void*)"Word Wrap");
82-
83-
int checkbox_state_listener = native_menu_item_add_listener(checkbox_item, NATIVE_MENU_ITEM_EVENT_STATE_CHANGED,
84-
on_menu_item_state_changed, (void*)"Word Wrap");
85+
int file_listener = native_menu_item_add_listener(file_item, NATIVE_MENU_ITEM_EVENT_CLICKED,
86+
on_menu_item_clicked, (void*)"New File");
8587

86-
int radio1_state_listener = native_menu_item_add_listener(radio_item1, NATIVE_MENU_ITEM_EVENT_STATE_CHANGED,
87-
on_menu_item_state_changed, (void*)"View Mode 1");
88+
int checkbox_select_listener = native_menu_item_add_listener(checkbox_item, NATIVE_MENU_ITEM_EVENT_CLICKED,
89+
on_menu_item_clicked, (void*)"Word Wrap");
8890

89-
int radio2_state_listener = native_menu_item_add_listener(radio_item2, NATIVE_MENU_ITEM_EVENT_STATE_CHANGED,
90-
on_menu_item_state_changed, (void*)"View Mode 2");
91-
92-
int exit_listener = native_menu_item_add_listener(exit_item, NATIVE_MENU_ITEM_EVENT_SELECTED,
93-
on_menu_item_selected, (void*)"Exit");
91+
int exit_listener = native_menu_item_add_listener(exit_item, NATIVE_MENU_ITEM_EVENT_CLICKED,
92+
on_menu_item_clicked, (void*)"Exit");
9493

9594
// Add menu event listeners
9695
int menu_open_listener = native_menu_add_listener(menu, NATIVE_MENU_EVENT_OPENED,
@@ -100,15 +99,13 @@ int main() {
10099
on_menu_closed, (void*)"Main Menu");
101100

102101
// Check if listeners were added successfully
103-
if (file_listener == -1 || checkbox_select_listener == -1 || checkbox_state_listener == -1 ||
104-
radio1_state_listener == -1 || radio2_state_listener == -1 || exit_listener == -1 ||
102+
if (file_listener == -1 || checkbox_select_listener == -1 || exit_listener == -1 ||
105103
menu_open_listener == -1 || menu_close_listener == -1) {
106104
printf("Failed to add some event listeners\n");
107105
} else {
108106
printf("All event listeners added successfully\n");
109-
printf("Listener IDs: file=%d, checkbox_select=%d, checkbox_state=%d, radio1=%d, radio2=%d, exit=%d, menu_open=%d, menu_close=%d\n",
110-
file_listener, checkbox_select_listener, checkbox_state_listener,
111-
radio1_state_listener, radio2_state_listener, exit_listener,
107+
printf("Listener IDs: file=%d, checkbox_select=%d, exit=%d, menu_open=%d, menu_close=%d\n",
108+
file_listener, checkbox_select_listener, exit_listener,
112109
menu_open_listener, menu_close_listener);
113110
}
114111

@@ -124,6 +121,26 @@ int main() {
124121

125122
printf("Menu created with %zu items\n", native_menu_get_item_count(menu));
126123

124+
// Add submenu to demonstrate submenu events
125+
native_menu_t submenu = native_menu_create();
126+
native_menu_item_t submenu_item1 = native_menu_item_create("Submenu Item 1", NATIVE_MENU_ITEM_TYPE_NORMAL);
127+
native_menu_item_t submenu_item2 = native_menu_item_create("Submenu Item 2", NATIVE_MENU_ITEM_TYPE_NORMAL);
128+
129+
native_menu_add_item(submenu, submenu_item1);
130+
native_menu_add_item(submenu, submenu_item2);
131+
132+
native_menu_item_t submenu_parent = native_menu_item_create("Tools", NATIVE_MENU_ITEM_TYPE_SUBMENU);
133+
native_menu_item_set_submenu(submenu_parent, submenu);
134+
native_menu_add_item(menu, submenu_parent);
135+
136+
// Add submenu event listeners
137+
int submenu_open_listener = native_menu_item_add_listener(submenu_parent, NATIVE_MENU_ITEM_EVENT_SUBMENU_OPENED,
138+
on_menu_item_submenu_opened, (void*)"Tools");
139+
int submenu_close_listener = native_menu_item_add_listener(submenu_parent, NATIVE_MENU_ITEM_EVENT_SUBMENU_CLOSED,
140+
on_menu_item_submenu_closed, (void*)"Tools");
141+
142+
printf("Added submenu with %zu items\n", native_menu_get_item_count(submenu));
143+
127144
// Demonstrate programmatic triggering
128145
printf("\n=== Testing Programmatic Event Triggering ===\n");
129146

@@ -145,14 +162,14 @@ int main() {
145162
// Demonstrate listener removal
146163
printf("\n=== Testing Listener Removal ===\n");
147164

148-
printf("Removing checkbox state listener...\n");
149-
if (native_menu_item_remove_listener(checkbox_item, checkbox_state_listener)) {
150-
printf("Checkbox state listener removed successfully\n");
165+
printf("Removing checkbox click listener...\n");
166+
if (native_menu_item_remove_listener(checkbox_item, checkbox_select_listener)) {
167+
printf("Checkbox click listener removed successfully\n");
151168
} else {
152-
printf("Failed to remove checkbox state listener\n");
169+
printf("Failed to remove checkbox click listener\n");
153170
}
154171

155-
printf("Triggering checkbox item after removing state listener...\n");
172+
printf("Triggering checkbox item after removing click listener...\n");
156173
native_menu_item_trigger(checkbox_item);
157174

158175
// Show menu as context menu (this may not work in console applications)
@@ -171,10 +188,10 @@ int main() {
171188
native_menu_item_t additional_item = native_menu_item_create("Additional Test", NATIVE_MENU_ITEM_TYPE_NORMAL);
172189

173190
// Test that we can add multiple listeners for the same event
174-
int additional_listener1 = native_menu_item_add_listener(additional_item, NATIVE_MENU_ITEM_EVENT_SELECTED,
175-
on_menu_item_selected, (void*)"Additional Test 1");
176-
int additional_listener2 = native_menu_item_add_listener(additional_item, NATIVE_MENU_ITEM_EVENT_SELECTED,
177-
on_menu_item_selected, (void*)"Additional Test 2");
191+
int additional_listener1 = native_menu_item_add_listener(additional_item, NATIVE_MENU_ITEM_EVENT_CLICKED,
192+
on_menu_item_clicked, (void*)"Additional Test 1");
193+
int additional_listener2 = native_menu_item_add_listener(additional_item, NATIVE_MENU_ITEM_EVENT_CLICKED,
194+
on_menu_item_clicked, (void*)"Additional Test 2");
178195

179196
printf("Added multiple listeners for the same event\n");
180197
printf("Triggering item with multiple listeners...\n");
@@ -191,18 +208,25 @@ int main() {
191208
printf("This example demonstrates:\n");
192209
printf("1. Creating menus and menu items with different types\n");
193210
printf("2. Using the new event listener API with native_menu_item_add_listener()\n");
194-
printf("3. Handling NATIVE_MENU_ITEM_EVENT_SELECTED and NATIVE_MENU_ITEM_EVENT_STATE_CHANGED\n");
211+
printf("3. Handling NATIVE_MENU_ITEM_EVENT_CLICKED\n");
195212
printf("4. Handling NATIVE_MENU_EVENT_OPENED and NATIVE_MENU_EVENT_CLOSED\n");
196-
printf("5. Programmatic event triggering\n");
197-
printf("6. Event listener removal with native_menu_item_remove_listener()\n");
198-
printf("7. Multiple listeners for the same event type\n");
213+
printf("5. Handling NATIVE_MENU_ITEM_EVENT_SUBMENU_OPENED and NATIVE_MENU_ITEM_EVENT_SUBMENU_CLOSED\n");
214+
printf("6. Programmatic event triggering\n");
215+
printf("7. Event listener removal with native_menu_item_remove_listener()\n");
216+
printf("8. Multiple listeners for the same event type\n");
217+
printf("9. Manual state management for checkbox and radio items\n");
218+
printf("10. Submenu support with event handling\n");
199219

200220
// Cleanup
201221
native_menu_item_destroy(file_item);
202222
native_menu_item_destroy(checkbox_item);
203223
native_menu_item_destroy(radio_item1);
204224
native_menu_item_destroy(radio_item2);
205225
native_menu_item_destroy(exit_item);
226+
native_menu_item_destroy(submenu_item1);
227+
native_menu_item_destroy(submenu_item2);
228+
native_menu_item_destroy(submenu_parent);
229+
native_menu_destroy(submenu);
206230
native_menu_destroy(menu);
207231

208232
return 0;

examples/menu_example/main.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ int main() {
2525
// Set up radio group
2626
radioItem1->SetRadioGroup(1);
2727
radioItem2->SetRadioGroup(1);
28-
radioItem1->SetChecked(true);
28+
radioItem1->SetState(MenuItemState::Checked);
2929

3030
// Set keyboard accelerators
3131
fileItem->SetAccelerator(KeyboardAccelerator("N", KeyboardAccelerator::Ctrl));
@@ -131,6 +131,16 @@ int main() {
131131

132132
auto submenuParent = MenuItem::Create("Tools", MenuItemType::Submenu);
133133
submenuParent->SetSubmenu(submenu);
134+
135+
// Add submenu event listeners
136+
submenuParent->AddListener<MenuItemSubmenuOpenedEvent>([](const MenuItemSubmenuOpenedEvent& event) {
137+
std::cout << "[EVENT] Submenu opened: ID " << event.GetItemId() << std::endl;
138+
});
139+
140+
submenuParent->AddListener<MenuItemSubmenuClosedEvent>([](const MenuItemSubmenuClosedEvent& event) {
141+
std::cout << "[EVENT] Submenu closed: ID " << event.GetItemId() << std::endl;
142+
});
143+
134144
menu->AddItem(submenuParent);
135145

136146
std::cout << "Added submenu with " << submenu->GetItemCount() << " items" << std::endl;
@@ -146,8 +156,9 @@ int main() {
146156
std::cout << "2. Using the new event system with AddListener<EventType>()" << std::endl;
147157
std::cout << "3. Handling MenuItemClickedEvent (state managed by application)" << std::endl;
148158
std::cout << "4. Handling MenuOpenedEvent and MenuClosedEvent" << std::endl;
149-
std::cout << "5. Programmatic event triggering" << std::endl;
150-
std::cout << "6. Submenu support with event propagation" << std::endl;
159+
std::cout << "5. Handling MenuItemSubmenuOpenedEvent and MenuItemSubmenuClosedEvent" << std::endl;
160+
std::cout << "6. Programmatic event triggering" << std::endl;
161+
std::cout << "7. Submenu support with event propagation" << std::endl;
151162

152163
} catch (const std::exception& e) {
153164
std::cerr << "Error: " << e.what() << std::endl;

examples/tray_icon_c_example/main.c

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,20 @@
1515
#include "../../src/capi/tray_manager_c.h"
1616

1717
// Event callback functions
18-
void on_menu_item_selected(const void* event, void* user_data) {
19-
const native_menu_item_selected_event_t* selected_event = (const native_menu_item_selected_event_t*)event;
20-
printf("Menu item clicked: ID=%ld, Text='%s'\n", selected_event->item_id,
21-
selected_event->item_text);
18+
void on_menu_item_clicked(const void* event, void* user_data) {
19+
const native_menu_item_clicked_event_t* clicked_event = (const native_menu_item_clicked_event_t*)event;
20+
printf("Menu item clicked: ID=%ld, Text='%s'\n", clicked_event->item_id,
21+
clicked_event->item_text);
2222

23-
if (strcmp(selected_event->item_text, "Exit") == 0) {
23+
if (strcmp(clicked_event->item_text, "Exit") == 0) {
2424
printf("Exiting application...\n");
2525
exit(0);
26-
} else if (strcmp(selected_event->item_text, "Show Message") == 0) {
26+
} else if (strcmp(clicked_event->item_text, "Show Message") == 0) {
2727
printf("Hello from tray menu!\n");
2828
}
2929
}
3030

31-
void on_menu_item_state_changed(const void* event, void* user_data) {
32-
const native_menu_item_state_changed_event_t* state_event = (const native_menu_item_state_changed_event_t*)event;
33-
printf("Checkbox state changed: ID=%ld, Checked=%s\n", state_event->item_id,
34-
state_event->checked ? "true" : "false");
35-
}
31+
3632

3733
void on_tray_left_click(void* user_data) {
3834
printf("Tray icon left clicked!\n");
@@ -103,13 +99,13 @@ int main() {
10399
native_menu_item_set_accelerator(exit_item, &exit_accel);
104100

105101
// Set checkbox state
106-
native_menu_item_set_checked(checkbox, true);
102+
native_menu_item_set_state(checkbox, NATIVE_MENU_ITEM_STATE_CHECKED);
107103

108104
// Set up event listeners using new API
109-
native_menu_item_add_listener(item1, NATIVE_MENU_ITEM_EVENT_SELECTED, on_menu_item_selected, NULL);
110-
native_menu_item_add_listener(item2, NATIVE_MENU_ITEM_EVENT_SELECTED, on_menu_item_selected, NULL);
111-
native_menu_item_add_listener(exit_item, NATIVE_MENU_ITEM_EVENT_SELECTED, on_menu_item_selected, NULL);
112-
native_menu_item_add_listener(checkbox, NATIVE_MENU_ITEM_EVENT_STATE_CHANGED, on_menu_item_state_changed, NULL);
105+
native_menu_item_add_listener(item1, NATIVE_MENU_ITEM_EVENT_CLICKED, on_menu_item_clicked, NULL);
106+
native_menu_item_add_listener(item2, NATIVE_MENU_ITEM_EVENT_CLICKED, on_menu_item_clicked, NULL);
107+
native_menu_item_add_listener(exit_item, NATIVE_MENU_ITEM_EVENT_CLICKED, on_menu_item_clicked, NULL);
108+
native_menu_item_add_listener(checkbox, NATIVE_MENU_ITEM_EVENT_CLICKED, on_menu_item_clicked, NULL);
113109

114110
// Add items to menu
115111
native_menu_add_item(menu, item1);

0 commit comments

Comments
 (0)