const std = @import("std"); const dvui = @import("dvui"); const entypo = dvui.entypo; const Backend = @import("SDLBackend"); var gpa_instance = std.heap.GeneralPurposeAllocator(.{}){}; const gpa = gpa_instance.allocator(); const vsync = true; pub fn main() !void { var backend = try Backend.init(.{ .width = 480, .height = 360, .vsync = vsync, .title = "File Picker", }); defer backend.deinit(); var win = try dvui.Window.init(@src(), 0, gpa, backend.backend()); defer win.deinit(); win.theme = &dvui.Adwaita.light; var ctx = try Ctx.init(); loop: while (true) { var nstime = win.beginWait(backend.hasEvent()); try win.begin(nstime); backend.clear(); const quit = try backend.addAllEvents(&win); if (quit or .exit == try ctx.frame()) break :loop; const end_micros = try win.end(.{}); backend.setCursor(win.cursorRequested()); backend.renderPresent(); const wait_event_micros = win.waitTime(end_micros, null); backend.waitEventTimeout(wait_event_micros); } } pub const Ctx = struct { path: []u8 = "", filename: []u8 = "", entries: std.ArrayListUnmanaged(std.fs.IterableDir.Entry) = .{}, pub fn init() !Ctx { var ret = Ctx{}; try ret.list_files(); return ret; } pub const ExitStatus = enum { ok, exit }; pub fn frame(ctx: *Ctx) !ExitStatus { const bg = dvui.Options{ .background = true, .expand = .horizontal, .corner_radius = dvui.Rect.all(0), }; const opts = dvui.Options{ .corner_radius = dvui.Rect.all(0), }; var box = try dvui.box(@src(), .vertical, bg.override(.{ .expand = .both, })); defer box.deinit(); { var top = try dvui.box(@src(), .horizontal, bg); defer top.deinit(); if (try dvui.button(@src(), "..", opts)) { try std.os.chdir(".."); try ctx.list_files(); } const te = try dvui.textEntry(@src(), .{ .text = ctx.path }, bg); defer te.deinit(); } { var bottom = try dvui.box(@src(), .horizontal, opts.override(.{ .expand = .horizontal, .gravity_y = 1, })); defer bottom.deinit(); const button_enabled = ctx.filename.len != 0; var button_opts = opts.override(.{ .gravity_x = 1 }); if (button_enabled) { button_opts.color_style = .accent; } else { button_opts.color_style = .control; button_opts.color_hover = button_opts.color(.fill); } if (try dvui.button(@src(), "ok", button_opts) and button_enabled) { std.debug.print("{s}/{s}\n", .{ ctx.path, ctx.filename }); return .exit; } const tl = try dvui.textEntry(@src(), .{ .text = ctx.filename }, opts.override(.{ .expand = .horizontal, })); defer tl.deinit(); } { var scroll = try dvui.scrollArea(@src(), .{}, .{ .expand = .both }); defer scroll.deinit(); for (ctx.entries.items, 0..) |entry, i| { const name, const icon, const color_style: dvui.Theme.ColorStyle = switch (entry.kind) { .directory => .{ "folder", entypo.folder, .window }, .file => .{ "file", entypo.text_document, .control }, else => .{ "other", entypo.help, .content }, }; const id = opts.override(.{ .id_extra = i }); const wide = id.override(.{ .color_style = color_style, .expand = .horizontal, .margin = dvui.Rect.all(0), }); var bw = dvui.ButtonWidget.init(@src(), .{}, wide); try bw.install(); bw.processEvents(); try bw.drawBackground(); const m = try dvui.menu(@src(), .horizontal, id); try dvui.icon(@src(), name, icon, id.override(.{ .gravity_y = 0.4 })); try dvui.labelNoFmt(@src(), entry.name, id); m.deinit(); var clicked = bw.clicked(); try bw.drawFocus(); bw.deinit(); if (clicked) switch (entry.kind) { .directory => { try std.os.chdir(entry.name); try ctx.list_files(); }, else => { gpa.free(ctx.filename); ctx.filename = try gpa.dupe(u8, entry.name); }, }; } } return .ok; } pub fn list_files(ctx: *Ctx) !void { var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; const path = try std.os.realpath(".", &buf); ctx.path = try gpa.dupe(u8, path); const cwd = std.fs.cwd(); const iterable = try cwd.openIterableDir(".", .{}); var iterator = iterable.iterate(); ctx.entries.clearRetainingCapacity(); while (try iterator.next()) |entry| { const name = try gpa.dupe(u8, entry.name); try ctx.entries.append(gpa, .{ .name = name, .kind = entry.kind }); } const Entry = std.fs.IterableDir.Entry; const items = ctx.entries.items; const closures = struct { pub fn less_than_0(_: void, l: Entry, r: Entry) bool { return std.mem.lessThan(u8, l.name, r.name); } pub fn less_than_1(_: void, l: Entry, r: Entry) bool { return @intFromEnum(l.kind) < @intFromEnum(r.kind); } }; std.mem.sort(Entry, items, {}, closures.less_than_0); std.mem.sort(Entry, items, {}, closures.less_than_1); } };