terminal: setScrollingRegion

This commit is contained in:
Mitchell Hashimoto
2022-05-11 08:52:22 -07:00
parent 2c0fbfccc6
commit 5a85546b2e
2 changed files with 75 additions and 6 deletions

View File

@ -534,15 +534,15 @@ pub fn setCursorUp(self: *Window, amount: u16) !void {
} }
pub fn setCursorCol(self: *Window, col: u16) !void { pub fn setCursorCol(self: *Window, col: u16) !void {
try self.terminal.setCursorPos(self.terminal.cursor.y + 1, col); self.terminal.setCursorPos(self.terminal.cursor.y + 1, col);
} }
pub fn setCursorRow(self: *Window, row: u16) !void { pub fn setCursorRow(self: *Window, row: u16) !void {
try self.terminal.setCursorPos(row, self.terminal.cursor.x + 1); self.terminal.setCursorPos(row, self.terminal.cursor.x + 1);
} }
pub fn setCursorPos(self: *Window, row: u16, col: u16) !void { pub fn setCursorPos(self: *Window, row: u16, col: u16) !void {
try self.terminal.setCursorPos(row, col); self.terminal.setCursorPos(row, col);
} }
pub fn eraseDisplay(self: *Window, mode: terminal.EraseDisplay) !void { pub fn eraseDisplay(self: *Window, mode: terminal.EraseDisplay) !void {

View File

@ -28,10 +28,21 @@ tabstops: Tabstops,
rows: usize, rows: usize,
cols: usize, cols: usize,
/// The current scrolling region.
scrolling_region: ScrollingRegion,
/// Screen represents a presentable terminal screen made up of lines and cells. /// Screen represents a presentable terminal screen made up of lines and cells.
const Screen = std.ArrayListUnmanaged(Line); const Screen = std.ArrayListUnmanaged(Line);
const Line = std.ArrayListUnmanaged(Cell); const Line = std.ArrayListUnmanaged(Cell);
/// Scrolling region is the area of the screen designated where scrolling
/// occurs. Wen scrolling the screen, only this viewport is scrolled.
const ScrollingRegion = struct {
// Precondition: top < bottom
top: usize,
bottom: usize,
};
/// Cell is a single cell within the terminal. /// Cell is a single cell within the terminal.
const Cell = struct { const Cell = struct {
/// Each cell contains exactly one character. The character is UTF-8 encoded. /// Each cell contains exactly one character. The character is UTF-8 encoded.
@ -64,6 +75,10 @@ pub fn init(alloc: Allocator, cols: usize, rows: usize) !Terminal {
.screen = .{}, .screen = .{},
.cursor = .{ .x = 0, .y = 0 }, .cursor = .{ .x = 0, .y = 0 },
.tabstops = try Tabstops.init(alloc, cols, 8), .tabstops = try Tabstops.init(alloc, cols, 8),
.scrolling_region = .{
.top = 0,
.bottom = rows - 1,
},
}; };
} }
@ -151,7 +166,7 @@ pub fn reverseIndex(self: *Terminal, alloc: Allocator) !void {
// the right-most column. If row is 0, it is adjusted to 1. If row is // the right-most column. If row is 0, it is adjusted to 1. If row is
// greater than the bottom-most row it is adjusted to the bottom-most // greater than the bottom-most row it is adjusted to the bottom-most
// row. // row.
pub fn setCursorPos(self: *Terminal, row: usize, col: usize) !void { pub fn setCursorPos(self: *Terminal, row: usize, col: usize) void {
self.cursor.x = @minimum(self.cols, col) -| 1; self.cursor.x = @minimum(self.cols, col) -| 1;
self.cursor.y = @minimum(self.rows, row) -| 1; self.cursor.y = @minimum(self.rows, row) -| 1;
} }
@ -427,6 +442,34 @@ pub fn scrollDown(self: *Terminal, alloc: Allocator) !void {
self.screen.items[0] = .{}; self.screen.items[0] = .{};
} }
/// Set Top and Bottom Margins If bottom is not specified, 0 or bigger than
/// the number of the bottom-most row, it is adjusted to the number of the
/// bottom most row.
///
/// If top < bottom set the top and bottom row of the scroll region according
/// to top and bottom and move the cursor to the top-left cell of the display
/// (when in cursor origin mode is set to the top-left cell of the scroll region).
///
/// Otherwise: Set the top and bottom row of the scroll region to the top-most
/// and bottom-most line of the screen.
///
/// Top and bottom are 1-indexed.
pub fn setScrollingRegion(self: *Terminal, top: usize, bottom: usize) void {
var t = if (top == 0) 1 else top;
var b = @minimum(bottom, self.rows);
if (t >= b) {
t = 1;
b = self.rows;
}
self.scrolling_region = .{
.top = t - 1,
.bottom = b - 1,
};
self.setCursorPos(1, 1);
}
fn getOrPutCell(self: *Terminal, alloc: Allocator, x: usize, y: usize) !*Cell { fn getOrPutCell(self: *Terminal, alloc: Allocator, x: usize, y: usize) !*Cell {
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
@ -520,12 +563,38 @@ test "Terminal: setCursorPosition" {
try testing.expectEqual(@as(usize, 0), t.cursor.y); try testing.expectEqual(@as(usize, 0), t.cursor.y);
// Setting it to 0 should keep it zero (1 based) // Setting it to 0 should keep it zero (1 based)
try t.setCursorPos(0, 0); t.setCursorPos(0, 0);
try testing.expectEqual(@as(usize, 0), t.cursor.x); try testing.expectEqual(@as(usize, 0), t.cursor.x);
try testing.expectEqual(@as(usize, 0), t.cursor.y); try testing.expectEqual(@as(usize, 0), t.cursor.y);
// Should clamp to size // Should clamp to size
try t.setCursorPos(81, 81); t.setCursorPos(81, 81);
try testing.expectEqual(@as(usize, 79), t.cursor.x); try testing.expectEqual(@as(usize, 79), t.cursor.x);
try testing.expectEqual(@as(usize, 79), t.cursor.y); try testing.expectEqual(@as(usize, 79), t.cursor.y);
} }
test "Terminal: setScrollingRegion" {
var t = try init(testing.allocator, 80, 80);
defer t.deinit(testing.allocator);
// Initial value
try testing.expectEqual(@as(usize, 0), t.scrolling_region.top);
try testing.expectEqual(@as(usize, t.rows - 1), t.scrolling_region.bottom);
// Move our cusor so we can verify we move it back
t.setCursorPos(5, 5);
t.setScrollingRegion(3, 7);
// Cursor should move back to top-left
try testing.expectEqual(@as(usize, 0), t.cursor.x);
try testing.expectEqual(@as(usize, 0), t.cursor.y);
// Scroll region is set
try testing.expectEqual(@as(usize, 2), t.scrolling_region.top);
try testing.expectEqual(@as(usize, 6), t.scrolling_region.bottom);
// Scroll region invalid
t.setScrollingRegion(7, 3);
try testing.expectEqual(@as(usize, 0), t.scrolling_region.top);
try testing.expectEqual(@as(usize, t.rows - 1), t.scrolling_region.bottom);
}