pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "game.TopBar"); allocator: std.mem.Allocator, arena: std.heap.ArenaAllocator, windowsMenu: std.ArrayListUnmanaged(*MenuEntry) = .{}, entriesByName: std.AutoHashMapUnmanaged(u32, *MenuEntry) = .{}, menuOpen: bool = false, pub fn init(self: *@This(), allocator: std.mem.Allocator, first: bool) !void { if (!first) return; self.* = .{ .allocator = allocator, .arena = std.heap.ArenaAllocator.init(allocator), }; try self.addWindowsMenu(.{ .name = "sample window", .vtable = &NeonObjectTable, .open = false, .ctx = self, .windowFunction = windowOpen, }); } pub fn setEntryOpen(self: *@This(), name: []const u8, open: ?bool) void { var n = core.MakeName(name); const x = self.entriesByName.get(n.handle()).?; if (open) |o| { x.open = o; } else { x.open = !x.open; } } pub fn addMenuObject(self: *@This(), ptr: anytype, comptime name: []const u8) !void { const T = @TypeOf(ptr.*); try self.addWindowsMenu(.{ .vtable = &T.NeonObjectTable, .name = name, .ctx = ptr, .windowFunction = T.windowOpen, }); } pub fn windowOpen(entry: *MenuEntry, dt: f64) void { _ = dt; ig.showDemoWindow(&entry.open); } pub fn addWindowsMenu(self: *@This(), entry: MenuEntry) !void { const new = try self.arena.allocator().create(MenuEntry); new.* = entry; var name = core.MakeName(entry.name); if (self.entriesByName.getEntry(name.handle())) |e| { core.logDisplay("TopBar", "{s} already registered, updating menu entry instead", .{entry.name}); e.value_ptr.*.* = entry; } else { try self.windowsMenu.append(self.arena.allocator(), new); try self.entriesByName.put(self.arena.allocator(), name.handle(), new); } } pub fn tick(self: *@This(), dt: f64) void { if (!self.menuOpen) { return; } if (ig.beginMainMenuBar()) { if (ig.beginMenu("Windows..", true)) { for (self.windowsMenu.items) |entry| { if (ig.menuItem_Bool(entry.name.ptr, null, entry.open, true)) { entry.open = !entry.open; } } ig.endMenu(); } if (ig.beginMenu("Operations", true)) { if (ig.menuItem_Bool("Recompile", null, false, true)) { core.engine_log("recompiling in DEBUG mode", .{}); var success = true; _ = core.shell.runCmd(self.allocator, &.{ "zig", "build", "install" }, ".") catch { core.graphics_logs("compile failed!"); success = false; }; if (success) core.graphics_logs("compile completed"); } ig.endMenu(); } ig.endMainMenuBar(); for (self.windowsMenu.items) |entry| { if (entry.open) { entry.updateFunc(); entry.windowFunction(entry, dt); } } } } pub fn destroy(self: *@This()) void { self.arena.deinit(); } pub const MenuEntry = struct { name: []const u8, open: bool = false, ctx: ?*anyopaque, windowFunction: *const fn (*MenuEntry, f64) void, vtable: *core.EngineObjectVTable, version: i32 = -1, pub fn updateFunc(self: *@This()) void { if (self.vtable.version != self.version) { const func = self.vtable.findFunc("windowOpen"); self.windowFunction = @ptrCast(@alignCast(func)); self.version = self.vtable.version; } } }; const ig = @import("../imgui.zig").api; const std = @import("std"); const core = @import("core");