diff options
| author | Gabriel Schneider <[email protected]> | 2026-07-05 19:42:46 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-08-01 15:02:07 -0300 |
| commit | 7988d9bc6e31210ff13994f18d5622dd57ba9341 (patch) | |
| tree | 62138868db52638cbefb18086dc226810fad761a /replay.zig | |
| parent | e36e946f9a56c4ab1d10cef3aace20fe7d274dfa (diff) | |
| download | pardes-7988d9bc6e31210ff13994f18d5622dd57ba9341.tar.gz pardes-7988d9bc6e31210ff13994f18d5622dd57ba9341.zip | |
sdl: ui_sdl rework (380+ lines) + replay changes
Diffstat (limited to 'replay.zig')
| -rw-r--r-- | replay.zig | 97 |
1 files changed, 74 insertions, 23 deletions
@@ -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 { |
