diff options
Diffstat (limited to 'src/pardes.zig')
| -rw-r--r-- | src/pardes.zig | 80 |
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 }); |
