diff --git a/src/assets/fonts/generic-mono.otf b/src/assets/fonts/generic-mono.otf deleted file mode 100644 index 422eed3..0000000 Binary files a/src/assets/fonts/generic-mono.otf and /dev/null differ diff --git a/src/main-scene.zig b/src/main-scene.zig index e4d8442..e8c31c9 100644 --- a/src/main-scene.zig +++ b/src/main-scene.zig @@ -12,16 +12,17 @@ const assert = std.debug.assert; const Allocator = std.mem.Allocator; const StringList = std.ArrayList([]const u8); -const example_roms = roms.listROMs(); +const rom_list = roms.listROMs(); allocator: Allocator, chip: *ChipContext, raylib_chip: *RaylibChip, chip_sound: rl.Sound, -rom: ?ROM = null, is_gui_open: bool = true, +selected_rom_index: i32 = 3, +rom_list_scroll_index: i32 = 0, pub fn genSinWave(wave: *rl.Wave, frequency: f32) void { assert(wave.sampleSize == 16); // Only 16 bits are supported @@ -68,7 +69,7 @@ pub fn init(allocator: Allocator) !Self { .chip_sound = chip_sound, }; - self.set_rom(example_roms[3]); + self.set_rom(3); return self; } @@ -81,8 +82,9 @@ pub fn deinit(self: *Self) void { self.allocator.destroy(self.chip); } -pub fn set_rom(self: *Self, rom: ROM) void { - self.rom = rom; +pub fn set_rom(self: *Self, index: i32) void { + self.selected_rom_index = index; + const rom = rom_list[@intCast(index)]; self.chip.reset(); self.chip.set_memory(0x200, rom.data); } @@ -115,10 +117,67 @@ pub fn draw(self: *Self) void { render_height ); - self.draw_gui(); + self.drawGui(self.allocator) catch @panic("GUI OOM"); } -pub fn draw_gui(self: *Self) void { +fn GuiListView( + allocator: Allocator, + bounds: rl.Rectangle, + items: []const []const u8, + scroll_index: *i32, + active: *i32, +) !i32 { + var needed_size: usize = 0; + needed_size += items.len-1; // N-1 character are needed for the separators + for (items) |item| { + needed_size += item.len; + } + + var str_buffer = try allocator.allocSentinel(u8, needed_size, 0); + defer allocator.free(str_buffer); + + var idx: usize = 0; + for (0.., items) |i, item| { + @memcpy(str_buffer[idx..(idx+item.len)], item); + idx += item.len; + + if (i != items.len-1) { + str_buffer[idx] = ';'; + idx += 1; + } + } + + return gui.GuiListView(bounds, str_buffer, scroll_index, active); +} + +fn GuiListViewMinWidth(allocator: Allocator, height: i32, items: []const []const u8) !i32 { + const border_width = gui.GuiGetStyle(.DEFAULT , @intFromEnum(gui.GuiControlProperty.BORDER_WIDTH)); + const item_spacing = gui.GuiGetStyle(.LISTVIEW, @intFromEnum(gui.GuiListViewProperty.LIST_ITEMS_SPACING)); + const item_height = gui.GuiGetStyle(.LISTVIEW, @intFromEnum(gui.GuiListViewProperty.LIST_ITEMS_HEIGHT)); + const font_size = gui.GuiGetStyle(.DEFAULT , @intFromEnum(gui.GuiDefaultProperty.TEXT_SIZE)); + const scrollbar_width = gui.GuiGetStyle(.LISTVIEW, @intFromEnum(gui.GuiListViewProperty.SCROLLBAR_WIDTH)); + + var max_item_width: i32 = 0; + for (items) |item| { + const item_z = try allocator.dupeZ(u8, item); + defer allocator.free(item_z); + const item_width = rl.MeasureText(item_z, font_size); + + max_item_width = @max(max_item_width, item_width); + } + + const item_count: i32 = @intCast(items.len); + const need_scrollbar = ((item_height + item_spacing)*item_count > height); + + var min_width = max_item_width + 2*item_spacing + border_width; + if (need_scrollbar) { + min_width += scrollbar_width; + } + + return min_width; +} + +pub fn drawGui(self: *Self, allocator: Allocator) !void { if (rl.IsKeyPressed(.KEY_TAB)) { self.is_gui_open = !self.is_gui_open; } @@ -137,12 +196,34 @@ pub fn draw_gui(self: *Self) void { const window_width: f32 = 300; const window_height: f32 = 100; const window = rl.Rectangle{ - .x = (screen_width-window_width)/2, - .y = (screen_height-window_height)/2, + .x = @trunc((screen_width-window_width)/2), + .y = @trunc((screen_height-window_height)/2), .width = window_width, .height = window_height }; if (gui.GuiWindowBox(window, "CHIP-8 Settings") == 1) { self.is_gui_open = false; } + + rl.rlPushMatrix(); + rl.rlTranslatef(window.x, window.y, 0); + + const rom_list_names = try allocator.alloc([]const u8, rom_list.len); + defer allocator.free(rom_list_names); + + for (0.., rom_list) |i, rom| { + rom_list_names[i] = rom.name; + } + + const list_height = 100; + const list_width = try GuiListViewMinWidth(allocator, list_height, rom_list_names) + 20; + _ = try GuiListView( + allocator, + .{.x=10,.y=24, .width=@floatFromInt(list_width), .height=list_height}, + rom_list_names, + &self.rom_list_scroll_index, + &self.selected_rom_index + ); + + rl.rlPopMatrix(); } diff --git a/src/main.zig b/src/main.zig index 82c49de..71c25f7 100755 --- a/src/main.zig +++ b/src/main.zig @@ -31,11 +31,6 @@ pub fn main() anyerror!void { var scene = try MainScene.init(allocator); defer scene.deinit(); - const font_size = 24; - const font_ttf_default_numchars = 95; // TTF font generation default charset: 95 glyphs (ASCII 32..126) - const font = rl.LoadFontEx("src/assets/fonts/generic-mono.otf", font_size, null, font_ttf_default_numchars); - defer rl.UnloadFont(font); - while (!rl.WindowShouldClose()) { var dt = rl.GetFrameTime(); scene.update(dt); diff --git a/src/roms.zig b/src/roms.zig index f4441a1..07f046e 100644 --- a/src/roms.zig +++ b/src/roms.zig @@ -1,3 +1,4 @@ +const std = @import("std"); const options = @import("options"); const rom_count = options.roms.len; @@ -10,8 +11,12 @@ pub fn listROMs() [options.roms.len]ROM { var roms: [options.roms.len]ROM = undefined; for (0.., options.roms) |i, file| { + const extension = std.mem.lastIndexOfScalar(u8, file, '.') orelse file.len; + const slash = std.mem.lastIndexOfScalar(u8, file, '/') orelse -1; + + const name = file[(slash+1)..extension]; roms[i] = ROM{ - .name = file, + .name = name, .data = @embedFile(file) }; }