diff options
| author | Gabriel Schneider <[email protected]> | 2026-07-03 14:43:30 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-08-01 15:02:07 -0300 |
| commit | d1ed5bd8f55935e6470c4a7559216119571a084f (patch) | |
| tree | 434478ce8a5dcd9aed367334816f54da9953e56d /pane_ui.zig | |
| parent | 153f0a88862507cf1f5dda71f71db3d6649dfeaa (diff) | |
| download | pardes-d1ed5bd8f55935e6470c4a7559216119571a084f.tar.gz pardes-d1ed5bd8f55935e6470c4a7559216119571a084f.zip | |
fixed scaling and fitting on web ui
Diffstat (limited to 'pane_ui.zig')
| -rw-r--r-- | pane_ui.zig | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/pane_ui.zig b/pane_ui.zig new file mode 100644 index 00000000..f53fa269 --- /dev/null +++ b/pane_ui.zig @@ -0,0 +1,273 @@ +pub const TOPBAR_H: u16 = 1; +pub const GUTTER: u16 = 2; +pub const BOX_H: u16 = 1; +pub const MINW: u16 = 10; +pub const MINH: u16 = 3; +pub const PREFIX_W: u16 = 5; + +pub const Rect = struct { x: u16, y: u16, w: u16, h: u16 }; + +pub const ScrollbarButton = enum { left, middle, right }; + +pub const Scrollbar = struct { + total: usize, + offset: usize, + len: usize, +}; + +pub fn Layout(comptime max_cols: usize, comptime max_panes: usize) type { + return struct { + ncol: usize, + col_weight: [max_cols]f32, + col_terms: [max_cols][max_panes]usize, + col_n: [max_cols]usize, + }; +} + +pub fn layoutFindTerm(l: anytype, id: usize) ?struct { col: usize, idx: usize } { + var c: usize = 0; + while (c < l.ncol) : (c += 1) { + var k: usize = 0; + while (k < l.col_n[c]) : (k += 1) { + if (l.col_terms[c][k] == id) return .{ .col = c, .idx = k }; + } + } + return null; +} + +pub fn layoutRemove(l: anytype, id: usize) void { + const f = layoutFindTerm(l, id) orelse return; + const c = f.col; + var k = f.idx; + while (k + 1 < l.col_n[c]) : (k += 1) l.col_terms[c][k] = l.col_terms[c][k + 1]; + l.col_n[c] -= 1; + if (l.col_n[c] == 0) { + if (l.ncol > 1) l.col_weight[if (c > 0) c - 1 else c + 1] += l.col_weight[c]; + var j = c; + while (j + 1 < l.ncol) : (j += 1) { + l.col_terms[j] = l.col_terms[j + 1]; + l.col_n[j] = l.col_n[j + 1]; + l.col_weight[j] = l.col_weight[j + 1]; + } + l.ncol -= 1; + } +} + +pub fn layoutInsert(l: anytype, c: usize, idx: usize, id: usize) void { + var k = l.col_n[c]; + while (k > idx) : (k -= 1) l.col_terms[c][k] = l.col_terms[c][k - 1]; + l.col_terms[c][idx] = id; + l.col_n[c] += 1; +} + +pub fn layoutAppendColumn(l: anytype, id: usize) void { + if (l.ncol >= l.col_weight.len) return; + const c = l.ncol; + l.col_weight[c] = 1; + l.col_terms[c][0] = id; + l.col_n[c] = 1; + l.ncol += 1; +} + +pub fn computeGeom( + l: anytype, + items: anytype, + sw: u16, + sh: u16, + rects: anytype, + col_x: anytype, + col_w: anytype, + comptime vweightFn: anytype, +) void { + if (l.ncol == 0) return; + var wsum: f32 = 0; + for (0..l.ncol) |c| wsum += l.col_weight[c]; + if (wsum <= 0) wsum = 1; + + var x: u16 = 0; + for (0..l.ncol) |c| { + const last = c + 1 == l.ncol; + const cw: u16 = if (last) (sw -| x) else @max(1, @as(u16, @intFromFloat(@round(@as(f32, @floatFromInt(sw)) * l.col_weight[c] / wsum)))); + col_x[c] = x; + col_w[c] = cw; + + var vsum: f32 = 0; + for (0..l.col_n[c]) |k| { + if (vweightFn(items, l.col_terms[c][k])) |vweight| vsum += vweight; + } + if (vsum <= 0) vsum = 1; + + var y: u16 = TOPBAR_H; + const avail_h = sh -| TOPBAR_H; + for (0..l.col_n[c]) |k| { + const id = l.col_terms[c][k]; + const vweight = vweightFn(items, id) orelse continue; + const lastk = k + 1 == l.col_n[c]; + const ch: u16 = if (lastk) (sh -| y) else @max(1, @as(u16, @intFromFloat(@round(@as(f32, @floatFromInt(avail_h)) * vweight / vsum)))); + rects[id] = .{ .x = x, .y = y, .w = cw, .h = ch }; + y +|= ch; + } + x +|= cw; + } +} + +pub fn moveTerm( + l: anytype, + rects: anytype, + col_x: anytype, + col_w: anytype, + id: usize, + cur_x: u16, + cur_y: u16, +) void { + const src = layoutFindTerm(l, id) orelse return; + var tc: usize = if (l.ncol > 0) l.ncol - 1 else 0; + for (0..l.ncol) |c| { + if (cur_x >= col_x[c] and cur_x < col_x[c] + col_w[c]) { + tc = c; + break; + } + } + + var ti: usize = 0; + var k: usize = 0; + while (k < l.col_n[tc]) : (k += 1) { + const cid = l.col_terms[tc][k]; + if (cid == id) continue; + const r = rects[cid]; + if (cur_y < r.y + r.h / 2) break; + ti += 1; + } + + const src_solo = l.col_n[src.col] == 1; + if (tc == src.col and src_solo) return; + layoutRemove(l, id); + if (src_solo and src.col < tc) tc -= 1; + if (tc >= l.ncol) tc = l.ncol - 1; + if (ti > l.col_n[tc]) ti = l.col_n[tc]; + layoutInsert(l, tc, ti, id); +} + +pub fn paneAt(rects: anytype, pane_count: usize, col: u16, row: u16) ?usize { + for (0..pane_count) |i| { + const r = rects[i]; + if (col >= r.x and col < r.x + r.w and row >= r.y and row < r.y + r.h) return i; + } + return null; +} + +pub fn paneAtSlots(rects: anytype, slots: anytype, col: u16, row: u16) ?usize { + for (slots.*, 0..) |slot, i| { + if (slot == null) continue; + const r = rects[i]; + if (col >= r.x and col < r.x + r.w and row >= r.y and row < r.y + r.h) return i; + } + return null; +} + +pub fn inMoveBox(r: Rect, col: u16, row: u16) bool { + return col < r.x + GUTTER and row < r.y + BOX_H; +} + +pub fn scrollbarClickDelta(r: Rect, button: ScrollbarButton, col: u16, row: u16) ?i32 { + if (col >= r.x + GUTTER or row < r.y + BOX_H) return null; + const local: i32 = @as(i32, row) - @as(i32, r.y + BOX_H); + return switch (button) { + .left, .middle => -local, + .right => local, + }; +} + +pub fn drawGutter( + root: anytype, + r: Rect, + active: bool, + active_box: [3]u8, + inactive_box: [3]u8, + track: [3]u8, + thumb: [3]u8, + scrollbar: ?Scrollbar, +) void { + const gut = root.child(.{ .x_off = r.x, .y_off = r.y, .width = GUTTER, .height = r.h }); + const box_bg = if (active) active_box else inactive_box; + var gy: u16 = 0; + while (gy < r.h) : (gy += 1) { + const bg = if (gy < BOX_H) box_bg else track; + var gx: u16 = 0; + while (gx < GUTTER) : (gx += 1) { + gut.writeCell(gx, gy, .{ .char = .{ .grapheme = " " }, .style = .{ .bg = .{ .rgb = bg } } }); + } + } + + const sb = scrollbar orelse return; + const track_h: usize = if (r.h > BOX_H) r.h - BOX_H else 0; + if (track_h == 0) return; + const total = if (sb.total == 0) 1 else sb.total; + const len = @max(1, (track_h * @max(1, sb.len)) / total); + const pos = (track_h * sb.offset) / total; + var sy: usize = 0; + while (sy < track_h) : (sy += 1) { + if (sy < pos or sy >= pos + len) continue; + var gx: u16 = 0; + while (gx < GUTTER) : (gx += 1) { + gut.writeCell(gx, @intCast(BOX_H + sy), .{ .char = .{ .grapheme = " " }, .style = .{ .bg = .{ .rgb = thumb } } }); + } + } +} + +pub fn overlayDash(win: anytype, col: u16, row: u16, glyph: []const u8) void { + var cell = win.readCell(col, row) orelse return; + cell.char.grapheme = glyph; + cell.default = false; + win.writeCell(col, row, cell); +} + +pub fn drawBorderV(win: anytype, col: u16, screen_h: u16) void { + var row: u16 = TOPBAR_H; + while (row < screen_h) : (row += 1) overlayDash(win, col, row, "╎"); +} + +pub fn drawBorderH(win: anytype, x: u16, width: u16, row: u16) void { + var col = x; + while (col < x + width) : (col += 1) overlayDash(win, col, row, "╌"); +} + +pub fn drawMovePreview(win: anytype, l: anytype, col_x: anytype, col_w: anytype, cur_x: u16, cur_y: u16, color: [3]u8) void { + var tc: usize = if (l.ncol > 0) l.ncol - 1 else 0; + for (0..l.ncol) |c| { + if (cur_x >= col_x[c] and cur_x < col_x[c] + col_w[c]) { + tc = c; + break; + } + } + var col: u16 = col_x[tc]; + while (col < col_x[tc] + col_w[tc]) : (col += 1) { + win.writeCell(col, cur_y, .{ .char = .{ .grapheme = "╌" }, .style = .{ .fg = .{ .rgb = color } } }); + } + win.writeCell(cur_x, cur_y, .{ .char = .{ .grapheme = "▌" }, .style = .{ .fg = .{ .rgb = color } } }); +} + +pub fn drawHoverResizeHints( + win: anytype, + l: anytype, + rects: anytype, + col_x: anytype, + col_w: anytype, + screen_h: u16, + hover_col: u16, + hover_row: u16, +) void { + var c: usize = 0; + while (c + 1 < l.ncol) : (c += 1) { + if (hover_col == col_x[c] + col_w[c] -| 1) drawBorderV(win, hover_col, screen_h); + } + var cc: usize = 0; + while (cc < l.ncol) : (cc += 1) { + if (hover_col < col_x[cc] or hover_col >= col_x[cc] + col_w[cc]) continue; + var k: usize = 0; + while (k + 1 < l.col_n[cc]) : (k += 1) { + const r = rects[l.col_terms[cc][k]]; + if (hover_row == r.y + r.h -| 1) drawBorderH(win, col_x[cc], col_w[cc], hover_row); + } + } +} |
