From 42748f92b77367db415c8b4d0f2e537a9e1ddde7 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Mon, 28 Sep 2026 00:13:11 -0300 Subject: Make the column and workspace tags Texts with the pane tag's keys The headers had their own one-line editor with its own keys: Enter always executed, h/l and J/K moved between headers and panes, Ctrl-c/x/v cut and pasted, and TagLine refused a newline that 9P accepted. They are now Texts like a pane's tag, with no prefix and a default until edited, so normal and insert mode, undo and the look and execute keys are the body's. Moving between them is the window keys' job: Up from a pane with nothing above it reaches its column's tag and then the workspace's, Down comes back, and Left and Right walk the column tags. Co-Authored-By: Claude Opus 5.5 --- src/mouse.zig | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) (limited to 'src/mouse.zig') diff --git a/src/mouse.zig b/src/mouse.zig index ae047c58..343051b0 100644 --- a/src/mouse.zig +++ b/src/mouse.zig @@ -292,9 +292,17 @@ pub fn handleMouse(p: *Pardes, m: Mouse) void { } } if (p.header_drag and m.button == config.select_button and (m.kind == .drag or m.kind == .release)) { + const t = p.focusedHeader() orelse { + p.header_drag = false; + return; + }; const x = if (p.header_column) |c| p.col_x[c] + TAG_TEXT_INSET else 0; - const bar = p.headerText(); - p.topbar_col = @intCast(@min(bar.len, panes.File.rawAtDisplay(bar, (tagColumn(p, if (p.header_column != null) .column else .workspace, p.header_column orelse 0, p.pointer_tag_hit, p.header_drag) orelse (mcol -| x)) + p.header_scroll))); + const bar = modal.lineSlice(edit.flatSurface(p, t) catch return, 0); + const at = @min(bar.len, panes.File.rawAtDisplay(bar, (tagColumn(p, if (p.header_column != null) .column else .workspace, p.header_column orelse 0, p.pointer_tag_hit, p.header_drag) orelse (mcol -| x)) + p.header_scroll)); + // the anchor stays where the press put it + t.cur_row = 0; + t.cur_col = @intCast(at); + t.vsel.active = t.vsel.col != t.cur_col; if (m.kind == .release) p.header_drag = false; return; } @@ -318,25 +326,25 @@ pub fn handleMouse(p: *Pardes, m: Mouse) void { }; if (column == null or mcol < x) return; } - if (p.header_column != column) { - p.rememberHeader(); - p.topbar_col = null; - p.header_anchor = null; - p.header_scroll = 0; - } - p.header_column = column; - const bar = p.headerText(); - const at = @min(bar.len, panes.File.rawAtDisplay(bar, (tagColumn(p, if (p.header_column != null) .column else .workspace, p.header_column orelse 0, p.pointer_tag_hit, p.header_drag) orelse (mcol -| x)) + p.header_scroll)); + const t = if (column) |c| &p.col_tags[c] else &p.global_tag; + const focused = p.header_focus and p.header_column == column; + const bar = modal.lineSlice(edit.flatSurface(p, t) catch return, 0); + const scroll = if (focused) p.header_scroll else 0; + const at = @min(bar.len, panes.File.rawAtDisplay(bar, (tagColumn(p, if (column != null) .column else .workspace, column orelse 0, p.pointer_tag_hit, p.header_drag) orelse (mcol -| x)) + scroll)); if (m.button == config.select_button) { - p.topbar_col = @intCast(at); - p.header_anchor = @intCast(at); - p.header_insert = true; + // A clicked header is typed straight into, as a clicked tag is. + if (!focused) p.enterHeader(column); + t.mode = .insert; + t.cur_row = 0; + t.cur_col = @intCast(at); + t.vsel = .{ .active = false, .row = 0, .col = @intCast(at) }; p.header_drag = true; } else { - const selected = if (p.topbar_col) |cur| blk: { - const a = p.header_anchor orelse cur; - break :blk if (at >= @min(a, cur) and at < @max(a, cur)) bar[@min(a, cur)..@max(a, cur)] else wordAtCol(bar, at); - } else wordAtCol(bar, at); + const span = if (focused) Pardes.headerSelection(t, bar) else null; + const selected = if (span) |sp| + (if (at >= sp.lo and at < sp.hi) bar[sp.lo..sp.hi] else wordAtCol(bar, at)) + else + wordAtCol(bar, at); const word = p.scratch.allocator().dupe(u8, selected) catch return; p.exitHeader(); if (word.len > 0) { -- cgit v1.3