diff options
Diffstat (limited to 'src/pardes.zig')
| -rw-r--r-- | src/pardes.zig | 109 |
1 files changed, 62 insertions, 47 deletions
diff --git a/src/pardes.zig b/src/pardes.zig index 3c0205d2..dd7fc5ed 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -7128,6 +7128,7 @@ pub const Pardes = struct { } fn enterTagEdit(p: *Pardes, pane: *Pane, col: i32) void { + if (pane.tag_edit and pane.prompt == .none) pane.tag_saved_col = pane.tag_col; const edit0: i32 = @intCast((p.tagPrefix(pane) catch return).len); p.seedTail(pane); if (!pane.tag_init) return; @@ -7141,14 +7142,18 @@ pub const Pardes = struct { const tail = pane.tagSlice(); const lead: i32 = @intCast(tail.len - std.mem.trimStart(u8, tail, " ").len); if (col < 0) { - pane.tag_col = @intCast(@min(edit0 + lead, end)); const text = p.tagText(p.scratch.allocator(), pane) catch return; - if (p.paneIdOf(pane)) |id| { + if (pane.tag_saved_col) |saved| { + pane.tag_col = @intCast(modal.graphemeStart(text, @min(saved, text.len))); + } else { + pane.tag_col = @intCast(@min(edit0 + lead, end)); + } + if (pane.tag_saved_col == null) if (p.paneIdOf(pane)) |id| { const width = @max(1, p.rects[id].w -| config.GUTTER); // Reveal the suffix together with its commands, not merely the // first command letter at the viewport's far right edge. pane.tag_scroll = @intCast(panes.File.displayWidth(text) + 1 -| width); - } + }; } else { const text = p.tagText(p.scratch.allocator(), pane) catch return; pane.tag_col = @intCast(@min(text.len, panes.File.rawAtDisplay(text, @as(usize, @intCast(col)) + pane.tag_scroll))); @@ -7156,6 +7161,7 @@ pub const Pardes = struct { } fn exitTagEdit(pane: *Pane) void { + if (pane.tag_edit and pane.prompt == .none) pane.tag_saved_col = pane.tag_col; if (pane.isTerminal()) { if (pane.tag_edit) pane.mode = pane.tag_mode; } else pane.mode = .normal; @@ -7334,7 +7340,7 @@ pub const Pardes = struct { const text = p.tagText(p.scratch.allocator(), pane) catch return; const cur: usize = @min(@as(usize, pane.tag_col), text.len); // Esc abandons the command line: back to the body, tail kept as text - if (hit(key, config.escape)) return exitTagEdit(pane); + if (hit(key, config.escape) or hit(key, &.{.{ .cp = 'j' }})) return exitTagEdit(pane); if (hit(key, config.look_key) or hit(key, config.exec_key)) { const cmd = if (hit(key, config.look_key)) config.look_cmd else config.exec_cmd; const txt = p.tagChordText(pane) orelse return; @@ -7364,7 +7370,9 @@ pub const Pardes = struct { return; } const dir: ?Builtin = for (config.window_keys) |wk| { - if (hit(key, &.{wk.letter})) break wk.cmd; + var letter = wk.letter; + letter.cp = std.ascii.toUpper(@intCast(letter.cp)); + if (hit(key, &.{letter})) break wk.cmd; } else null; if (dir) |d| { const from = p.active; @@ -7372,17 +7380,13 @@ pub const Pardes = struct { if (p.active == from) { if (d == .Up) { exitTagEdit(pane); - p.topbar_col = 0; - p.header_column = if (p.columnBarHeight() > 0) (layout.findPane(p, from) orelse return).col else null; - p.header_insert = false; - p.header_anchor = null; - p.header_drag = false; + p.enterHeader(if (p.columnBarHeight() > 0) (layout.findPane(p, from) orelse return).col else null); } return; } const dest = p.panes[p.active] orelse return; exitTagEdit(pane); // the pane we left is fully out of its tag... - p.enterTagEdit(dest, -1); // ...and the tail's start is the new park + p.enterTagEdit(dest, -1); if (dest.tag_edit) dest.mode = .normal; return; } @@ -7421,23 +7425,46 @@ pub const Pardes = struct { return modal.moveWord(text, .{ .anchor = cur, .head = modal.nextGrapheme(text, cur) }, 1, t); } + fn rememberHeader(p: *Pardes) void { + const col = p.topbar_col orelse return; + const line = if (p.header_column) |c| &p.col_tags[c] else &p.global_tag; + line.saved_col = col; + } + + pub fn exitHeader(p: *Pardes) void { + p.rememberHeader(); + p.topbar_col = null; + p.header_column = null; + p.header_anchor = null; + p.header_drag = false; + p.header_scroll = 0; + } + + fn enterHeader(p: *Pardes, column: ?usize) void { + p.rememberHeader(); + p.header_column = column; + const line = if (column) |c| &p.col_tags[c] else &p.global_tag; + const text = p.headerText(); + p.topbar_col = @intCast(modal.graphemeStart(text, @min(line.saved_col orelse 0, text.len))); + p.header_insert = false; + p.header_anchor = null; + p.header_drag = false; + p.header_scroll = 0; + } + fn topbarKey(p: *Pardes, key: Key) void { const bar = p.headerText(); // the line GROWS a `Restore <path>` word the moment a dump lands, so // the column is clamped against the live line on every use const cur: usize = @min(@as(usize, p.topbar_col orelse return), bar.len); - if (hit(key, config.escape)) { - p.topbar_col = null; // the active pane still has its body focus - p.header_column = null; - p.header_anchor = null; - p.header_drag = false; - p.header_scroll = 0; + if (hit(key, config.escape) or (!p.header_insert and hit(key, &.{.{ .cp = 'j' }}))) { + p.exitHeader(); return; } if (hit(key, config.look_key) or hit(key, config.exec_key)) { const a = p.header_anchor orelse @as(u16, @intCast(cur)); const word = if (a != cur) bar[@min(a, cur)..@max(a, cur)] else wordAtCol(bar, cur); - p.topbar_col = null; + p.exitHeader(); if (word.len > 0) { if (!p.header_insert) { // Preserve the workspace tag's keyboard command semantics. @@ -7490,9 +7517,7 @@ pub const Pardes = struct { } if (hit(key, config.topbar_down)) { if (p.header_column == null and p.columnBarHeight() > 0) { - p.header_column = if (layout.findPane(p, p.active)) |f| f.col else 0; - p.header_scroll = 0; - p.topbar_col = 0; + p.enterHeader(if (layout.findPane(p, p.active)) |f| f.col else 0); return; } var dest: ?usize = null; @@ -7507,17 +7532,14 @@ pub const Pardes = struct { } const d = dest orelse return; const pane = p.panes[d].?; - p.topbar_col = null; - p.header_column = null; + p.exitHeader(); p.active = d; p.enterTagEdit(pane, -1); if (pane.tag_edit) pane.mode = .normal; return; } - if (key.cp == 'k' or key.cp == Key.up) { - p.header_column = null; - p.header_anchor = null; - p.topbar_col = 0; + if (hit(key, config.topbar_up)) { + p.enterHeader(null); return; } const col: ?usize = if (hit(key, config.topbar_left)) @@ -7905,7 +7927,7 @@ pub const Pardes = struct { const pane = p.panes[id] orelse return; if (!pane.isTerminal()) return; p.active = id; - p.topbar_col = null; + p.exitHeader(); if (pane.tag_edit) exitTagEdit(pane); if (pane.mode == .tty) { pane.mode = .normal; @@ -10948,6 +10970,9 @@ pub const Pardes = struct { if (mrow < TOPBAR_H + p.columnBarHeight() and m.kind == .press and (m.button == config.select_button or m.button == config.exec_button or m.button == config.look_button)) { + if (m.button == config.select_button) if (p.panes[p.active]) |pane| { + if (pane.tag_edit) exitTagEdit(pane); + }; var column: ?usize = null; var x: u16 = 0; if (mrow >= TOPBAR_H) { @@ -10962,6 +10987,7 @@ pub const Pardes = struct { if (column == null) return; } if (p.header_column != column) { + p.rememberHeader(); p.topbar_col = null; p.header_anchor = null; p.header_scroll = 0; @@ -10970,7 +10996,6 @@ pub const Pardes = struct { const bar = p.headerText(); const at = @min(bar.len, panes.File.rawAtDisplay(bar, (mcol -| x) + p.header_scroll)); if (m.button == config.select_button) { - if (p.panes[p.active]) |pane| exitTagEdit(pane); p.topbar_col = @intCast(at); p.header_anchor = @intCast(at); p.header_insert = true; @@ -10981,7 +11006,7 @@ pub const Pardes = struct { 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 word = p.scratch.allocator().dupe(u8, selected) catch return; - p.topbar_col = null; + p.exitHeader(); if (word.len > 0) { if (m.button == config.exec_button) { const named = std.meta.stringToEnum(Builtin, word); @@ -10993,10 +11018,7 @@ pub const Pardes = struct { return; } if (m.kind == .press) { - p.topbar_col = null; - p.header_column = null; - p.header_anchor = null; - p.header_drag = false; + p.exitHeader(); } switch (m.button) { @@ -11083,6 +11105,9 @@ pub const Pardes = struct { const id = hovered orelse return; const r = p.rects[id]; const pane = p.panes[id] orelse return; + if (id != p.active) if (p.panes[p.active]) |previous| { + if (previous.tag_edit) exitTagEdit(previous); + }; const tag_y = if (p.settings.tag_bottom) r.y + r.h -| BOX_H else r.y; const body_y = if (p.settings.tag_bottom) r.y else r.y + BOX_H; if (mcol < r.x + config.GUTTER) { @@ -11992,13 +12017,7 @@ pub const Pardes = struct { const tagline_metrics_changed = setting.action == .tagline_size and p.settings.font.tagline_percent != previous_tagline_percent; if (p.settings.column_tags != previous_column_tags) { - if (p.header_column != null) { - p.topbar_col = null; - p.header_column = null; - p.header_anchor = null; - p.header_drag = false; - p.header_scroll = 0; - } + if (p.header_column != null) p.exitHeader(); } if (transition_changed or tagline_metrics_changed or p.settings.column_tags != previous_column_tags) { p.abandonPanelAnimations(); @@ -12474,11 +12493,7 @@ pub const Pardes = struct { /// deferred greetings. The mirror of the prototype's loop epilogue. fn sync(p: *Pardes) void { if (p.header_column != null and p.columnBarHeight() == 0) { - p.topbar_col = null; - p.header_column = null; - p.header_anchor = null; - p.header_drag = false; - p.header_scroll = 0; + p.exitHeader(); } p.reapPanes(); layout.compute(p); @@ -12486,7 +12501,7 @@ pub const Pardes = struct { p.trackJump(); for (&p.panes, 0..) |*slot, id| { const pane = slot.* orelse continue; - if (pane.tag_name_len != null and (id != p.active or p.topbar_col != null)) + if (pane.tag_edit and pane.prompt == .none and (id != p.active or p.topbar_col != null)) exitTagEdit(pane); if (comptime terminal_panes) if (pane.terminal) |state| { var off: u16 = 0; |
