diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-27 23:06:11 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:14 -0300 |
| commit | 95a3a1bb822e583988bfe0f312d0ef219b27a454 (patch) | |
| tree | 95306e8d3f36f8af05b3ea919f6cdef1e94a028b /src/mouse.zig | |
| parent | c8f0c25a854b8e169db08a84d1fdcd915a70aaa0 (diff) | |
| download | pardes-95a3a1bb822e583988bfe0f312d0ef219b27a454.tar.gz pardes-95a3a1bb822e583988bfe0f312d0ef219b27a454.zip | |
Give a pane's body its own Text holding the cursor, selections, mode and undo
acme keeps what edits a text in its Text (dat.h:171-190) and the window
holds a body and a tag of that type. The cursor, the selections, the modal
state and the edit-buffer undo move off Pane into Text.zig, Pane holds
them as its body, and the edit and normal-mode operations take the Text
they edit. Nothing changes in behaviour; this is the step that lets the
tag become a second Text.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/mouse.zig')
| -rw-r--r-- | src/mouse.zig | 130 |
1 files changed, 65 insertions, 65 deletions
diff --git a/src/mouse.zig b/src/mouse.zig index 2a00bded..a840c8fc 100644 --- a/src/mouse.zig +++ b/src/mouse.zig @@ -378,7 +378,7 @@ pub fn handleMouse(p: *Pardes, m: Mouse) void { // next cursor move or left wheel pulls it back } else if (pane.file != null and !p.settings.wrap) { // wrapped there is nothing off to the right to reach - const line = panes.File.sourceLine(pane, pane.cur_row); + const line = panes.File.sourceLine(pane, pane.body.cur_row); const visual = panes.File.rawDisplayCol(line, @intCast(@max(0, pane.hscroll))); const next: usize = if (m.button == .wheel_right) visual +| @as(usize, @intCast(config.wheel_cols)) @@ -682,7 +682,7 @@ fn gestureRange(p: *Pardes, id: usize, text: []const u8, on_tag: bool, operand: const top = @min(sel.r0, sel.r1) - @as(i32, BOX_H); if (top < 0) break :lead null; const w = pane.wrapAt(top); - const col = edit.paneByteAtDisplay(p, pane, w.line, w.at, @min(sel.c0, sel.c1) - panes.File.gutterWidth(pane)); + const col = edit.paneByteAtDisplay(p, &pane.body, w.line, w.at, @min(sel.c0, sel.c1) - panes.File.gutterWidth(pane)); break :lead .{ .row = @intCast(@max(0, w.line)), .col = @intCast(@max(0, col)), @@ -720,22 +720,22 @@ fn mirrorTtySelection(p: *Pardes, pane: *Pane) void { const body = body_layer.bodyText(p, p.scratch.allocator(), pane) catch return; const line = modal.lineSlice(body, @intCast(@max(0, endpoint.row - panes.Terminal.gridOffset(pane)))); const byte = if (gesture.c1 >= gesture.c0) modal.prevGrapheme(line, @min(endpoint.hi, line.len)) else endpoint.lo; - pane.cur_row = pane.surfRow(endpoint.row); + pane.body.cur_row = pane.surfRow(endpoint.row); var shown = std.mem.trimEnd(u8, line[@min(endpoint.prompt_bytes, line.len)..], " \t"); if (pane.ovl) |overlay| { - const relative = pane.cur_row - overlay.row; + const relative = pane.body.cur_row - overlay.row; if (relative >= 0 and relative < modal.lineCount(overlay.text)) shown = modal.lineSlice(overlay.text, @intCast(relative)); } - pane.cur_col = @intCast(modal.graphemeStart(shown, @min(shown.len, byte -| endpoint.prompt_bytes))); - pane.cur_pinned = true; - pane.msel.active = false; - pane.vsel.active = false; - pane.nsel = 0; - pane.select = false; - pane.append_at = null; - pane.sticky_col = -1; - pane.normal.clear(); + pane.body.cur_col = @intCast(modal.graphemeStart(shown, @min(shown.len, byte -| endpoint.prompt_bytes))); + pane.body.cur_pinned = true; + pane.body.msel.active = false; + pane.body.vsel.active = false; + pane.body.nsel = 0; + pane.body.select = false; + pane.body.append_at = null; + pane.body.sticky_col = -1; + pane.body.normal.clear(); edit.setYank(p, text); } @@ -813,27 +813,27 @@ fn dragRelease(p: *Pardes, button: Mouse.Button) void { const dragged = sl.c0 != sl.c1 or sl.r0 != sl.r1; if (!dragged) pane.sel[sel_slot].state = .none; if (dragged) edit.capturePointerSelection(p, pane, sel_slot) catch {}; - if (dragged and pane.mode == .tty) mirrorTtySelection(p, pane); + if (dragged and pane.body.mode == .tty) mirrorTtySelection(p, pane); const body_vis = sl.r1 - @as(i32, BOX_H); - if (body_vis >= 0 and pane.mode != .tty) { + if (body_vis >= 0 and pane.body.mode != .tty) { const w = pane.wrapAt(body_vis); - pane.cur_row = w.line; - pane.cur_col = edit.paneByteAtDisplay( + pane.body.cur_row = w.line; + pane.body.cur_col = edit.paneByteAtDisplay( p, - pane, + &pane.body, w.line, w.at, sl.c1 - (if (pane.file != null) @as(i32, panes.File.gutterWidth(pane)) else 0), ); - pane.cur_pinned = true; - if (edit.clampFileCursor(pane)) pane.ensureCursorVisible(); - pane.msel.active = false; - pane.normal.clear(); - pane.vsel.active = false; - pane.nsel = 0; // a click says WHERE the one cursor is - pane.select = false; - pane.append_at = null; - pane.sticky_col = -1; + pane.body.cur_pinned = true; + if (edit.clampFileCursor(&pane.body)) pane.ensureCursorVisible(); + pane.body.msel.active = false; + pane.body.normal.clear(); + pane.body.vsel.active = false; + pane.body.nsel = 0; // a click says WHERE the one cursor is + pane.body.select = false; + pane.body.append_at = null; + pane.body.sticky_col = -1; if (s.ctrl) p.lspRequest(s.id, .definition, ""); } } else { @@ -842,7 +842,7 @@ fn dragRelease(p: *Pardes, button: Mouse.Button) void { // this file -- a sticky context header's number included, // since the row map holds each header's own source line. if (s.button == config.look_button and clk.r0 >= BOX_H and clk.r0 == clk.r1 and - clk.c0 == clk.c1 and clk.c0 >= 0 and pane.mode != .tty) + clk.c0 == clk.c1 and clk.c0 >= 0 and pane.body.mode != .tty) if (pane.file) |f| if (f.output == null and clk.c0 < panes.File.gutterWidth(pane)) { const line = pane.wrapAt(clk.r0 - BOX_H).line; if (line >= 0) { @@ -858,10 +858,10 @@ fn dragRelease(p: *Pardes, button: Mouse.Button) void { pane.sel[b] = expanded else if (operand.text == null) pane.sel[b].state = .none; - if (s.button == config.look_button and clk.r0 >= BOX_H and pane.mode != .tty) { - pane.cur_row = operand.row; - pane.cur_col = operand.col; - if (edit.clampFileCursor(pane)) pane.ensureCursorVisible(); + if (s.button == config.look_button and clk.r0 >= BOX_H and pane.body.mode != .tty) { + pane.body.cur_row = operand.row; + pane.body.cur_col = operand.col; + if (edit.clampFileCursor(&pane.body)) pane.ensureCursorVisible(); } const txt = operand.text; const cmd = if (s.button == config.look_button) config.look_cmd else config.exec_cmd; @@ -909,7 +909,7 @@ fn chordCutPaste(p: *Pardes, cut: bool) void { if (cut and pv.selection_text.len > 0) edit.setYank(p, pv.selection_text); return; }; - if (pane.mode == .tty) { + if (pane.body.mode == .tty) { if (!s.chorded) { s.chorded = true; pane.sel[sel_slot].state = .none; // drop the sweep highlight @@ -944,44 +944,44 @@ fn chordCutPaste(p: *Pardes, cut: bool) void { const w1 = pane.wrapAt(@max(0, sl.r1 - @as(i32, BOX_H))); const row0 = w0.line; const row1 = w1.line; - const col0 = edit.paneByteAtDisplay(p, pane, w0.line, w0.at, sl.c0 - pfx); - const col1 = edit.paneByteAtDisplay(p, pane, w1.line, w1.at, sl.c1 - pfx); - pane.cur_row = row1; - pane.cur_col = col1; - pane.cur_pinned = true; - pane.msel.active = false; - pane.normal.clear(); - if (!pane.isTerminal()) pane.mode = .normal; - pane.vsel = .{ .active = row0 != row1 or col0 != col1, .row = row0, .col = col0, .explicit = false }; - pane.nsel = 0; + const col0 = edit.paneByteAtDisplay(p, &pane.body, w0.line, w0.at, sl.c0 - pfx); + const col1 = edit.paneByteAtDisplay(p, &pane.body, w1.line, w1.at, sl.c1 - pfx); + pane.body.cur_row = row1; + pane.body.cur_col = col1; + pane.body.cur_pinned = true; + pane.body.msel.active = false; + pane.body.normal.clear(); + if (!pane.isTerminal()) pane.body.mode = .normal; + pane.body.vsel = .{ .active = row0 != row1 or col0 != col1, .row = row0, .col = col0, .explicit = false }; + pane.body.nsel = 0; } if (cut) { - if (pane.vsel.active) edit.normalDelete(p, pane, true); - } else if (pane.vsel.active) { - edit.normalReplaceYank(p, pane); + if (pane.body.vsel.active) edit.normalDelete(p, &pane.body, true); + } else if (pane.body.vsel.active) { + edit.normalReplaceYank(p, &pane.body); } else if (pane.file) |*f| { // bare cursor: splice the register in literally (no linewise // magic) so a paste right after a cut restores the text exactly const y = p.yank orelse return; if (y.len == 0) return; - const at = modal.Cursor{ .row = @intCast(@max(0, pane.cur_row)), .col = @intCast(@max(0, pane.cur_col)) }; - edit.pushUndo(p, pane); + const at = modal.Cursor{ .row = @intCast(@max(0, pane.body.cur_row)), .col = @intCast(@max(0, pane.body.cur_col)) }; + edit.pushUndo(p, &pane.body); const new = modal.insertAt(p.gpa, f.content, at, y) catch return; panes.File.setContent(p, f, new); - pane.vsel = .{ .active = modal.nextGrapheme(y, 0) < y.len, .row = @intCast(at.row), .col = @intCast(at.col), .explicit = false }; + pane.body.vsel = .{ .active = modal.nextGrapheme(y, 0) < y.len, .row = @intCast(at.row), .col = @intCast(at.col), .explicit = false }; const end = modal.advanceBy(at, y); if (end.col > 0) { - pane.cur_row = @intCast(end.row); - pane.cur_col = @intCast(modal.prevGrapheme(modal.lineSlice(f.content, end.row), end.col)); + pane.body.cur_row = @intCast(end.row); + pane.body.cur_col = @intCast(modal.prevGrapheme(modal.lineSlice(f.content, end.row), end.col)); } else { // the register ended in '\n': the cursor lands ON that newline - pane.cur_row = @intCast(end.row -| 1); - pane.cur_col = @intCast(modal.lineSlice(f.content, end.row -| 1).len); + pane.body.cur_row = @intCast(end.row -| 1); + pane.body.cur_col = @intCast(modal.lineSlice(f.content, end.row -| 1).len); } - pane.cur_pinned = true; - pane.sticky_col = -1; + pane.body.cur_pinned = true; + pane.body.sticky_col = -1; pane.ensureCursorVisible(); - } else edit.pasteText(p, pane, p.yank orelse return, true, 1); + } else edit.pasteText(p, &pane.body, p.yank orelse return, true, 1); } test "plain left click clears explicit modal selection" { @@ -989,10 +989,10 @@ test "plain left click clears explicit modal selection" { const p = try Pardes.init(gpa, .{ .cols = 40, .rows = 10 }); defer p.deinit(); const pane = try p.setTestFile("abcdef\n"); - pane.cur_row = 0; - pane.cur_col = 5; - pane.vsel = .{ .active = true, .row = 0, .col = 1, .explicit = true }; - pane.msel = .{ .active = true, .r0 = 0, .r1 = 0 }; + pane.body.cur_row = 0; + pane.body.cur_col = 5; + pane.body.vsel = .{ .active = true, .row = 0, .col = 1, .explicit = true }; + pane.body.msel = .{ .active = true, .r0 = 0, .r1 = 0 }; var frame = std.heap.ArenaAllocator.init(gpa); defer frame.deinit(); @@ -1003,9 +1003,9 @@ test "plain left click clears explicit modal selection" { p.update(.{ .mouse = .{ .button = config.select_button, .kind = .press, .col = click_x, .row = body_y } }); p.update(.{ .mouse = .{ .button = config.select_button, .kind = .release, .col = click_x, .row = body_y } }); - try std.testing.expectEqual(@as(i32, 3), pane.cur_col); - try std.testing.expect(!pane.vsel.active); - try std.testing.expect(!pane.msel.active); + try std.testing.expectEqual(@as(i32, 3), pane.body.cur_col); + try std.testing.expect(!pane.body.vsel.active); + try std.testing.expect(!pane.body.msel.active); try std.testing.expectEqual(.none, pane.sel[@intFromEnum(config.select_button)].state); } @@ -1412,7 +1412,7 @@ test "mouse thumb buttons navigate once and clear selection gestures" { const p = try Pardes.init(std.testing.allocator, .{ .cols = 60, .rows = 12 }); defer p.deinit(); const pane = try p.setTestFile("alpha beta\ngamma delta\nepsilon zeta\n"); - pane.setRange(pane.file.?.content, 0, .{ .anchor = 1, .head = 5 }, true); + pane.body.setRange(pane.file.?.content, 0, .{ .anchor = 1, .head = 5 }, true); p.njumps = 3; p.jcur = 2; for (0..3) |i| p.jumps[i] = .{ .pane = 0, .serial = pane.serial, .line = @intCast(i + 1), .col = 1 }; @@ -1425,6 +1425,6 @@ test "mouse thumb buttons navigate once and clear selection gestures" { p.update(.{ .mouse = .{ .button = .forward, .kind = .press, .col = 0, .row = 0 } }); p.update(.{ .mouse = .{ .button = .forward, .kind = .release, .col = 0, .row = 0 } }); try std.testing.expectEqual(@as(usize, 2), p.jcur); - try std.testing.expect(!pane.vsel.active); + try std.testing.expect(!pane.body.vsel.active); try std.testing.expectEqual(.none, p.drag); } |
