terminal: Screen can hold selection

This commit is contained in:
Mitchell Hashimoto
2024-03-08 17:00:54 -08:00
parent 9b4ab0e209
commit 33e59707e2
2 changed files with 60 additions and 9 deletions

View File

@ -37,9 +37,11 @@ cursor: Cursor,
/// The saved cursor
saved_cursor: ?SavedCursor = null,
/// The selection for this screen (if any).
//selection: ?Selection = null,
selection: ?void = null,
/// The selection for this screen (if any). This MUST be a tracked selection
/// otherwise the selection will become invalid. Instead of accessing this
/// directly to set it, use the `select` function which will assert and
/// automatically setup tracking.
selection: ?Selection = null,
/// The charset state
charset: CharsetState = .{},
@ -827,6 +829,32 @@ pub fn manualStyleUpdate(self: *Screen) !void {
self.cursor.style_ref = &md.ref;
}
/// Set the selection to the given selection. If this is a tracked selection
/// then the screen will take overnship of the selection. If this is untracked
/// then the screen will convert it to tracked internally. This will automatically
/// untrack the prior selection (if any).
///
/// Set the selection to null to clear any previous selection.
///
/// This is always recommended over setting `selection` directly. Beyond
/// managing memory for you, it also performs safety checks that the selection
/// is always tracked.
pub fn select(self: *Screen, sel_: ?Selection) !void {
const sel = sel_ orelse {
if (self.selection) |*old| old.deinit(self);
self.selection = null;
return;
};
// If this selection is untracked then we track it.
const tracked_sel = if (sel.tracked()) sel else try sel.track(self);
errdefer if (!sel.tracked()) tracked_sel.deinit(self);
// Untrack prior selection
if (self.selection) |*old| old.deinit(self);
self.selection = tracked_sel;
}
/// Returns the raw text associated with a selection. This will unwrap
/// soft-wrapped edges. The returned slice is owned by the caller and allocated
/// using alloc, not the allocator associated with the screen (unless they match).
@ -4079,6 +4107,26 @@ test "Screen: resize more cols requiring a wide spacer head" {
}
}
test "Screen: select untracked" {
const testing = std.testing;
const alloc = testing.allocator;
var s = try init(alloc, 10, 10, 0);
defer s.deinit();
try s.testWriteString("ABC DEF\n 123\n456");
try testing.expect(s.selection == null);
const tracked = s.pages.countTrackedPins();
try s.select(Selection.init(
s.pages.pin(.{ .active = .{ .x = 0, .y = 0 } }).?,
s.pages.pin(.{ .active = .{ .x = 3, .y = 0 } }).?,
false,
));
try testing.expectEqual(tracked + 2, s.pages.countTrackedPins());
try s.select(null);
try testing.expectEqual(tracked, s.pages.countTrackedPins());
}
test "Screen: selectAll" {
const testing = std.testing;
const alloc = testing.allocator;

View File

@ -125,8 +125,8 @@ pub fn tracked(self: *const Selection) bool {
}
/// Convert this selection a tracked selection. It is asserted this is
/// an untracked selection.
pub fn track(self: *Selection, s: *Screen) !void {
/// an untracked selection. The tracked selection is returned.
pub fn track(self: *const Selection, s: *Screen) !Selection {
assert(!self.tracked());
// Track our pins
@ -137,10 +137,13 @@ pub fn track(self: *Selection, s: *Screen) !void {
const tracked_end = try s.pages.trackPin(end_pin);
errdefer s.pages.untrackPin(tracked_end);
self.bounds = .{ .tracked = .{
.start = tracked_start,
.end = tracked_end,
} };
return .{
.bounds = .{ .tracked = .{
.start = tracked_start,
.end = tracked_end,
} },
.rectangle = self.rectangle,
};
}
/// Returns the top left point of the selection.