terminal/new: scrolling viewport into active area pins to active

This commit is contained in:
Mitchell Hashimoto
2024-02-28 09:19:27 -08:00
parent bfa574fa60
commit 7ce4010f7a
3 changed files with 68 additions and 40 deletions

View File

@ -3839,6 +3839,7 @@ test "Screen: scrolling" {
} }
} }
// X
test "Screen: scroll down from 0" { test "Screen: scroll down from 0" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; const alloc = testing.allocator;

View File

@ -203,53 +203,42 @@ pub fn scroll(self: *PageList, behavior: Scroll) void {
switch (behavior) { switch (behavior) {
.active => self.viewport = .{ .active = {} }, .active => self.viewport = .{ .active = {} },
.top => self.viewport = .{ .top = {} }, .top => self.viewport = .{ .top = {} },
.delta_row => |n| { .delta_row => |n| delta_row: {
if (n == 0) return; if (n == 0) return;
const top = self.getTopLeft(.viewport); const top = self.getTopLeft(.viewport);
const offset: RowOffset = if (n < 0) switch (top.backwardOverflow(@intCast(-n))) { const offset: RowOffset = if (n < 0) switch (top.backwardOverflow(@intCast(-n))) {
.offset => |v| v, .offset => |v| v,
.overflow => |v| v.end, .overflow => |v| v.end,
} else forward: { } else switch (top.forwardOverflow(@intCast(n))) {
// Not super happy with the logic to scroll forward. I think
// this is pretty slow, but it is human-driven (scrolling
// this way) so hyper speed isn't strictly necessary. Still,
// it feels bad.
const forward_offset = switch (top.forwardOverflow(@intCast(n))) {
.offset => |v| v, .offset => |v| v,
.overflow => |v| v.end, .overflow => |v| v.end,
}; };
var final_offset: ?RowOffset = forward_offset; // If we are still within the active area, then we pin the
// viewport to active. This isn't EXACTLY the same behavior as
// Ensure we have at least rows rows in the viewport. There // other scrolling because normally when you scroll the viewport
// is probably a smarter way to do this. // is pinned to _that row_ even if new scrollback is created.
var page = self.pages.last.?; // But in a terminal when you get to the bottom and back into the
var rem = self.rows; // active area, you usually expect that the viewport will now
while (rem > page.data.size.rows) { // follow the active area.
rem -= page.data.size.rows; const active = self.getTopLeft(.active);
if (offset.page == active.page) {
// If we see our forward page here then we know its if (offset.row_offset >= active.row_offset) {
// beyond the active area and we can set final null. self.viewport = .{ .active = {} };
if (page == forward_offset.page) final_offset = null; break :delta_row;
}
page = page.prev.?; // assertion: we always have enough rows for active } else active: {
// Check forward pages too.
var page = active.page.next orelse break :active;
while (true) {
if (page == offset.page) {
self.viewport = .{ .active = {} };
break :delta_row;
}
page = page.next orelse break :active;
} }
const active_offset = .{ .page = page, .row_offset = page.data.size.rows - rem };
// If we have a final still and we're on the same page
// but the active area is before the forward area, then
// we can use the active area.
if (final_offset != null and
active_offset.page == forward_offset.page and
forward_offset.row_offset > active_offset.row_offset)
{
final_offset = active_offset;
} }
break :forward final_offset orelse active_offset;
};
self.viewport = .{ .exact = offset }; self.viewport = .{ .exact = offset };
}, },
@ -1100,6 +1089,25 @@ test "PageList scroll delta row forward into active" {
} }
} }
test "PageList scroll delta row back without space preserves active" {
const testing = std.testing;
const alloc = testing.allocator;
var s = try init(alloc, 80, 24, null);
defer s.deinit();
s.scroll(.{ .delta_row = -1 });
{
const pt = s.getCell(.{ .viewport = .{} }).?.screenPoint();
try testing.expectEqual(point.Point{ .screen = .{
.x = 0,
.y = 0,
} }, pt);
}
try testing.expect(s.viewport == .active);
}
test "PageList scroll clear" { test "PageList scroll clear" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; const alloc = testing.allocator;

View File

@ -1113,3 +1113,22 @@ test "Screen: scrolling" {
try testing.expectEqualStrings("2EFGH\n3IJKL", contents); try testing.expectEqualStrings("2EFGH\n3IJKL", contents);
} }
} }
test "Screen: scroll down from 0" {
const testing = std.testing;
const alloc = testing.allocator;
var s = try init(alloc, 10, 3, 0);
defer s.deinit();
try s.testWriteString("1ABCD\n2EFGH\n3IJKL");
// Scrolling up does nothing, but allows it
s.scroll(.{ .delta_row = -1 });
try testing.expect(s.pages.viewport == .active);
{
const contents = try s.dumpStringAlloc(alloc, .{ .viewport = .{} });
defer alloc.free(contents);
try testing.expectEqualStrings("1ABCD\n2EFGH\n3IJKL", contents);
}
}