widgets(table): implemented optional active row contents callback & context
- Added `active_content_fn` as an optional callback function that can be provided to `TableContext` to vertically expand the active row with additional info. - Added `active_ctx` and `active_y_off` to support the `active_content_fn`. - Updated the Table Widget example with
This commit is contained in:
parent
a99fd3c29f
commit
999b0f4f00
2 changed files with 95 additions and 27 deletions
|
@ -40,6 +40,7 @@ pub fn main() !void {
|
|||
var loop: vaxis.Loop(union(enum) {
|
||||
key_press: vaxis.Key,
|
||||
winsize: vaxis.Winsize,
|
||||
table_upd,
|
||||
}) = .{ .tty = &tty, .vaxis = &vx };
|
||||
try loop.init();
|
||||
try loop.start();
|
||||
|
@ -89,6 +90,7 @@ pub fn main() !void {
|
|||
// TUI State
|
||||
var active: ActiveSection = .mid;
|
||||
var moving = false;
|
||||
var see_content = false;
|
||||
|
||||
// Create an Arena Allocator for easy allocations on each Event.
|
||||
var event_arena = heap.ArenaAllocator.init(alloc);
|
||||
|
@ -157,6 +159,8 @@ pub fn main() !void {
|
|||
else try rows_list.append(demo_tbl.row);
|
||||
demo_tbl.sel_rows = try rows_list.toOwnedSlice();
|
||||
}
|
||||
// See Row Content
|
||||
if (key.matches(vaxis.Key.enter, .{})) see_content = !see_content;
|
||||
},
|
||||
.btm => {
|
||||
if (key.matchesAny(&.{ vaxis.Key.up, 'k' }, .{}) and moving) active = .mid
|
||||
|
@ -183,7 +187,57 @@ pub fn main() !void {
|
|||
moving = false;
|
||||
},
|
||||
.winsize => |ws| try vx.resize(alloc, tty.anyWriter(), ws),
|
||||
//else => {},
|
||||
else => {},
|
||||
}
|
||||
|
||||
// Content
|
||||
seeRow: {
|
||||
if (!see_content) {
|
||||
demo_tbl.active_content_fn = null;
|
||||
demo_tbl.active_ctx = &{};
|
||||
break :seeRow;
|
||||
}
|
||||
const RowContext = struct{
|
||||
row: []const u8,
|
||||
bg: vaxis.Color,
|
||||
};
|
||||
const row_ctx = RowContext{
|
||||
.row = try fmt.allocPrint(event_alloc, "Row #: {d}", .{ demo_tbl.row }),
|
||||
.bg = demo_tbl.active_bg,
|
||||
};
|
||||
demo_tbl.active_ctx = &row_ctx;
|
||||
demo_tbl.active_content_fn = struct{
|
||||
fn see(win: *vaxis.Window, ctx_raw: *const anyopaque) !usize {
|
||||
const ctx: *const RowContext = @alignCast(@ptrCast(ctx_raw));
|
||||
win.height = 5;
|
||||
const see_win = win.child(.{
|
||||
.x_off = 0,
|
||||
.y_off = 1,
|
||||
.width = .{ .limit = win.width },
|
||||
.height = .{ .limit = 4 },
|
||||
});
|
||||
see_win.fill(.{ .style = .{ .bg = ctx.bg } });
|
||||
const content_logo =
|
||||
\\
|
||||
\\░█▀▄░█▀█░█░█░░░█▀▀░█▀█░█▀█░▀█▀░█▀▀░█▀█░▀█▀
|
||||
\\░█▀▄░█░█░█▄█░░░█░░░█░█░█░█░░█░░█▀▀░█░█░░█░
|
||||
\\░▀░▀░▀▀▀░▀░▀░░░▀▀▀░▀▀▀░▀░▀░░▀░░▀▀▀░▀░▀░░▀░
|
||||
;
|
||||
const content_segs: []const vaxis.Cell.Segment = &.{
|
||||
.{
|
||||
.text = ctx.row,
|
||||
.style = .{ .bg = ctx.bg },
|
||||
},
|
||||
.{
|
||||
.text = content_logo,
|
||||
.style = .{ .bg = ctx.bg },
|
||||
},
|
||||
};
|
||||
_ = try see_win.print(content_segs, .{});
|
||||
return see_win.height;
|
||||
}
|
||||
}.see;
|
||||
loop.postEvent(.table_upd);
|
||||
}
|
||||
|
||||
// Sections
|
||||
|
@ -200,7 +254,7 @@ pub fn main() !void {
|
|||
.height = .{ .limit = win.height / top_div },
|
||||
});
|
||||
for (title_segs[0..]) |*title_seg|
|
||||
title_seg.*.style.bg = if (active == .top) selected_bg else other_bg;
|
||||
title_seg.style.bg = if (active == .top) selected_bg else other_bg;
|
||||
top_bar.fill(.{ .style = .{
|
||||
.bg = if (active == .top) selected_bg else other_bg,
|
||||
} });
|
||||
|
|
|
@ -19,6 +19,15 @@ pub const TableContext = struct {
|
|||
|
||||
/// Active status of the Table.
|
||||
active: bool = false,
|
||||
/// Active Content Callback Function.
|
||||
/// If available, this will be called to vertically expand the active row with additional info.
|
||||
active_content_fn: ?*const fn(*vaxis.Window, *const anyopaque) anyerror!usize = null,
|
||||
/// Active Content Context
|
||||
/// This will be provided to the `active_content` callback when called.
|
||||
active_ctx: *const anyopaque = &{},
|
||||
/// Y Offset for rows beyond the Active Content.
|
||||
/// (This will be calculated automatically)
|
||||
active_y_off: usize = 0,
|
||||
|
||||
/// The Background Color for the Active Row and Column Header.
|
||||
selected_bg: vaxis.Cell.Color,
|
||||
|
@ -37,10 +46,6 @@ pub const TableContext = struct {
|
|||
y_off: usize = 0,
|
||||
|
||||
/// Column Width
|
||||
<<<<<<< HEAD
|
||||
/// Note, this should be treated as Read Only. The Column Width will be calculated during `drawTable()`.
|
||||
col_width: ?usize = 0,
|
||||
=======
|
||||
/// Note, if this is left `null` the Column Width will be dynamically calculated during `drawTable()`.
|
||||
//col_width: ?usize = null,
|
||||
col_width: WidthStyle = .dynamic_fill,
|
||||
|
@ -56,7 +61,6 @@ pub const WidthStyle = union(enum) {
|
|||
static_all: usize,
|
||||
/// Statically set individual Column Widths to specific values.
|
||||
static_individual: []const usize,
|
||||
>>>>>>> 7918b49 (widgets(table): implemented customizable column widths)
|
||||
};
|
||||
|
||||
/// Draw a Table for the TUI.
|
||||
|
@ -136,7 +140,7 @@ pub fn drawTable(
|
|||
.{ .limit = win.height },
|
||||
);
|
||||
|
||||
if (table_ctx.col > headers.len - 1) table_ctx.*.col = headers.len - 1;
|
||||
if (table_ctx.col > headers.len - 1) table_ctx.col = headers.len - 1;
|
||||
var col_start: usize = 0;
|
||||
for (headers[0..], 0..) |hdr_txt, idx| {
|
||||
const col_width = try calcColWidth(
|
||||
|
@ -149,7 +153,6 @@ pub fn drawTable(
|
|||
const hdr_bg =
|
||||
if (table_ctx.active and idx == table_ctx.col) table_ctx.active_bg else if (idx % 2 == 0) table_ctx.hdr_bg_1 else table_ctx.hdr_bg_2;
|
||||
const hdr_win = table_win.child(.{
|
||||
//.x_off = idx * col_width,
|
||||
.x_off = col_start,
|
||||
.y_off = 0,
|
||||
.width = .{ .limit = col_width },
|
||||
|
@ -168,39 +171,51 @@ pub fn drawTable(
|
|||
_ = try hdr.print(seg[0..], .{ .wrap = .word });
|
||||
}
|
||||
|
||||
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 (table_ctx.active_content_fn == null) table_ctx.active_y_off = 0;
|
||||
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 (table_ctx.row + table_ctx.active_y_off >= end -| 1)
|
||||
end -|= table_ctx.active_y_off;
|
||||
if (end > data_items.len) end = data_items.len;
|
||||
table_ctx.*.start = tableStart: {
|
||||
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_items.len - 1)
|
||||
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;
|
||||
end = table_ctx.start + max_items;
|
||||
if (table_ctx.row + table_ctx.active_y_off >= end -| 1)
|
||||
end -|= table_ctx.active_y_off;
|
||||
if (end > data_items.len) end = data_items.len;
|
||||
for (data_items[table_ctx.start..end], 0..) |data, idx| {
|
||||
table_ctx.active_y_off = 0;
|
||||
for (data_items[table_ctx.start..end], 0..) |data, row| {
|
||||
const row_bg = rowBG: {
|
||||
if (table_ctx.active and table_ctx.start + idx == table_ctx.row)
|
||||
if (table_ctx.active and table_ctx.start + row == table_ctx.row)
|
||||
break :rowBG table_ctx.active_bg;
|
||||
if (table_ctx.sel_rows) |rows| {
|
||||
if (mem.indexOfScalar(usize, rows, table_ctx.start + idx) != null) break :rowBG table_ctx.selected_bg;
|
||||
if (mem.indexOfScalar(usize, rows, table_ctx.start + row) != null) break :rowBG table_ctx.selected_bg;
|
||||
}
|
||||
if (idx % 2 == 0) break :rowBG table_ctx.row_bg_1;
|
||||
if (row % 2 == 0) break :rowBG table_ctx.row_bg_1;
|
||||
break :rowBG table_ctx.row_bg_2;
|
||||
};
|
||||
|
||||
const row_win = table_win.initChild(
|
||||
0,
|
||||
1 + idx,
|
||||
.{ .limit = table_win.width },
|
||||
.{ .limit = 1 },
|
||||
);
|
||||
var row_win = table_win.child(.{
|
||||
.x_off = 0,
|
||||
.y_off = 1 + row + table_ctx.active_y_off,
|
||||
.width = .{ .limit = table_win.width },
|
||||
.height = .{ .limit = 1 },
|
||||
});
|
||||
if (table_ctx.start + row == table_ctx.row) {
|
||||
table_ctx.active_y_off = if (table_ctx.active_content_fn) |content| try content(&row_win, table_ctx.active_ctx) else 0;
|
||||
//if (table_ctx.row + table_ctx.active_y_off >= end)
|
||||
// try drawTable(alloc, win, headers, data_list, table_ctx);
|
||||
}
|
||||
const DataT = @TypeOf(data);
|
||||
col_start = 0;
|
||||
const item_fields = meta.fields(DataT);
|
||||
|
@ -215,7 +230,6 @@ pub fn drawTable(
|
|||
const item = @field(data, item_field.name);
|
||||
const ItemT = @TypeOf(item);
|
||||
const item_win = row_win.child(.{
|
||||
//.x_off = item_idx * col_width,
|
||||
.x_off = col_start,
|
||||
.y_off = 0,
|
||||
.width = .{ .limit = col_width },
|
||||
|
|
Loading…
Add table
Reference in a new issue