summaryrefslogtreecommitdiff
path: root/src/pardes.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/pardes.zig')
-rw-r--r--src/pardes.zig80
1 files changed, 58 insertions, 22 deletions
diff --git a/src/pardes.zig b/src/pardes.zig
index 4dd35758..b303acbb 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -1456,21 +1456,21 @@ pub const Pardes = struct {
// ---- tag + selection text (chord sources) ----
- /// the live tag prefix: mode + cwd/path (an image has only its path — the
- /// renderer toggles it used to spell out are builtins now, under SPC t)
+ /// the live tag prefix: the pane's cwd/path, and nothing else (an image
+ /// still names its kind — the renderer toggles it used to spell out are
+ /// builtins now, under SPC t). The mode used to lead this as a word; it is
+ /// the one character in the layout box now (renderPane), so every tagline
+ /// starts four columns further left and spends them on the path instead.
+ ///
+ /// Arena-allocated like everything the renderer is handed — the tag is
+ /// retained through the frame and a cwd can be rewritten under us by the
+ /// next shell report, so this owns its bytes rather than lending the
+ /// pane's.
fn tagPrefix(p: *Pardes, pane: *Pane) ![]u8 {
const arena = p.scratch.allocator();
if (pane.image) |iv| return std.fmt.allocPrint(arena, config.tag_image ++ " {s}", .{iv.path});
- if (pane.file) |f| {
- const fmode = if (pane.mode == .insert) config.tag_insert else config.tag_normal;
- return std.fmt.allocPrint(arena, "{s} {s}", .{ fmode, f.path });
- }
- const mode = switch (pane.mode) {
- .normal => config.tag_normal,
- .insert => config.tag_insert,
- .tty => config.tag_tty,
- };
- return std.fmt.allocPrint(arena, "{s} {s}", .{ mode, pane.cwdSlice() });
+ if (pane.file) |f| return arena.dupe(u8, f.path);
+ return arena.dupe(u8, pane.cwdSlice());
}
/// the editable tail: the user's edited buffer once touched, else defaults
@@ -7016,7 +7016,44 @@ pub const Pardes = struct {
s.clearRect(tx, r.y, tw, r.h);
if (th.bg) |bg| s.fill(tx, r.y, tw, r.h, .{ .bg = .{ .rgb = bg } });
- // tag: live prefix (mode + cwd/path) + editable tail, on a dim bar
+ // the layout box: the pane's MODE, one character, in the gutter cells
+ // of the tag row. Same box you drag a pane by — the whole GUTTER is
+ // still painted and the `.move` press hit-test in handleMouse is
+ // untouched — it just carries the one piece of state that used to cost
+ // four columns of every tagline.
+ // The glyph goes in column 0, directly above the scrollbar's ink
+ // column below it, so a pane's chrome reads as one line down its left
+ // edge; column 1 stays plain colour, and that blank half is what keeps
+ // the thing reading as a BOX rather than as a letter someone dropped
+ // in the gutter.
+ //
+ // The mode shown is the BODY's. A tag edit hijacks pane.mode to insert
+ // (tags are always insert; the real one is parked in tag_mode), and a
+ // badge that flipped every time you clicked a tagline would be
+ // reporting the tag's mode on the pane's box. Reading tag_mode also
+ // makes the parked value VISIBLE: a terminal being tag-edited still
+ // shows `$`, which is exactly the invariant ttyclick.snap exists for.
+ //
+ // The ink is picked off the box's own brightness instead of being
+ // named in the theme, because box/box_dim come from a generated
+ // theme's cursor and selection colours — light on a light theme — and
+ // a fixed white would vanish there. Rec.601-ish integer weights.
+ // ponytail: two colours, black or white, chosen at a fixed threshold.
+ // Upgrade to the theme's own fg/bg pair when a theme turns up whose
+ // box wants a tint rather than a contrast.
+ const box_bg = if (active) th.box else th.box_dim;
+ const box_lum = (@as(u16, box_bg[0]) * 3 + @as(u16, box_bg[1]) * 6 + @as(u16, box_bg[2])) / 10;
+ const box_ink: [3]u8 = if (box_lum > 140) .{ 0x00, 0x00, 0x00 } else .{ 0xff, 0xff, 0xff };
+ const box_style: CellStyle = .{ .fg = .{ .rgb = box_ink }, .bg = .{ .rgb = box_bg } };
+ s.fill(r.x, r.y, config.GUTTER, BOX_H, box_style);
+ const box_mode = if (pane.tag_edit) pane.tag_mode else pane.mode;
+ s.set(r.x, r.y, switch (box_mode) {
+ .normal => config.tag_normal,
+ .insert => config.tag_insert,
+ .tty => config.tag_tty,
+ }, box_style);
+
+ // tag: live prefix (cwd/path) + editable tail, on a dim bar
const tag_style: CellStyle = .{ .fg = .{ .rgb = th.tag_fg }, .bg = .{ .rgb = th.tag_bg } };
s.fill(tx, r.y, tw, BOX_H, .{ .bg = .{ .rgb = th.tag_bg } });
const tag = try p.tagText(arena, pane);
@@ -7062,8 +7099,6 @@ pub const Pardes = struct {
// thumbless gutter so it reads like any other pane.
if (pane.image != null) {
p.drawImage(pane, r, tx, tw);
- const box_bg2 = if (active) th.box else th.box_dim;
- s.fill(r.x, r.y, config.GUTTER, BOX_H, .{ .bg = .{ .rgb = box_bg2 } });
// thumbless, but the same one column as the real scrollbar below —
// that is the whole point of drawing it
s.fill(r.x, r.y + BOX_H, 1, r.h -| BOX_H, .{ .bg = .{ .rgb = th.scroll_track } });
@@ -7215,7 +7250,9 @@ pub const Pardes = struct {
}
}
- // gutter: move box on top, scrollbar track + thumb below.
+ // gutter below the tag row: scrollbar track + thumb. (The move box on
+ // the tag row itself is drawn up with the tag, since it now carries
+ // the mode and belongs with the rest of that row.)
//
// The bar is ONE column: column 1 is the track/thumb, column 2 is the
// pane's own background. That second fill is not optional — render()
@@ -7223,16 +7260,15 @@ pub const Pardes = struct {
// panes read as chrome, so a column left unpainted here keeps the
// track colour and the bar looks two wide again.
//
- // The move box above stays the full GUTTER. It is a target you drag,
- // not a gauge you read, and its whole width is the affordance — the
- // step where the box ends and the narrower bar begins is the one place
- // on screen that says those are two different pieces of chrome.
+ // The move box above stays the full GUTTER even though only half of it
+ // has ink in it. It is a target you drag, not a gauge you read, and its
+ // whole width is the affordance — the step where the box ends and the
+ // narrower bar begins is the one place on screen that says those are
+ // two different pieces of chrome.
//
// Nothing here touches layout or hit-testing: the gutter is still
// config.GUTTER columns and the click handlers still scroll on any of
// them, so the blank column is still live. Only the ink narrowed.
- const box_bg = if (active) th.box else th.box_dim;
- s.fill(r.x, r.y, config.GUTTER, BOX_H, .{ .bg = .{ .rgb = box_bg } });
if (r.h > BOX_H) {
s.fill(r.x, r.y + BOX_H, 1, r.h - BOX_H, .{ .bg = .{ .rgb = th.scroll_track } });
s.fill(r.x + 1, r.y + BOX_H, 1, r.h - BOX_H, .{ .bg = pane_bg });