pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "main"); const GameContext = @This(); allocator: std.mem.Allocator, commandQueue: core.RingQueue(Task), activeCommand: ?*sys.SubprocessTask = null, showInstructions: bool = false, dockingInitialized: bool = false, const Task = union(enum(u8)) { subprocess: *sys.SubprocessTask, func: *const fn (*GameContext) void, }; pub fn init(allocator: std.mem.Allocator) !*@This() { const self = try allocator.create(@This()); self.* = .{ .allocator = allocator, .commandQueue = try core.RingQueue(Task).init(self.allocator, 4096), }; return self; } pub fn prepare(self: *@This()) !void { _ = self; core.engine_log("program ready", .{}); if (core.getEngineObject(imgui.utils.TopBar)) |topbar| { topbar.menuOpen = true; } } pub fn tick(self: *@This(), dt: f64) void { _ = dt; self.tickTasks() catch {}; if (self.activeCommand != null or self.commandQueue.count() > 0) { ig.setNextWindowPos(.{ .x = 20, .y = 20 }, .{}, .{}); if (ig.begin("tasks", null, .{ .always_auto_resize = true, .no_title_bar = true, .no_move = true, .no_resize = true, .no_collapse = true, })) {} ig.end(); } // Initialize docking layout on first run if (!self.dockingInitialized) { // Get the main viewport dockspace ID (created by dockSpaceOverViewport) const main_dockspace_id = ig.getID_Str("##DockSpaceViewport_11111111"); // Dock our toolbox window to the center of the main dockspace ig.dockBuilderDockWindow("Toolbox Window", main_dockspace_id); ig.dockBuilderFinish(main_dockspace_id); self.dockingInitialized = true; } // Create a docked window if (ig.begin("Toolbox Window", null, .{})) { ig.textf("This is a docked toolbox window", .{}); if (ig.button("Recompile Shaders", .{})) { start_RecompileShaders() catch {}; } ig.separator(); ig.textf("Additional toolbox content can go here", .{}); if (self.activeCommand != null or self.commandQueue.count() > 0) { ig.textf("outstanding tasks: {d}", .{self.commandQueue.count() + 1}); if (self.activeCommand) |cmd| { ig.textf("current command: ", .{}); for (cmd.args) |arg| { ig.textf("{s}", .{arg}); ig.sameLine(0, 4); } } } } ig.end(); } fn start_RecompileShaders() !void {} fn tickTasks(self: *@This()) !void { if (self.activeCommand == null) { if (self.commandQueue.popFromLocked()) |task| { switch (task) { .subprocess => |t| { self.activeCommand = t; // core.engine_log("starting task {any}", .{t.args}); try t.run(); }, .func => |f| { f(self); }, } } } if (self.activeCommand) |active| { if (active.checkComplete()) { active.destroy(); core.engine_logs("task complete"); self.activeCommand = null; } } } fn loadFileAllocFromCwd(allocator: std.mem.Allocator, dir: std.fs.Dir, path: []const u8) ![]u8 { var file = try dir.openFile(path, .{}); defer file.close(); const filesize = (try file.stat()).size + 1; // add null byte const buffer: []u8 = try allocator.alloc(u8, filesize); errdefer allocator.free(buffer); try file.reader().readNoEof(buffer[0 .. buffer.len - 1]); buffer[buffer.len - 1] = 0; return buffer; } fn displayInstructions(self: *@This()) void { self.showInstructions = true; } pub fn deinit(self: *@This()) void { self.allocator.destroy(self); } fn copyRecursiveDir( allocator: std.mem.Allocator, src_dir: *std.fs.Dir, dest_dir: *std.fs.Dir, ) !void { var walker = try src_dir.walk(allocator); defer walker.deinit(); while (try walker.next()) |entry| { switch (entry.kind) { .file => { try entry.dir.copyFile(entry.basename, dest_dir.*, entry.path, .{}); }, .directory => { try dest_dir.makeDir(entry.path); }, else => {}, } } } pub fn main() anyerror!void { var spec = try api.getSpec("New Project Dialogue"); try spec.put("useGPA", .{ .boolean = false }); try spec.put("configName", .{ .string = "toolbox" }); _ = api.startEngine(&NeonObjectTable, &spec); } const builtin = @import("builtin"); const std = @import("std"); const api = @import("backlog"); const imgui = api.imgui; const ig = api.imgui.api; const core = api.core; const sys = api.sys;