From 9d8dd7511150ca25ae42ae898545a5c0a3d5b729 Mon Sep 17 00:00:00 2001 From: Tim Culverhouse Date: Wed, 18 Dec 2024 10:02:26 -0600 Subject: [PATCH] window: fix negative offset clip Accumulate any negative offsets in order to properly clip windows to their parents. Previously, we only clipped if the window would be off the screen from a negative offset. In the diagram below, we should expect that if B is a child of A, it can't print in the non-overlapping area. That is, B can only print in the overlapping region with A because it is a child of A. This patch fixes this. +-----------------------------------------------------+ | | | +--------+ | | | B | | | | +---|-----+ | | | | | | | | +--------+ | | | | | | | | A | | | +---------+ | | | | | | | +-----------------------------------------------------+ --- src/Vaxis.zig | 2 ++ src/Window.zig | 58 ++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/src/Vaxis.zig b/src/Vaxis.zig index a32cf28..4e3f420 100644 --- a/src/Vaxis.zig +++ b/src/Vaxis.zig @@ -205,6 +205,8 @@ pub fn window(self: *Vaxis) Window { return .{ .x_off = 0, .y_off = 0, + .parent_x_off = 0, + .parent_y_off = 0, .width = self.screen.width, .height = self.screen.height, .screen = &self.screen, diff --git a/src/Window.zig b/src/Window.zig index 712656b..5875f4c 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -9,10 +9,16 @@ const gw = @import("gwidth.zig"); const Window = @This(); -/// horizontal offset from the screen +/// absolute horizontal offset from the screen x_off: i17, -/// vertical offset from the screen +/// absolute vertical offset from the screen y_off: i17, +/// relative horizontal offset, from parent window. This only accumulates if it is negative so that +/// we can clip the window correctly +parent_x_off: i17, +/// relative vertical offset, from parent window. This only accumulates if it is negative so that +/// we can clip the window correctly +parent_y_off: i17, /// width of the window. This can't be larger than the terminal screen width: u16, /// height of the window. This can't be larger than the terminal screen @@ -36,6 +42,8 @@ fn initChild( return Window{ .x_off = x_off + self.x_off, .y_off = y_off + self.y_off, + .parent_x_off = @min(self.parent_x_off + x_off, 0), + .parent_y_off = @min(self.parent_y_off + y_off, 0), .width = width, .height = height, .screen = self.screen, @@ -165,9 +173,14 @@ pub fn child(self: Window, opts: ChildOptions) Window { /// writes a cell to the location in the window pub fn writeCell(self: Window, col: u16, row: u16, cell: Cell) void { - if (self.height <= row or self.width <= col) return; - if (self.x_off + col < 0) return; - if (self.y_off + row < 0) return; + if (self.height <= row or + self.width <= col or + self.x_off + col < 0 or + self.y_off + row < 0 or + self.parent_x_off + col < 0 or + self.parent_y_off + row < 0) + return; + self.screen.writeCell(@intCast(col + self.x_off), @intCast(row + self.y_off), cell); } @@ -176,7 +189,9 @@ pub fn readCell(self: Window, col: u16, row: u16) ?Cell { if (self.height <= row or self.width <= col or self.x_off + col < 0 or - self.y_off + row < 0) + self.y_off + row < 0 or + self.parent_x_off + col < 0 or + self.parent_y_off + row < 0) return null; return self.screen.readCell(@intCast(col + self.x_off), @intCast(row + self.y_off)); } @@ -465,6 +480,8 @@ test "Window size set" { var parent = Window{ .x_off = 0, .y_off = 0, + .parent_x_off = 0, + .parent_y_off = 0, .width = 20, .height = 20, .screen = undefined, @@ -479,6 +496,8 @@ test "Window size set too big" { var parent = Window{ .x_off = 0, .y_off = 0, + .parent_x_off = 0, + .parent_y_off = 0, .width = 20, .height = 20, .screen = undefined, @@ -493,6 +512,8 @@ test "Window size set too big with offset" { var parent = Window{ .x_off = 0, .y_off = 0, + .parent_x_off = 0, + .parent_y_off = 0, .width = 20, .height = 20, .screen = undefined, @@ -507,6 +528,8 @@ test "Window size nested offsets" { var parent = Window{ .x_off = 1, .y_off = 1, + .parent_x_off = 0, + .parent_y_off = 0, .width = 20, .height = 20, .screen = undefined, @@ -517,6 +540,25 @@ test "Window size nested offsets" { try std.testing.expectEqual(11, ch.y_off); } +test "Window offsets" { + var parent = Window{ + .x_off = 0, + .y_off = 0, + .parent_x_off = 0, + .parent_y_off = 0, + .width = 20, + .height = 20, + .screen = undefined, + }; + + const ch = parent.initChild(10, 10, 21, 21); + const ch2 = ch.initChild(-4, -4, null, null); + // Reading ch2 at row 0 should be null + try std.testing.expect(ch2.readCell(0, 0) == null); + // Should not panic us + ch2.writeCell(0, 0, undefined); +} + test "print: grapheme" { const alloc = std.testing.allocator_instance.allocator(); const unicode = try Unicode.init(alloc); @@ -525,6 +567,8 @@ test "print: grapheme" { const win: Window = .{ .x_off = 0, .y_off = 0, + .parent_x_off = 0, + .parent_y_off = 0, .width = 4, .height = 2, .screen = &screen, @@ -592,6 +636,8 @@ test "print: word" { const win: Window = .{ .x_off = 0, .y_off = 0, + .parent_x_off = 0, + .parent_y_off = 0, .width = 4, .height = 2, .screen = &screen,