@@ -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,10 @@ var defaultWHWND: HWND = undefined;
162163pub const Window = struct {
163164 hwnd : HWND ,
164165 source_dpi : u32 = 96 ,
166+ /// List of menus and submenus
167+ menus : std .ArrayList (HMENU ),
168+ /// List of menu item callbacks, where the index is the menu item ID
169+ menu_item_callbacks : std .ArrayList (? * const fn () void ),
165170
166171 const className = _T ("capyWClass" );
167172 pub usingnamespace Events (Window );
@@ -249,7 +254,13 @@ pub const Window = struct {
249254 }
250255
251256 defaultWHWND = hwnd ;
252- return Window { .hwnd = hwnd };
257+ return Window {
258+ .hwnd = hwnd ,
259+ .menus = std .ArrayList (HMENU ).init (lib .internal .lasting_allocator ),
260+ .menu_item_callbacks = std .ArrayList (? * const fn () void ).init (
261+ lib .internal .lasting_allocator ,
262+ ),
263+ };
253264 }
254265
255266 // TODO: handle the fact that ONLY the root child must forcibly draw a background
@@ -275,6 +286,56 @@ pub const Window = struct {
275286 _ = win32 .SetWindowTextW (self .hwnd , utf16 );
276287 }
277288
289+ fn initMenu (self : * Window , menu : HMENU , items : []const lib.MenuItem ) ! void {
290+ for (items ) | item | {
291+ if (item .items .len > 0 ) {
292+ const submenu = win32 .CreateMenu ().? ;
293+ _ = win32 .AppendMenuA (
294+ menu ,
295+ win32 .MENU_ITEM_FLAGS .initFlags (.{ .POPUP = 1 }),
296+ @intFromPtr (submenu ),
297+ item .config .label ,
298+ );
299+ try initMenu (self , submenu , item .items );
300+ // Append submenus in reverse order for freeing correctly
301+ try self .menus .append (submenu );
302+ } else {
303+ _ = win32 .AppendMenuA (
304+ menu ,
305+ win32 .MENU_ITEM_FLAGS .initFlags (.{}),
306+ self .menu_item_callbacks .items .len ,
307+ item .config .label ,
308+ );
309+ try self .menu_item_callbacks .append (item .config .onClick );
310+ }
311+ }
312+ }
313+
314+ fn clearAndFreeMenus (self : * Window ) void {
315+ for (self .menus .items ) | menu | {
316+ var position_index : u32 = 0 ;
317+ // Delete all items until failure to delete
318+ while (win32 .DeleteMenu (menu , position_index , win32 .MF_BYPOSITION ) != 0 ) {
319+ position_index += 1 ;
320+ }
321+ }
322+ self .menus .clearAndFree ();
323+ self .menu_item_callbacks .clearAndFree ();
324+ }
325+
326+ pub fn setMenuBar (self : * Window , bar : lib.MenuBar ) void {
327+ const rootMenu = win32 .CreateMenu ().? ;
328+ self .clearAndFreeMenus ();
329+ self .initMenu (rootMenu , bar .menus ) catch {
330+ // TODO: Handle error in appropriate way
331+ };
332+ self .menus .append (rootMenu ) catch {
333+ // TODO: Handle error in appropriate way
334+ };
335+ _ = win32 .SetMenu (self .hwnd , rootMenu );
336+ getEventUserData (self .hwnd ).classUserdata = @intFromPtr (self );
337+ }
338+
278339 pub fn setSourceDpi (self : * Window , dpi : u32 ) void {
279340 self .source_dpi = dpi ;
280341 }
@@ -341,6 +402,16 @@ pub fn Events(comptime T: type) type {
341402 else = > {},
342403 }
343404 }
405+ // For menubar item events, HIWORD(wp) and lp are set to 0.
406+ else if (code == 0 ) {
407+ const data = getEventUserData (hwnd );
408+ const window : * Window = @ptrFromInt (data .classUserdata );
409+ const id : u16 = @intCast (wp & 0xFFFF );
410+
411+ if (window .menu_item_callbacks .items [id ]) | callback | {
412+ callback ();
413+ }
414+ }
344415 },
345416 win32 .WM_CTLCOLOREDIT = > {
346417 const data = getEventUserData (@as (HWND , @ptrFromInt (@as (usize , @bitCast (lp )))));
0 commit comments