summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-15 17:24:57 -0300
committerGabriel Schneider <[email protected]>2026-09-15 17:26:23 -0300
commitebe561b8083782a105a2ee29fb0a031d1c2427d3 (patch)
tree73d8fae6c7345449b6235b54a44eba87655823d9
parent6560da36cf8111575cd3fec443a42de6eff82f5f (diff)
downloadpardes-ebe561b8083782a105a2ee29fb0a031d1c2427d3.tar.gz
pardes-ebe561b8083782a105a2ee29fb0a031d1c2427d3.zip
Keep column grips blank and muted in every focus state
-rw-r--r--docs/tags.md3
-rw-r--r--docs/themes.md4
-rw-r--r--src/gui/gui.zig7
-rw-r--r--src/macos/Sources/PardesView.swift2
-rw-r--r--src/pardes.zig2
-rw-r--r--src/web/app.mjs2
-rw-r--r--test/column_drag.zig37
-rw-r--r--test/web-snapshots/touch.mjs6
8 files changed, 50 insertions, 13 deletions
diff --git a/docs/tags.md b/docs/tags.md
index 20c8c67a..eb11c0a9 100644
--- a/docs/tags.md
+++ b/docs/tags.md
@@ -7,7 +7,8 @@ pane (or its first pane when focus comes from another column). This makes
`New` appears only in the column tag by default; pane tags keep their own
save, terminal, close, and collapse commands. `Tty` opens a new embedded terminal.
Column command text starts after its drag grip. The column grip has a different
-color from a pane grip. Drag it left or right to move the whole column; the
+color from a pane grip. It stays blank and muted even when its column is active.
+Drag it left or right to move the whole column; the
insertion line previews its destination. Only columns between the old and new
positions shift. Each column keeps its width, panes and command text.
diff --git a/docs/themes.md b/docs/themes.md
index 2c1e1696..dac389f5 100644
--- a/docs/themes.md
+++ b/docs/themes.md
@@ -105,8 +105,8 @@ so existing exported themes remain valid.
| `tag_active_bg`, `tag_active_fg` | Focused pane tag surface and text | `tag_bg`, `tag_fg` |
| `tag_name_fg` | Filename or terminal `Tty` command tint | The corresponding normal or active tag foreground |
| `tag_active_name_fg` | Filename or terminal `Tty` tint in active tags | `tag_name_fg`, then the active tag foreground |
-| `column_box` | Focused column drag grip, distinct from pane grips | `num` |
-| `column_box_dim` | Inactive column drag grip | Equal mix of `column_box` and `tag_bg` |
+| `column_box` | Base accent for the column grip's fallback color | `num` |
+| `column_box_dim` | Column drag grip in every focus state | Equal mix of `column_box` and `tag_bg` |
| `border` | Quiet separators | `scroll_track` |
| `lineno_active` | Restrained current line number foreground | `lineno` |
| `search_bg`, `search_fg` | Search matches, independent of selection | `sel_bg`, `sel_fg` |
diff --git a/src/gui/gui.zig b/src/gui/gui.zig
index 10ac839e..590e5aa8 100644
--- a/src/gui/gui.zig
+++ b/src/gui/gui.zig
@@ -6568,7 +6568,7 @@ fn appendPaneChrome(
fn appendTagGrips(g: *const Gui, surface: *const pardes.Surface, layout: CellLayout, builder: *OverlayBuilder) void {
if (g.tagline_height < 6 or g.tagline_width < 3) return;
for (surface.tagLayers()) |layer| {
- if (layer.cols == 0 or layer.kind == .workspace or layer.viewport.x < config.GUTTER) continue;
+ if (layer.cols == 0 or layer.kind != .pane or layer.viewport.x < config.GUTTER) continue;
const x = layer.viewport.x - config.GUTTER;
const y = layer.viewport.y;
if (x >= surface.cols or y >= surface.rows) continue;
@@ -6586,7 +6586,7 @@ fn appendTagGrips(g: *const Gui, surface: *const pardes.Surface, layout: CellLay
}
}
-test "column and pane grip marks share physical centering" {
+test "column grips stay unmarked while pane marks retain physical centering" {
var g: Gui = undefined;
g.tagline_height = 12;
g.tagline_width = 6;
@@ -6598,8 +6598,7 @@ test "column and pane grip marks share physical centering" {
var vertices: [24]OverlayVertex = undefined;
var builder: OverlayBuilder = .{ .vertices = &vertices, .win_w = 200, .win_h = 80 };
appendTagGrips(&g, &surface, .{ .w = 10, .h = 20, .x_off = 0, .y_off = 0 }, &builder);
- try std.testing.expectEqual(@as(usize, 24), builder.len);
- for (0..12) |i| try std.testing.expectEqual(vertices[i].x, vertices[i + 12].x);
+ try std.testing.expectEqual(@as(usize, 12), builder.len);
try std.testing.expectApproxEqAbs(@as(f32, 57.0 / 200.0 * 2.0 - 1.0), vertices[0].x, 0.0001);
}
diff --git a/src/macos/Sources/PardesView.swift b/src/macos/Sources/PardesView.swift
index 4c122a13..ba05ea51 100644
--- a/src/macos/Sources/PardesView.swift
+++ b/src/macos/Sources/PardesView.swift
@@ -1173,7 +1173,7 @@ final class PardesView: NSView {
private func drawTagGrip(_ ctx: CGContext, values: [Int], canonical: UnsafePointer<pardes_cell_s>, cols: Int, rows: Int) {
let width = Int(pardes_grip_columns())
let x = values[4] - width, y = values[5]
- if values[1] == 0 || x < 0 || x >= cols || y >= rows || taglines.height < 6 || taglines.width < 3 { return }
+ if values[1] != 2 || x < 0 || x >= cols || y >= rows || taglines.height < 6 || taglines.width < 3 { return }
var cell = canonical[y * cols + x]
let text = withUnsafeBytes(of: &cell.text) { String(decoding: $0.prefix(Int(cell.len)), as: UTF8.self) }
if text != " " { return }
diff --git a/src/pardes.zig b/src/pardes.zig
index 91605d38..bd7c43e9 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -13262,7 +13262,7 @@ pub const Pardes = struct {
const active_col = if (layout.findPane(p, p.active)) |f| f.col == c else false;
const bg = if (active_col and p.settings.focus_tint) chrome.tag_active_bg else chrome.tag_bg;
const fg = if (active_col and p.settings.focus_tint) chrome.tag_active_fg else chrome.tag_fg;
- const grip_bg = if (active_col) chrome.column_box else chrome.column_box_dim;
+ const grip_bg = chrome.column_box_dim;
const lum = (@as(u16, grip_bg[0]) * 3 + @as(u16, grip_bg[1]) * 6 + @as(u16, grip_bg[2])) / 10;
const ink: [3]u8 = if (lum > 140) .{ 0, 0, 0 } else .{ 255, 255, 255 };
const grip_style: CellStyle = .{ .bg = .{ .rgb = grip_bg }, .fg = .{ .rgb = ink }, .font_role = .tagline };
diff --git a/src/web/app.mjs b/src/web/app.mjs
index 3dcf6ab5..65e1240d 100644
--- a/src/web/app.mjs
+++ b/src/web/app.mjs
@@ -123,7 +123,7 @@ export class DomRenderer {
const gripCols = this.wasm.pardes_grip_columns?.() ?? 2;
const gripX = values[4] - gripCols;
const gripCell = canonical[values[5]*this.cols+gripX];
- const grip = values[1] !== 0 && gripX >= 0 && gripCell?.text === " " && th >= 6 && tw >= 3;
+ const grip = values[1] === 2 && gripX >= 0 && gripCell?.text === " " && th >= 6 && tw >= 3;
const geometry = [bw,bh,tw,th,offset,...values,grip,gripCell?.fg].join(":");
const cached = this.tagNodes.get(i);
if (cached && cached.geometry === geometry && cached.bytes.length === bytes.length && cached.bytes.every((byte,index)=>byte===bytes[index])) {
diff --git a/test/column_drag.zig b/test/column_drag.zig
index 341c8b70..1c9a344e 100644
--- a/test/column_drag.zig
+++ b/test/column_drag.zig
@@ -169,3 +169,40 @@ test "column grip is separate from compact text and the resize seam" {
try std.testing.expect(p.drag == .border_v);
p.update(.{ .mouse = .{ .button = .left, .kind = .release, .col = seam, .row = body_row } });
}
+
+test "column grips stay blank and muted across focus hover and drag" {
+ const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 80, .rows = 20 });
+ defer p.deinit();
+ _ = try p.setTestFile("left\n");
+ p.presentation.enabled = false;
+ p.settings.column_tags = true;
+ p.row_metrics = .{ .body_w = 10, .body_h = 20, .tagline_w = 5, .tagline_h = 10 };
+ try std.testing.expect(p.executeBuiltinLine(0, "New"));
+ try std.testing.expect(layout.splitColumn(p, p.active, p.active, false));
+ var frame = std.heap.ArenaAllocator.init(p.gpa);
+ defer frame.deinit();
+ for ([_]bool{ false, true }) |focus_tint| {
+ p.settings.focus_tint = focus_tint;
+ for (0..p.ncol) |active_column| {
+ p.active = p.col_panes[active_column][0];
+ const x = p.col_x[active_column];
+ p.update(.{ .mouse = .{ .button = .none, .kind = .motion, .col = x, .row = pardes.TOPBAR_H } });
+ for ([_]bool{ false, true }) |dragging| {
+ if (dragging) mouse(p, .press, x);
+ _ = frame.reset(.retain_capacity);
+ const surface = try p.render(frame.allocator());
+ const muted: pardes.Color = .{ .rgb = p.chromeTheme().column_box_dim };
+ for (0..p.ncol) |column| {
+ for (0..config.GUTTER) |offset| {
+ const cell = surface.at(p.col_x[column] + @as(u16, @intCast(offset)), pardes.TOPBAR_H);
+ try std.testing.expectEqualStrings(" ", cell.grapheme());
+ try std.testing.expectEqual(muted, cell.style.bg);
+ try std.testing.expectEqual(pardes.FontRole.tagline, cell.style.font_role);
+ }
+ try std.testing.expectEqual(p.col_x[column] + config.GUTTER, (try tagLayer(p, column)).viewport.x);
+ }
+ if (dragging) mouse(p, .release, x);
+ }
+ }
+ }
+}
diff --git a/test/web-snapshots/touch.mjs b/test/web-snapshots/touch.mjs
index 3820117e..b78cbdc0 100644
--- a/test/web-snapshots/touch.mjs
+++ b/test/web-snapshots/touch.mjs
@@ -100,7 +100,7 @@ export default async function ({ grid, touch, wait, stable, delay, evaluate, mou
renderer.render();
const columnGrip=renderer.tagRoot.querySelector('[data-tag-grip]');
const marks=node=>[...node.children].map(mark=>[parseFloat(mark.style.left),parseFloat(mark.style.top),parseFloat(mark.style.width)]);
- result.columnMarks=marks(columnGrip);
+ result.columnMarks=columnGrip ? marks(columnGrip) : [];
tagValues[1]=2;tagValues[5]=1;bytes[40]=32;data.setUint32(48,0x778899,true);
renderer.render();
result.paneMarks=marks(renderer.tagRoot.querySelector('[data-tag-grip]'));
@@ -119,8 +119,8 @@ export default async function ({ grid, touch, wait, stable, delay, evaluate, mou
gripVisible:"",covered:"hidden",background:"rgb(51, 68, 85)",cached:true,bgInvalidates:true},
"tag text uses its own columns after a fixed body-width grip, with cursor and cached base background");
- assert.deepEqual(compactGeometry.columnMarks,[[7,13,6],[7,16,6]]);
- assert.deepEqual(compactGeometry.paneMarks,[[7,33,6],[7,36,6]],"pane and column handles share physical horizontal mark geometry");
+ assert.deepEqual(compactGeometry.columnMarks,[],"column handles have no mark");
+ assert.deepEqual(compactGeometry.paneMarks,[[7,33,6],[7,36,6]],"pane handles keep their physical mark geometry");
let frame = await grid();
assert.equal(tags(frame).length, 1);