mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-25 13:16:11 +03:00
terminal2: delete kitty by intersecting cursor
This commit is contained in:
@ -1765,6 +1765,47 @@ pub const Pin = struct {
|
|||||||
return .{ .row = rac.row, .cell = rac.cell };
|
return .{ .row = rac.row, .cell = rac.cell };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if this pin is between the top and bottom, inclusive.
|
||||||
|
//
|
||||||
|
// Note: this is primarily unit tested as part of the Kitty
|
||||||
|
// graphics deletion code.
|
||||||
|
pub fn isBetween(self: Pin, top: Pin, bottom: Pin) bool {
|
||||||
|
if (comptime std.debug.runtime_safety) {
|
||||||
|
if (top.page == bottom.page) {
|
||||||
|
// If top is bottom, must be ordered.
|
||||||
|
assert(top.y <= bottom.y);
|
||||||
|
if (top.y == bottom.y) {
|
||||||
|
assert(top.x <= bottom.x);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If top is not bottom, top must be before bottom.
|
||||||
|
var page = top.page.next;
|
||||||
|
while (page) |p| : (page = p.next) {
|
||||||
|
if (p == bottom.page) break;
|
||||||
|
} else assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.page == top.page) {
|
||||||
|
if (self.y < top.y) return false;
|
||||||
|
if (self.y > top.y) return true;
|
||||||
|
return self.x >= top.x;
|
||||||
|
}
|
||||||
|
if (self.page == bottom.page) {
|
||||||
|
if (self.y > bottom.y) return false;
|
||||||
|
if (self.y < bottom.y) return true;
|
||||||
|
return self.x <= bottom.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
var page = top.page.next;
|
||||||
|
while (page) |p| : (page = p.next) {
|
||||||
|
if (p == bottom.page) break;
|
||||||
|
if (p == self.page) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// Move the pin down a certain number of rows, or return null if
|
/// Move the pin down a certain number of rows, or return null if
|
||||||
/// the pin goes beyond the end of the screen.
|
/// the pin goes beyond the end of the screen.
|
||||||
pub fn down(self: Pin, n: usize) ?Pin {
|
pub fn down(self: Pin, n: usize) ?Pin {
|
||||||
@ -1785,7 +1826,7 @@ pub const Pin = struct {
|
|||||||
|
|
||||||
/// Move the offset down n rows. If the offset goes beyond the
|
/// Move the offset down n rows. If the offset goes beyond the
|
||||||
/// end of the screen, return the overflow amount.
|
/// end of the screen, return the overflow amount.
|
||||||
fn downOverflow(self: Pin, n: usize) union(enum) {
|
pub fn downOverflow(self: Pin, n: usize) union(enum) {
|
||||||
offset: Pin,
|
offset: Pin,
|
||||||
overflow: struct {
|
overflow: struct {
|
||||||
end: Pin,
|
end: Pin,
|
||||||
@ -1823,7 +1864,7 @@ pub const Pin = struct {
|
|||||||
|
|
||||||
/// Move the offset up n rows. If the offset goes beyond the
|
/// Move the offset up n rows. If the offset goes beyond the
|
||||||
/// start of the screen, return the overflow amount.
|
/// start of the screen, return the overflow amount.
|
||||||
fn upOverflow(self: Pin, n: usize) union(enum) {
|
pub fn upOverflow(self: Pin, n: usize) union(enum) {
|
||||||
offset: Pin,
|
offset: Pin,
|
||||||
overflow: struct {
|
overflow: struct {
|
||||||
end: Pin,
|
end: Pin,
|
||||||
|
@ -6,6 +6,7 @@ const ArenaAllocator = std.heap.ArenaAllocator;
|
|||||||
|
|
||||||
const command = @import("graphics_command.zig");
|
const command = @import("graphics_command.zig");
|
||||||
const point = @import("../point.zig");
|
const point = @import("../point.zig");
|
||||||
|
const PageList = @import("../PageList.zig");
|
||||||
const internal_os = @import("../../os/main.zig");
|
const internal_os = @import("../../os/main.zig");
|
||||||
const stb = @import("../../stb/main.zig");
|
const stb = @import("../../stb/main.zig");
|
||||||
|
|
||||||
@ -451,16 +452,8 @@ pub const Image = struct {
|
|||||||
/// be rounded up to the nearest grid cell since we can't place images
|
/// be rounded up to the nearest grid cell since we can't place images
|
||||||
/// in partial grid cells.
|
/// in partial grid cells.
|
||||||
pub const Rect = struct {
|
pub const Rect = struct {
|
||||||
top_left: point.ScreenPoint = .{},
|
top_left: PageList.Pin,
|
||||||
bottom_right: point.ScreenPoint = .{},
|
bottom_right: PageList.Pin,
|
||||||
|
|
||||||
/// True if the rect contains a given screen point.
|
|
||||||
pub fn contains(self: Rect, p: point.ScreenPoint) bool {
|
|
||||||
return p.y >= self.top_left.y and
|
|
||||||
p.y <= self.bottom_right.y and
|
|
||||||
p.x >= self.top_left.x and
|
|
||||||
p.x <= self.bottom_right.x;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Easy base64 encoding function.
|
/// Easy base64 encoding function.
|
||||||
|
@ -242,12 +242,17 @@ pub const ImageStorage = struct {
|
|||||||
},
|
},
|
||||||
|
|
||||||
.intersect_cursor => |delete_images| {
|
.intersect_cursor => |delete_images| {
|
||||||
if (true) @panic("TODO");
|
self.deleteIntersecting(
|
||||||
const target = (point.Viewport{
|
alloc,
|
||||||
.x = t.screen.cursor.x,
|
t,
|
||||||
.y = t.screen.cursor.y,
|
.{ .active = .{
|
||||||
}).toScreen(&t.screen);
|
.x = t.screen.cursor.x,
|
||||||
self.deleteIntersecting(alloc, t, target, delete_images, {}, null);
|
.y = t.screen.cursor.y,
|
||||||
|
} },
|
||||||
|
delete_images,
|
||||||
|
{},
|
||||||
|
null,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
.intersect_cell => |v| {
|
.intersect_cell => |v| {
|
||||||
@ -378,18 +383,22 @@ pub const ImageStorage = struct {
|
|||||||
fn deleteIntersecting(
|
fn deleteIntersecting(
|
||||||
self: *ImageStorage,
|
self: *ImageStorage,
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
t: *const terminal.Terminal,
|
t: *terminal.Terminal,
|
||||||
p: point.ScreenPoint,
|
p: point.Point,
|
||||||
delete_unused: bool,
|
delete_unused: bool,
|
||||||
filter_ctx: anytype,
|
filter_ctx: anytype,
|
||||||
comptime filter: ?fn (@TypeOf(filter_ctx), Placement) bool,
|
comptime filter: ?fn (@TypeOf(filter_ctx), Placement) bool,
|
||||||
) void {
|
) void {
|
||||||
|
// Convert our target point to a pin for comparison.
|
||||||
|
const target_pin = t.screen.pages.pin(p) orelse return;
|
||||||
|
|
||||||
var it = self.placements.iterator();
|
var it = self.placements.iterator();
|
||||||
while (it.next()) |entry| {
|
while (it.next()) |entry| {
|
||||||
const img = self.imageById(entry.key_ptr.image_id) orelse continue;
|
const img = self.imageById(entry.key_ptr.image_id) orelse continue;
|
||||||
const rect = entry.value_ptr.rect(img, t);
|
const rect = entry.value_ptr.rect(img, t);
|
||||||
if (rect.contains(p)) {
|
if (target_pin.isBetween(rect.top_left, rect.bottom_right)) {
|
||||||
if (filter) |f| if (!f(filter_ctx, entry.value_ptr.*)) continue;
|
if (filter) |f| if (!f(filter_ctx, entry.value_ptr.*)) continue;
|
||||||
|
entry.value_ptr.deinit(t);
|
||||||
self.placements.removeByPtr(entry.key_ptr);
|
self.placements.removeByPtr(entry.key_ptr);
|
||||||
if (delete_unused) self.deleteIfUnused(alloc, img.id);
|
if (delete_unused) self.deleteIfUnused(alloc, img.id);
|
||||||
}
|
}
|
||||||
@ -547,13 +556,13 @@ pub const ImageStorage = struct {
|
|||||||
) Rect {
|
) Rect {
|
||||||
// If we have columns/rows specified we can simplify this whole thing.
|
// If we have columns/rows specified we can simplify this whole thing.
|
||||||
if (self.columns > 0 and self.rows > 0) {
|
if (self.columns > 0 and self.rows > 0) {
|
||||||
return .{
|
var br = switch (self.pin.downOverflow(self.rows)) {
|
||||||
.top_left = self.point,
|
.offset => |v| v,
|
||||||
.bottom_right = .{
|
.overflow => |v| v.end,
|
||||||
.x = @min(self.point.x + self.columns, t.cols - 1),
|
|
||||||
.y = self.point.y + self.rows,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
br.x = @min(self.pin.x + self.columns, t.cols - 1);
|
||||||
|
|
||||||
|
return .{ .top_left = self.pin.*, .bottom_right = br };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate our cell size.
|
// Calculate our cell size.
|
||||||
@ -574,12 +583,16 @@ pub const ImageStorage = struct {
|
|||||||
const width_cells: u32 = @intFromFloat(@ceil(width_f64 / cell_width_f64));
|
const width_cells: u32 = @intFromFloat(@ceil(width_f64 / cell_width_f64));
|
||||||
const height_cells: u32 = @intFromFloat(@ceil(height_f64 / cell_height_f64));
|
const height_cells: u32 = @intFromFloat(@ceil(height_f64 / cell_height_f64));
|
||||||
|
|
||||||
|
// TODO(paged-terminal): clean this logic up above
|
||||||
|
var br = switch (self.pin.downOverflow(height_cells)) {
|
||||||
|
.offset => |v| v,
|
||||||
|
.overflow => |v| v.end,
|
||||||
|
};
|
||||||
|
br.x = @min(self.pin.x + width_cells, t.cols - 1);
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.top_left = self.point,
|
.top_left = self.pin.*,
|
||||||
.bottom_right = .{
|
.bottom_right = br,
|
||||||
.x = @min(self.point.x + width_cells, t.cols - 1),
|
|
||||||
.y = self.point.y + height_cells,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -769,37 +782,38 @@ test "storage: delete placement by specific id" {
|
|||||||
try testing.expectEqual(tracked + 2, t.screen.pages.countTrackedPins());
|
try testing.expectEqual(tracked + 2, t.screen.pages.countTrackedPins());
|
||||||
}
|
}
|
||||||
|
|
||||||
// test "storage: delete intersecting cursor" {
|
test "storage: delete intersecting cursor" {
|
||||||
// const testing = std.testing;
|
const testing = std.testing;
|
||||||
// const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
// var t = try terminal.Terminal.init(alloc, 100, 100);
|
var t = try terminal.Terminal.init(alloc, 100, 100);
|
||||||
// defer t.deinit(alloc);
|
defer t.deinit(alloc);
|
||||||
// t.width_px = 100;
|
t.width_px = 100;
|
||||||
// t.height_px = 100;
|
t.height_px = 100;
|
||||||
//
|
const tracked = t.screen.pages.countTrackedPins();
|
||||||
// var s: ImageStorage = .{};
|
|
||||||
// defer s.deinit(alloc);
|
var s: ImageStorage = .{};
|
||||||
// try s.addImage(alloc, .{ .id = 1, .width = 50, .height = 50 });
|
defer s.deinit(alloc, &t);
|
||||||
// try s.addImage(alloc, .{ .id = 2, .width = 25, .height = 25 });
|
try s.addImage(alloc, .{ .id = 1, .width = 50, .height = 50 });
|
||||||
// try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 0, .y = 0 } });
|
try s.addImage(alloc, .{ .id = 2, .width = 25, .height = 25 });
|
||||||
// try s.addPlacement(alloc, 1, 2, .{ .point = .{ .x = 25, .y = 25 } });
|
try s.addPlacement(alloc, 1, 1, .{ .pin = try trackPin(&t, .{ .x = 0, .y = 0 }) });
|
||||||
//
|
try s.addPlacement(alloc, 1, 2, .{ .pin = try trackPin(&t, .{ .x = 25, .y = 25 }) });
|
||||||
// t.screen.cursor.x = 12;
|
|
||||||
// t.screen.cursor.y = 12;
|
t.screen.cursorAbsolute(12, 12);
|
||||||
//
|
|
||||||
// s.dirty = false;
|
s.dirty = false;
|
||||||
// s.delete(alloc, &t, .{ .intersect_cursor = false });
|
s.delete(alloc, &t, .{ .intersect_cursor = false });
|
||||||
// try testing.expect(s.dirty);
|
try testing.expect(s.dirty);
|
||||||
// try testing.expectEqual(@as(usize, 1), s.placements.count());
|
try testing.expectEqual(@as(usize, 1), s.placements.count());
|
||||||
// try testing.expectEqual(@as(usize, 2), s.images.count());
|
try testing.expectEqual(@as(usize, 2), s.images.count());
|
||||||
//
|
try testing.expectEqual(tracked + 1, t.screen.pages.countTrackedPins());
|
||||||
// // verify the placement is what we expect
|
|
||||||
// try testing.expect(s.placements.get(.{
|
// verify the placement is what we expect
|
||||||
// .image_id = 1,
|
try testing.expect(s.placements.get(.{
|
||||||
// .placement_id = .{ .tag = .external, .id = 2 },
|
.image_id = 1,
|
||||||
// }) != null);
|
.placement_id = .{ .tag = .external, .id = 2 },
|
||||||
// }
|
}) != null);
|
||||||
//
|
}
|
||||||
|
|
||||||
// test "storage: delete intersecting cursor plus unused" {
|
// test "storage: delete intersecting cursor plus unused" {
|
||||||
// const testing = std.testing;
|
// const testing = std.testing;
|
||||||
// const alloc = testing.allocator;
|
// const alloc = testing.allocator;
|
||||||
|
Reference in New Issue
Block a user