widgets(textinput): add inserSliceAtCursor and sliceToCursor

Add two helps methods to textinput:

1. inserSliceAtCursor allows inserting a slice of bytes at the cursor
   position. This allows a program to insert a string at the cursor
   position and allow the internal state of the textinput to properly
   track where the cursor should be
2. sliceToCursor: allow users to obtain an (allocated) slice of the
   content from the beginning of the input to the cursor position
This commit is contained in:
Tim Culverhouse 2024-03-29 12:51:41 -05:00
parent da988cc8d9
commit ea6a4a714b

View file

@ -70,6 +70,33 @@ pub fn update(self: *TextInput, event: Event) !void {
} }
} }
/// insert text at the cursor position
pub fn insertSliceAtCursor(self: *TextInput, data: []const u8) !void {
var iter = GraphemeIterator.init(data);
var byte_offset_to_cursor = self.byteOffsetToCursor();
while (iter.next()) |text| {
try self.buf.insertSliceBefore(byte_offset_to_cursor, text.slice(data));
byte_offset_to_cursor += text.len;
self.cursor_idx += 1;
self.grapheme_count += 1;
}
}
pub fn sliceToCursor(self: *TextInput, buf: []u8) []const u8 {
const offset = self.byteOffsetToCursor();
assert(offset <= buf.len); // provided buf was too small
if (offset <= self.buf.items.len) {
@memcpy(buf[0..offset], self.buf.items[0..offset]);
} else {
@memcpy(buf[0..self.buf.items.len], self.buf.items);
const second_half = self.buf.secondHalf();
const copy_len = offset - self.buf.items.len;
@memcpy(buf[self.buf.items.len .. self.buf.items.len + copy_len], second_half[0..copy_len]);
}
return buf[0..offset];
}
pub fn draw(self: *TextInput, win: Window) void { pub fn draw(self: *TextInput, win: Window) void {
if (self.cursor_idx > self.prev_cursor_idx and if (self.cursor_idx > self.prev_cursor_idx and
self.prev_cursor_col > (win.width -| self.scroll_offset)) self.prev_cursor_col > (win.width -| self.scroll_offset))
@ -265,3 +292,15 @@ test "assertion" {
try input.update(.{ .key_press = astronaut_emoji }); try input.update(.{ .key_press = astronaut_emoji });
} }
} }
test "sliceToCursor" {
var alloc = std.testing.allocator_instance;
var input = init(alloc.allocator());
try input.insertSliceAtCursor("hello, world");
input.cursor_idx = 2;
var buf: [32]u8 = undefined;
try std.testing.expectEqualStrings("he", input.sliceToCursor(&buf));
input.buf.moveGap(3);
input.cursor_idx = 5;
try std.testing.expectEqualStrings("hello", input.sliceToCursor(&buf));
}