···34343535 /// Column Width
3636 /// Note, this should be treated as Read Only. The Column Width will be calculated during `drawTable()`.
3737- col_width: usize = 0,
3737+ col_width: ?usize = 0,
3838};
39394040/// Draw a Table for the TUI.
4141pub fn drawTable(
4242 /// This should be an ArenaAllocator that can be deinitialized after each event call.
4343- /// The Allocator is only used in two cases:
4444- /// 1. If a cell is a non-String. If the Allocator is not provided, those cells will show "[unsupported (TypeName)]".
4545- /// 2. To show that a value is too large to fit into a cell. If the Allocator is not provided, they'll just be cutoff.
4343+ /// The Allocator is only used in three cases:
4444+ /// 1. If a cell is a non-String. (If the Allocator is not provided, those cells will show "[unsupported (TypeName)]".)
4545+ /// 2. To show that a value is too large to fit into a cell using '...'. (If the Allocator is not provided, they'll just be cutoff.)
4646+ /// 3. To copy a MultiArrayList into a normal slice. (Note, this is an expensive operation. Prefer to pass a Slice or ArrayList if possible.)
4647 alloc: ?mem.Allocator,
4748 /// The parent Window to draw to.
4849 win: vaxis.Window,
4950 /// Headers for the Table
5051 headers: []const []const u8,
5151- /// This must be an ArrayList.
5252+ /// This must be a Slice, ArrayList, or MultiArrayList.
5353+ /// Note, MultiArrayList support currently requires allocation.
5254 data_list: anytype,
5355 // The Table Context for this Table.
5456 table_ctx: *TableContext,
5557) !void {
5858+ var di_is_mal = false;
5959+ const data_items = getData: {
6060+ const DataListT = @TypeOf(data_list);
6161+ const data_ti = @typeInfo(DataListT);
6262+ switch (data_ti) {
6363+ .Pointer => |ptr| {
6464+ if (ptr.size != .Slice) return error.UnsupportedTableDataType;
6565+ break :getData data_list;
6666+ },
6767+ .Struct => {
6868+ const di_fields = meta.fields(DataListT);
6969+ const al_fields = meta.fields(std.ArrayList([]const u8));
7070+ const mal_fields = meta.fields(std.MultiArrayList(struct{ a: u8 = 0, b: u32 = 0 }));
7171+ // Probably an ArrayList
7272+ const is_al = comptime if (
7373+ mem.indexOf(u8, @typeName(DataListT), "MultiArrayList") == null and
7474+ mem.indexOf(u8, @typeName(DataListT), "ArrayList") != null and
7575+ al_fields.len == di_fields.len
7676+ ) isAL: {
7777+ var is = true;
7878+ for (al_fields, di_fields) |al_field, di_field|
7979+ is = is and mem.eql(u8, al_field.name, di_field.name);
8080+ break :isAL is;
8181+ } else false;
8282+ if (is_al) break :getData data_list.items;
8383+8484+ // Probably a MultiArrayList
8585+ const is_mal = if (
8686+ mem.indexOf(u8, @typeName(DataListT), "MultiArrayList") != null and
8787+ mal_fields.len == di_fields.len
8888+ ) isMAL: {
8989+ var is = true;
9090+ inline for (mal_fields, di_fields) |mal_field, di_field|
9191+ is = is and mem.eql(u8, mal_field.name, di_field.name);
9292+ break :isMAL is;
9393+ } else false;
9494+ if (!is_mal) return error.UnsupportedTableDataType;
9595+ if (alloc) |_alloc| {
9696+ di_is_mal = true;
9797+ const mal_slice = data_list.slice();
9898+ const DataT = @TypeOf(mal_slice.get(0));
9999+ var data_out_list = std.ArrayList(DataT).init(_alloc);
100100+ for (0..mal_slice.len) |idx| try data_out_list.append(mal_slice.get(idx));
101101+ break :getData try data_out_list.toOwnedSlice();
102102+ }
103103+ return error.UnsupportedTableDataType;
104104+ },
105105+ else => return error.UnsupportedTableDataType,
106106+ }
107107+ };
108108+ defer if (di_is_mal) alloc.?.free(data_items);
109109+56110 const table_win = win.initChild(
57111 0,
58112 table_ctx.y_off,
···87141 _ = try hdr.print(seg[0..], .{ .wrap = .word });
88142 }
891439090- const max_items = if (data_list.items.len > table_win.height -| 1) table_win.height -| 1 else data_list.items.len;
144144+ const max_items = if (data_items.len > table_win.height -| 1) table_win.height -| 1 else data_items.len;
91145 var end = table_ctx.*.start + max_items;
9292- if (end > data_list.items.len) end = data_list.items.len;
146146+ if (end > data_items.len) end = data_items.len;
93147 table_ctx.*.start = tableStart: {
94148 if (table_ctx.row == 0)
95149 break :tableStart 0;
96150 if (table_ctx.row < table_ctx.start)
97151 break :tableStart table_ctx.start - (table_ctx.start - table_ctx.row);
9898- if (table_ctx.row >= data_list.items.len - 1)
9999- table_ctx.*.row = data_list.items.len - 1;
152152+ if (table_ctx.row >= data_items.len - 1)
153153+ table_ctx.*.row = data_items.len - 1;
100154 if (table_ctx.row >= end)
101155 break :tableStart table_ctx.start + (table_ctx.row - end + 1);
102156 break :tableStart table_ctx.start;
103157 };
104158 end = table_ctx.*.start + max_items;
105105- if (end > data_list.items.len) end = data_list.items.len;
106106- for (data_list.items[table_ctx.start..end], 0..) |data, idx| {
159159+ if (end > data_items.len) end = data_items.len;
160160+ for (data_items[table_ctx.start..end], 0..) |data, idx| {
107161 const row_bg =
108162 if (table_ctx.active and table_ctx.start + idx == table_ctx.row) table_ctx.selected_bg else if (idx % 2 == 0) table_ctx.row_bg_1 else table_ctx.row_bg_2;
109163