widgets(table): add support for Slices and MultiArrayLists
- Updated the Table Widget to support Slices and MultiArrayLists (in addtion to ArrayLists) for the `data_list` parameter of `drawTable()`.
This commit is contained in:
parent
707c050a76
commit
9487916406
3 changed files with 71 additions and 11 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,3 +3,4 @@ zig-cache/
|
|||
zig-out/
|
||||
*.log
|
||||
Session*.*vim
|
||||
commit_msg
|
||||
|
|
|
@ -23,6 +23,9 @@ pub fn main() !void {
|
|||
const users_buf = try alloc.dupe(User, users[0..]);
|
||||
const user_list = std.ArrayList(User).fromOwnedSlice(alloc, users_buf);
|
||||
defer user_list.deinit();
|
||||
var user_mal = std.MultiArrayList(User){};
|
||||
for (users_buf[0..]) |user| try user_mal.append(alloc, user);
|
||||
defer user_mal.deinit(alloc);
|
||||
|
||||
var tty = try vaxis.Tty.init();
|
||||
defer tty.deinit();
|
||||
|
@ -191,7 +194,9 @@ pub fn main() !void {
|
|||
event_alloc,
|
||||
middle_bar,
|
||||
&.{ "First", "Last", "Username", "Email", "Phone#" },
|
||||
//users_buf[0..],
|
||||
user_list,
|
||||
//user_mal,
|
||||
&demo_tbl,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -34,25 +34,79 @@ pub const TableContext = struct {
|
|||
|
||||
/// Column Width
|
||||
/// Note, this should be treated as Read Only. The Column Width will be calculated during `drawTable()`.
|
||||
col_width: usize = 0,
|
||||
col_width: ?usize = 0,
|
||||
};
|
||||
|
||||
/// Draw a Table for the TUI.
|
||||
pub fn drawTable(
|
||||
/// This should be an ArenaAllocator that can be deinitialized after each event call.
|
||||
/// The Allocator is only used in two cases:
|
||||
/// 1. If a cell is a non-String. If the Allocator is not provided, those cells will show "[unsupported (TypeName)]".
|
||||
/// 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.
|
||||
/// The Allocator is only used in three cases:
|
||||
/// 1. If a cell is a non-String. (If the Allocator is not provided, those cells will show "[unsupported (TypeName)]".)
|
||||
/// 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.)
|
||||
/// 3. To copy a MultiArrayList into a normal slice. (Note, this is an expensive operation. Prefer to pass a Slice or ArrayList if possible.)
|
||||
alloc: ?mem.Allocator,
|
||||
/// The parent Window to draw to.
|
||||
win: vaxis.Window,
|
||||
/// Headers for the Table
|
||||
headers: []const []const u8,
|
||||
/// This must be an ArrayList.
|
||||
/// This must be a Slice, ArrayList, or MultiArrayList.
|
||||
/// Note, MultiArrayList support currently requires allocation.
|
||||
data_list: anytype,
|
||||
// The Table Context for this Table.
|
||||
table_ctx: *TableContext,
|
||||
) !void {
|
||||
var di_is_mal = false;
|
||||
const data_items = getData: {
|
||||
const DataListT = @TypeOf(data_list);
|
||||
const data_ti = @typeInfo(DataListT);
|
||||
switch (data_ti) {
|
||||
.Pointer => |ptr| {
|
||||
if (ptr.size != .Slice) return error.UnsupportedTableDataType;
|
||||
break :getData data_list;
|
||||
},
|
||||
.Struct => {
|
||||
const di_fields = meta.fields(DataListT);
|
||||
const al_fields = meta.fields(std.ArrayList([]const u8));
|
||||
const mal_fields = meta.fields(std.MultiArrayList(struct{ a: u8 = 0, b: u32 = 0 }));
|
||||
// Probably an ArrayList
|
||||
const is_al = comptime if (
|
||||
mem.indexOf(u8, @typeName(DataListT), "MultiArrayList") == null and
|
||||
mem.indexOf(u8, @typeName(DataListT), "ArrayList") != null and
|
||||
al_fields.len == di_fields.len
|
||||
) isAL: {
|
||||
var is = true;
|
||||
for (al_fields, di_fields) |al_field, di_field|
|
||||
is = is and mem.eql(u8, al_field.name, di_field.name);
|
||||
break :isAL is;
|
||||
} else false;
|
||||
if (is_al) break :getData data_list.items;
|
||||
|
||||
// Probably a MultiArrayList
|
||||
const is_mal = if (
|
||||
mem.indexOf(u8, @typeName(DataListT), "MultiArrayList") != null and
|
||||
mal_fields.len == di_fields.len
|
||||
) isMAL: {
|
||||
var is = true;
|
||||
inline for (mal_fields, di_fields) |mal_field, di_field|
|
||||
is = is and mem.eql(u8, mal_field.name, di_field.name);
|
||||
break :isMAL is;
|
||||
} else false;
|
||||
if (!is_mal) return error.UnsupportedTableDataType;
|
||||
if (alloc) |_alloc| {
|
||||
di_is_mal = true;
|
||||
const mal_slice = data_list.slice();
|
||||
const DataT = @TypeOf(mal_slice.get(0));
|
||||
var data_out_list = std.ArrayList(DataT).init(_alloc);
|
||||
for (0..mal_slice.len) |idx| try data_out_list.append(mal_slice.get(idx));
|
||||
break :getData try data_out_list.toOwnedSlice();
|
||||
}
|
||||
return error.UnsupportedTableDataType;
|
||||
},
|
||||
else => return error.UnsupportedTableDataType,
|
||||
}
|
||||
};
|
||||
defer if (di_is_mal) alloc.?.free(data_items);
|
||||
|
||||
const table_win = win.initChild(
|
||||
0,
|
||||
table_ctx.y_off,
|
||||
|
@ -87,23 +141,23 @@ pub fn drawTable(
|
|||
_ = try hdr.print(seg[0..], .{ .wrap = .word });
|
||||
}
|
||||
|
||||
const max_items = if (data_list.items.len > table_win.height -| 1) table_win.height -| 1 else data_list.items.len;
|
||||
const max_items = if (data_items.len > table_win.height -| 1) table_win.height -| 1 else data_items.len;
|
||||
var end = table_ctx.*.start + max_items;
|
||||
if (end > data_list.items.len) end = data_list.items.len;
|
||||
if (end > data_items.len) end = data_items.len;
|
||||
table_ctx.*.start = tableStart: {
|
||||
if (table_ctx.row == 0)
|
||||
break :tableStart 0;
|
||||
if (table_ctx.row < table_ctx.start)
|
||||
break :tableStart table_ctx.start - (table_ctx.start - table_ctx.row);
|
||||
if (table_ctx.row >= data_list.items.len - 1)
|
||||
table_ctx.*.row = data_list.items.len - 1;
|
||||
if (table_ctx.row >= data_items.len - 1)
|
||||
table_ctx.*.row = data_items.len - 1;
|
||||
if (table_ctx.row >= end)
|
||||
break :tableStart table_ctx.start + (table_ctx.row - end + 1);
|
||||
break :tableStart table_ctx.start;
|
||||
};
|
||||
end = table_ctx.*.start + max_items;
|
||||
if (end > data_list.items.len) end = data_list.items.len;
|
||||
for (data_list.items[table_ctx.start..end], 0..) |data, idx| {
|
||||
if (end > data_items.len) end = data_items.len;
|
||||
for (data_items[table_ctx.start..end], 0..) |data, idx| {
|
||||
const row_bg =
|
||||
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;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue