summaryrefslogtreecommitdiff
path: root/replay.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-07-05 19:42:46 -0300
committerGabriel Schneider <[email protected]>2026-08-01 15:02:07 -0300
commit7988d9bc6e31210ff13994f18d5622dd57ba9341 (patch)
tree62138868db52638cbefb18086dc226810fad761a /replay.zig
parente36e946f9a56c4ab1d10cef3aace20fe7d274dfa (diff)
downloadpardes-7988d9bc6e31210ff13994f18d5622dd57ba9341.tar.gz
pardes-7988d9bc6e31210ff13994f18d5622dd57ba9341.zip
sdl: ui_sdl rework (380+ lines) + replay changes
Diffstat (limited to 'replay.zig')
-rw-r--r--replay.zig97
1 files changed, 74 insertions, 23 deletions
diff --git a/replay.zig b/replay.zig
index 04448eb9..4add78f0 100644
--- a/replay.zig
+++ b/replay.zig
@@ -83,6 +83,7 @@ pub const Command = struct {
mouse: ui.Mouse,
paste: []const u8,
pinch: f32,
+ touch_scroll_delta: f32,
} = .nop;
};
@@ -97,6 +98,36 @@ const synStyle = paint.synStyle;
const highlightFile = paint.highlightFile;
const cellStyle = paint.cellStyle;
+const SelectAction = enum { none, colors, nextcolor };
+const Builtin = struct { name: []const u8, action: SelectAction };
+const builtins = [_]Builtin{
+ .{ .name = "Colors", .action = .colors },
+ .{ .name = "NextColor", .action = .nextcolor },
+};
+
+fn topbarText(state: dump.State) []const u8 {
+ return if (state.topbar.len > 0) state.topbar else "pardes replay";
+}
+
+fn wordAtCol(str: []const u8, col: u16) []const u8 {
+ const idx: usize = col;
+ if (idx >= str.len or str[idx] == ' ') return "";
+ var lo: usize = idx;
+ while (lo > 0 and str[lo - 1] != ' ') lo -= 1;
+ var hi: usize = idx;
+ while (hi < str.len and str[hi] != ' ') hi += 1;
+ return str[lo..hi];
+}
+
+fn onSelect(text: []const u8) SelectAction {
+ const cmd = std.mem.trim(u8, text, " \t\r\n");
+ if (cmd.len == 0) return .none;
+ inline for (builtins) |b| {
+ if (std.mem.eql(u8, cmd, b.name)) return b.action;
+ }
+ return .none;
+}
+
fn printUsage(io: std.Io) !void {
try std.Io.File.stdout().writeStreamingAll(io, "Usage: pardes-replay <dump.zon>\n");
}
@@ -170,7 +201,7 @@ fn setPaneVWeight(panes: []Pane, id: usize, weight: f32) void {
panes[id].vweight = weight;
}
-fn renderPane(alloc: std.mem.Allocator, root: ui.Window, p: *Pane, r: Rect, active: bool) !void {
+fn renderPane(alloc: std.mem.Allocator, root: ui.Window, p: *Pane, r: Rect, active: bool, colors_on: bool) !void {
if (r.w <= GUTTER or r.h == 0) return;
const text = root.child(.{ .x_off = r.x + GUTTER, .y_off = r.y, .width = r.w - GUTTER, .height = r.h });
text.clear();
@@ -199,18 +230,20 @@ fn renderPane(alloc: std.mem.Allocator, root: ui.Window, p: *Pane, r: Rect, acti
_ = text.printSegment(.{ .text = line, .style = body_style }, .{ .row_offset = BOX_H + vr, .wrap = .none });
}
- const rt = &p.terminal.?;
- vr = 0;
- while (vr < body_h and vr < p.rows) : (vr += 1) {
- var c: u16 = 0;
- while (c < body_w) : (c += 1) {
- const ci = rt.term.screens.active.pages.getCell(.{ .viewport = .{ .x = @intCast(c), .y = @intCast(vr) } }) orelse continue;
- if (ci.cell.wide == .spacer_tail) continue;
- if (text.readCell(c, BOX_H + vr)) |cell| {
- var nc = cell;
- nc.default = false;
- nc.style = cellStyle(ci);
- text.writeCell(c, BOX_H + vr, nc);
+ if (colors_on) {
+ const rt = &p.terminal.?;
+ vr = 0;
+ while (vr < body_h and vr < p.rows) : (vr += 1) {
+ var c: u16 = 0;
+ while (c < body_w) : (c += 1) {
+ const ci = rt.term.screens.active.pages.getCell(.{ .viewport = .{ .x = @intCast(c), .y = @intCast(vr) } }) orelse continue;
+ if (ci.cell.wide == .spacer_tail) continue;
+ if (text.readCell(c, BOX_H + vr)) |cell| {
+ var nc = cell;
+ nc.default = false;
+ nc.style = cellStyle(ci);
+ text.writeCell(c, BOX_H + vr, nc);
+ }
}
}
}
@@ -237,7 +270,7 @@ fn renderPane(alloc: std.mem.Allocator, root: ui.Window, p: *Pane, r: Rect, acti
}
}
- if (p.highlights.len > 0) {
+ if (colors_on and p.highlights.len > 0) {
var lines = std.mem.splitScalar(u8, p.content, '\n');
var skip: usize = 0;
while (skip < p.scroll) : (skip += 1) if (lines.next() == null) break;
@@ -295,6 +328,8 @@ fn render(
hover_col: u16,
hover_row: u16,
ov_pinch_scale: f32,
+ ov_touch_scroll_delta: f32,
+ colors_on: bool,
) !void {
var arena: std.heap.ArenaAllocator = .init(gpa);
defer arena.deinit();
@@ -309,23 +344,23 @@ fn render(
image.ensureLoadedFromMemory(iv, p.image_bytes, vx, gpa, writer);
image.ensureGrid(iv, gpa, p.cols, p.rows);
}
- try renderPane(alloc, win, p, rects[id], id == active);
+ try renderPane(alloc, win, p, rects[id], id == active, colors_on);
}
const bar = win.child(.{ .x_off = 0, .y_off = 0, .width = screen_w, .height = TOPBAR_H });
bar.fill(.{ .char = .{ .grapheme = " " }, .style = .{ .bg = rgbColor(T().tag_bg) } });
- const topbar = if (state.topbar.len > 0) state.topbar else "pardes replay";
+ const topbar = topbarText(state);
_ = bar.printSegment(.{ .text = topbar, .style = .{ .fg = rgbColor(T().tag_fg), .bg = rgbColor(T().tag_bg) } }, .{ .wrap = .none });
pane_ui.drawInteractionPreview(win, layout, rects, col_x, col_w, screen_h, drag, hover_col, hover_row, T().scroll_thumb);
- // debug overlay with pinch scale
- if (ov_pinch_scale != 1.0) {
- var ov_buf: [64]u8 = undefined;
- const s = std.fmt.bufPrint(&ov_buf, "pinch {d:6.4}", .{ov_pinch_scale}) catch "";
+ // debug overlay with gesture telemetry
+ if (ov_pinch_scale != 1.0 or ov_touch_scroll_delta != 0.0) {
+ var ov_buf: [96]u8 = undefined;
+ const s = std.fmt.bufPrint(&ov_buf, "pinch {d:6.4}\ntscroll {d:7.4}", .{ ov_pinch_scale, ov_touch_scroll_delta }) catch "";
if (s.len > 0) {
- const ow: u16 = 16;
- const oh: u16 = 2;
+ const ow: u16 = 18;
+ const oh: u16 = 3;
const ox: u16 = if (screen_w > ow) screen_w - ow else 0;
const ov = win.child(.{ .x_off = ox, .y_off = TOPBAR_H, .width = ow, .height = oh, .border = .{ .where = .all } });
ov.fill(.{ .char = .{ .grapheme = " " }, .style = .{ .bg = rgbColor(T().scroll_track) } });
@@ -441,6 +476,8 @@ const App = struct {
hover_col: u16 = 0,
hover_row: u16 = 0,
ov_pinch_scale: f32 = 1.0,
+ ov_touch_scroll_delta: f32 = 0.0,
+ colors_on: bool = true,
fn init(
self: *App,
@@ -486,6 +523,8 @@ const App = struct {
self.drag = .none;
self.hover_col = 0;
self.hover_row = 0;
+ self.ov_touch_scroll_delta = 0.0;
+ self.colors_on = true;
self.tty = try ui.Tty.init(io, &self.tty_buf);
self.vx = try ui.init(io, gpa, env_map, .{});
@@ -549,6 +588,8 @@ const App = struct {
if (hovered) |id| scrollBy(&self.panes[id], 1);
} else if (mouse.button == .wheel_up and mouse.type == .press) {
if (hovered) |id| scrollBy(&self.panes[id], -1);
+ } else if (mouse.type == .press and mouse.button == .middle and mrow < TOPBAR_H) {
+ self.actOnTopbarWord(mcol);
} else if (mouse.type == .press and mouse.button == .left) {
var handled = false;
var c: usize = 0;
@@ -658,9 +699,11 @@ const App = struct {
},
.key_press => |key| {
if (key.matches('q', .{}) or key.matches(ui.Key.escape, .{}) or key.matches('c', .{ .ctrl = true })) return false;
+ if (key.matches(ui.Key.tab, .{}) and self.hover_row < TOPBAR_H) self.actOnTopbarWord(self.hover_col);
},
.paste => |text| self.gpa.free(text),
.pinch => |scale| self.ov_pinch_scale = scale,
+ .touch_scroll_delta => |delta| self.ov_touch_scroll_delta = delta,
.quit, .close => return false,
else => {},
}
@@ -677,9 +720,17 @@ const App = struct {
p.rows = rows;
clampScroll(p);
}
- 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, self.hover_col, self.hover_row, self.ov_pinch_scale);
+ 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, self.hover_col, self.hover_row, self.ov_pinch_scale, self.ov_touch_scroll_delta, self.colors_on);
return true;
}
+
+ fn actOnTopbarWord(self: *App, col: u16) void {
+ switch (onSelect(wordAtCol(topbarText(self.state), col))) {
+ .none => {},
+ .colors => self.colors_on = !self.colors_on,
+ .nextcolor => paint.nextTheme(),
+ }
+ }
};
fn wasmFrame(arg: ?*anyopaque) callconv(.c) void {