From 09c35b22793153d201fa202b52671a6442e5e2e1 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Sun, 9 Aug 2026 01:26:08 -0300 Subject: a builtin to put the pane taglines at the bottom --- src/gui/gui.zig | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) (limited to 'src/gui/gui.zig') diff --git a/src/gui/gui.zig b/src/gui/gui.zig index e1d5c86d..1f7812e4 100644 --- a/src/gui/gui.zig +++ b/src/gui/gui.zig @@ -500,6 +500,12 @@ const WebChromeTarget = struct { col: u16, row: u16 }; /// Fat-finger hit test for web layout chrome. A gesture is classified once at /// finger-down, then never changes into scrolling. Handle coordinates are /// snapped onto the core's one-cell resize edge; body rows remain untouched. +/// +/// The tag row and the stacked-pair seam come from the core's own rule: +/// Tagbottom puts a pane's tag on its LAST row, which makes its FIRST row an +/// ordinary scrollable body row and moves the h-handle down to the lower +/// pane's first. Kept a line-for-line clone of web.zig's chromeTarget — the +/// two must classify a finger identically. fn webChromeTarget(core: *const pardes.Pardes, col: u16, row: u16) ?WebChromeTarget { if (row < pardes.TOPBAR_H) return .{ .col = col, .row = row }; @@ -508,8 +514,9 @@ fn webChromeTarget(core: *const pardes.Pardes, col: u16, row: u16) ?WebChromeTar for (core.panes, 0..) |slot, id| { if (slot == null) continue; const rect = core.rects[id]; - if (row == rect.y and col >= rect.x and col < rect.x + rect.w and col < rect.x + config.GUTTER) - return .{ .col = col, .row = rect.y }; + const tag = if (core.tag_bottom) rect.y + rect.h -| pardes.BOX_H else rect.y; + if (row == tag and col >= rect.x and col < rect.x + rect.w and col < rect.x + config.GUTTER) + return .{ .col = col, .row = tag }; } for (0..core.ncol -| 1) |column| { @@ -520,7 +527,7 @@ fn webChromeTarget(core: *const pardes.Pardes, col: u16, row: u16) ?WebChromeTar if (col < core.col_x[column] or col >= core.col_x[column] + core.col_w[column]) continue; for (0..core.col_n[column] -| 1) |index| { const rect = core.rects[core.col_terms[column][index]]; - const handle = rect.y + rect.h -| 1; + const handle = if (core.tag_bottom) rect.y +| rect.h else rect.y + rect.h -| 1; if (row == handle) return .{ .col = col, .row = handle }; } } @@ -529,8 +536,9 @@ fn webChromeTarget(core: *const pardes.Pardes, col: u16, row: u16) ?WebChromeTar for (core.panes, 0..) |slot, id| { if (slot == null) continue; const rect = core.rects[id]; - if (row == rect.y and col >= rect.x and col < rect.x + rect.w) - return .{ .col = col, .row = rect.y }; + const tag = if (core.tag_bottom) rect.y + rect.h -| pardes.BOX_H else rect.y; + if (row == tag and col >= rect.x and col < rect.x + rect.w) + return .{ .col = col, .row = tag }; } // One-cell tolerance around thin separators, after exact tag rows have had @@ -543,7 +551,7 @@ fn webChromeTarget(core: *const pardes.Pardes, col: u16, row: u16) ?WebChromeTar if (col < core.col_x[column] or col >= core.col_x[column] + core.col_w[column]) continue; for (0..core.col_n[column] -| 1) |index| { const rect = core.rects[core.col_terms[column][index]]; - const handle = rect.y + rect.h -| 1; + const handle = if (core.tag_bottom) rect.y +| rect.h else rect.y + rect.h -| 1; if (cellDistance(row, handle) == 1) return .{ .col = col, .row = handle }; } } @@ -984,6 +992,7 @@ const Gui = struct { // scroll_delta the way the wheel arm of dispatch does. scroll_pane: ?usize = null, scroll_rect: pardes.Rect = .{ .x = 0, .y = 0, .w = 0, .h = 0 }, + scroll_body_y: u16 = 0, // that rect's first BODY row (Tagbottom moves it) scroll_col: u16 = 0, // where the wheel turned: crossed rows are delivered scroll_row: u16 = 0, // THERE, not wherever the pointer has drifted to since scroll_delta: f32 = 0, // finite raw distance waiting for the render batch @@ -2884,6 +2893,10 @@ fn stepScroll(g: *Gui, core: *pardes.Pardes, gpa: std.mem.Allocator) void { } g.scroll_lag = st.lag; g.scroll_rect = r; + // the body origin travels with the rect: the two draw sites below have no + // core to ask, and with Tagbottom the body starts at r.y, not r.y + BOX_H + const body_y = if (core.tag_bottom) r.y else r.y + pardes.BOX_H; + g.scroll_body_y = body_y; if (st.rows != 0) { // The offset opens a gap at the trailing edge of the travel, and what // belongs in it is the row that just left the pane: already gone from @@ -2896,7 +2909,7 @@ fn stepScroll(g: *Gui, core: *pardes.Pardes, gpa: std.mem.Allocator) void { const bw = r.w - config.GUTTER; const bh = r.h - pardes.BOX_H; const k: u16 = @intCast(@min(@abs(st.rows), @as(i32, bh))); - const srow = r.y + pardes.BOX_H + (if (st.rows > 0) k - 1 else bh - k); + const srow = body_y + (if (st.rows > 0) k - 1 else bh - k); if (srow < s.rows) { if (g.scroll_edge.len < bw) { gpa.free(g.scroll_edge); @@ -2923,7 +2936,7 @@ fn emitScrollRows(g: *Gui, instances: [*]CellInstance, base: u32, surface: *pard const r = g.scroll_rect; if (r.w <= config.GUTTER or r.h <= pardes.BOX_H) return 0; const x0 = r.x + config.GUTTER; - const y0 = r.y + pardes.BOX_H; + const y0 = g.scroll_body_y; const bw = r.w - config.GUTTER; const bh = r.h - pardes.BOX_H; if (x0 + bw > surface.cols or y0 + bh > surface.rows) return 0; // resized under us @@ -2960,7 +2973,7 @@ fn emitScrollRows(g: *Gui, instances: [*]CellInstance, base: u32, surface: *pard fn scrollScissor(g: *const Gui, layout: CellLayout, sw: u32, sh: u32) c.SDL_Rect { const r = g.scroll_rect; const px = layout.x_off + @as(f32, @floatFromInt(r.x + config.GUTTER)) * layout.w; - const py = layout.y_off + @as(f32, @floatFromInt(r.y + pardes.BOX_H)) * layout.h; + const py = layout.y_off + @as(f32, @floatFromInt(g.scroll_body_y)) * layout.h; const pw = @as(f32, @floatFromInt(r.w - config.GUTTER)) * layout.w; const ph = @as(f32, @floatFromInt(r.h - pardes.BOX_H)) * layout.h; const x0 = std.math.clamp(@as(i32, @intFromFloat(@floor(px))), 0, @as(i32, @intCast(sw))); -- cgit v1.3