mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 16:56:09 +03:00
terminal: Screen can hold selection
This commit is contained in:
@ -37,9 +37,11 @@ cursor: Cursor,
|
|||||||
/// The saved cursor
|
/// The saved cursor
|
||||||
saved_cursor: ?SavedCursor = null,
|
saved_cursor: ?SavedCursor = null,
|
||||||
|
|
||||||
/// The selection for this screen (if any).
|
/// The selection for this screen (if any). This MUST be a tracked selection
|
||||||
//selection: ?Selection = null,
|
/// otherwise the selection will become invalid. Instead of accessing this
|
||||||
selection: ?void = null,
|
/// directly to set it, use the `select` function which will assert and
|
||||||
|
/// automatically setup tracking.
|
||||||
|
selection: ?Selection = null,
|
||||||
|
|
||||||
/// The charset state
|
/// The charset state
|
||||||
charset: CharsetState = .{},
|
charset: CharsetState = .{},
|
||||||
@ -827,6 +829,32 @@ pub fn manualStyleUpdate(self: *Screen) !void {
|
|||||||
self.cursor.style_ref = &md.ref;
|
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
|
/// Returns the raw text associated with a selection. This will unwrap
|
||||||
/// soft-wrapped edges. The returned slice is owned by the caller and allocated
|
/// 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).
|
/// 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" {
|
test "Screen: selectAll" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
|
@ -125,8 +125,8 @@ pub fn tracked(self: *const Selection) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Convert this selection a tracked selection. It is asserted this is
|
/// Convert this selection a tracked selection. It is asserted this is
|
||||||
/// an untracked selection.
|
/// an untracked selection. The tracked selection is returned.
|
||||||
pub fn track(self: *Selection, s: *Screen) !void {
|
pub fn track(self: *const Selection, s: *Screen) !Selection {
|
||||||
assert(!self.tracked());
|
assert(!self.tracked());
|
||||||
|
|
||||||
// Track our pins
|
// Track our pins
|
||||||
@ -137,10 +137,13 @@ pub fn track(self: *Selection, s: *Screen) !void {
|
|||||||
const tracked_end = try s.pages.trackPin(end_pin);
|
const tracked_end = try s.pages.trackPin(end_pin);
|
||||||
errdefer s.pages.untrackPin(tracked_end);
|
errdefer s.pages.untrackPin(tracked_end);
|
||||||
|
|
||||||
self.bounds = .{ .tracked = .{
|
return .{
|
||||||
.start = tracked_start,
|
.bounds = .{ .tracked = .{
|
||||||
.end = tracked_end,
|
.start = tracked_start,
|
||||||
} };
|
.end = tracked_end,
|
||||||
|
} },
|
||||||
|
.rectangle = self.rectangle,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the top left point of the selection.
|
/// Returns the top left point of the selection.
|
||||||
|
Reference in New Issue
Block a user