textTyped
Parameters
- self:*TextEntryWidget
- *TextEntryWidget
- new:[]const u8
- []const u8
- selected:bool
- bool
Source
Implementation
pub fn textTyped(self: *TextEntryWidget, new: []const u8, selected: bool) void {
// strip out carriage returns, which we get from copy/paste on windows
if (std.mem.findScalar(u8, new, '\r')) |idx| {
self.textTyped(new[0..idx], selected);
self.textTyped(new[idx + 1 ..], selected);
return;
}
var sel = self.textLayout.selectionGet(self.len);
if (!sel.empty()) {
// delete selection
self.textChangedRemoved(sel.start, sel.end);
@memmove(self.text[sel.start..][0 .. self.len - sel.end], self.text[sel.end..self.len]);
self.len -= (sel.end - sel.start);
sel.end = sel.start;
sel.cursor = sel.start;
}
const space_left = self.text.len - self.len;
if (space_left < new.len) {
var new_size = realloc_bin_size * (@divTrunc(self.len + new.len, realloc_bin_size) + 1);
switch (self.init_opts.text) {
.buffer => {},
.buffer_dynamic => |b| {
new_size = @min(new_size, b.limit);
b.backing.* = b.allocator.realloc(self.text, new_size) catch |err| blk: {
dvui.logError(@src(), err, "{x} TextEntryWidget.textTyped failed to realloc backing (current size {d}, new size {d})", .{ self.data().id, self.text.len, new_size });
break :blk b.backing.*;
};
self.text = b.backing.*;
},
.array_list => |al| {
new_size = @min(new_size, al.limit);
al.backing.ensureTotalCapacity(al.allocator, new_size) catch |err| {
dvui.logError(@src(), err, "{x} TextEntryWidget.textTyped failed to realloc ArrayList backing (current size {d}, new size {d})", .{ self.data().id, self.text.len, new_size });
};
self.text = al.backing.items.ptr[0..@min(al.limit, al.backing.capacity)];
},
.internal => |i| {
new_size = @min(new_size, i.limit);
// If we are the same size then there is no work to do
// This is important because same sized data allocations will be reused
if (new_size != self.text.len) {
// NOTE: Using prev_text is safe because data is trashed and stays valid until the end of the frame
const prev_text = self.text;
dvui.dataSetSliceCopies(null, self.data().id, "_buffer", &[_]u8{0}, new_size);
self.text = dvui.dataGetSlice(null, self.data().id, "_buffer", []u8).?;
const min_len = @min(prev_text.len, self.text.len);
if (self.text.ptr != prev_text.ptr) {
@memcpy(self.text[0..min_len], prev_text[0..min_len]);
}
}
},
}
}
var new_len = @min(new.len, self.text.len - self.len);
// find start of last utf8 char
var last: usize = new_len -| 1;
while (last < new_len and new[last] & 0xc0 == 0x80) {
last -|= 1;
}
// if the last utf8 char can't fit, don't include it
if (last < new_len) {
const utf8_size = std.unicode.utf8ByteSequenceLength(new[last]) catch 0;
if (utf8_size != (new_len - last)) {
new_len = last;
}
}
// make room if we can
if (new_len > 0 and sel.cursor + new_len < self.text.len) {
@memmove(self.text[sel.cursor + new_len ..][0 .. self.len - sel.cursor], self.text[sel.cursor..self.len]);
}
if (new_len > 0) {
self.textChangedAdded(sel.cursor, new_len);
}
// update our len and maintain 0 termination if possible
self.setLen(self.len + new_len);
// insert
@memmove(self.text[sel.cursor..][0..new_len], new[0..new_len]);
if (selected) {
sel.start = sel.cursor;
sel.cursor += new_len;
sel.end = sel.cursor;
} else {
sel.cursor += new_len;
sel.end = sel.cursor;
sel.start = sel.cursor;
}
if (std.mem.findScalar(u8, new[0..new_len], '\n') != null) {
sel.affinity = .after;
}
// we might have dropped to a new line, so make sure the cursor is visible
self.textLayout.scroll_to_cursor_next_frame = true;
dvui.refresh(null, @src(), self.data().id);
}