From fb87946e09bf33c0fe663ccba6db14a63e7cfda4 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Mon, 28 Sep 2026 19:31:46 -0300 Subject: A B1 sweep held past a pane's edge scrolls the body, as acme's frselect does Dragging a selection beyond the top or bottom of a pane stopped at the last visible row. Now, while B1 is held past the body's top or bottom (at a pane that reaches the screen's bottom, its last row, since the pointer cannot leave the screen), the body scrolls a line a tick; the sweep's start keeps its place in the text, and a sweep that scrolled ends as the text's own selection. Also here: a ctrl-click is not half a double-click (lspdebug's golden stays as it was), and a test that clicks many points in a row resets the double-click between them. Co-Authored-By: Claude Opus 5.5 --- test/panes.zig | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'test/panes.zig') diff --git a/test/panes.zig b/test/panes.zig index d8dab298..a6a95124 100644 --- a/test/panes.zig +++ b/test/panes.zig @@ -86,6 +86,8 @@ const TagLayerTests = struct { const source_y = @as(f32, @floatFromInt(layer.viewport.y)) + 0.5; const painted_x = shown.x + (x / 10 - track.to.x) / track.to.w * shown.w; const painted_y = shown.y + (source_y - track.to.y) / track.to.h * shown.h; + // Each is a first click, whatever cell the one before hit. + p.last_click = null; for ([_]pardes.Mouse.Kind{ .press, .release }) |kind| { var event = mouse(layer, x, kind); // Hosts inverse the exact painted pixel into the tag envelope; @@ -1057,6 +1059,32 @@ const ClickCursorTests = struct { p.update(.{ .mouse = .{ .button = button, .kind = .release, .col = x, .row = y } }); } + test "a B1 sweep held past a pane's bottom scrolls the body and keeps its start" { + const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 60, .rows = 12 }); + defer p.deinit(); + var text: std.ArrayList(u8) = .empty; + defer text.deinit(std.testing.allocator); + for (0..60) |i| try text.print(std.testing.allocator, "line{d}\n", .{i}); + const pane = try p.setTestFile(text.items); + var frame = std.heap.ArenaAllocator.init(p.gpa); + defer frame.deinit(); + _ = try p.render(frame.allocator()); + const rect = p.rects[0]; + const x = rect.x + config.GUTTER + panes.File.gutterWidth(pane); + const y = p.bodyTop(pane, rect); + // The pane reaches the screen's bottom: its last row is the edge. + const below = rect.y + rect.h - 1; + p.update(.{ .mouse = .{ .button = .left, .kind = .press, .col = x, .row = y + 1 } }); + p.update(.{ .mouse = .{ .button = .left, .kind = .drag, .col = x + 2, .row = below } }); + for (0..20) |_| p.update(.tick); + try std.testing.expect(pane.scroll() >= 20); + _ = try p.render(frame.allocator()); // a frame between the ticks and the release + p.update(.{ .mouse = .{ .button = .left, .kind = .release, .col = x + 2, .row = below } }); + const got = pardes.edit.currentSelText(p, pane).?; + try std.testing.expect(std.mem.startsWith(u8, got, "line1\nline2\n")); + try std.testing.expect(std.mem.count(u8, got, "\n") > 20); + } + test "everything a B1 chord does while B1 is down is one undo step" { const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 60, .rows = 12 }); defer p.deinit(); -- cgit v1.3