mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 16:56:09 +03:00
terminal/kitty-gfx: delete intersecting cursor
This commit is contained in:
@ -1021,11 +1021,7 @@ pub fn eraseDisplay(
|
|||||||
self.screen.cursor.pending_wrap = false;
|
self.screen.cursor.pending_wrap = false;
|
||||||
|
|
||||||
// Clear all Kitty graphics state for this screen
|
// Clear all Kitty graphics state for this screen
|
||||||
self.screen.kitty_images.delete(
|
self.screen.kitty_images.delete(alloc, self, .{ .all = true });
|
||||||
alloc,
|
|
||||||
&self.screen,
|
|
||||||
.{ .all = true },
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
.below => {
|
.below => {
|
||||||
|
@ -123,11 +123,9 @@ pub const ImageStorage = struct {
|
|||||||
pub fn delete(
|
pub fn delete(
|
||||||
self: *ImageStorage,
|
self: *ImageStorage,
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
screen: *const Screen,
|
t: *const terminal.Terminal,
|
||||||
cmd: command.Delete,
|
cmd: command.Delete,
|
||||||
) void {
|
) void {
|
||||||
_ = screen;
|
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
.all => |delete_images| if (delete_images) {
|
.all => |delete_images| if (delete_images) {
|
||||||
// We just reset our entire state.
|
// We just reset our entire state.
|
||||||
@ -161,6 +159,14 @@ pub const ImageStorage = struct {
|
|||||||
if (v.delete) self.deleteIfUnused(alloc, v.image_id);
|
if (v.delete) self.deleteIfUnused(alloc, v.image_id);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.intersect_cursor => |delete_images| {
|
||||||
|
const target = (point.Viewport{
|
||||||
|
.x = t.screen.cursor.x,
|
||||||
|
.y = t.screen.cursor.y,
|
||||||
|
}).toScreen(&t.screen);
|
||||||
|
self.deleteIntersecting(alloc, t, target, delete_images);
|
||||||
|
},
|
||||||
|
|
||||||
else => log.warn("unimplemented delete command: {}", .{cmd}),
|
else => log.warn("unimplemented delete command: {}", .{cmd}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -169,7 +175,9 @@ pub const ImageStorage = struct {
|
|||||||
fn deleteIfUnused(self: *ImageStorage, alloc: Allocator, image_id: u32) void {
|
fn deleteIfUnused(self: *ImageStorage, alloc: Allocator, image_id: u32) void {
|
||||||
var it = self.placements.iterator();
|
var it = self.placements.iterator();
|
||||||
while (it.next()) |kv| {
|
while (it.next()) |kv| {
|
||||||
if (kv.key_ptr.image_id == image_id) return;
|
if (kv.key_ptr.image_id == image_id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we get here, we can delete the image.
|
// If we get here, we can delete the image.
|
||||||
@ -179,6 +187,25 @@ pub const ImageStorage = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Deletes all placements intersecting a screen point.
|
||||||
|
fn deleteIntersecting(
|
||||||
|
self: *ImageStorage,
|
||||||
|
alloc: Allocator,
|
||||||
|
t: *const terminal.Terminal,
|
||||||
|
p: point.ScreenPoint,
|
||||||
|
delete_unused: bool,
|
||||||
|
) void {
|
||||||
|
var it = self.placements.iterator();
|
||||||
|
while (it.next()) |entry| {
|
||||||
|
const img = self.imageById(entry.key_ptr.image_id) orelse continue;
|
||||||
|
const sel = entry.value_ptr.selection(img, t);
|
||||||
|
if (sel.contains(p)) {
|
||||||
|
self.placements.removeByPtr(entry.key_ptr);
|
||||||
|
if (delete_unused) self.deleteIfUnused(alloc, img.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Every placement is uniquely identified by the image ID and the
|
/// Every placement is uniquely identified by the image ID and the
|
||||||
/// placement ID. If an image ID isn't specified it is assumed to be 0.
|
/// placement ID. If an image ID isn't specified it is assumed to be 0.
|
||||||
/// Likewise, if a placement ID isn't specified it is assumed to be 0.
|
/// Likewise, if a placement ID isn't specified it is assumed to be 0.
|
||||||
@ -269,7 +296,7 @@ test "storage: delete all placements and images" {
|
|||||||
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||||
try s.addPlacement(alloc, 2, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
try s.addPlacement(alloc, 2, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||||
|
|
||||||
s.delete(alloc, &t.screen, .{ .all = true });
|
s.delete(alloc, &t, .{ .all = true });
|
||||||
try testing.expect(s.dirty);
|
try testing.expect(s.dirty);
|
||||||
try testing.expectEqual(@as(usize, 0), s.images.count());
|
try testing.expectEqual(@as(usize, 0), s.images.count());
|
||||||
try testing.expectEqual(@as(usize, 0), s.placements.count());
|
try testing.expectEqual(@as(usize, 0), s.placements.count());
|
||||||
@ -289,7 +316,7 @@ test "storage: delete all placements" {
|
|||||||
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||||
try s.addPlacement(alloc, 2, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
try s.addPlacement(alloc, 2, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||||
|
|
||||||
s.delete(alloc, &t.screen, .{ .all = false });
|
s.delete(alloc, &t, .{ .all = false });
|
||||||
try testing.expect(s.dirty);
|
try testing.expect(s.dirty);
|
||||||
try testing.expectEqual(@as(usize, 0), s.placements.count());
|
try testing.expectEqual(@as(usize, 0), s.placements.count());
|
||||||
try testing.expectEqual(@as(usize, 3), s.images.count());
|
try testing.expectEqual(@as(usize, 3), s.images.count());
|
||||||
@ -309,7 +336,7 @@ test "storage: delete all placements by image id" {
|
|||||||
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||||
try s.addPlacement(alloc, 2, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
try s.addPlacement(alloc, 2, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||||
|
|
||||||
s.delete(alloc, &t.screen, .{ .id = .{ .image_id = 2 } });
|
s.delete(alloc, &t, .{ .id = .{ .image_id = 2 } });
|
||||||
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, 3), s.images.count());
|
try testing.expectEqual(@as(usize, 3), s.images.count());
|
||||||
@ -329,7 +356,7 @@ test "storage: delete all placements by image id and unused images" {
|
|||||||
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||||
try s.addPlacement(alloc, 2, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
try s.addPlacement(alloc, 2, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||||
|
|
||||||
s.delete(alloc, &t.screen, .{ .id = .{ .delete = true, .image_id = 2 } });
|
s.delete(alloc, &t, .{ .id = .{ .delete = true, .image_id = 2 } });
|
||||||
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());
|
||||||
@ -350,7 +377,7 @@ test "storage: delete placement by specific id" {
|
|||||||
try s.addPlacement(alloc, 1, 2, .{ .point = .{ .x = 1, .y = 1 } });
|
try s.addPlacement(alloc, 1, 2, .{ .point = .{ .x = 1, .y = 1 } });
|
||||||
try s.addPlacement(alloc, 2, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
try s.addPlacement(alloc, 2, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||||
|
|
||||||
s.delete(alloc, &t.screen, .{ .id = .{
|
s.delete(alloc, &t, .{ .id = .{
|
||||||
.delete = true,
|
.delete = true,
|
||||||
.image_id = 1,
|
.image_id = 1,
|
||||||
.placement_id = 2,
|
.placement_id = 2,
|
||||||
@ -359,3 +386,81 @@ test "storage: delete placement by specific id" {
|
|||||||
try testing.expectEqual(@as(usize, 2), s.placements.count());
|
try testing.expectEqual(@as(usize, 2), s.placements.count());
|
||||||
try testing.expectEqual(@as(usize, 3), s.images.count());
|
try testing.expectEqual(@as(usize, 3), s.images.count());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "storage: delete intersecting cursor" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var t = try terminal.Terminal.init(alloc, 100, 100);
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
t.width_px = 100;
|
||||||
|
t.height_px = 100;
|
||||||
|
|
||||||
|
var s: ImageStorage = .{};
|
||||||
|
defer s.deinit(alloc);
|
||||||
|
try s.addImage(alloc, .{ .id = 1, .width = 50, .height = 50 });
|
||||||
|
try s.addImage(alloc, .{ .id = 2, .width = 25, .height = 25 });
|
||||||
|
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 0, .y = 0 } });
|
||||||
|
try s.addPlacement(alloc, 1, 2, .{ .point = .{ .x = 25, .y = 25 } });
|
||||||
|
|
||||||
|
t.screen.cursor.x = 12;
|
||||||
|
t.screen.cursor.y = 12;
|
||||||
|
|
||||||
|
s.delete(alloc, &t, .{ .intersect_cursor = false });
|
||||||
|
try testing.expect(s.dirty);
|
||||||
|
try testing.expectEqual(@as(usize, 1), s.placements.count());
|
||||||
|
try testing.expectEqual(@as(usize, 2), s.images.count());
|
||||||
|
|
||||||
|
// verify the placement is what we expect
|
||||||
|
try testing.expect(s.placements.get(.{ .image_id = 1, .placement_id = 2 }) != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "storage: delete intersecting cursor plus unused" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var t = try terminal.Terminal.init(alloc, 100, 100);
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
t.width_px = 100;
|
||||||
|
t.height_px = 100;
|
||||||
|
|
||||||
|
var s: ImageStorage = .{};
|
||||||
|
defer s.deinit(alloc);
|
||||||
|
try s.addImage(alloc, .{ .id = 1, .width = 50, .height = 50 });
|
||||||
|
try s.addImage(alloc, .{ .id = 2, .width = 25, .height = 25 });
|
||||||
|
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 0, .y = 0 } });
|
||||||
|
try s.addPlacement(alloc, 1, 2, .{ .point = .{ .x = 25, .y = 25 } });
|
||||||
|
|
||||||
|
t.screen.cursor.x = 12;
|
||||||
|
t.screen.cursor.y = 12;
|
||||||
|
|
||||||
|
s.delete(alloc, &t, .{ .intersect_cursor = true });
|
||||||
|
try testing.expect(s.dirty);
|
||||||
|
try testing.expectEqual(@as(usize, 1), s.placements.count());
|
||||||
|
try testing.expectEqual(@as(usize, 2), s.images.count());
|
||||||
|
|
||||||
|
// verify the placement is what we expect
|
||||||
|
try testing.expect(s.placements.get(.{ .image_id = 1, .placement_id = 2 }) != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "storage: delete intersecting cursor hits multiple" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var t = try terminal.Terminal.init(alloc, 100, 100);
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
t.width_px = 100;
|
||||||
|
t.height_px = 100;
|
||||||
|
|
||||||
|
var s: ImageStorage = .{};
|
||||||
|
defer s.deinit(alloc);
|
||||||
|
try s.addImage(alloc, .{ .id = 1, .width = 50, .height = 50 });
|
||||||
|
try s.addImage(alloc, .{ .id = 2, .width = 25, .height = 25 });
|
||||||
|
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 0, .y = 0 } });
|
||||||
|
try s.addPlacement(alloc, 1, 2, .{ .point = .{ .x = 25, .y = 25 } });
|
||||||
|
|
||||||
|
t.screen.cursor.x = 26;
|
||||||
|
t.screen.cursor.y = 26;
|
||||||
|
|
||||||
|
s.delete(alloc, &t, .{ .intersect_cursor = true });
|
||||||
|
try testing.expect(s.dirty);
|
||||||
|
try testing.expectEqual(@as(usize, 0), s.placements.count());
|
||||||
|
try testing.expectEqual(@as(usize, 1), s.images.count());
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user