Skip to content

Commit d85af5c

Browse files
committed
win32: Added menubars to backend
1 parent 60fa439 commit d85af5c

File tree

1 file changed

+72
-1
lines changed

1 file changed

+72
-1
lines changed

src/backends/win32/backend.zig

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const zigwin32 = @import("zigwin32");
1212
const win32 = zigwin32.everything;
1313
const gdi = @import("gdip.zig");
1414
const HWND = win32.HWND;
15+
const HMENU = win32.HMENU;
1516
const HINSTANCE = win32.HINSTANCE;
1617
const RECT = win32.RECT;
1718
const MSG = win32.MSG;
@@ -162,6 +163,10 @@ var defaultWHWND: HWND = undefined;
162163
pub 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

Comments
 (0)