@@ -12,6 +12,7 @@ const zigwin32 = @import("zigwin32");
1212const win32 = zigwin32 .everything ;
1313const gdi = @import ("gdip.zig" );
1414const HWND = win32 .HWND ;
15+ const HMENU = win32 .HMENU ;
1516const HINSTANCE = win32 .HINSTANCE ;
1617const RECT = win32 .RECT ;
1718const MSG = win32 .MSG ;
@@ -162,6 +163,9 @@ var defaultWHWND: HWND = undefined;
162163pub const Window = struct {
163164 hwnd : HWND ,
164165 source_dpi : u32 = 96 ,
166+ root_menu : ? HMENU ,
167+ /// List of menu item callbacks, where the index is the menu item ID
168+ menu_item_callbacks : std .ArrayList (? * const fn () void ),
165169
166170 const className = _T ("capyWClass" );
167171 pub usingnamespace Events (Window );
@@ -249,7 +253,13 @@ pub const Window = struct {
249253 }
250254
251255 defaultWHWND = hwnd ;
252- return Window { .hwnd = hwnd };
256+ return Window {
257+ .hwnd = hwnd ,
258+ .root_menu = null ,
259+ .menu_item_callbacks = std .ArrayList (? * const fn () void ).init (
260+ lib .internal .lasting_allocator ,
261+ ),
262+ };
253263 }
254264
255265 // TODO: handle the fact that ONLY the root child must forcibly draw a background
@@ -275,6 +285,51 @@ pub const Window = struct {
275285 _ = win32 .SetWindowTextW (self .hwnd , utf16 );
276286 }
277287
288+ fn initMenu (self : * Window , menu : HMENU , items : []const lib.MenuItem ) ! void {
289+ for (items ) | item | {
290+ if (item .items .len > 0 ) {
291+ const submenu = win32 .CreateMenu ().? ;
292+ _ = win32 .AppendMenuA (
293+ menu ,
294+ win32 .MENU_ITEM_FLAGS .initFlags (.{ .POPUP = 1 }),
295+ @intFromPtr (submenu ),
296+ item .config .label ,
297+ );
298+ try initMenu (self , submenu , item .items );
299+ } else {
300+ _ = win32 .AppendMenuA (
301+ menu ,
302+ win32 .MENU_ITEM_FLAGS .initFlags (.{}),
303+ self .menu_item_callbacks .items .len ,
304+ item .config .label ,
305+ );
306+ try self .menu_item_callbacks .append (item .config .onClick );
307+ }
308+ }
309+ }
310+
311+ fn clearAndFreeMenus (self : * Window ) void {
312+ _ = win32 .DestroyMenu (self .root_menu );
313+ self .menu_item_callbacks .clearAndFree ();
314+ self .root_menu = null ;
315+ }
316+
317+ pub fn setMenuBar (self : * Window , bar : lib.MenuBar ) void {
318+ // Detach and free current menu (if exists) from window first.
319+ _ = win32 .SetMenu (self .hwnd , null );
320+ self .clearAndFreeMenus ();
321+
322+ const root_menu = win32 .CreateMenu ().? ;
323+ self .initMenu (root_menu , bar .menus ) catch {
324+ // TODO: Handle error in appropriate way
325+ };
326+ if (win32 .SetMenu (self .hwnd , root_menu ) != 0 ) {
327+ self .root_menu = root_menu ;
328+ } else {
329+ self .menu_item_callbacks .clearAndFree ();
330+ }
331+ }
332+
278333 pub fn setSourceDpi (self : * Window , dpi : u32 ) void {
279334 self .source_dpi = dpi ;
280335 }
@@ -341,6 +396,20 @@ pub fn Events(comptime T: type) type {
341396 else = > {},
342397 }
343398 }
399+ // For menubar item events, HIWORD(wp) and lp are set to 0.
400+ else if (code == 0 ) {
401+ const data = getEventUserData (hwnd );
402+ const window_ptr : ? * Window = @ptrCast (@alignCast (data .peerPtr ));
403+ const id : u16 = @intCast (wp & 0xFFFF );
404+
405+ if (window_ptr ) | window | {
406+ if (id < window .menu_item_callbacks .items .len ) {
407+ if (window .menu_item_callbacks .items [id ]) | callback | {
408+ callback ();
409+ }
410+ }
411+ }
412+ }
344413 },
345414 win32 .WM_CTLCOLOREDIT = > {
346415 const data = getEventUserData (@as (HWND , @ptrFromInt (@as (usize , @bitCast (lp )))));
0 commit comments