summaryrefslogtreecommitdiff
path: root/main.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-07-01 11:15:30 -0300
committerGabriel Schneider <[email protected]>2026-07-01 11:15:56 -0300
commitc301ae3567204666c914ba4b36336d3a91af0cd1 (patch)
tree35313e47977ac4e0e6ff8ffaaf1caa0aa3ed9de7 /main.zig
parent6570dc04d6343cbbf31c0eb2363f9bec817aa4db (diff)
downloadpardes-c301ae3567204666c914ba4b36336d3a91af0cd1.tar.gz
pardes-c301ae3567204666c914ba4b36336d3a91af0cd1.zip
changed some draggable styling
Diffstat (limited to 'main.zig')
-rw-r--r--main.zig116
1 files changed, 68 insertions, 48 deletions
diff --git a/main.zig b/main.zig
index 3bb0cfda..67904722 100644
--- a/main.zig
+++ b/main.zig
@@ -158,6 +158,10 @@ const msel_fg = [3]u8{ 0xd8, 0xc8, 0xa8 };
// are skipped so there's no per-frame cost. Toggled by the "Colors" tagbar command.
var colors_on: bool = true;
+// The stats overlay, toggled by the "Debug" builtin. Global (like colors_on) so the
+// central command dispatcher can flip it; the render reads it.
+var show_debug: bool = false;
+
// Color themes. Chrome (scrollbar/box/selection/edit colors above) stays fixed;
// only the page background, text, tag bar and syntax colors swap. `bg`/`fg` are
// null in the dark theme so it keeps the terminal's own default colors (i.e. looks
@@ -244,24 +248,27 @@ fn synStyle(s: Syn) ?struct { fg: [3]u8, bold: bool } {
};
}
-// Builtins. Clicking the name runs the action. Window-specific ones (Del, Delcol)
-// live in each pane's tag; window-agnostic ones (Kill, Newcol, Tutor) live in the
-// single global tagbar at the top. onSelect matches against the whole list, so a
-// name works wherever it's shown.
-const SelectAction = enum { none, send, newcol, delcol, del, tutor, kill };
+// Builtins. Executing the name (middle-click / Tab) runs the action through the ONE
+// dispatcher (onSelect + actOnSelection), no matter where the name is shown — a pane
+// body, a file, the tagbar. onSelect matches against the whole list, so a name works
+// wherever it appears. Window-specific ones (Del, Delcol) live in each pane's tag;
+// window-agnostic ones (Kill, Newcol, Tutor, Debug, Colors, NextColor) in the tagbar.
+const SelectAction = enum { none, send, newcol, delcol, del, tutor, kill, debug, colors, nextcolor };
const Builtin = struct { name: []const u8, action: SelectAction };
const builtins = [_]Builtin{
.{ .name = "Kill", .action = .kill },
.{ .name = "Newcol", .action = .newcol },
.{ .name = "Tutor", .action = .tutor },
+ .{ .name = "Debug", .action = .debug },
+ .{ .name = "Colors", .action = .colors },
+ .{ .name = "NextColor", .action = .nextcolor },
.{ .name = "Del", .action = .del },
.{ .name = "Delcol", .action = .delcol },
};
// per-pane tag commands (window-specific).
const pane_builtins_str = "Del Delcol";
-// the global tagbar: window-agnostic commands. Kill closes everything; Debug
-// toggles the stats overlay, Colors the ansi/syntax recolor, NextColor cycles the
-// theme (all handled in the tagbar click, not a SelectAction).
+// the global tagbar: window-agnostic commands. Kill closes everything; Debug toggles
+// the stats overlay, Colors the ansi/syntax recolor, NextColor cycles the theme.
const topbar_str = "Kill Newcol Tutor Debug Colors NextColor";
// Path of the generated bash rc that adds OSC 133 semantic prompts (shell
@@ -835,6 +842,16 @@ fn ttyForDir(
return nt;
}
+// Overlay a resize-handle dash on a cell WITHOUT touching its colors: read the cell,
+// swap only the grapheme, write it back. So the boundary hint reads as a dashed line
+// over whatever content is already there — the only UI change is the character.
+fn overlayDash(win: vaxis.Window, col: u16, row: u16, glyph: []const u8) void {
+ var cell = win.readCell(col, row) orelse return;
+ cell.char.grapheme = glyph; // static literal — safe under vaxis's retained render
+ cell.default = false; // make it draw with the kept style, not as a blank cell
+ win.writeCell(col, row, cell);
+}
+
// Tile the screen: columns by col_weight, then each column's windows by vweight.
// Last column/window absorbs rounding so the tiling always fills the screen.
// `out_*` written for every live term and live column.
@@ -851,13 +868,15 @@ fn computeGeom(
var wsum: f32 = 0;
for (0..l.ncol) |c| wsum += l.col_weight[c];
if (wsum <= 0) wsum = 1;
- const sep_cols: u16 = @intCast(l.ncol - 1);
- const avail_w: u16 = sw -| sep_cols;
+ // Panes tile edge to edge — NO reserved inter-pane gap. The cells that used to be
+ // an empty drag strip are now usable pane content; the resize handle is the shared
+ // boundary itself (a pane's right/bottom edge), surfaced as a dashed line on hover.
+ // Only the top row is held back, for the global tagbar.
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(avail_w)) * l.col_weight[c] / wsum))));
+ 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;
@@ -867,9 +886,7 @@ fn computeGeom(
if (terms[l.col_terms[c][k]]) |t| vsum += t.vweight;
}
if (vsum <= 0) vsum = 1;
- const sep_rows: u16 = if (n > 0) @intCast(n - 1) else 0;
- // reserve the top row for the global tagbar: panes tile [TOPBAR_H, sh).
- const avail_h: u16 = (sh -| TOPBAR_H) -| sep_rows;
+ const avail_h: u16 = sh -| TOPBAR_H;
var y: u16 = TOPBAR_H;
for (0..n) |k| {
@@ -878,9 +895,9 @@ fn computeGeom(
const lastk = (k + 1 == n);
const ch: u16 = if (lastk) (sh -| y) else @max(1, @as(u16, @intFromFloat(@round(@as(f32, @floatFromInt(avail_h)) * t.vweight / vsum))));
rects[id] = .{ .x = x, .y = y, .w = cw, .h = ch };
- y +|= ch + 1;
+ y +|= ch;
}
- x +|= cw + 1;
+ x +|= cw;
}
}
@@ -2315,6 +2332,9 @@ fn actOnSelection(
}
switch (onSelect(button, txt)) {
.none => {},
+ .debug => show_debug = !show_debug,
+ .colors => colors_on = !colors_on,
+ .nextcolor => theme_idx = (theme_idx + 1) % themes.len,
.kill => return true, // close everything: signal the main loop to quit
.send => {
// terminal: run in itself. file pane: run in a shell sitting in the
@@ -2527,7 +2547,6 @@ pub fn main(init: std.process.Init) !void {
var hover_col: u16 = 0; // last mouse position; used to tint the resize border under it
var hover_row: u16 = 0;
var ctrl_w_pending: bool = false; // Ctrl-w prefix for helix-style pane focus
- var show_debug: bool = false; // the stats overlay; toggled by the Debug tagbar command
while (true) {
const event = try loop.nextEvent();
@@ -2556,29 +2575,26 @@ pub fn main(init: std.process.Init) !void {
// action context; Kill/Newcol/Tutor ignore it.
if (mouse.button == .middle) {
const word = wordAtCol(topbar_str, mcol);
- if (std.mem.eql(u8, word, "Debug")) {
- show_debug = !show_debug;
- } else if (std.mem.eql(u8, word, "Colors")) {
- colors_on = !colors_on;
- } else if (std.mem.eql(u8, word, "NextColor")) {
- theme_idx = (theme_idx + 1) % themes.len;
- } else if (word.len > 0) {
+ if (word.len > 0)
if (actOnSelection(io, gpa, &loop, &terms, &layout, &rects, &active, screen_w, screen_h, vx.caps.kitty_graphics, .middle, active, word)) break;
- }
}
} else if (mouse.type == .press and mouse.button == .left) {
var handled = false;
- // vertical separator (between columns)?
+ // vertical resize handle = the LEFT pane's last column (its own
+ // content, not the right pane's gutter). The border check runs before
+ // the pane hit test, so a press there resizes instead of clicking.
var c: usize = 0;
while (c + 1 < layout.ncol and !handled) : (c += 1) {
- if (mcol == col_x[c] + col_w[c]) {
+ if (mcol == col_x[c] + col_w[c] -| 1) {
drag = .{ .border_v = .{ .left_col = c, .cur_x = mcol } };
handled = true;
}
}
- // horizontal separator (between stacked windows)?
+ // horizontal resize handle = the UPPER pane's last row (a's own last
+ // line, sitting right above b's tag) — not b's tag row, so the tag +
+ // its box-drag stay clean.
if (!handled) {
var cc: usize = 0;
outer: while (cc < layout.ncol) : (cc += 1) {
@@ -2587,7 +2603,7 @@ pub fn main(init: std.process.Init) !void {
while (k + 1 < layout.col_n[cc]) : (k += 1) {
const id = layout.col_terms[cc][k];
const r = rects[id];
- if (mrow == r.y + r.h) {
+ if (mrow == r.y + r.h -| 1) {
drag = .{ .border_h = .{ .col = cc, .top_idx = k, .cur_y = mrow } };
handled = true;
break :outer;
@@ -2684,7 +2700,10 @@ pub fn main(init: std.process.Init) !void {
const c = d.left_col;
if (c + 1 < layout.ncol) {
const combined: f32 = @floatFromInt(col_w[c] + col_w[c + 1]);
- var nl: f32 = @floatFromInt(d.cur_x -| col_x[c]);
+ // handle sits on the left pane's LAST column (inclusive),
+ // so the new left width includes it: cur_x - col_x + 1.
+ // A click with no drag then leaves the width unchanged.
+ var nl: f32 = @floatFromInt((d.cur_x + 1) -| col_x[c]);
const minw: f32 = MINW;
nl = std.math.clamp(nl, minw, @max(minw, combined - minw));
const pair = layout.col_weight[c] + layout.col_weight[c + 1];
@@ -2700,7 +2719,9 @@ pub fn main(init: std.process.Init) !void {
const b = layout.col_terms[c][k + 1];
if (terms[a]) |ta| if (terms[b]) |tb| {
const combined: f32 = @floatFromInt(rects[a].h + rects[b].h);
- var nt: f32 = @floatFromInt(d.cur_y -| rects[a].y);
+ // handle is the upper pane's LAST row (inclusive) ->
+ // new upper height includes it: cur_y - y + 1.
+ var nt: f32 = @floatFromInt((d.cur_y + 1) -| rects[a].y);
const minh: f32 = MINH;
nt = std.math.clamp(nt, minh, @max(minh, combined - minh));
const pair = ta.vweight + tb.vweight;
@@ -2979,22 +3000,23 @@ pub fn main(init: std.process.Init) !void {
bar.fill(.{ .char = .{ .grapheme = " " }, .style = .{ .bg = rgbColor(T().tag_bg) } });
_ = bar.printSegment(.{ .text = topbar_str, .style = .{ .fg = rgbColor(T().tag_fg), .bg = rgbColor(T().tag_bg) } }, .{ .wrap = .none });
- // No static separators: each pane's tagline (top) and scrollbar gutter
- // (left) already read as borders. The 1-cell gap between panes stays as
- // an (invisible) drag zone; the dashed preview below shows it on drag.
+ // No static separators and no reserved gap: each pane's tagline (top) and
+ // scrollbar gutter (left) read as the borders, and adjacent panes abut. The
+ // resize handle is a pane's own trailing edge (left pane's last col / upper
+ // pane's last row) — hinted by a dash overlay that keeps the cell's colors.
- // dashed preview while dragging; commit happens on release.
+ // resize-handle hint: overlay a dash on the boundary cells, keeping their
+ // colors (only the glyph changes). Shown while dragging a border and, in the
+ // else branch, on hover.
switch (drag) {
.border_v => |d| {
var ry: u16 = TOPBAR_H;
- while (ry < screen_h) : (ry += 1)
- win.writeCell(d.cur_x, ry, .{ .char = .{ .grapheme = "╎" }, .style = .{ .fg = .{ .rgb = gray.lit } } });
+ while (ry < screen_h) : (ry += 1) overlayDash(win, d.cur_x, ry, "╎");
},
.border_h => |d| {
const c = d.col;
var rx: u16 = col_x[c];
- while (rx < col_x[c] + col_w[c]) : (rx += 1)
- win.writeCell(rx, d.cur_y, .{ .char = .{ .grapheme = "╌" }, .style = .{ .fg = .{ .rgb = gray.lit } } });
+ while (rx < col_x[c] + col_w[c]) : (rx += 1) overlayDash(win, rx, d.cur_y, "╌");
},
.move => |d| {
// insertion indicator: dashed line at cur_y across the target column
@@ -3011,15 +3033,14 @@ pub fn main(init: std.process.Init) !void {
win.writeCell(d.cur_x, d.cur_y, .{ .char = .{ .grapheme = "▌" }, .style = .{ .fg = .{ .rgb = gray.mid } } });
},
else => {
- // not dragging: preview the resize border under the cursor with the
- // same dashed line the drag shows, drawn on top (no background tint),
- // so the invisible drag zones are discoverable on hover.
+ // not dragging: hint the resize handle under the cursor with the same
+ // dash overlay. Vertical = the left pane's last column; horizontal =
+ // the upper pane's last row (a's own last line, just above b's tag).
var c: usize = 0;
while (c + 1 < layout.ncol) : (c += 1) {
- if (hover_col != col_x[c] + col_w[c]) continue;
+ if (hover_col != col_x[c] + col_w[c] -| 1) continue;
var ry: u16 = TOPBAR_H;
- while (ry < screen_h) : (ry += 1)
- win.writeCell(hover_col, ry, .{ .char = .{ .grapheme = "╎" }, .style = .{ .fg = .{ .rgb = gray.lit } } });
+ while (ry < screen_h) : (ry += 1) overlayDash(win, hover_col, ry, "╎");
}
var cc: usize = 0;
while (cc < layout.ncol) : (cc += 1) {
@@ -3027,10 +3048,9 @@ pub fn main(init: std.process.Init) !void {
var k: usize = 0;
while (k + 1 < layout.col_n[cc]) : (k += 1) {
const r = rects[layout.col_terms[cc][k]];
- if (hover_row != r.y + r.h) continue;
+ if (hover_row != r.y + r.h -| 1) continue;
var rx: u16 = col_x[cc];
- while (rx < col_x[cc] + col_w[cc]) : (rx += 1)
- win.writeCell(rx, hover_row, .{ .char = .{ .grapheme = "╌" }, .style = .{ .fg = .{ .rgb = gray.lit } } });
+ while (rx < col_x[cc] + col_w[cc]) : (rx += 1) overlayDash(win, rx, hover_row, "╌");
}
}
},