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 /replay.zig | |
| parent | 153f0a88862507cf1f5dda71f71db3d6649dfeaa (diff) | |
| download | pardes-d1ed5bd8f55935e6470c4a7559216119571a084f.tar.gz pardes-d1ed5bd8f55935e6470c4a7559216119571a084f.zip | |
fixed scaling and fitting on web ui
Diffstat (limited to 'replay.zig')
| -rw-r--r-- | replay.zig | 246 |
1 files changed, 55 insertions, 191 deletions
@@ -6,6 +6,7 @@ const replay_config = @import("replay_config"); const dump = @import("dump.zig"); const paint = @import("paint.zig"); const image = @import("image.zig"); +const pane_ui = @import("pane_ui.zig"); const is_emscripten = builtin.os.tag == .emscripten; const embedded_dump = if (replay_config.embedded_dump) @embedFile("replay.dump.zon") else ""; @@ -25,14 +26,14 @@ pub const std_options_debug_io: std.Io = if (is_emscripten) else std_options_debug_threaded_io.?.io(); -const TOPBAR_H: u16 = 1; -const GUTTER: u16 = 2; -const BOX_H: u16 = 1; -const MINW: u16 = 10; -const MINH: u16 = 3; -const PREFIX_W: u16 = 5; +const TOPBAR_H: u16 = pane_ui.TOPBAR_H; +const GUTTER: u16 = pane_ui.GUTTER; +const BOX_H: u16 = pane_ui.BOX_H; +const MINW: u16 = pane_ui.MINW; +const MINH: u16 = pane_ui.MINH; +const PREFIX_W: u16 = pane_ui.PREFIX_W; -const Rect = struct { x: u16, y: u16, w: u16, h: u16 }; +const Rect = pane_ui.Rect; const ReplayTerm = struct { term: ghostty_vt.Terminal, @@ -60,12 +61,8 @@ const Pane = struct { } }; -const Layout = struct { - ncol: usize, - col_weight: [dump.max_cols]f32, - col_terms: [dump.max_cols][dump.max_panes]usize, - col_n: [dump.max_cols]usize, -}; +const Layout = pane_ui.Layout(dump.max_cols, dump.max_panes); +const moveTerm = pane_ui.moveTerm; const Drag = union(enum) { none, @@ -164,115 +161,8 @@ fn terminalBodyText(alloc: std.mem.Allocator, p: *Pane) ![]const u8 { return out.toOwnedSlice(alloc); } -fn layoutFindTerm(l: *const Layout, 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; -} - -fn layoutRemove(l: *Layout, 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 and 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; - } -} - -fn layoutInsert(l: *Layout, 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; -} - -fn computeGeom( - l: *const Layout, - panes: []const Pane, - sw: u16, - sh: u16, - rects: *[dump.max_panes]Rect, - col_x: *[dump.max_cols]u16, - col_w: *[dump.max_cols]u16, -) 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| vsum += panes[l.col_terms[c][k]].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 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)) * panes[id].vweight / vsum)))); - rects[id] = .{ .x = x, .y = y, .w = cw, .h = ch }; - y +|= ch; - } - x +|= cw; - } -} - -fn moveTerm( - l: *Layout, - rects: *const [dump.max_panes]Rect, - col_x: *const [dump.max_cols]u16, - col_w: *const [dump.max_cols]u16, - 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); +fn paneVWeight(panes: []const Pane, id: usize) ?f32 { + return panes[id].vweight; } fn renderPane(alloc: std.mem.Allocator, root: ui.Window, p: *Pane, r: Rect, active: bool) !void { @@ -291,16 +181,7 @@ fn renderPane(alloc: std.mem.Allocator, root: ui.Window, p: *Pane, r: Rect, acti if (p.image_view) |*iv| { if (body_h > 0) image.draw(iv, text.child(.{ .y_off = BOX_H, .height = body_h }), body_w, body_h); - const gut = root.child(.{ .x_off = r.x, .y_off = r.y, .width = GUTTER, .height = r.h }); - const box_bg = if (active) accent else accent_dim; - var gy: u16 = 0; - while (gy < r.h) : (gy += 1) { - const bg = if (gy < BOX_H) box_bg else T().scroll_track; - var gx: u16 = 0; - while (gx < GUTTER) : (gx += 1) { - gut.writeCell(gx, gy, .{ .char = .{ .grapheme = " " }, .style = .{ .bg = rgbColor(bg) } }); - } - } + pane_ui.drawGutter(root, r, active, accent, accent_dim, T().scroll_track, T().scroll_thumb, null); return; } @@ -381,44 +262,15 @@ fn renderPane(alloc: std.mem.Allocator, root: ui.Window, p: *Pane, r: Rect, acti } } - const gut = root.child(.{ .x_off = r.x, .y_off = r.y, .width = GUTTER, .height = r.h }); - const box_bg = if (active) accent else accent_dim; - var gy: u16 = 0; - while (gy < r.h) : (gy += 1) { - const bg = if (gy < BOX_H) box_bg else T().scroll_track; - var gx: u16 = 0; - while (gx < GUTTER) : (gx += 1) { - gut.writeCell(gx, gy, .{ .char = .{ .grapheme = " " }, .style = .{ .bg = rgbColor(bg) } }); - } - } - - const track_h: usize = if (r.h > BOX_H) r.h - BOX_H else 0; - if (track_h > 0) { - var sb_total: usize = undefined; - var sb_off: usize = undefined; - var sb_len: usize = undefined; - if (p.terminal) |*rt| { - const sb = rt.term.screens.active.pages.scrollbar(); - sb_total = sb.total; - sb_off = sb.offset; - sb_len = sb.len; - } else { - sb_total = lineCount(p.content); - sb_off = p.scroll; - sb_len = p.rows; - } - 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_off) / 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 = rgbColor(T().scroll_thumb) } }); - } - } - } + const scrollbar: pane_ui.Scrollbar = if (p.terminal) |*rt| blk: { + const sb = rt.term.screens.active.pages.scrollbar(); + break :blk .{ .total = sb.total, .offset = sb.offset, .len = sb.len }; + } else .{ + .total = lineCount(p.content), + .offset = p.scroll, + .len = p.rows, + }; + pane_ui.drawGutter(root, r, active, accent, accent_dim, T().scroll_track, T().scroll_thumb, scrollbar); } fn render( @@ -428,8 +280,13 @@ fn render( state: dump.State, panes: []Pane, rects: *[dump.max_panes]Rect, + layout: *const Layout, + col_x: *const [dump.max_cols]u16, + col_w: *const [dump.max_cols]u16, screen_w: u16, + screen_h: u16, active: usize, + drag: Drag, ) !void { var arena: std.heap.ArenaAllocator = .init(gpa); defer arena.deinit(); @@ -452,15 +309,14 @@ fn render( const topbar = if (state.topbar.len > 0) state.topbar else "pardes replay"; _ = bar.printSegment(.{ .text = topbar, .style = .{ .fg = rgbColor(T().tag_fg), .bg = rgbColor(T().tag_bg) } }, .{ .wrap = .none }); - try vx.render(writer); -} - -fn paneAt(rects: *const [dump.max_panes]Rect, 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; + switch (drag) { + .border_v => |d| pane_ui.drawBorderV(win, d.cur_x, screen_h), + .border_h => |d| pane_ui.drawBorderH(win, col_x[d.col], col_w[d.col], d.cur_y), + .move => |d| pane_ui.drawMovePreview(win, layout, col_x, col_w, d.cur_x, d.cur_y, T().scroll_thumb), + .none => {}, } - return null; + + try vx.render(writer); } fn deinitPanes(gpa: std.mem.Allocator, panes: []Pane) void { @@ -626,7 +482,7 @@ const App = struct { try self.loop.start(); try self.loop.installResizeHandler(); - computeGeom(&self.layout, self.panes, self.screen_w, self.screen_h, &self.rects, &self.col_x, &self.col_w); + pane_ui.computeGeom(&self.layout, self.panes, self.screen_w, self.screen_h, &self.rects, &self.col_x, &self.col_w, paneVWeight); } fn deinit(self: *App) void { @@ -655,16 +511,14 @@ const App = struct { fn step(self: *App, event: @TypeOf(Command.value)) !bool { switch (event) { .winsize => |ws| { - if (!is_emscripten) { - self.screen_w = ws.cols; - self.screen_h = ws.rows; - try self.vx.resize(self.gpa, self.tty.writer(), ws); - } + self.screen_w = ws.cols; + self.screen_h = ws.rows; + try self.vx.resize(self.gpa, self.tty.writer(), ws); }, .mouse => |mouse| { const mcol: u16 = if (mouse.col < 0) 0 else @min(@as(u16, @intCast(mouse.col)), self.screen_w -| 1); const mrow: u16 = if (mouse.row < 0) 0 else @min(@as(u16, @intCast(mouse.row)), self.screen_h -| 1); - const hovered = paneAt(&self.rects, self.panes.len, mcol, mrow); + const hovered = pane_ui.paneAt(&self.rects, self.panes.len, mcol, mrow); if (mouse.button == .wheel_down and mouse.type == .press) { if (hovered) |id| scrollBy(&self.panes[id], 1); @@ -698,13 +552,23 @@ const App = struct { if (!handled) if (hovered) |id| { self.active = id; const r = self.rects[id]; - if (mcol < r.x + GUTTER and mrow < r.y + BOX_H) { + if (pane_ui.inMoveBox(r, mcol, mrow)) { self.drag = .{ .move = .{ .id = id, .cur_x = mcol, .cur_y = mrow } }; - } else if (mcol < r.x + GUTTER) { - const local: i32 = @as(i32, mrow) - @as(i32, r.y + BOX_H); - scrollBy(&self.panes[id], -local); + } else if (pane_ui.scrollbarClickDelta(r, .left, mcol, mrow)) |delta| { + scrollBy(&self.panes[id], delta); } }; + } else if (mouse.type == .press and (mouse.button == .middle or mouse.button == .right)) { + if (hovered) |id| { + const r = self.rects[id]; + const sb_button: pane_ui.ScrollbarButton = if (mouse.button == .right) .right else .middle; + if (pane_ui.scrollbarClickDelta(r, sb_button, mcol, mrow)) |delta| { + if (mouse.button == .right) self.active = id; + scrollBy(&self.panes[id], delta); + } else { + self.active = id; + } + } } else if (mouse.type == .press) { if (hovered) |id| self.active = id; } else if (mouse.type == .drag) { @@ -775,7 +639,7 @@ const App = struct { else => {}, } - computeGeom(&self.layout, self.panes, self.screen_w, self.screen_h, &self.rects, &self.col_x, &self.col_w); + pane_ui.computeGeom(&self.layout, self.panes, self.screen_w, self.screen_h, &self.rects, &self.col_x, &self.col_w, paneVWeight); for (self.panes, 0..) |*p, id| { const r = self.rects[id]; const cols = @max(1, r.w -| GUTTER); @@ -787,7 +651,7 @@ const App = struct { p.rows = rows; clampScroll(p); } - try render(&self.vx, self.gpa, self.tty.writer(), self.state, self.panes, &self.rects, self.screen_w, self.active); + try render(&self.vx, self.gpa, self.tty.writer(), self.state, self.panes, &self.rects, &self.layout, &self.col_x, &self.col_w, self.screen_w, self.screen_h, self.active, self.drag); return true; } }; |
